Hash函数及其应用

本文部分内容摘自网络,参考资料链接会在文后给出,在此感谢原作者的分享。

计算理论中,没有Hash函数的说法,只有单向函数的说法。所谓的单向函数,是一个复杂的定义,大家可以去看计算理论或者密码学方面的数据。用“人类”的语言描述,单向函数就是:如果某个函数在给定输入的时候,很容易计算出其结果来;而当给定结果的时候,很难计算出输入来,这就是单向函数。各种加密函数都可以被认为是单向函数的逼近。Hash函数(或者称为散列函数)也可以看成是单向函数的一个逼近。即它接近于满足单向函数的定义。 

Hash函数还有另外的含义。实际中的Hash函数是指把一个大范围映射到一个小范围。把大范围映射到一个小范围的目的往往是为了节省空间,使得数据容易保存。除此以外,Hash函数往往应用于查找上。所以,在考虑使用Hash函数之前,需要明白它的几个限制:

  • Hash的主要原理就是把大范围映射到小范围;所以,你输入的实际值的个数必须和小范围相当或者比它更小。不然冲突就会很多。
  • 由于Hash逼近单向函数;所以,你可以用它来对数据进行加密。
  • 不同的应用对Hash函数有着不同的要求;比如,用于加密的Hash函数主要考虑它和单项函数的差距,而用于查找的Hash函数主要考虑它映射到小范围的冲突率。

由于实现了Hash的数据结构支持随机读取(即直接定位,而不需要涉及各类查找算法),检索效率非常高,成为了很多存储引擎的首选,著名的有redis、memcache等,但是Hash的特性决定了一些应用场景下的不足:

  • Hash 索引仅仅能满足"=","IN"和"<=>"查询,不能使用范围查询。
  • Hash 索引无法被用来避免数据的排序操作。(即Hash函数并不会自排序,相对的如B树,本身带有排序信息,在节点增删改时按规则维护)
  • Hash 索引不能利用部分索引键查询。

稍加扩展的话,我们还可以将Hash应用在各种数据分布式技术中,这方面说的比较多的是“一致性哈希算法”,著名的开源分布式NoSQL数据库系统Cassandra就应用了这一算法。

对于数据检索的低层面应用,主要是各类集合类型。在设计相关类型时,要考虑适当的Hash算法,考虑因素主要是以下几个方面:

  • 计算Hash值所需的时间。
  • Hash表长度。
  • Hash值分布情况。
  • 数据的查找频率。
  • Hash值冲突(重复)的概率。

冲突解决技术可分为两大类:开散列法(又称为链地址法)和闭散列法(又称为开发地址法)。可假设实现Hash结构时,数据存放在预先用数组实现的一片连续的地址空间,两种冲突解决技术的区别在于发生冲突的元素是存储在这片数组的空间之外(开散列法,一般为附加链表形式)还是空间之内(闭散列法)。与闭散列法相比,开散列法有如下优缺点:

  • 开散列法处理冲突无二次聚集现象,因此平均查找时间较短。
  • 由于开散列法中各链表上的节点空间是动态申请的,因此适合无法确定表长的情况。
  • 指针需要额外空间,故当记录规模较小时,闭散列法较为节省空间。
  • 在.NET中,链表的各个元素分散于托管堆各处,这会给垃圾回收带来压力,影响程序性能。

在C#中,实现了Hash函数的集合类我知道的有两个:System.Collections.Hashtable和System.Collections.Generic.Dictionary<TKey,TValue>,这两者区别如下:

  • Hashtable采用闭散列法来解决冲突,而Dictionary采用开散列法来解决冲突。
  • Hashtable在空间不够时,会自动扩容,在扩容时会重新计算所有元素的哈希码和哈希地址,会消耗大量时间进行计算,Dictionary不存在这个问题(自然Dictionary在空间不够时也要开辟新的空间,不过不需要重新计算和安排原有数据的哈希值和哈希地址,这方面内容可看Dictionary的源码便一清二楚了。
  • Hashtable的线程安全包含几个层次,默认可由多个读取器线程一个写入线程使用;若要允许多线程写入(在没有线程读取的情况下),则需要通过Synchronized方法返回的包装完成;如果使用一个或多个读取器以及一个或多个编写器,则Synchronized包装不提供线程安全的访问,此时应使用SyncRoot锁定集合。(MSDN说了这么多,然后告诉我说Hashtable是线程安全的,难道不是在玩我?)Dictionary没这么复杂,只要Lock(SyncRoot)即可。

ps:关于NoSql,文中涉及了若干NoSql数据库,博主就在此简单说下对NoSql的一些个人见解。现在NoSql可谓风生水起,恰如当年web2.0、ajax刚兴起的时候,其实都不是非常高深的技术,但却能打破传统,一领风骚好多年,所以说技术是其次,思想才是最重要的。ok扯远了,NoSql和Sql存储引擎差不多,总归就那么几种,文中说到的Hash是一种,B数是一种,还有LSM树之类的,顶多在局部稍作改进以适应场景。它们真正的区别在于,NoSql不必非常顾忌数据库范式的约束,从而极大提高了读写速度和扩展能力,比如写操作不care事务,在每秒写几万几十万的数据量下,光这点就能甩Sql几条街。可以说各类NoSql的争奇斗艳,其实都是以取消或部分取消数据库范式的约束为前提,看似很小的改变,能换来性能的巨大提升,当然这也伴随着数据冗余、安全性不高等Sqls深恶痛绝的问题。上帝总是公平的,任何事物都没有绝对的好坏,就看你把它们用在什么地方。

参考资料:

Hash函数的几种

一致性哈希算法应用及优化(最简洁明了的教程)

三种基本的存储引擎比较

NoSQL数据库探讨之一 - 为什么要用非关系数据库?

 

转载请注明本文出处:http://www.cnblogs.com/newton/p/4561273.html

上一篇:C中const用法


下一篇:谈谈Scala的抽取器(Extractor)