【18】哈希算法

1. 什麼是哈希算法?

  1. 定義
    將任意長度的二進制值串映射成固定長度的二進制值串,這個映射的規則就是哈希算法,而通過原始數據映射之後得到的二進制值串就是哈希值。
  2. 如何設計一個優秀的哈希算法?
    1)單向哈希:
    從哈希值不能反向推導出哈希值(所以哈希算法也叫單向哈希算法)。
    2)篡改無效:
    對輸入敏感,哪怕原始數據只修改一個Bit,最後得到的哈希值也大不相同。
    3)散列衝突:
    散列衝突的概率要很小,對於不同的原始數據,哈希值相同的概率非常小。
    4)執行效率:
    哈希算法的執行效率要儘量高效,針對較長的文本,也能快速計算哈希值。

2. 哈希算法的常見應用有哪些?

7個常見應用:安全加密、唯一標識、數據校驗、散列函數、負載均衡、數據分片、分佈式存儲。

  1. 安全加密
    1)常用於加密的哈希算法:
    MD5:MD5 Message-Digest Algorithm,MD5消息摘要算法
    SHA:Secure Hash Algorithm,安全散列算法
    DES:Data Encryption Standard,數據加密標準
    AES:Advanced Encryption Standard,高級加密標準
    2)對用於加密的哈希算法,有兩點格外重要,第一點是很難根據哈希值反向推導出原始數據,第二點是散列衝突的概率要小。
    3)在實際開發中要權衡破解難度和計算時間來決定究竟使用哪種加密算法。
  2. 唯一標識
    通過哈希算法計算出數據的唯一標識,從而用於高效檢索數據。
  3. 數據校驗
    利用哈希算法對輸入數據敏感的特點,可以對數據取哈希值,從而高效校驗數據是否被篡改過。
  4. 散列函數
    散列函數中用到的哈希算法更加關注散列後的值能不能平均分佈,以及散列函數的執行快慢。
  5. 負載均衡(哈希算法代替映射表)
    【需求】
    如何實現一個會話粘滯(session sticky)的負載均衡算法?也就是說,在一次會話中的所有請求都路由到同一個服務器上。
    【解決方案】
    通過哈希算法對客戶端IP或會話ID計算哈希值,將取得的哈希值與服務器列表的大小進行取模運算,最終得到的值就是應該被路由到的服務器編號。這樣,就可以把同一個IP過來的請求都路由到同一個後端服務器上。
  6. 數據分片(突破單機資源限制)
    如何統計“搜索關鍵詞”出現的次數?
    【需求描述】
    假如我們有1T的日誌文件,這裏面記錄了用戶的搜索關鍵詞,我們想要快速統計出每個關鍵詞被搜索的次數,該怎麼做呢?
    【問題分析】
    這個問題有兩個難點,第一個是搜索的日誌很大,沒辦法放到一臺機器的內存中。第二個是隻用一臺機器來處理這麼巨大的數據,處理時間會很長。
    【解決方案】
    先對數據進行分片,然後採用多臺(比如n臺)機器進行處理。具體做法:從搜索記錄的日誌文件中依次讀取每個關鍵詞,並通過哈希函數計算該關鍵詞的哈希值,然後跟機器的臺數n取模,最終得到值就是該關鍵詞應該被分到的機器編號,這樣相同的關鍵詞一定會被分配到同一臺機器上,數據分配完成後,由多臺機器並行進行統計,最後合併起來就是最終結果。
    實際上,這裏的處理過程也是 MapReduce 的基本設計思想。
    如何快速判斷圖片是否存在圖庫中?
    【需求描述】
    假設現在我們的圖庫中有1億張圖片,如何快速判斷圖片是否在圖庫中?基本方式是給每個圖片去唯一表示(或者信息摘要),然後構建散列表。
    【問題分析】
    很顯然,在單臺機器上構建散列表示行不通的,因爲單臺機器的內存有限,而1億張圖片構建散列表遠遠超過了單臺機器的內存上限。
    【解決方案】
    準備n臺機器,讓每臺機器只維護一部分圖片對應的散列表。我們每次從圖庫中讀取一個圖片,計算唯一標識,然後與機器個數n求餘取模,得到的值就對應要分配的機器編號,然後將這個圖片的唯一表示和圖片路徑發往對應的機器構建散列表。
    當我們要判斷一個圖片是否在圖庫中時,我們通過同樣的哈希算法,計算這個圖片的唯一表示,然後與機器個數n求餘取模。假設得到的值是k,那就去編號爲k的機器構建的散列表中查找。
    如何估算給1億張圖片構建散列表大約需要多少臺機器?
    散列表中每個數據單元包含兩個信息,哈希值和圖片文件的路徑。假設我們通過 MD5 來計算哈希值,那長度就是 128 比特,也就是 16 字節。文件路徑長度的上限是 256 字節,我們可以假設平均長度是 128 字節。如果我們用鏈表法來解決衝突,那還需要存儲指針,指針只佔用 8 字節。所以,散列表中每個數據單元就佔用 152 字節(這裏只是估算,並不準確)。
    假設一臺機器的內存大小爲 2GB,散列表的裝載因子爲 0.75,那一臺機器可以給大約 1000 萬(2GB*0.75/152)張圖片構建散列表。所以,如果要對 1 億張圖片構建索引,需要大約十幾臺機器。在工程中,這種估算還是很重要的,能讓我們事先對需要投入的資源、資金有個大概的瞭解,能更好地評估解決方案的可行性。
    實際上,針對這種海量數據的處理問題,我們都可以採用多機分佈式處理。藉助這種分片的思路,可以突破單機內存、CPU 等資源的限制。
    7.分佈式存儲(一致性哈希算法,解決在擴容或縮容時,產生的緩存等數據搬移問題)
    什麼是分佈式存儲?
    分佈式存儲就是將數據存儲在多臺機器上並提供高效的讀取、寫入支持。那如何決定將哪個數據放到哪個機器上呢?可以利用數據分片的思想,即通過哈希算法對數據取哈希值,然後對機器個數取模,這個最終值就是應該存儲的緩存機器編號。
    遇到的問題是什麼?
    如果數據持續增多,原來的機器數量已經不能滿足需求,就需要增加機器,這時就麻煩了,因爲所有的數據都需要重新哈希值進行再次分配。這就相當於,緩存中的數據一下子都失效了,所有的數據請求都會穿透緩存,直接去請求數據庫。這樣就可能發生雪崩效應,壓垮數據庫。
    解決方案是什麼?
    1)這時,需要一種方法,使得新加入一個機器後,並不需要做大量的數據搬移。那就是在分佈式系統中應用非常廣泛的一致性哈希算法。
    2)一致性哈希算法的基本思想是什麼呢?爲了說清楚這個問題,我們假設有k個機器,數據的哈希值範圍是[0-MAX],我們將整個範圍劃分成m個小區間(m遠大於k),每個機器複雜m/k個小區間。當有新機器加入的時候,我們就將某幾個小區間的數據,從原來的機器中搬移到新的機器中。這樣,既不用全部重新哈希、搬移數據,也保持了各個機器上數據量的均衡。

3. 思考

  1. 如何防止數據庫中的用戶信息被脫庫?你會如何存儲用戶密碼這麼重要的數據嗎?
    答:
    1)使用MD5進行加密
    2)字典攻擊:如果用戶信息被“脫庫”,黑客雖然拿到的是加密之後的密文,但可以通過“猜”的方式來破解密碼,這是因爲,有些用戶的密碼太簡單。
    3)針對字典攻擊,我們可以引入一個鹽(salt),跟用戶密碼組合在一起,增加密碼的複雜度。
  2. 現在,區塊鏈是一個很火的領域,它被很多人神祕化,不過其底層的實現原理並不複雜。其中,哈希算法就是它的一個非常重要的理論基礎。你能講一講區塊鏈使用的是哪種哈希算法嗎?是爲了解決什麼問題而使用的呢?
    答:區塊鏈是一塊塊區塊組成的,每個區塊分爲兩部分:區塊頭和區塊體。
    區塊頭保存着 自己區塊體 和 上一個區塊頭 的哈希值。
    因爲這種鏈式關係和哈希值的唯一性,只要區塊鏈上任意一個區塊被修改過,後面所有區塊保存的哈希值就不對了。
    區塊鏈使用的是 SHA256 哈希算法,計算哈希值非常耗時,如果要篡改一個區塊,就必須重新計算該區塊後面所有的區塊的哈希值,短時間內幾乎不可能做到。
  3. 哈希算法還有很多其他的應用,比如網絡協議中的 CRC 校驗、Git commit id 等等。除了這些,你還能想到其他用到哈希算法的地方嗎?

4. 參考資料

  1. 王爭老師在極客時間的專欄《數據結構與算法之美》
  2. 專欄下的所有評論

5. 聲明

本文章是學習王爭老師在極客時間專欄——《數據結構與算法之美》的學習總結,文章很多內容直接引用了專欄下的回覆,推薦大家購買王爭老師的專欄進行更加詳細的學習。本文僅供學習使用,勿作他用,如侵犯權益,請聯繫我,立即刪除。

後記:
我從本碩藥學零基礎轉行計算機,自學路上,走過很多彎路,也慶幸自己喜歡記筆記,把知識點進行總結,幫助自己成功實現轉行。
如果想和我一起交流學習,歡迎大家關注我的微信公衆號No Bug編程筆記,通過掃描下方二維碼或者搜索NoBugNotes關注。這個公衆號主要是分享和記錄自己學習編程的筆記,如:C – > Python – > Java等,後續還會分享自己面試以及職場上的成長心得。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章