Rocksdb引擎記錄格式

     Rocksdb是一個kv引擎,由facebook團隊基於levelDB改進而來,Rocksdb採用LSM-tree存儲數據,良好的讀寫特性以及壓縮特性使得其很是受歡迎。此外,Rocksdb引擎做爲插件已經集成在facebook維護的MySQL分支,用戶能夠經過SQL來訪問rocksDB。本文主要經過分析Rocksdb引擎的記錄格式,並經過對比innodb,來讓你們瞭解Rocksdb。Rocksdb做爲一個kv引擎,用戶經過put(key,value)來寫入key,或者經過get(key)接口來獲取value,對rocksdb自己而言,每條記錄都是一個key-value。當Rocksdb做爲一個存儲引擎接入到MySQL時,key-value結構如何存儲表中各個索引,以及如何記錄中各個列的信息是本文要具體討論的。rocksdb引擎與innodb引擎相似,也是採用索引組織表,不管是表(主鍵索引)仍是二級索引都是以LSM tree方式組織,rocksdb記錄主要包括三部分,key,value和meta三部份內容,具體見下表,而後我經過介紹一條具體記錄在rocksdb引擎中的存儲格式來講明問題。html

rocksdb基本記錄存儲格式mysql

key_sizesql

key優化

value_sizespa

value插件

PK/SecKeycode

Columns dataorm

SeqenceId,flag server

create table row_format( id int not null, c1 int, c2 char(10) not null, c3 char(10), c4 varchar(10), c5 varchar(10) not null, c6 blob, c7 binary(10) not null, c8 varbinary(10)) engine=rocksdb; insert into row_format(id,c2,c4,c5,c7) values(1,'abc','abc','efg','111')

 key部分:
htm

Index_id

key

4bytes

8bytes

0x7fdfa4278ea0: 0x00    0x00    0x01    0x7b    0x00    0x00    0x00    0x00

0x7fdfa4278ea8: 0x00    0x00    0x00    0x05

Index_id:索引的編號,全局惟一。

rowid:因爲表沒有主鍵,系統會產生一個bigint類型的rowid做爲主鍵,佔用8個字節,而innodb引擎的rowid佔6個字節,須要注意的是rowid存儲採用的大端的存儲(高位存儲低字節),這裏主要是爲了memcompare。

Value部分:

 

Null-flag

ID

C1

C2

C3

C4

C5

C6

C7

C8

Length

1B

4B

----

30B

----

4B

4B

----

10B

----

Value

 

1

 

abc0x20…

 

len+value

len+value

 

1110x00…

 

0x7fdfa4251e50: 0x1b    0x01    0x00    0x00    0x00    0x61    0x62    0x63

0x7fdfa4251e58: 0x20    0x20    0x20    0x20    0x20    0x20    0x20    0x20

0x7fdfa4251e60: 0x20    0x20    0x20    0x20    0x20    0x20    0x20    0x20

0x7fdfa4251e68: 0x20    0x20    0x20    0x20    0x20    0x20    0x20    0x20

0x7fdfa4251e70: 0x20    0x20    0x20    0x03    0x61    0x62    0x63    0x03

0x7fdfa4251e78: 0x65    0x66    0x67    0x31    0x31    0x31    0x00    0x00

0x7fdfa4251e80: 0x00    0x00    0x00    0x00    0x00

說明:

  1. Value的最前面部分(0x1b)就是存放記錄的null信息。根據記錄中能夠爲null字段的個數,確認須要佔用的字節數,若是小於8個,則只須要一個字節。例子中,c1,c3,c4,c6,c8都可覺得null,所以須要5個bit,因此用1個byte表示Null-flag便可,因爲插入記錄中,c4不爲null,則對應的bit爲0,也就是0x00011011。
  2. 對於null,不管是定長仍是非定長數據類型,都不佔用真實的存儲空間,只須要一個bit位來表示爲null便可。
  3. 空串’’與null,上面提到了null須要佔一個標記位,而對於’’,若是是變長字段仍然須要存儲長度信息,對於定長字段,則會補全。
  4. 對於變長字段,好比varchar,0x03 0x61 0x62 0x63數據有len+data組成,若是數據長度小於256,len只須要佔用一個byte;若是len大於255,且小於65536,則須要佔用2個字節,對於longblob類型,則須要佔4個字節。
  5. 對於定長字段,不須要存長度信息直接存儲data,若是不足則補充。補充字符有點詫異,對於char類型,補充0x20,對於binary類型,補充0x00。
  6. 對於lob類型,好比tinyblob,blob,mediumblob,longblob,以及對應的text類型,處理策略與varchar相似,存儲長度的字節數根據數據類型的範圍肯定,好比blob長度佔用2個字節,而longblob的長度佔4個字節。因此在rocksdb裏面,沒有innodb中所謂「溢出頁」的概念。對於innodb引擎,若是blob字段內容超過768字節,多餘的data存儲在溢出頁,頁內經過20個字節指向溢出頁,主要包括第一個blob頁的space_id,page_no和起始偏移,若是存在多個blob頁,則頁與頁之間經過相似的方式進行關聯。具體能夠參考btr0cur.h文件中關於BTR_EXTERN_xxx相關的宏定義,以及接口btr_copy_externally_stored_field_prefix_low。
  7. 有關value部分的存儲實現能夠參考rocksdb引擎接口convert_record_to_storage_format,convert_record_from_storage_format和innodb引擎接口row_mysql_store_col_in_innobase_format,row_sel_field_store_in_mysql_format。

Meta部分:

Meta部分主要是SequenceID,這個SequenceID在事務提交時產生,主要用於rocksDB實現MVCC,用於可見性判斷,此外Meta中還包含flag信息,因爲標示記錄類型,put,delete,singleDelete等,具體而言Sequence佔7個字節,flag佔1個字節。

rocksdb索引格式

      Rocksdb中,全部的數據都是經過索引來組織,與Innodb相似,也是索引組織表,每一個索引有一個全局惟一的index_id。索引主要包括兩類:主鍵索引和二級索引,前面介紹的記錄格式,也就是主鍵索引的格式,包括key,value和meta三部分。二級索引也包含key,value和meta三部分,可是value中不包含任何數據,只是包含checksum信息。

主鍵索引

key

Value

Meta

Index_id

PK

NULL標記位

列數據

Checksum(可選)

SeqId,flag

二級索引

key

Value

Meta

Index_id

SecondaryKey

PK

Checksum(可選)

SeqId,flag

對比innodb引擎(innodb_file_format=Barracuda,row_format=compact)

innodb記錄格式

變長字段長度列表

NULL標記位

record_header

Trxid

Roll_ptr

列數據

create table row_format( id int not null, c1 int, c2 char(10) not null, c3 char(10), c4 varchar(10), c5 varchar(10) not null, c6 blob, c7 binary(10) not null, c8 varbinary(10)) engine=innodb; insert into row_format(id,c2,c4,c5,c7) values(1,'1234','ab','efg','111');

記錄內容:

0000c0b0  00 00 03 02 0a 1b 00 00  18 ff b5 00 00 00 00 28  |...............(|

0000c0c0  00 00 00 00 01 01 03 83  00 00 01 36 01 10 80 00  |...........6....|

0000c0d0  00 01 31 32 33 34 20 20  20 20 20 20 61 62 65 66  |..1234      abef|

0000c0e0  67 31 31 31 00 00 00 00  00 00 00 00 00 00 00 00  |g111............|

說明:

     1.   03 02 0a這裏存的是長度信息,全部非null的變長列信息都逆序存在一塊兒,這裏按前後順序是c5,c4,c2,這裏innodb將char(10)也看成變長字段處理了。

     2.  1b存儲的是null信息,與rocksdb對null處理一致。00 00  18 ff b5存儲的是record-header。

     3. 00 00 00 00 28 00 00 00 00 01 01 03 83  00 00 01 36 01 10, 這三部分別是rowid,trxid和roll_ptr,分別佔6個字節,6個字節和7個字節。

     4. 最後一部分是數據,null不佔任何存儲空間,與rocksdb處理相似。區別在於對於char類型的處理,innodb將字段c2類型char(10)補齊到10個字節,存儲爲31 32 33 34 20 20 20 20 20 20,將其做爲varchar處理,記錄了長度信息;而Rocksdb則是補齊到30個字節(utf8字符集),做爲char處理,不記錄長度信息。

      總體而言,innodb記錄格式包含了record_header(記錄頭信息),佔5個字節,主要包括記錄號(heap_no),列數目,下一條記錄的位置以及是否刪除等信息。rocksdb則相對簡單,只有總體的value-size,以及經過Meta中flag標示記錄的狀態put 或者是delete。innodb將變長列長度信息集中存放在一塊兒,使得查找任意列的代價都差很少,而rocksdb的變長列信息則是放在每列的前面,訪問最後一列須要逐一計算前面的列,才能定位。此外,因爲innodb引擎與rocksdb引擎因爲實現MVCC的機制不一樣,致使innodb引擎和rocksdb引擎須要存儲的額外信息也不一樣。Innodb實現MVCC依賴於回滾段信息,記錄須要額外存儲trxid和roll_ptr兩個字段,分別是6個字節和7個字節(type,rsegid,pageNO,offset),其中type佔一個bit位,標示insert 或者是update類型,rsegid回滾段id佔7bit位,pageNo佔4個字節,頁內偏移佔2個字節。Rocksdb實現MVCC則是依賴於SequenceID,經過SequenceID來判斷記錄的可見性,SequenceID佔7個字節。

      細節上來講,RocksDB引擎和innodb引擎在處理null,char和varchar的方式相似,但innodb對於char類型作了優化,統一做爲varchar處理。另外rocksdb引擎沒有對blob作特殊處理。你可能會有疑問,rocksdb不是也有block_size嗎,若是設置爲16k,blob數據超過16k怎麼辦?對於innodb而言,因爲表實質是以一個個page經過B-tree組織起來的,每一個page是固定大小,當記錄很是大時,就須要藉助溢出頁,經過連接的方式關聯起來。而rocksdb中block_size只是一個壓縮單位,並無嚴格約束,文件內容以block組織,因爲文件中block多是壓縮過的,所以每一個block的大小不固定,經過偏移來定位具體某個block的位置。若是遇到大的blob數據,則可能這個block比較大,記錄全部數據存儲在一塊兒,不會跨block。

      對於索引長度限制也有所不一樣,對於innodb引擎來講,索引中單列長度不能超過767個字節,而rocksdb引擎單列長度不超過2048個字節,具體能夠參考max_supported_key_part_length各自的實現;整個索引的長度,rocksdb和innodb都限制在3072個字節,其實是server層的限制,由於它們的各自限制的長度都比server層的大。具體能夠參考各自max_supported_key_length的實現。

參考文檔

http://dev.mysql.com/doc/refman/5.7/en/innodb-physical-record.html

http://hedengcheng.com/?p=127

http://www.cnblogs.com/zhoujinyi/articles/2726462.html

相關文章
相關標籤/搜索