閱讀61 返回首頁    go 阿裏雲 go 技術社區[雲棲]


MySQL · myrocks · myrocks寫入分析

寫入流程

myrocks的寫入流程可以簡單的分為以下幾步來完成

  1. 將解析後的記錄(kTypeValue/kTypeDeletion)寫入到WriteBatch中
  2. 將WAL日誌寫入log文件
  3. 將WriteBatch中的內容寫到memtable中,事務完成

其中第2,3步在提交時完成

WriteBatch與Myrocks事務處理密切相關,事務中的記錄提交前都以字符串的形式存儲在WriteBatch->rep_中,要麼都提交,要麼都回滾。 回滾的邏輯比較簡單,隻需要清理WriteBatch->rep_即可。詳見TransactionImpl::Rollback

一個簡單的insert 寫入WriteBatch堆棧如下

#0  rocksdb::WriteBatchInternal::Put
#1  rocksdb::WriteBatch::Put
#2  myrocks::ha_rocksdb::update_pk
#3  myrocks::ha_rocksdb::update_indexes
#4  myrocks::ha_rocksdb::update_write_row
#5  myrocks::ha_rocksdb::write_row
#6  handler::ha_write_row
#7  write_record
#8  mysql_insert
#9  mysql_execute_command
#10 mysql_parse
#11 dispatch_command
#12 do_command
#13 do_handle_one_connection

一個簡單的insert commit堆棧如下

#0  rocksdb::InlineSkipList<rocksdb::MemTableRep::KeyComparator const&>::Insert
#1  rocksdb::(anonymous namespace)::SkipListRep::Insert
#2  rocksdb::MemTable::Add
#3  rocksdb::MemTableInserter::PutCF
#4  rocksdb::WriteBatch::Iterate
#5  rocksdb::WriteBatch::Iterate
#6  rocksdb::WriteBatchInternal::InsertInto
#7  rocksdb::DBImpl::WriteImpl
#8  rocksdb::DBImpl::Write 
#9  rocksdb::TransactionImpl::Commit
#10 myrocks::Rdb_transaction_impl::commit_no_binlog
#11 myrocks::Rdb_transaction::commit
#12 myrocks::rocksdb_commit
#13 ha_commit_low
#14 TC_LOG_MMAP::commit 
#15 ha_commit_trans
#16 trans_commit_stmt
#17 mysql_execute_command
#18 mysql_parse
#19 dispatch_command
#20 do_command
#21 do_handle_one_connection

提交流程及優化

這裏隻分析rocksdb引擎的提交流程,實際MyRocks提交時還需先寫binlog(binlog開啟的情況).

rocksdb引擎提交時就完成兩個事情
1. 寫WAL日誌(WAL開啟的情況下rocksdb_write_disable_wal=off)
2. 將之前的WriteBatch寫入到memtable中

然而,寫WAL是一個串行操作。為了提高提交的效率, rocksdb引入了group commit機製。

待提交的事務都依次加入到提交的writer隊列中,這個writer隊列被劃分為一個一個group. 每個group有一個leader, 其他為follower,leader負責批量寫WAL。每個group由雙向鏈表link_older, link_newer鏈接。如下圖所示

屏幕快照 2017-07-11 下午7.46.22.png

每個writer可能的狀態如下

  • Init: writer的初始狀態
  • Header: writer被選為leader
  • Follower: writer被選為follower
  • LockedWating: writer在等待自己轉變為指定的狀態
  • Completed:writer操作完成

writer的狀態變遷跟group是否並發寫memtable有關
當開啟並發寫memtable(rocksdb_allow_concurrent_memtable_write=on)且group中的writer至少有兩個時,group才會並發寫。

group並發寫時writer的狀態變遷圖如下:

屏幕快照 2017-07-14 下午1.25.27.png

group非並發寫時writer的狀態變遷圖如下:

屏幕快照 2017-07-11 下午7.46.50.png

源碼結構圖如下(圖片來自林青)
屏幕快照 2017-07-14 下午1.44.46.png

上麵的圖是在group內writer並發寫memtable的情形。
非並發寫memtable時,沒有LaunchParallelFollowers/CompleteParallelWorker, Insertmemtable是由leader串行寫入的。
這裏group commit有以下要點
1. 同一時刻隻有一個leader, leader完成操作後,才設置下一個leader
2. 需要等一個group都完成後,才會進行下一個group
3. group中最後一個完成的writer負責完成提交和設置下一個leader
4. Leader 負責批量寫WAL
5. 隻有leader才會去調整雙向鏈表link_older,link_newer.

注意這裏2,3 應該可以優化改進為

  • 不需要等一個group完成再進行下一個group
  • 不同group的follower可以並發執行
  • 隻有leader負責完成提交和設置下一個leader

寫入控製

rocksdb在提交寫入時,需考慮以下幾種情況,詳見PreprocessWrite

  • WAL日誌滿,WAL日誌超過rocksdb_max_total_wal_size,會從所有的colomn family中找出含有最老日誌(the earliest log containing a prepared section)的column family進行flush, 以釋放WAL日誌空間
  • Buffer滿,全局的write buffer超過rocksdb_db_write_buffer_size時,會從所有的colomn family中找出最先創建的memtable進行切換,詳見HandleWriteBufferFull
  • 某些條件會觸發延遲寫
    • max_write_buffer_number > 3且 未刷immutable memtable總數 >=max_write_buffer_number-1
    • 自動compact開啟時,level0的文件總數 >= level0_slowdown_writes_trigger
  • 某些條件會觸發停寫
    • 未刷immutable memtable總數 >=max_write_buffer_number
    • 自動compact開啟時,level0的文件總數 >= level0_stop_writes_trigger

具體可參考RecalculateWriteStallConditions

總結

rocksdb寫入流程還有優化空間,Facebook也有相關的優化。

最後更新:2017-07-21 09:03:08

  上一篇:go  HybridDB · 源碼分析 · MemoryContext 內存管理和內存異常分析
  下一篇:go  MSSQL · 實現分析 · Extend Event實現審計日誌對SQL Server性能影響