mysql5.7 : innodb 事務子系統優化
本文總體介紹了幾個和事務子系統相關的worklog以及其代碼實作。這部分代碼值得細讀,因為他們是5.7 innodb比較核心的改動,極大的提升了隻讀場景下的性能。
這個worklog包含幾點變化:
第一,無需顯示的開啟隻讀事務,所有的事務開始預設為隻讀事務,當遇到讀寫sql時,自動加入讀寫清單。
第二,隻讀事務不為其配置設定事務id,是以如果show engine innodb status時看到大量事務的id表現的很怪異時(非常大的整數值),不要覺得奇怪。
該改進帶來的最大的好處是你無需修改你的業務sql。其實這才是使用者能接受的特性,如果沒有量級别的提升,誰會願意去改代碼呢?
不過顯而易見的,這個優化也帶了某些 運維的‘退化’,例如你再也無法從show engine innodb status中發現一個活躍的長時間不送出的隻讀事務(例如:begin;select;select…),你需要去查詢innodb_trx表來獲得這些資訊。
我們以一個典型的例子來開啟這個話題,首先準備一個簡單的表。隔離級别為read-commit
create table t1 (a int primary key, b int);
insert into t1 values (1,rand()*100),(2,rand()*100);
begin;
以begin顯式開啟一個事務;
b) select * from t1;
配置設定一個事務句柄:
ha_innobase::open ha_innobase::info_low update_thd check_trx_exists innobase_trx_allocate trx_allocate_for_mysql
新配置設定的事務句柄會加入到trx_sys->mysql_trx_list,并重複使用。
開始一個隻讀事務,開啟的事務,不配置設定事務id, 不配置設定復原段
row_search_mvcc->trx_start_if_not_started->trx_start_low
assign read view
row_search_mvcc trx_assign_read_view mvcc::view_open
配置設定的read view會拷貝目前的活躍事務id,設定最高和最低可見事務id,然後加入到活躍事務的read view連結清單上(mvcc::m_views)
c) update t1 set b=b+1 where a=2;
lock_table trx_set_rw_mode
将事務轉換成讀寫事務模式,配置設定復原段,配置設定事務id。加入讀寫事務連結清單(trx_sys->rw_trx_ids, trx_sys->rw_trx_set, trx_sys->rw_trx_list)。
d) commit; 事務送出
代碼:
<a href="http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/5209">http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/5209</a>
在該worklog種優化了read view的建立,對mvcc控制視圖部分的代碼進行了重構。
具體包括以下幾個方面:
在之前版本中,read view的建立的複雜度為o(n),因為需要掃描讀寫事務連結清單;
現在建立一個read view 需要以下幾步:
step 1:
view->prepare(trx->id);
拷貝事務id(不包含自己的事務id),相當于做一個目前活躍讀寫事務的快照存放在視圖中,直接使用memcpy的方式 (copy_trx_ids(trx_sys->rw_trx_ids)),這一點和percona的優化是一樣的。
設定m_low_limit_no ,m_low_limit_id
step 2:
view->complete();
設定視圖的m_up_limit_id,表示所有小于這個值的修改都可見
step 3:
ut_list_add_first(m_views, view);
将視圖加入到活躍視圖連結清單中。
b) 在之前版本中是在持有trx_sys mutex時建立的read view。
為了降低配置設定/釋放read view的開銷,維護了兩個read view連結清單,一個用于放目前活躍的視圖連結清單,一個用于放空閑的、可配置設定的視圖連結清單。
當系統啟動時,會初始化一定數量的read view放到空閑連結清單上。
percona實作了類似的方案,不同的是percona的read view在事務完成後不是放到空閑連結清單,而是下次繼續重用(但從活躍連結清單移除,不管是否是讀寫事務)
c) 對于autocommit的隻讀事務,即時目前沒有活躍事務,也可能因為建立read view ,而大量别的線程在釋放read view,導緻trx_sys mutex沖突。
針對該問題,實際上我已經在博文http://mysqllover.com/?p=1087中描述過了,對于自動送出的查詢,在關閉read view時是不從視圖連結清單上移除的,在再次開啟事務重用該read view時,如果這期間沒有讀寫事務,都無需重新初始化read view,直接使用即可。 是以如果一台執行個體上的都是自動送出的隻讀事務,完全可以避免trx_sys mutex的開銷。
d) 需要持有trx_sys mutex來周遊rw_trx_list,以判斷更改是否可見,或者根據事務id擷取事務對象trx_t
由于已經儲存了事務id的快照,是以直接根據二分查找查找有序數組即可,無需周遊讀寫事務連結清單
參考函數readview::changes_visible
trx_sys_t新增成員rw_trx_set,用于維護從trx_id到trx_t的映射。這樣可以根據事務id快速找到對應的事務對象而無需掃描事務連結清單。參考函數trx_get_rw_trx_by_id
主要更改:
<a href="http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/6203">http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/6203</a>
<a href="http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/6204">http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/6204</a>
<a href="http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/6205">http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/6205</a>
<a href="http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/6224">http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/6224</a>
<a href="http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/6236">http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/6236</a>
在完成上述修改後,無需再維持ro_trx_list了,因為所有事務預設都被當做隻讀事務,這個連結清單開銷完全可以忽略掉的。
<a href="http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/6788">http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/6788</a>
該worklog主要實作了隐式鎖向現式鎖轉換的一個優化點。在擷取活躍事務對象時,無需持有lock_sys mutex鎖。
在連續記憶體中預配置設定事務對象,保持記憶體的連續性有利于編譯器或者cpu做出某些優化,例如記憶體預取之類的(不是很了解這一塊,不展開叙述)
為了實作事務對象記憶體配置設定,回收等,在底層分裝了一些pool類,事務對象實際上被管理在一個池結構中。
後面我再單獨寫一篇部落格來介紹新加的這些底層結構。
在啟動時,初始化trx_pools (trx_pool_init),初始化時配置設定4m記憶體,在shutdown時釋放(trx_pool_close())
為了管理事務對象池,設計了三個類:trxfactory,trxpoollock,trxpoolmanagerlock
擷取事務對象:trx_create_low —-> trx_pools->get()
釋放事務對象:trx_free —-> trx_pools->free(trx)
<a href="http://dev.mysql.com/worklog/task/?id=6906">http://dev.mysql.com/worklog/task/?id=6906</a>
相關代碼:
<a href="http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/5744">http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/5744</a>
<a href="http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/5750">http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/5750</a>
http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/5753
<a href="http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/5756">http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/5756</a>
<a href="http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/5786">http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/5786</a>