mysql併發執行緒控制之innodb_thread_concurrency的改進

myownstars發表於2015-03-08

innodb_thread_concurrency演算法介紹

1、server層到innodb層讀寫資料是一條一條記錄進行的,每次讀寫都會進/出一次InnoDB層(row_search_for_mysql),進入InnoDB層的時候會檢查當前併發執行緒數目,當超過innodb_thread_concurrency時,執行緒將嘗試spin和sleep並再次檢查,如果併發數還是超過innodb_thread_concurrency,執行緒將進入到一個FIFO中等待被喚醒,讀寫記錄結束後退出InnoDB層時會將當前併發執行緒數減1,並檢查其是否低於innodb_thread_concurrency,如果是的話,從FIFO中喚醒一個等待的執行緒,保證併發執行緒不會超過innodb_thread_concurrency引數。

2、當執行緒進入InnoDB層後,但在獲取資料時由於鎖請求無法得到滿足而需要掛起時,執行緒將強制退出InnoDB層,當鎖請求滿足後,執行緒繼續執行並強制進入到InnoDB層,這會導致實際併發執行緒數不是嚴格控制在innodb_thread_concurrency之內

 

 

程式碼呼叫邏輯

主要函式:

innodb_srv_conc_enter_innodb

innodb_srv_conc_exit_innodb

srv_conc_force_enter_innodb

srv_conc_force_exit_innodb

 

呼叫邏輯

進入/退出InnoDB層

ha_innobase::index_read

ha_innobase::general_fetch

row_check_index_for_mysql

                ...

                innodb_srv_conc_enter_innodb(prebuilt->trx);

 

                ret = row_search_for_mysql((byte*) buf, mode, prebuilt,  match_mode, 0);

 

                innodb_srv_conc_exit_innodb(prebuilt->trx);

                ...

 

 

鎖等待邏輯:

srv_suspend_mysql_thread

                ...

                if (trx->declared_to_be_inside_innodb) {

 

                                was_declared_inside_innodb = TRUE;

 

                                /* We must declare this OS thread to exit InnoDB, since a

                                possible other thread holding a lock which this thread waits

                                for must be allowed to enter, sooner or later */

                                srv_conc_force_exit_innodb(trx);

                }

 

                /* Suspend this thread and wait for the event. */

 

                thd_wait_begin(trx->mysql_thd, THD_WAIT_ROW_LOCK);

                os_event_wait(event);

                thd_wait_end(trx->mysql_thd);

 

                if (was_declared_inside_innodb) {

 

                                /* Return back inside InnoDB */

                                srv_conc_force_enter_innodb(trx);

                }

 

 

5.6的改進

從5.6開始,預設編譯會使用GCC atomic,可避免熱點鎖srv_conc_mutex的頻繁加鎖/釋放。

Pre-5.5如果超過併發限制時,就給其執行緒分配一個slot,讓其進入訊號量等待,原子操作則無需使用訊號量。

function srv_conc_enter_innodb:

ifdef HAVE_ATOMIC_BUILTINS

    srv_conc_enter_innodb_with_atomics(trx);

#else

    srv_conc_enter_innodb_without_atomics(trx);

#endif /* HAVE_ATOMIC_BUILTINS */

 

5.6引入adaptive sleep,允許innodb根據系統負載進行自適應調整,當innodb_adaptive_max_sleep_delay>0, innodb_thread_sleep_delay則 會動態調整(以前者為上限)。

ada還ptive 的演算法是:

  如果sleep了當前值以後還是不能進入,就把sleep時間+1;

  如果還有執行緒在sleep時,已經有了空閒執行緒,就把當前值的sleep 時間減半。

innodb_thread_sleep_delay降低比增加的更快,這樣在併發執行緒數很高時,當限制併發數早就達到,其他執行緒的每次sleep時間會緩慢拉長。而當Innodb層很空閒時,sleep時間又會快速降到非常低

調整sleep到一個最佳化值的目的是,過小的sleep值可能會產生太多的執行緒切換,但過長的sleep時間,在併發比較空閒的時候又會影響效能。新的併發控制策略有利於隨著負載的變化而做自適應調整。


 

Innodb_thread_concurrency不足

1執行緒因為鎖等待而退出innodb層,當獲取鎖時可以直接重入innodb(跳過此引數檢查),因此係統併發執行的執行緒數可能大於此引數值。

2程式碼路徑靠後,此時執行緒已經開始執行命令,進入到ha_innobase,應該在mysql_start_query前限制即thread_running。

為此好多技術高手開發了相應patch,從server層控制併發執行的執行緒數,下文將做描述。

 

 

參考文章

 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/15480802/viewspace-1452232/,如需轉載,請註明出處,否則將追究法律責任。

相關文章