資料庫沒有完美的儲存引擎

qing_yun發表於2024-02-02

儲存引擎是資料庫的核心元件,儲存引擎的一些特性決定了資料庫的一些基本效能特性。儲存引擎的選擇也是資料庫開發人員十分謹慎的一個方面。以前我也在一些文章裡介紹了一些常見的儲存引擎,也對這些儲存引擎的優缺點做了概括。不過如果沒有真實的去使用,去匹配應用負載,僅僅是紙上談兵,是完全不夠的。

目前資料庫中使用最為廣泛的儲存引擎不外乎兩個系列三種引擎,分散式資料庫喜歡採用的LSM-TREE儲存,以及普通通用型資料庫常用的BTREE/HEAP儲存。BTREE和HEAP儲存是兩種十分相近的儲存引擎結構,Oracle,PostgreSQL,MySQL等資料庫都使用BTREE/HEAP結構。BTREE是一個泛稱,實際上資料結構是一棵增強的B+TREE。

這是一張來自網上的兩大陣營的一些資料庫產品的清單,我們可以看出傳統資料庫使用B-TREE結構的比較多,而一些新興的資料庫系統往往使用LSM-TREE。LSM-TREE是Log-Structured Merge Tree的簡稱,是一種典型的不可變結構的儲存引擎,其目的是為了降低可變結構儲存引擎中寫入資料需要先找到已經存在的PAGE,然後再進行修改的成本,從而實現更高併發的寫入。因此LSM-TREE引擎的資料庫天生對高併發寫入/修改操作十分友好。不過LSM-TREE結構也不是完美的,在讀效能上需要在多個副本之間做協同,因此讀效能會受到一定的影響。

HEAP/BTREE結構的儲存引擎雖然從結構上來說比較接近,不過依然存在一定的差異。在我們常見的資料庫中,Oracle、PostgreSQL等是採用HEAP結構的,這種結構的頁和不同的BTREE頁不同,採用slotted page。Mysql、達夢等資料庫使用的是傳統的BTREE儲存結構。

實際上這麼說也不是很準確,只能說這些資料庫的預設儲存引擎是使用這種資料組織方式的。實際上Oracle中有heap表,有簇表,有混合列壓縮的表。其中簇表是BREE結構的。達夢的儲存引擎有多種資料組織方式:B樹資料、堆表資料、列存資料、點陣圖索引,其中B樹資料是普通的達夢表的預設組織方式。

Oracle的資料塊結構是一種典型的SLOTTED PAGE的結構,塊頭從上往下增長,而資料從尾部向塊頭生長。中間是一個可變長的slot指示器。

如上圖所示,透過一個定長陣列指示器結構,指出每一行行頭的位置,這主要是為了解決不定長記錄的儲存問題,從而使空間利用率達到最高。

Oracle資料塊使用一個kdbt的結構來指出某個block中有多少條記錄,並且kdbpri這個指示器與塊頭的偏移量。kdbpri就是這個slot陣列。

上面是MySQL innodb儲存引擎的一個邏輯示意圖,這是一種典型的BTREE結構儲存引擎。BTREE結構的儲存引擎也不是完全相同的。主要的區別是leaf node segment是否和資料儲存在一個段裡。達夢明顯是分開的。實際上BTREE結構的儲存結構,所有的資料儲存都是按照主鍵的順序儲存的。

表空間是一個邏輯結構,可以被認為是innodb的頂層邏輯結構,所有的資料都必須屬於某個表空間。預設的innodb引擎有一個ibdata1表空間,預設的資料都儲存在這個表空格鍵中。如果設定了innodb_file_per_table引數,每張表都會建立獨立的檔案。不過只有表的資料、索引等會儲存在每張表自己的檔案中,UNDO資料、事務控制資訊、INSERT BUFFER等仍然會儲存在系統共享的表空間中。在Innodb儲存引擎中,一張表會分為兩個段,其中一個段是葉節點段,儲存實際的資料,另外一個段是索引段,儲存索引的指標資訊。而表中DML的UNDO資訊會儲存在rollback segment中。

上面這張innodb的邏輯結構圖畫的十分清晰,所有的表的行資料是儲存在extent中的,而每個extent是多個連續的PAGE組成的,每個PAGE中儲存了行資料。實際上這個leaf node segment和Oracle的TABLE SEGMENT是十分類似的,所不同的是多了一個Index segments。相當於在建立表的時候同時又預設建立了一個主鍵索引。Mysql在窗這個主鍵索引的時候,會區分不同的情況。如果要建立的表上沒有設定主鍵索引,那麼會選擇表上的一個非空唯一性索引作為主鍵索引,如果不存在這樣的索引,那麼Mysql會使用一個六位元組的唯一性自增值窗一個主鍵索引。

Mysql innodb等採用B樹儲存結構的儲存引擎一般採用上圖的模式,當資料被插入表的時候,會根據主鍵索引或者簇索引的指示插入到某個位置,而不會像Oracle那樣,透過segment的free space bitmap尋找空閒位置插入。

Innodb的PAGE結構與HEAP結構的類似,不過在空閒空間管理上是完全不同的。前面是FILE HEADER/PAGE HEADER,中間是資料記錄,資料記錄也是從低地址往高地址寫,和Oracle相反。這是因為BTREE儲存結構不需要和slotted page一樣,在塊裡放一個指示器,其行指示器的功能被BTREE替代了。

Innodb的這種儲存結構,並不存在一個十分友好的類似Oracle的記錄實體地址的ROWID這樣的結構。所以要想定位某條資料記錄,需要使用主鍵或者簇主鍵的方式來實現。主鍵可以定義某條記錄的唯一性地址,因此Mysql的某張表上的其他索引(secondary index)的索引中儲存的鍵值不像Oracle那樣儲存ROWID就可以了,而是儲存的是主鍵中這一行的地址指標。基於一個secondary index的查詢首先找出某些行的主鍵,然後再去掃描一次主鍵索引,才能找到相關行的地址,再找到這條記錄。比起有rowid的Oracle資料庫,這裡多了一次主鍵索引的掃描。

可能有些朋友會覺得,是不是heap結構一定優於BTREE結構呢?其實還是回到今天的標題,沒有完美的儲存引擎。針對不同的應用場景,heap和BTREE各有優勢。BTREE結構寫入資料時按主鍵排序的,而且併發寫入時資料並不是按照插入順序寫入資料塊,如果主鍵存在一定的無序性,那麼併發寫入的資料可以被打散到多個塊中,從而緩解熱塊衝突的壓力。而二級索引的結構雖然對讀取資料的操作有影響,對於存在多條索引的資料寫入,資料修改,是有優勢的。因為只要主鍵的鍵值不變,行資料的變化,行在資料塊中儲存的變化,不需要變更第二索引。

因此我們可以十分明確的肯定,不同的儲存結構都各有利弊,並不能很直接的說哪種更好。不過在開發高併發,大資料量的系統的時候,瞭解儲存引擎的一些特點,可以有效的避免一些問題。比如在Mysql、達夢等資料庫中建表,儘可能定義一個顯式的主鍵,從而避免系統自動新增主鍵。另外如果某張表的熱塊衝突特別嚴重的時候,主鍵可以考慮選擇隨機性的資料,而不是單邊增長的資料,就可以有效的進行資料打散,從而降低熱塊衝突的可能性。

來自 “ 白鱔的洞穴 ”, 原文作者:白鱔;原文連結:https://mp.weixin.qq.com/s/Bd9jkvuqYrw9lIwZteiSLA,如有侵權,請聯絡管理員刪除。

相關文章