區域性性原理:資料庫效能跟不上,加個快取就好了?
平時進行服務端軟體開發的時候,我們通常會把資料儲存在資料庫裡。而服務端系統遇到的第一個效能瓶頸,往往就發生在訪問資料庫的時候。這個時候,大部分工程師和架構師會拿出一種叫作“快取”的武器,通過使用 Redis 或者 Memcache 這樣的開源軟體,在資料庫前面提供一層快取的資料,來緩解資料庫面臨的壓力,提升服務端的程式效能。
那麼,不知道你有沒有想過,這種新增快取的策略一定是有效的嗎?或者說,這種策略在什麼情況下是有效的呢?如果從理論角度去分析,新增快取一定是我們的最佳策略麼?進一步地,如果我們對於訪問效能的要求非常高,希望資料在 1 毫秒,乃至 100 微秒內完成處理,我們還能用這個新增快取的策略麼?
理解區域性性原理
我們先來回顧一下,上一講的這張不同儲存器的效能和價目表。可以看到,不同的儲存器裝置之間,訪問速度、價格和容量都有幾十乃至上千倍的差異。
以上一講的 Intel 8265U 的 CPU 為例,它的 L1 Cache 只有 256K,L2 Cache 有個 1MB,L3 Cache 有 12MB。一共 13MB 的儲存空間,如果按照 7 美元 /1MB 的價格計算,就要 91 美元。
我們的記憶體有 8GB,容量是 CPU Cache 的 600 多倍,按照表上的價格差不多就是 120 美元。如果按照今天京東上的價格,恐怕不到 40 美元。128G 的 SSD 和 1T 的 HDD,現在的價格加起來也不會超過 100 美元。雖然容量是記憶體的 16 倍乃至 128 倍,但是它們的訪問速度卻不到記憶體的 1/1000。
效能和價格的巨大差異,給我們工程師帶來了一個挑戰:我們能不能既享受 CPU Cache 的速度,又享受記憶體、硬碟巨大的容量和低廉的價格呢?你可以停下來自己思考一下,或者點選文章右上方的“請朋友讀”,邀請你的朋友一起來思考這個問題。然後,再一起聽我的講解。
好了,現在我公佈答案。想要同時享受到這三點,前輩們已經探索出了答案,那就是,儲存器中資料的區域性性原理(Principle of Locality)。我們可以利用這個區域性性原理,來制定管理和訪問資料的策略。這個區域性性原理包括時間區域性性(temporal locality)和空間區域性性(spatial locality)這兩種策略。
我們先來看時間區域性性。這個策略是說,如果一個數據被訪問了,那麼它在短時間內還會被再次訪問。這麼看這個策略有點奇怪是吧?我用一個簡單的例子給你解釋下,你一下就能明白了。
比如說,《哈利波特與魔法石》這本小說,我今天讀了一會兒,沒讀完,明天還會繼續讀。同理,在一個電子商務型系統中,如果一個使用者打開了 App,看到了首屏。我們推斷他應該很快還會再次訪問網站的其他內容或者頁面,我們就將這個使用者的個人資訊,從儲存在硬碟的資料庫讀取到記憶體的快取中來。這利用的就是時間區域性性。
同一份資料在短時間內會反覆多次被訪問
我們再來看空間區域性性。這個策略是說,如果一個數據被訪問了,那麼和它相鄰的資料也很快會被訪問。
們還拿剛才讀《哈利波特與魔法石》的例子來說。我讀完了這本書之後,感覺這書不錯,所以就會借閱整套“哈利波特”。這就好比我們的程式,在訪問了陣列的首項之後,多半會迴圈訪問它的下一項。因為,在儲存資料的時候,陣列內的多項資料會儲存在相鄰的位置。這就好比圖書館會把“哈利波特”系列放在一個書架上,擺放在一起,載入的時候,也會一併載入。我們去圖書館借書,往往會一次性把 7 本都借回來。
相鄰的資料會被連續訪問
有了時間區域性性和空間區域性性,我們不用再把所有資料都放在記憶體裡,也不用都放在 HDD 硬碟上,而是把訪問次數多的資料,放在貴但是快一點的儲存器裡,把訪問次數少的資料,放在慢但是大一點的儲存器裡。這樣組合使用記憶體、SSD 硬碟以及 HDD 硬碟,使得我們可以用最低的成本提供實際所需要的資料儲存、管理和訪問的需求。