這篇文章是對知乎上如何本身實現一個關係型數據庫的一個嘗試性回答,後續會不斷更新。mysql
對外數據模型爲關係型數據庫,內部的實現主要分紅兩大類,一類是disk-based,好比mysql,postgres,一類是memory based,後者包括MemSQL,SAP HAHA,OceanBase。這裏說一個disk-based的關係型數據庫涉及多少東西。算法
上世紀70/80年代內存不大,數據不能都放在內存裏,大部分數據都存在磁盤上,讀數據也須要從磁盤讀,然而讀寫磁盤太慢了,因此就在內存裏作了一個buffer pool,將已經讀過的數據緩存到buffer pool中,寫的時候也是寫到buffer pool中就返回,buffer pool的功能就是管理數據在磁盤和內存的移動。在buffer pool中數據的管理單位是page。page大小通常幾十KB。通常均可以配置。若是buffer pool中沒有空閒的page,就須要將某一個page提出buffer pool,若是它是dirty page,就須要flush到磁盤,這裏又須要一個LRU算法。一個page包含多條記錄,page的格式須要設計用來支持變長字段。若是這時宕機了,buffer pool中的數據就丟了。這就須要REDO log,將對數據的修改先寫到redo log中,而後寫buffer pool,而後返回給客戶端,隨後,buffer pool中的dirty page會被刷到數據文件中(NO FORCE)。那麼重啓的時候,數據就能從redo log中恢復。REDO log還沒刷完就刷數據到磁盤能夠加快寫入速度,缺點就是恢復的時候須要回放UNDO log,回滾一些尚未提交的事務的修改。寫log又分爲邏輯log和物理log,還有物理邏輯log。簡單說邏輯log就是記錄操做,好比將某個值從1改爲2.而物理log記錄具體到record的位置,例如某個page的某個record的某個field,原來的值是多少,新值是多少等。邏輯log的問題是併發狀況下不太好恢復成一致。物理log對於某些操做好比create table又過於瑣碎,因此通常數據庫都採用混合的方式。爲了跟蹤系統中各類操做的順序,這就須要爲log分配id,記作LSN(log sequence number)。系統中記錄各類LSN,好比pageLSN, flushedLSN等等。爲了加快宕機恢復速度,須要按期寫checkpoint,checkpoint就是一個LSN。
以上ACID裏的C和D有關。下面說A和I,即原子性和隔離性。sql
這兩個性質經過concurrency control來保證。隔離級別有不少種,最開始有4種,從低到高read uncommitted, read committed, repeatable read, serializable。serializable就是多個事務併發執行的結果和某種順序執行事務的結果相同。除了serializable,其餘都有各類問題。好比repeatable read有幻讀問題(phantom),避免幻讀須要gap lock。read committed有幻讀和不可重複讀問題。後來又多了一些隔離級別,好比snapshot isolation,snapshot isolation也有write skew問題。早期,併發控制協議大可能是基於兩階段鎖來作的(2PL),因此早期只有前面提到的四種隔離級別,後來,又出現一類併發控制協議,統稱爲Timestamp Ordering,因此又多了snapshot isolation等隔離級別。關於隔離級別,能夠看看這篇A Critique of ANSI SQL Isolation Levels。2PL須要處理deadlock的問題。數據庫
Timestamp Ordering大致的思想就是認爲事務之間衝突不大,不須要加鎖,只在commit的時候check是否有衝突。屬於一種樂觀鎖。
Timestamp Ordering具體來講包括多種,最多見的MVCC就是這類,還有一類叫作OCC(optimistic concurrency control)。MVCC就是對於事務的每次更新都產生新的版本,使用時間戳作版本號。讀的時候能夠讀指定版本或者讀最新的版本。幾乎主流數據庫都支持MVCC,由於MVCC讀寫互相不阻塞,讀性能高。MySQL的回滾段就是用來保存老的版本。MVCC須要有後臺線程來作再也不須要的版本的回收工做。Postgres的vacuum就是作這事的。OCC和MVCC的區別是,OCC協議中,事務的修改保存在私有空間(好比客戶端),commit的時候再去檢測衝突,一般的作法是事務開始時看一下本身要修改的數據的最後一次修改的時間戳,提交的時候去check是否這個時間戳變大了,若是是,說明被別人改過了,衝突。衝突後能夠回滾或者重試。緩存
上面這些搞定了就實現了數據庫的核心,而後爲了性能,須要index,一般有兩種,一種支持順序掃描B+Tree,還有一種是Hash Index。單條讀適合用Hash Index,O(1)時間複雜度,順序掃描只適合用B+Tree,O(logN)複雜度。而後,有些查詢只須要掃描索引就能獲得結果,有些查詢直接掃描數據表就能獲得結果,有些查詢能夠走二級索引,經過二級索引找到數據表而後獲得結果。。具體用哪一種方式就是優化器的事了。性能優化
再外圍一些,關係型數據庫天然須要支持SQL了,由SQL變成最後能夠執行的物理執行計劃中間又有不少步,首先SQL經過詞法語法分析生成抽象語法樹,而後planner基於這棵樹生成邏輯執行計劃,邏輯執行計劃的生成一般涉及到等價謂詞重寫,子查詢消除等邏輯層面的優化技術,優化的目的固然是性能。好比等價謂詞重寫,用大於小於謂詞消除like,between .. and..等不能利用索引的謂詞。下一步是邏輯執行計劃生成物理執行計劃,物理執行計劃樹每一個節點是一個operator,operator的執行就是實實在在的操做,好比掃表的operator,filter opertor。一個邏輯執行計劃一般能夠有多個物理執行對應,選擇哪一個就涉及到物理執行計劃優化,這裏涉及到經典的cost model,綜合考慮內存,CPU, I/O,網絡等。最典型的,三表join,從左到右仍是右到左,使用hash join,仍是sort merge join等。關於查詢優化器能夠參考數據庫查詢優化器的藝術:原理解析與SQL性能優化網絡