title: mysql · myrocks · myrocks之事務處理
mysql目前支援的事務引擎有innodb,tokudb. rocksdb加入mysql陣營後,mysql支援的事務引擎增長至3個。
myrocks目前支援的事務隔離級别有read-committed和repeatable-read. 同innodb一樣,myrocks也支援mvcc機制。
可以說,myrocks提供了很好的事務支援,能夠滿足的一般業務的事務需求。
談到rocksdb事務,就必須提及rocksdb中的sequence number機制。rocksdb中的每一條記錄都有一個sequence number, 這個sequence number存儲在記錄的key中。
對于同樣的user key記錄,在rocksdb中可能存在多條,但他們的sequence number不同。
sequence number是實作事務處理的關鍵,同時也是mvcc的基礎。
snapshot是rocksdb的快照資訊,snapshot實際就是對應一個sequence number.
簡單的講,假設snapshot的sequence number為sa, 那麼對于此snapshot來說,隻能看到sequence number<=sa的記錄,sequence number>sa的記錄是不可見的。
snapshot 結構
snapshot 主要包含sequence number和snapshot建立時間,sequence number 取自目前的sequence number.
snapshot 管理
snapshot由全局雙向連結清單管理,根據sequence number排序。snapshot的建立和删除都需要維護雙向連結清單。
snapshot與compact
rocksdb的compact操作與snapshot有緊密聯系。以我們熟悉的innodb為例,rocksdb的compact類似于innodb的purge操作, 而snapshot類似于innodb的read view. innodb做purge操作時會根據已有的read view來判斷哪些undo log可以purge,而rocksdb的compact操作會根據已有snapshot資訊即全局雙向連結清單來判斷哪些記錄在compace時可以清理。
判斷的大體原則是,從全局雙向連結清單取出最小的snapshot sequence number sn. 如果已删除的老記錄sequence number <=sn, 那麼這些老記錄在compact時可以清理掉。
有了snapshot,mvcc實作起來就很順利了。記錄的sequence number天然的提供了記錄的多版本資訊。
每次查詢使用者記錄時,并不需要加鎖。而是根據目前的sequence number sn建立一個snapshot, 查詢過程中隻取小于或等于sn的最大sequence number的記錄。查詢結束時釋放snapshot.
關鍵代碼段
隔離級别也是通過snapshot來實作的。在innodb中,隔離級别為read-committed時,事務中每的個stmt都會建立一個read view, 隔離級别為repeatable-read時,隻在事務開啟時建立一次read view. rocksdb同innodb類似,隔離級别為read-committed時,事務中每的個stmt都會建立一個snapshot, 隔離級别為repeatable-read時,隻在事務開啟時第一個stmt建立一次snapshot.
關鍵代碼片段
隔離級别實作差異
在read committed隔離級别下,如果一個大事務要更新1000w行,當它更新了前900w行時,
同時另一個事務已經更新了後100w行,那麼myrocks會重新擷取快照,再次嘗試更新,這樣
而之前的處理方式是直接報死鎖錯誤。
innodb不會出現上述情況,當第一個大事更新是會持有b樹的index lock, 第二個事務會一直等待index lock直至第一個事務送出完成。
myrocks目前隻支援一種鎖類型:排他鎖(x鎖),并且所有的鎖資訊都儲存在記憶體中。
鎖結構
每個鎖實際上存儲的哪條記錄被哪個事務鎖住。
每個鎖實際是key和lockinfo的映射. 鎖資訊都儲存在map中
為了減少全局鎖資訊通路的沖突, rocksdb将鎖資訊進行按key hash分區,
同時每個column family 存儲一個這樣的lockmap.
鎖相關參數:
max_num_locks:事務鎖個數限制
expiration:事務過期時間
通過設定以上兩個參數,來控制事務鎖占用過多的記憶體。
死鎖檢測
rocksdb内部實作了簡單的死鎖檢測機制,每次加鎖發生等待時都會向下面的map中插入一條等待資訊,表示一個事務id等待另一個事務id.
同時會檢查wait_txn_map_是否存在等待環路,存在環路則發生死鎖。
死鎖檢測關鍵代碼片段
死鎖檢測相關參數
deadlock_detect:是否開啟死鎖檢測
deadlock_detect_depth:死鎖檢查深度,預設50
gap lock
innodb中是存在gap lock的,主要是為了實作repeatable read和唯一性檢查的。
而在rocksdb中,不支援gap lock(rocksdb insert是也會多對唯一鍵加鎖,以防止重複插入,
嚴格的來講也算是gap lock).
那麼在rocksdb一些需要gap lock的地方,目前是報錯和列印日志來處理的。
相關參數
gap_lock_write_log: 隻列印日志,不傳回錯誤
gap_lock_raise_error: 列印日志并且傳回錯誤
鎖示例
直接看例子

myrocks最近也支援了binlog xa.
在開啟binlog的情況下,myrocks送出時,會經曆兩階段送出階段。
prepare階段,根據server層生成的xid(由mysqlxid+server_id+qurey_id組成),在rockdb内部執行2pc操作,生成prepare(xid),endprepare()記錄。
commit階段,根據事務成還是失敗,生成commit(xid)或rollback(xid)記錄。
myrocks在事務處理方面還有些不完善的地方,比如鎖類型隻有單一的x鎖,不支援gap lock,純記憶體鎖占用記憶體等。 myrocks社群正在持續改進中,一起期待。