python如何调用和nebula相同的hash函数算法

  • nebula 版本:3.0.1
  • 部署方式:单机,windows
  • 安装方式:Docker
  • 是否为线上版本:Y
  • 硬件信息
    • 磁盘:SSD
    • CPU、内存信息:4核,16G
  • 问题的具体描述

    你好,请教一下,VID使用的字符串类型。
    我的数据中个别记录的主键特别长,但绝大多数记录的主键都很短,想使用hash 编码的方式生成。
    python中如何调用和nebula相同的hash算法。

现在确实没有现成的 mmh2 64 位的 python 包
只能找到 mmh3 和 mmh2 32 bit 的 :sob:

mmh3 和 mmh2 32 bit 是否可以实现生成的hash值和nebula的相同?或者对于个别主键特别长的问题有其他解决方法?

  1. yield hash(xxx) 要求远端算一下
  2. 参考种子自己实现下,和 yield hash(xxx)对比

python的随机种子是10进制int类型的,“0xc70f6907UL”代表的是什么类型的数据?是不是可以转成10进制int类型?

Python 里16进制数字表示和 c 是一样的,你可以直接用它。

$ python
Python 3.8.10 (default, Mar 15 2022, 12:22:08)
>>> 0xc70f6907
3339675911

Python 里现成的库不支持 mmh2 64 bit,所以不能和 nebula 服务端一致:sob:

不过如吴老师提到,如果允许你可以从 nebulaclient 直接 return hash(“foo”) 请求 nebula 的 hash。

如果量大的话,可以考虑自己去实现这个 hash。

后边我有时间可以研究做一个出来,不过短期有点忙。

1 个赞

好的 我试一下 十分感谢

搞出来了一个能用的

def bytes_to_long(bytes):
    assert len(bytes) == 8
    return sum((b << (k * 8) for k, b in enumerate(bytes)))


def murmur64(data, seed = 0xc70f6907):

    import ctypes

    m = ctypes.c_uint64(0xc6a4a7935bd1e995).value

    r = ctypes.c_uint32(47).value

    MASK = ctypes.c_uint64(2 ** 64 - 1).value

    data_as_bytes = bytearray(data)

    seed = ctypes.c_uint64(seed).value

    h = seed ^ ((m * len(data_as_bytes)) & MASK)

    off = int(len(data_as_bytes)/8)*8
    for ll in range(0, off, 8):
        k = bytes_to_long(data_as_bytes[ll:ll + 8])
        k = (k * m) & MASK
        k = k ^ ((k >> r) & MASK)
        k = (k * m) & MASK
        h = (h ^ k)
        h = (h * m) & MASK

    l = len(data_as_bytes) & 7

    if l >= 7:
        h = (h ^ (data_as_bytes[off+6] << 48))

    if l >= 6:
        h = (h ^ (data_as_bytes[off+5] << 40))

    if l >= 5:
        h = (h ^ (data_as_bytes[off+4] << 32))

    if l >= 4:
        h = (h ^ (data_as_bytes[off+3] << 24))

    if l >= 3:
        h = (h ^ (data_as_bytes[off+2] << 16))

    if l >= 2:
        h = (h ^ (data_as_bytes[off+1] << 8))

    if l >= 1:
        h = (h ^ data_as_bytes[off])
        h = (h * m) & MASK

    h = h ^ ((h >> r) & MASK)
    h = (h * m) & MASK
    h = h ^ ((h >> r) & MASK)

    return ctypes.c_long(h).value

$ python
>>> print(str(murmur64(bytes("to_be_hashed", encoding = "utf8"), seed=0xc70f6907)))

-1098333533029391540
(root@nebula) [(none)]> YIELD hash("to_be_hashed");
+----------------------+
| hash("to_be_hashed") |
+----------------------+
| -1098333533029391540 |
+----------------------+
Got 1 rows (time spent 550/6257 us)

Ref: hash - Is there a pure python implementation of MurmurHash? - Stack Overflow

放在了 gist 上

https://gist.github.com/wey-gu/5543c33987c0a5e8f7474b9b80cd36aa

谢谢

追求性能还是用 rust/cpp 打包成 python module

你好,我这边运行出来的结果是 -1897782452

是 python3么?

是的,3.7,和3.8都试了一下。

嗯嗯,我是在 3.8.10/3.9.12 上从帖子上复制再执行都是这个期待中的值

你在windows下装的python是64 bit 的么?

是的,我试过了在linux上是没问题的,应该是跟系统有关

嗯嗯,还不是位数的问题,有意思,好奇是哪一步结果有差别,我没有环境,如果你感兴趣可以两边每一步print一下看看是哪里行为有差异

改一个方法就行了, ctypes.c_longlong(h)
return ctypes.c_longlong(h).value

啊哈,原来 c 里边win和unix的 long 的定义不同,cool

此话题已在最后回复的 7 天后被自动关闭。不再允许新回复。