MySQL+InnoDB semi-consitent read原理及實現分析

來源:互聯網
上載者:User

標籤:執行   return   mysq   str   not   分析   commit   詳細分析   條件   

semi-consistent簡介

對於熟悉MySQL,或者是看過InnoDB源碼的朋友們來說,可能會聽說過一個新鮮的名詞:semi-consistent read 。 何謂semi-consistent read?以下一段文字,摘於semi-consistent read一文:

A type of read operation used for UPDATE statements, that is a combination of read committed and consistent read. When an UPDATE statement examines a row that is already locked, InnoDB returns the latest committed version to MySQL so that MySQL can determine whether the row matches the WHERE condition of the UPDATE. If the row matches (must be updated), MySQL reads the row again, and this time InnoDB either locks it or waits for a lock on it. This type of read operation can only happen when the transaction has the read committed isolation level, or when the innodb_locks_unsafe_for_binlog option is enabled.

簡單來說,semi-consistent read是read committed與consistent read兩者的結合。一個update語句,如果讀到一行已經加鎖的記錄,此時InnoDB返回記錄最近提交的版本,由MySQL上層判斷此版本是否滿足update的where條件。若滿足(需要更新),則MySQL會重新發起一次讀操作,此時會讀取行的最新版本(並加鎖)。

semi-consistent read只會發生在read committed隔離等級下,或者是參數innodb_locks_unsafe_for_binlog被設定為true。

MySQL server與InnoDB引擎是如何進行互動?InnoDB引擎如何?semi-consistent read?請見下面的詳細分析。

semi-consistent實現MySQL Server層

從上面的描述中可以看出,semi-consistent read僅僅針對於update操作,因此在sql_update.cc的mysql_update方法中,有如下調用:

sql_update.cc::mysql_update()

// 通知底層引擎,嘗試進行semi consistent read

    // 是否真正進行semi consistent read,由底層引擎決定

table->file->try_semi_consistent_read(1);

// InnoDB引擎決定當前update是否可以進行semi-consistent read

        // 具體的處理方法,在下節中分析

ha_innodb.cc::try_semi_consistent_read(bool yes);

// 進行update的讀與更新操作

// update操作完成之後,關閉semi-consistent read

table->file->try_semi_consistent_read(0);

MySQL Server層處理semi-consistent較為簡單,接下來看看InnoDB Engine的處理方式。

InnoDB Engine層

InnoDB Engine層面,對於semi-consistent read的處理,包括兩方面的邏輯:

  • 判斷當前語句是否可以支援semi-consistent read
  • fetch next時,對於semi-consistent read的特殊處理
是否採用semi-consistent read

前面提到,MySQL Server在update時,會調用引擎的try_semi_consistent_read方法,來嘗試進行semi-consistent read,而是否進行semi-consistent read,則交由底層處理。

ha_innodb.cc::try_semi_consistent_read()

if (yes &&

(srv_locks_unsafe_for_binlog

|| prebuilt->trx->isolation_level <= TRX_ISO_READ_COMMITTED))

prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;

簡單分析下,當使用者佈建系統參數innodb_locks_unsafe_for_binlog為true,或者是採用的交易隔離等級為read committed(或以下)時,設定prebuilt->row_read_type參數,標識當前update語句使用semi-consistent read,fetch next時需要有針對性的做特殊處理。

Fetch Next特殊處理邏輯

InnoDB fetch next的主函數入口是row_search_for_mysql,此函數如何針對性的處理semi-consistent read呢?

row0sel.c::row_search_for_mysql()

// 嘗試對於定位到的記錄加鎖

err = sel_set_rec_lock();

case DB_LOCK_WAIT:

// 如果加鎖需要等待,則判斷是否可以進行semi-consistent read

        // 判斷條件為:

        // 1. prebuilt->row_read_type必須設定為ROW_READ_TRY_SEMI_CONSISTEN

        // 2. 當前scan必須是range scan或者是全表掃描,而非unique scan

        // 3. 當前索引必須是聚簇索引

        // 4. 不滿足以上三個條件,就不能進行semi-consistent read,進行加鎖等待

        // 注意:若不需要加鎖等待,那麼也不需要進行semi-consistent read,直接

        // 讀取記錄的最新版本即可,沒有加鎖等待的開銷。

if ((prebuilt->row_read_type != ROW_READ_TRY_SEMI_CONSISTENT)

            || unique_search

|| index != clust_index)

goto lock_wait_or_error;

// 可以進行semi-consistent read,根據記錄的目前的版本,構造最新的commit版本

        // 若沒有commit版本,目前的版本為最新版本,則直接讀取下一條記錄

        // 若存在commit版本,則設定did_semi_consistent_read為TRUE

row_sel_build_committed_vers_for_mysql();

if (old_vers == NULL)

goto next_rec;

did_semi_consistent_read = TRUE;

// 若本次update scan,由於加鎖等待,使用了semi-consistent,則設定相應的參數

        // 該參數,在下一小節提到的MySQL針對semi-consistent最佳化中有用

if (did_semi_consistent_read)

prebuilt->row_read_type = ROW_READ_DID_SEMI_CONSISTENT;

else

prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;

// 至此,InnoDB的fetch next針對semi-consistent read的處理完畢

最佳化:Unlock unmatched row

上面提到的是semi-consistent read的功能實現,除此之外,MySQL針對semi-consistent read,還做了最佳化措施:對於update scan返回的不滿足條件的記錄,提前放鎖。

MySQL Server層流程

sql_update.cc::mysql_update()

// 判斷當前scan返回的記錄,是否滿足update的where條件

    // 若滿足,則進行update操作

if (!(select && select->skip_record())

// 若不滿足update的where條件,則選擇將目前記錄上的行鎖提前釋放

else

table->file->unlock_row();

InnoDB Engine層流程

ha_innobd.cc::unlock_row();

switch (prebuilt->row_read_type)

// 若系統未設定參數innodb_locks_unsafe_for_binlog,同時隔離等級大於

    // TRX_ISO_READ_COMMITTED,則不可提前釋放不滿足條件的行鎖

    // 否則可以提前釋放不滿足條件的行鎖

case ROW_READ_WITH_LOCKS:

if (!srv_locks_unsafe_for_binlog &&

prebuilt->trx->isolation_level > TRX_ISO_READ_COMMITTED)

break;

// 若當前系統已採用SEMI_CONSISTENT read,但是沒有鎖等待,加鎖直接成功

    // 那麼此時直接釋放不滿足條件的行鎖

case ROW_READ_TRY_SEMI_CONSISTENT:

row_unlock_for_mysql();

// 若當前系統已採用SEMI_CONSISTENT read,並且有鎖等待,構造了commit版本

    // 沒有在commit版本上加鎖,因此也無鎖可放,直接返回即可

case ROW_READ_DID_SEMI_CONSISTENT:

prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;

break;

semi-consistent優缺點分析優點
  • 減少了更新同一行記錄時的衝突,減少鎖等待。

    無並發衝突,讀記錄最新版本並加鎖;有並發衝突,讀事務最新的commit版本,不加鎖,無需鎖等待。

  • 可以提前放鎖,進一步減少並發衝突機率。

    對於不滿足update更新條件的記錄,可以提前放鎖,減少並發衝突的機率。

  • 在理解了semi-consistent read原理及實現方案的基礎上,可以酌情考慮使用semi-consistent read,提高系統的並發效能。
缺點
  • 非衝突序列化策略,因此對於binlog來說,是不安全的

    兩條語句,根據執行順序與提交順序的不同,通過binlog複製到備庫後的結果也會不同。不是完全的衝突序列化結果。

    因此只能在事務的隔離等級為read committed(或以下),或者設定了innodb_locks_unsafe_for_binlog參數的情況下才能夠使用。

測試案例構造semi-consistent read

set binlog_format=mixed;

set session transaction isolation level repeatable read;

create table t1(a int not null) engine=innodb DEFAULT CHARSET=latin1;

insert into t1 values (1),(2),(3),(4),(5),(6),(7);

session 1:                                                session 2:

set autocommit=0;

update t1 set a = a + 10;

set binlog_format=mixed;

set session transaction isolation level read committed;

update t1 set a = a + 100 where a > 10;

此時,session 2不需要等待session 1,雖然session 1的更新後項滿足session 2的條件,但是由於session 2進行了semi-consistent read,讀取到的記錄的前項為(1-7),不滿足session 2的更新where條件,因此session 2直接返回。

session 2直接返回,0 rows affected。

構造unlock unmatched row

set binlog_format=mixed;

set session transaction isolation level repeatable read;

create table t1(a int not null) engine=innodb DEFAULT CHARSET=latin1;

insert into t1 values (1),(2),(3),(4),(5),(6),(7);

session 1:                                                session 2:

set autocommit=0;

update t1 set a = a + 10;

commit;

set binlog_format=mixed;

set session transaction isolation level repeatable read;

set autocommit = 0;

update t1 set a = a + 100 where a < 10;

select * from t1 lock in share mode;

session 1在session 2開始前已經提交,session 2可以進行semi-consistent read。並且讀到的都是session 1的更新後項,完成加鎖。但是由於更新後項均不滿足session 2的where條件,session 2會釋放所有行上的鎖(由MySQL Server層判斷並調用unlock_row方法釋允許存取鎖)。

此時,session 1再次執行select * from t1 lock in share mode語句,直接成功。因為session 2已經將所有的行鎖提前釋放。

朋友們可以試試將session 2的隔離等級改為repeatable read,那麼此時session 1就會等待session 2提交。

 

更多內容:http://www.wangzhanjianshegs.com/網站建設

MySQL+InnoDB semi-consitent read原理及實現分析

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.