天天看點

MySQL核心月報 2014.08-MySQL· 捉蟲動态·long semaphore waits

<b>現象描述:</b>

innodb引擎,父表和子表通過foreign constraint進行關聯,因為在更新資料時需要check外鍵constraint,當父表被大量的子表referenced時候,那麼在open innodb資料字典的時候,需要open所有的child table和所有的foreign constraint,導緻持有dict_sys-&gt;mutex時間過長,産生long semaphore wait, 然後innodb crash了。

<b>case複現</b>

<b>分析過程</b>

<b>1. 資料字典</b>

innodb使用系統表空間儲存表相關的資料字典,系統的資料字典包括:

在load某個表的時候,分别從這些表中把表相關的index,column, index_field, foreign, foreign_col資料儲存到dictionary cache中。 對應的記憶體對象分别是:dict_col_struct,dict_field_struct,dict_index_struct,dict_table_struct,dict_foreign_struct。

<b>2. open過程</b>

dict_load_table:

<b>3. load foreign的詳細過程</b>

3.1 根據表名t1 查找sys_foreign.

而sys_foreign表上一共有三個索引:     

是以,根據for_name='t1', ref_name='t1'檢索出來所有相關的foreign_id.

3.2 加入cache

因為沒有專門的cache,foreign分别加入到for_name-&gt;foreign_list, ref_name-&gt;referenced_list。 問題的關鍵:因為foreign是全局唯一的,但foreign又與兩個表關聯,是以,有可能在open 其它表的時候已經打開過,是以,create foreign對象後,需要判斷以下四個list,是否已經存在,如果存在就直接使用。

dict_foreign_find:分别查詢這四個list,如果已經存在,則free建立的foreign對象,引用已經存在的。

如果不存在,把建立的foreign加入到for_name-&gt;foreign_list,ref_name-&gt;referenced_list連結清單中。

<b>4. 問題的原因:</b>

因為第一次load,是以find都沒有找到,但這四個都是list,随着open的越來越多,檢索的代價越來越大。 而整個過程中,都一直持有trx_sys-&gt;mutex,最終導緻了long semaphore wait。

<b>5. 問題改進方法:</b>

在mysql 5.5.39版本中,進行了修複,修複的方法就是,除了foreign_list,referenced_list。 另外又增加了兩個red_black tree,如下源碼所示:

這樣dict_foreign_find的過程中,通過red_black tree進行檢索,時間複雜度降到o(log n).

繼續閱讀