ハッシュアルゴリズムの原理と応用トーク

 

著者:jeffhe、テンセントIEG開発エンジニア

ハッシュを述べたように、私は、学生のほとんどは、まだ火が今の技術の背後に火がいくつかの角度から、原則としてハッシュアルゴリズムや実用化に続いて、ハッシュブロック鎖にあるあるある基本原則の一つ前、不慣れではないと考えていますハッシュアルゴリズムを説明します。

 

1.ハッシュは何ですか

また、英語に対応するハッシュ、ハッシュ、として知られているハッシュは、ハッシュです。基本的な原理は、固定長出力にハッシュアルゴリズムにより、入力任意の長さです。このマッピング規則は、対応するハッシュアルゴリズムであり、バイナリ文字列の後に生データのハッシュ値をマッピングされます。MD5とSHA開発活動は、多くの場合、長い歴史のハッシュアルゴリズムで使用されています。

echo md5("这是一个测试文案");
// 输出结果:2124968af757ed51e71e6abeac04f98d

この例では、这是一个测试文案元の値、2124968af757ed51e71e6abeac04f98d ハッシュ・アルゴリズムのハッシュ値を介して、です。ハッシュアルゴリズムは、固定長の値の処理空間にマッピングされ、任意の長さの元の値の空間全体を処理することです。

 

2、ハッシュ機能

それを必要とする要件の種類良いハッシュアルゴリズム、?

  • A)は、元のデータ反転しないことがあり、ハッシュ値から導出
    明確MD5の上記の例から分かるように、データの後の地図データは、元に対応しません

  • B)は、入力データの小さな変化は、完全に異なるハッシュ値となり、同一のデータが同一の値となります

    echo md5("这是一个测试文案");
    // 输出结果:2124968af757ed51e71e6abeac04f98d
    echo md5("这是二个测试文案");
    // 输出结果:bcc2a4bb4373076d494b2223aef9f702
    

    私たちは単語のみを変更しましたが、全体の結果のハッシュ値が非常に大きな変化が生じていることがわかります。

  • C)、ハッシュアルゴリズムの効率は、効率的に、長いテキストを迅速にハッシュ値を算出することができます。

  • D)、衝突ハッシュアルゴリズムの確率が小さくなるように

    以来、入力空間のハッシュ値の原理は、ハッシュ空間にマッピングされ、スペースが入力空間のハッシュ値よりはるかに小さいです。引出しの原理によれば、ケースは、異なる入力が同じ出力にマッピングされるであろうがあります。だから、良いハッシュアルゴリズムとして、我々は可能な限り小さく、このような紛争の可能性を必要とします。

10りんごは、我々が劣らず2個のリンゴより置く少なくとも一つの引き出しがあることがわかります、9つの引き出しに入れてどんなにをこれら10りんごを取り、テーブルの上にあります。この現象は、私たちが呼んで「引き出しの原則。」引き出しの意味の一般原則:「そこのn + 1の要素である2つあり、少なくとも1セットを持っている必要があり、n個のセットに行けば、各引き出しの代表のセットは、各Appleは、要素を表すことができた場合要素。「引き出しの原理は、時には鳩の巣原理と呼ばれています。これは数学的な組み合わせの重要な原則であります

 

3、ハッシュ衝突ソリューション

言及した以前のハッシュアルゴリズムは、そうだとすれば、競合の必要性が対処するならば、我々はハッシュに遭遇したとき、それに対処する方法をする必要があり、競合が存在しますでしょうか?より一般的なアルゴリズムを使用链地址法して开放地址法

3.1チェーンの送付方法

リストのアドレスアレイは、遭遇したハッシュ衝突が連続して処理され、リストの後ろに追加されたときに、対応するデータを格納するためのリンクリストを使用することです。

メソッドの住所概略図チェーン

チェーンアドレス処理の流れは以下の通りである:
要素は、最初の配列の挿入位置を決定するために、キーのハッシュ値を算出する要素を追加します。データのない複製は、現在の位置に存在しない場合、直接、現在の位置に加えました。競合に直面したとき、リンクされたリストに同じラインのハッシュ値の要素の後ろに加えました。機能のリストは、リンクリストのハッシュ値と同じです。HashMapのJavaデータ構造は、衝突、JDK1.8以上8時間リストのデータに対処するために、この方法を使用赤黒木の使用を最適化されています。、スペースの理由により、興味のある学生は、この記事を参照することができ、関連するデータ構造の一切の深い議論がありません。

「Javaの一つは-HashMapの設定しました」

 

3.2オープニングアドレス法則

キーと値のペア、M> NのサイズM N個の格納された配列は、アドレス方式をされた開口 私たちは、欠員解決の衝突紛争の配列に依存する必要があります。この戦略の方法の全てに基づいて、「オープンアドレス」ハッシュテーブルと呼ばれています。リニア検出方法は、「オープンアドレス」ハッシュテーブルの実装の比較的一般的な種類です。リニア検出方法の核となるアイデアは、あなたが空のユニットを見つけるか、テーブル全体を検索するまで、次の単位を見るために、競合が発生したときにするテーブルです。それは単にです:紛争のイベントは、長いハッシュテーブルは十分な大きさであるように、空のハッシュアドレスは常に見つけることができるよう、次の空のハッシュアドレスを探して行ってきました。

検出方法の線形数学的記述である:H(K、I)=(H(K、0)+ i)がMOD M、Iはプローブの第一ラウンドの電流を表します。I = 1の場合、すなわち、プローブは、H(K、0)のであり、iが= 2、即ち、次の。この方法は、単純にダウンプロービングされます。モッズメートルは言った:最初からトップにテーブルとリターンの底に到達した後。

競合解決に対処するためのオープンは、線形検出方式に加えて、二つの他のより古典的な検出方法、二次検出(プロービング二次)二重ハッシュ(ダブルハッシュ)があります。しかし、検出方法が使用されているに関係なく、ハッシュテーブル、ハッシュ衝突確率が大幅に改善され、いくつかのアイドル位置。ハッシュテーブルの最高の動作効率を維持するために、通常の状況下では、我々は、空きスロットの一定割合にそのハッシュテーブルを確保しようとします。私たちは、使用装载因子欠員の数を表すために(負荷率)を。

=テーブル/ハッシュテーブル内の要素の数でポピュレートハッシュテーブルの負荷率。大きな負荷率は、貧しい、パフォーマンスをより多くの競合を説明しました。

 

例3.3 2つのスキームデモ

8は長ハッシュ想定される、ハッシュ関数H(K)= K 7国防省 、 キーワードシーケンス所与{32,14,23,2、20}は
メソッドのリストを使用する場合、対応するデータ構造を以下に示しますショー:

法律デモ一覧

線形検出方式を使用する場合、結果に対応するデータを以下に示します:

オープニングアドレス - リニア検出方法

ここでの違いは、この要素が次の空の位置に、またはノード2の位置が、リスト処理における線形検出方法遭遇競合する場合は、次のデータを競合する2 2つのアルゴリズムということです。

 

4、日常活動でのアプリケーションのハッシュアルゴリズム

在日常运营活动中,我们活动开发经常遇到的应用场景是信息加密、数据校验、负载均衡。下面分别对这三种应用场景进行讲解。

4.1 信息加密

首先我们看一下信息加密的应用。2011年CSDN脱库事件,导致超过600W的用户的密码泄露,让人失望的是,CSDN是明文存储用户的注册邮箱和密码的。作为用户的非常隐私的信息,最简单的保护措施就是对密码进行hash加密。在客户端对用户输入的密码进行hash运算,然后在服务端的数据库中保存用户密码的hash值。由于服务器端也没有存储密码的明文,所以目前很多网站也就不再有找回密码的功能了。

这里也友情提示一下大家:如果在使用中发现某网站还有提供找回密码的功能,就要好好担心下这个网站的安全性了。

看到这里有些同学会觉得那么我们是不是对用户输入的密码进行一次MD5加密就可以了呢,这样就算恶意用户知道了hash值,也没有办法拿到用户的真实密码。假设用户的密码是123456789,经过一次md5以后得到的值是:

25f9e794323b453885f5181f1b624d0b

那么是不是使用了这个加密后的字符串来存密码就万无一失了呢,理想总是很丰满,而现实总是很骨感的。

大家可以看一下这个网站:

https://www.cmd5.com/

这里是该网站的相关介绍:

本站针对md5、sha1等全球通用公开的加密算法进行反向查询,通过穷举字符组合的方式,创建了明文密文对应查询数据库,创建的记录约90万亿条,占用硬盘超过500TB,查询成功率95%以上,很多复杂密文只有本站才可查询。已稳定运行十余年,国内外享有盛誉。

md5反查结果

那么一般针对这种问题,我们的解决之道就是引入salt(加盐),即利用特殊字符(盐)和用户的输入合在一起组成新的字符串进行加密。通过这样的方式,增加了反向查询的复杂度。但是这样的方式也不是万无一失,如果发生了盐被泄露的问题,就需要所有用到的地方来重置密码。

针对salt泄露的问题,其实还有一种解决办法,即使用HMAC进行加密(Hash-based Message Authentication Code)。这种算法的核心思路是加密使用的key是从服务器端获取的,每一个用户的是不一样的。如果发生了泄露,那么也就是这一个用户的会被泄露,不会影响到全局。

这里也留给大家一个思考点,如果恶意用户直接抓取了你的活动参与链接,也就是拿到了你计算后的hash值,那从技术的角度上说,我们还有没有其他可以提升恶意用户的违法成本呢?

 

4.2 数据校验

git commit id
使用过git的同学都应该清楚,每次git提交后都有一个commit id,比如:

19d02d2cc358e59b3d04f82677dbf3808ae4fc40

就是一次git commit的结果,那么这个id是如何生成出来的呢?查阅了相关资料,使用如下代码可以进行查看:

printf "commit %s\0" $(git cat-file commit HEAD | wc -c); git cat-file commit HEAD

git的commit id主要包括了以下几部分内容:Tree 哈希,parent哈希、作者信息和本次提交的备注。

单次git commit相关信息

针对这些信息进行SHA-1 算法后得到值就是本次提交的commit id。简单来讲,就是对于单次提交的头信息的一个校验和。

Linux kernel开创者和Git的开发者——Linus说,Git使用了sha1并非是为了安全性,而是为了数据的完整性;它可以保证,在很多年后,你重新checkout某个commit时,一定是它多年前的当时的状态,完全一摸一样,完全值得信任。

但最新研究表明,理论上对其进行哈希碰撞(hash collision,不同的两块数据有相同的hash值)的攻击可以在2^51(2的51次方)左右的次数内实现。不过由于commit id 是针对单个仓库里的,所以实际应用中我们可以认为如果两个文件的SHA-1值是相同的,那么它们确是完全相同的内容。

注:对于git里tree、parent等结构感兴趣的同学,可以参考下这篇文章《Git 内部原理 - Git 对象》,这里由于篇幅原因就不进行深入分析了。

  • 版权校验
    在数据校验方面的另一个应用场景就是版权的保护或者违禁信息的打击,比如某个小视频,第一个用户上传的时候,我们认为是版权所有者,计算一个hash值存下来。当第二个用户上传的时候,同样计算hash值,如果hash值一样的话,就算同一个文件。这种方案其实也给用户传播违禁文件提高了一些门槛,不是简单的换一个名字或者改一下后缀名就可以躲避掉打击了。(当然这种方式也是可以绕过的,图片的你随便改一下颜色,视频去掉一帧就又是完全不同的hash值了。注意:我没有教你变坏,我只是和你在讨论这个技术。。。)另外我们在社区里,也会遇到玩家重复上传同一张图片或者视频的情况,使用这种校验的方式,可以有效减少cos服务的存储空间。

  • 大文件分块校验
    使用过bt的同学都有经验,在p2p网络中会把一个大文件拆分成很多小的数据各自传输。这样的好处是如果某个小的数据块在传输过程中损坏了,只要重新下载这个块就好。为了确保每一个小的数据块都是发布者自己传输的,我们可以对每一个小的数据块都进行一个hash的计算,维护一个hash List,在收到所有数据以后,我们对于这个hash List里的每一块进行遍历比对。这里有一个优化点是如果文件分块特别多的时候,如果遍历对比就会效率比较低。可以把所有分块的hash值组合成一个大的字符串,对于这个字符串再做一次Hash运算,得到最终的hash(Root hash)。在实际的校验中,我们只需要拿到了正确的Root hash,即可校验Hash List,也就可以校验每一个数据块了。

大文件分块示意图

4.3 负载均衡

活动开发同学在应对高星级业务大用户量参与时,都会使用分库分表,针对用户的openid进行hashtime33取模,就可以得到对应的用户分库分表的节点了。

活动分库分表示意图

 

如上图所示,这里其实是分了10张表,openid计算后的hash值取模10,得到对应的分表,在进行后续处理就好。对于一般的活动或者系统,我们一般设置10张表或者100张表就好。

下面我们来看一点复杂的问题,假设我们活动初始分表了10张,运营一段时间以后发现需要10张不够,需要改到100张。这个时候我们如果直接扩容的话,那么所有的数据都需要重新计算Hash值,大量的数据都需要进行迁移。如果更新的是缓存的逻辑,则会导致大量缓存失效,发生雪崩效应,导致数据库异常。造成这种问题的原因是hash算法本身的缘故,只要是取模算法进行处理,则无法避免这种情况。针对这种问题,我们就需要利用一致性hash进行相应的处理了。

一致性hash的基本原理是将输入的值hash后,对结果的hash值进行2^32取模,这里和普通的hash取模算法不一样的点是在一致性hash算法里将取模的结果映射到一个环上。将缓存服务器与被缓存对象都映射到hash环上以后,从被缓存对象的位置出发,沿顺时针方向遇到的第一个服务器,就是当前对象将要缓存于的服务器,由于被缓存对象与服务器hash后的值是固定的,所以,在服务器不变的情况下,一个openid必定会被缓存到固定的服务器上,那么,当下次想要访问这个用户的数据时,只要再次使用相同的算法进行计算,即可算出这个用户的数据被缓存在哪个服务器上,直接去对应的服务器查找对应的数据即可。这里的逻辑其实和直接取模的是一样的。如下图所示:

初始3台机器的情况

初始情况如下:用户1的数据在服务器A里,用户2、3的数据存在服务器C里,用户4的数据存储在服务器B里

下面我们来看一下当服务器数量发生变化的时候,相应影响的数据情况:

  • 服务器缩容

服务器缩容

服务器B发生了故障,进行剔除后,只有用户4的数据发生了异常。这个时候我们需要继续按照顺时针的方案,把缓存的数据放在用户A上面。

  • 服务器扩容
    同样的,我们进行了服务器扩容以后,新增了一台服务器D,位置落在用户2和3之间。按照顺时针原则,用户2依然访问的是服务器C的数据,而用户3顺时针查询后,发现最近的服务器是D,后续数据就会存储到d上面。

服务器扩容示意图

  • 虚拟节点
    当然这只是一种理想情况,实际使用中,由于服务器节点数量有限,有可能出现分布不均匀的情况。这个时候会出现大量数据都被映射到某一台服务器的情况,如下图左侧所示。为了解决这个问题,我们采用了虚拟节点的方案。虚拟节点实际节点(实际的物理服务器)在hash环上的复制品,一个实际节点可以对应多个虚拟节点。虚拟节点越多,hash环上的节点就越多,数据被均匀分布的概率就越大。

虚拟节点示意图

如右图所示,B、C、D 是原始节点复制出来的虚拟节点,原本都要访问机器D的用户1、4,分别被映射到了B,D。通过这样的方式,起到了一个服务器均匀分布的作用。

 

5、几种hash算法的扩展应用

下面介绍几种大家可能不经常遇到的应用,由于篇幅原因,不做深入介绍,只抛砖引玉。

5.1 SimHash

simHash是google用于海量文本去重的一种方法,它是一种局部敏感hash。那什么叫局部敏感呢,假定两个字符串具有一定的相似性,在hash之后,仍然能保持这种相似性,就称之为局部敏感hash。普通的hash是不具有这种属性的。simhash被Google用来在海量文本中去重。

simHash算法的思路大致如下:

  • 将Doc进行关键词抽取(其中包括分词和计算权重),抽取出n个(关键词,权重)对, 即图中的多个(feature, weight)。记为 feature_weight_pairs = [fw1, fw2 … fwn],其中 fwn = (feature_n,weight_n)。

  • 对每个feature_weight_pairs中的feature进行hash。然后对hash_weight_pairs进行位的纵向累加,如果该位是1,则+weight,如果是0,则-weight,最后生成bits_count个数字,大于0标记1,小于0标记0

  • 最后转换成一个64位的字节,判断重复只需要判断他们的特征字的距离是不是<n (n根据经验一般取3),就可以判断两个文档是否相似。

SimHash计算流程

如下图所示,当两个文本只有一个字变化时,如果使用普通Hash则会导致两次的结果发生较大改变,而SimHash的局部敏感特性,会导致只有部分数据发生变化。

SimHash结果

5.2 GeoHash

GeoHash将地球作为为一个二维平面进行递归分解。每个分解后的子块在一定经纬度范围内拥有相同的编码。以下图为例,这个矩形区域内所有的点(经纬度坐标)都共享相同的GeoHash字符串,这样既可以保护隐私(只表示大概区域位置而不是具体的点),又比较容易做缓存。

GeoHash示意图

下面以一个例子来理解下这个算法,我们对纬度39.3817进行逼近编码 :

  • 地球纬度区间是[-90,90],对于这个区间进行二分划分左区间[-90,0), 右区间[0,90]。39.3817属于右区间,标记为1

  • 将右区间[0,90]继续进行划分,左区间[0,45) ,右区间[45,90]。39.3817属于左区间,标记为0

  • 递归上面的过程,随着每次迭代,区间[a,b]会不断接近39.3817。递归的次数决定了生成的序列长度。

  • 对于经度做同样的处理。得到的字符串,偶数位放经度,奇数位放纬度,把2串编码组合生成新串。对于新串转成对应10进制查出实际的base32编码就是类似WX4ER的hash值。

整体递归过程如下表所示:

这里有一篇文章详细介绍了GeoHash,有兴趣的同学可以移步这里:

是什么能让 APP 快速精准定位到我们的位置?

 

 

5.3 布隆过滤器

布隆过滤器被广泛用于黑名单过滤、垃圾邮件过滤、爬虫判重系统以及缓存穿透问题。对于数量小,内存足够大的情况,我们可以直接用hashMap或者hashSet就可以满足这个活动需求了。但是如果数据量非常大,比如5TB的硬盘上放满了用户的参与数据,需要一个算法对这些数据进行去重,取得活动的去重参与用户数。这种时候,布隆过滤器就是一种比较好的解决方案了。

布隆过滤器其实是基于bitmap的一种应用,在1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数,用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难,主要用于大数据去重、垃圾邮件过滤和爬虫url记录中。核心思路是使用一个bit来存储多个元素,通过这样的方式来减少内存的消耗。通过多个hash函数,将每个数据都算出多个值,存放在bitmap中对应的位置上。

布隆过滤器的原理见下图所示:

布隆过滤器原理示意

上图所示的例子中,数据a、b、c经过三次hash映射后,对应的bit位都是1,表示这三个数据已经存在了。而d这份数据经过映射后有一个结果是0,则表明d这个数据一定没有出现过。布隆过滤器存在假阳率(判定存在的元素可能不存在)的问题,但是没有假阴率(判断不存在的原因可能存在)的问题。即对于数据e,三次映射的结果都是1,但是这份数据也可能没有出现过。

误判率的数据公式如下所示:

其中,p是误判率,n是容纳的元素,m是需要的存储空间。由公示可以看出,布隆过滤器的长度会直接影响误报率,布隆过滤器越长其误报率越小。哈希函数的个数也需要权衡,个数越多则布隆过滤器 bit 位置位 1 的速度越快,且布隆过滤器的效率越低;但是如果太少的话,则会导致误报率升高。

6、总结

Hash算法作为一种活动开发经常遇到的算法,我们在使用中不仅仅要知道这种算法背后真正的原理,才可以在使用上做到有的放矢。

发布了357 篇原创文章 · 获赞 71 · 访问量 17万+

おすすめ

転載: blog.csdn.net/sinat_26811377/article/details/104618921
おすすめ