关于 Hash 函数
问题起源于为数据库中的记录定义主键,显然,一般总能找到一个或几个属性能够作为记录的唯一标志。但如果能够用一个整数来作为主键,那么在进行连接查询时效率显然要更高。如果这个整数是根据记录的主键属性计算而得到的一个值,那么就更妙了。因为,现在的问题就是,要寻找一个合适的哈希函数,生成 64位的哈希值,要求非常小(几乎没有)的碰撞。
为什么不采用数据库的自增长字段(例如Oracle 的系列,MsSql的autoincrement)?自增长类型的主要问题在于,主键值是由数据库顺序分配的,因此,在这个值被分配前,无法知道它的值,而且,这个值和记录(或者说对象)本身没有任何关系,如果某条记录被删除,然后重新插入,那么它将被分配一个新的ID,无论如何也无法得到原来的那个ID值了,这从某种角度上来说,有点奇怪。另外,考虑数据采集的情况,数据库表A中原有100W条记录,每个都被分配了ID,现在新采集到了100W条记录到表B,那么要在这两个之间建立对应关系,就必须不得不用那些主键属性进行关联查询,但对于B表中那些没有A中对应记录的数据怎么办?让他们的ID为空值吗?
采用Hash值作为ID,显然可以很好的解决这个问题,不论在什么情况下,我们总能够非常容易的计算得到一个ID,而且,这个 ID 是跟对象的标志相关的,也就是说,只要是同一个对象,总是能计算到同样的ID值。
采用MD5、SHA等哈希算法来计算这个哈希值是一个最自然不过的思路了,不过问题是,这些健壮的哈希算法的输出一般都至少是128位,而数据库、以及编程语言,表示128位值都很不直观,目前似乎还没有什么语言或者数据库将Int128作为标准类型。
Int64到是一个非常常用的类型,不论是数据库,还是编程语言,都有很好的支持。但如何得到一个安全的64位哈希值能?综合考虑可用性以及可靠性,可以这样来设计这个ID值系统:
× 系统中维持一个对照表,用来记录一个对象的键值与他的哈希ID值之间的对应关系,这里的哈希ID,保留两个,一个取它的MD5值的后64位,一个取它的SHA值的后64位
× 在其他记录表中,使用MD5的后64位作为ID值。因为这里只选择了MD5的后64位,因此发生碰撞的可能性显然比128位要大很多了。
× 假设两个ID现在发生了冲突,考察他们对照表的对应项,他们应该有相同的MD5后64位,但应该有不同的SHA后64位(两个后64位都发生碰撞?这个概率也太小了),那么,我们可以用这两个不同的SHA后64位作为他们的ID,这样就解决了一个碰撞。
× 假设这个SHA ID值也正好被系统使用了,那只能说人品不好了
找到的一些资料:
http://www.serve.net/buz/hash.adt/java.002.html 这里介绍了几个简单的哈希函数,如 CBUhash PKPhash BUZhash 等
http://www.cris.com/~ttwang/tech/inthash.htm 这篇文章介绍了 Knuth ,Robert Jenkins 的哈希函数
http://burtleburtle.net/bob/hash/evahash.html 这是 Robert Jenkins 的论文
http://www.isthe.com/chongo/tech/comp/fnv/index.html 这是一种称为是 FNV 的哈希函数
http://isthe.com/chongo/src/fnv/hash_64.c 上述 FNV 64位哈希函数的源代码
http://en.wikipedia.org/wiki/Category:Cryptographic_hash_functions wikipedia 关于哈希函数的页面,东东不少
为什么不采用数据库的自增长字段(例如Oracle 的系列,MsSql的autoincrement)?自增长类型的主要问题在于,主键值是由数据库顺序分配的,因此,在这个值被分配前,无法知道它的值,而且,这个值和记录(或者说对象)本身没有任何关系,如果某条记录被删除,然后重新插入,那么它将被分配一个新的ID,无论如何也无法得到原来的那个ID值了,这从某种角度上来说,有点奇怪。另外,考虑数据采集的情况,数据库表A中原有100W条记录,每个都被分配了ID,现在新采集到了100W条记录到表B,那么要在这两个之间建立对应关系,就必须不得不用那些主键属性进行关联查询,但对于B表中那些没有A中对应记录的数据怎么办?让他们的ID为空值吗?
采用Hash值作为ID,显然可以很好的解决这个问题,不论在什么情况下,我们总能够非常容易的计算得到一个ID,而且,这个 ID 是跟对象的标志相关的,也就是说,只要是同一个对象,总是能计算到同样的ID值。
采用MD5、SHA等哈希算法来计算这个哈希值是一个最自然不过的思路了,不过问题是,这些健壮的哈希算法的输出一般都至少是128位,而数据库、以及编程语言,表示128位值都很不直观,目前似乎还没有什么语言或者数据库将Int128作为标准类型。
Int64到是一个非常常用的类型,不论是数据库,还是编程语言,都有很好的支持。但如何得到一个安全的64位哈希值能?综合考虑可用性以及可靠性,可以这样来设计这个ID值系统:
× 系统中维持一个对照表,用来记录一个对象的键值与他的哈希ID值之间的对应关系,这里的哈希ID,保留两个,一个取它的MD5值的后64位,一个取它的SHA值的后64位
× 在其他记录表中,使用MD5的后64位作为ID值。因为这里只选择了MD5的后64位,因此发生碰撞的可能性显然比128位要大很多了。
× 假设两个ID现在发生了冲突,考察他们对照表的对应项,他们应该有相同的MD5后64位,但应该有不同的SHA后64位(两个后64位都发生碰撞?这个概率也太小了),那么,我们可以用这两个不同的SHA后64位作为他们的ID,这样就解决了一个碰撞。
× 假设这个SHA ID值也正好被系统使用了,那只能说人品不好了
找到的一些资料:
http://www.serve.net/buz/hash.adt/java.002.html 这里介绍了几个简单的哈希函数,如 CBUhash PKPhash BUZhash 等
http://www.cris.com/~ttwang/tech/inthash.htm 这篇文章介绍了 Knuth ,Robert Jenkins 的哈希函数
http://burtleburtle.net/bob/hash/evahash.html 这是 Robert Jenkins 的论文
http://www.isthe.com/chongo/tech/comp/fnv/index.html 这是一种称为是 FNV 的哈希函数
http://isthe.com/chongo/src/fnv/hash_64.c 上述 FNV 64位哈希函数的源代码
http://en.wikipedia.org/wiki/Category:Cryptographic_hash_functions wikipedia 关于哈希函数的页面,东东不少