MySQL · myrocks · myrocks寫入分析
寫入流程
myrocks的寫入流程可以簡單的分為以下幾步來完成
- 將解析後的記錄(kTypeValue/kTypeDeletion)寫入到WriteBatch中
- 將WAL日誌寫入log文件
- 將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鏈接。如下圖所示
每個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的狀態變遷圖如下:
group非並發寫時writer的狀態變遷圖如下:
源碼結構圖如下(圖片來自林青)
上麵的圖是在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