本文翻譯自Coding-Geek文章:《 How does a relational database work》。
原文連接:http://coding-geek.com/how-databases-work/#Buffer-Replacement_strategies
本文翻譯了以下章節: node
經過前面的章節, 咱們已經理解了時間複雜和歸併排序的概念,接下來我要介紹三種數據結構。這三種數據結構很是重要,它們是現代數據庫系統的基石。我也會講一講數據庫索引的概念。算法
二位數組是一種最簡單的數據結構,一張數據庫表就能夠當作是一個二維數組。例如: 數據庫
儘管,二維數組用於存儲表數據很是好,可是當你須要從數組中根據某個條件查詢數據時,性能沒法接受。數組
例如:你想找出在英國工做的全部人,你須要遍歷每一行數據,判斷他是否屬於英國。這個過程須要執行N步操做(N取決於表的行數)。聽起來,性能也不算太差,但有更快的方法嗎?緩存
確定有,接下來就應該樹結構登場了。markdown
備註:現代數據庫使用更高級的數組結構來存儲表數據,如heap-organized tables或者index-organized tables。可是都沒有解決如何在數組中根據一些列的過濾條件快速篩選數據的問題。數據結構
這棵樹有15個節點。咱們看一下如何從中找到208這個元素:函數
接着看一下如何查找40這個元素:post
最終,兩次查詢的操做步驟數都是樹的高度。若是,你仔細閱讀過merge sort章節,應該知道樹的高度是log(N),因此該查找算法的時間複雜度是O(log(N))。還不錯。性能
文章內容很是抽象,讓咱們回到問題上來。除了簡單的整數型數據,考慮一下字符串,它是用於在前面的表中表示某我的的所屬國家信息的。假設,你已經構建了一個樹,包含前面表中的「country」字段數據。
這個查詢操做僅耗費了Log(N)步操做,而不是直接在數組中查詢所須要的N步。如今你也能猜到數據庫索引是什麼東西了吧?
你能爲任意多列數據創建索引(一列字符串,一列整型數,2列字符串,一列整型 + 一列字符串,一列日期類型等等)。只要你對這些列實現了比較函數,你就能控制主鍵在樹中的排列順序(數據庫已經爲基本數據類型實現了比較函數)。
儘管上面的二叉樹在查詢某個固定值時工做得很好,可是若是要查詢某個範圍內的全部數據,性能就很是低。它須要花費N步操做,由於須要比較樹中的每個節點以判斷它是否在指定的範圍內。此外,這種方式也很耗費I/O資源,由於要讀取整個索引樹。咱們須要找到一種高效的範圍查詢方法(range query)。爲了解決這個問題,現代數據庫使用B+樹,B+樹是前面二叉查詢樹的優化。在B+樹裏面:
其它節點的用途只在查詢的時候,幫助路由到想找的葉子節點。
在這個B+樹裏面,若是你查找40到100以前的數據:
假如,你須要查詢M個節點,樹有N個節點。查詢指定的值(40)的時間複雜度是Log(N), 跟以前的二叉樹查詢同樣。可是,一但你找到了節點(40),你還須要經過M步操做,遍歷收集M個後繼結點。B+的range query的時間複雜度是O(M+Log(N)), 相比以前二叉樹O(N)複雜度,性能提高不少。數據量越大,性能提高越明顯。你不須要讀取整顆樹,這也意味着更小的磁盤I/O讀取。
可是,這也帶來了新的問題(再一次遇到問題)。若是你往數據庫添加或者刪除一行記錄,同時也須要在B+樹中更新數據:
換句話說,B+樹必需要有自我調整樹平衡性和節點順序的能力。謝天謝地,智能化的數據刪除和數據插入操做使得B+樹的能保持以上特徵。這也帶來了成本:插入和刪除數據的時間複雜度是O(Log(N)), 這也是爲何你常常會聽到這樣一種觀點:索引太多不是什麼好事。實際上,這會下降插入/更新/刪除操做的效率,由於數據庫須要同時更新表的索引,每一個索引花費O(Log(N))的時間。
譯者注:凡事都有兩面,有利必有裨;選擇什麼樣的數據結構,是根據你的應用場景來的。
另外,索引也會增長tansaction manager的複雜度(最後一張將講到tansaction manager)。
更多的細節,你能夠在維基百科上搜索B+ Tree。若是你想要一個B+樹實現的樣例,你能夠讀一下這篇文章(https://blog.jcole.us/2013/01/07/the-physical-structure-of-innodb-index-pages/), 這篇文章的做者是MySQL的核心開發人員。他詳細講述了innoDB(MySQL數據庫引擎)是如何實現索引的。
最後一個重要的數據結構是hash table。當你須要快速查找一個數據時,hash table很是有用。另外,理解了hash table將幫助咱們理解後面將提到的一種經常使用數據庫鏈接技術:hash join。Hash table也常常用於存儲一些數據庫的內部管理數據,如lock table,buff pool等。這些概念在後面都會講到。
Hash table是一種能根據關鍵字快速查找數據的數據結構類型。構建hash table須要定義以下一些內容:
1) 對象關鍵字
2) 爲關鍵字定義的哈希函數(hash function)。對象的關鍵字用哈希函數計算的結果表示了對象存儲的位置(稱爲buckets)。
3) 關鍵字比較函數。一旦找到了對象所在的bucket,接下來須要在bucket內部,經過比較函數找到對應的對象。
比較函數我採用判斷兩個整型數值是否相等的方法比較。
咱們看一下如何找到hash table中找到元素78:
咱們再看一下如何查找元素59:
如你所見,查詢不一樣的值,時間複雜度是不一樣的。
若是你將哈希函數改成除以1000000取模(取數字的最後6位數,做爲bucket標識)。上面的第二次查詢只須要一步(在bucket 59中沒有任何數據)。找一個好的哈希函數,保證每一個bucket中存儲儘量少的數據,是很是困難的。
在上面的例子中找一個好的hash function很是容易。但這僅僅是一個簡單的樣例,若是關鍵字是以下數據類型,將很是困難:
1. 一個字符串(例如表示人的名)
2. 兩個字符串(例如同時表示人的姓和名)
3. 兩個字符串 + 一個日期(例如表示人的姓、名及生日)
設計一個好的hash function,哈希表的查詢時間爲O(1)。
爲何不使用數組? 問得好。
想了解更多的信息,你能夠讀一下介紹Java如何實現hash map的文章,它是hash map高效實現的一個樣例。理解本文中概念,你不須要懂JAVA。
已翻譯的《How does a relational database work》其它章節連接:
1. 關係型數據庫工做原理-時間複雜度:http://blog.csdn.net/ylforever/article/details/51205332
2. 關係型數據庫工做原理-歸併排序:http://blog.csdn.net/ylforever/article/details/51216916
3. 關係型數據庫工做原理-數據結構:http://blog.csdn.net/ylforever/article/details/51278954
4. 關係型數據庫工做原理-高速緩存:http://blog.csdn.net/ylforever/article/details/50990121
5. 關係型數據庫工做原理-事務管理(一):http://blog.csdn.net/ylforever/article/details/51048945
6. 關係型數據庫工做原理-事務管理(二):http://blog.csdn.net/ylforever/article/details/51082294