天天看點

MySQL · 捉蟲動态 · 删被引用索引導緻crash

設定 foreign_key_checks=0 删除被引用的索引後,再設定foreign_key_checks=1,對引用表進行dml操作會導緻 mysqld crash,以下是重制的測例:

## 分析

對于引用限制,在mysql實作中引用表和被引用表都會記錄表的引用關系。

以下連結清單記錄該表引用了哪些表,連結清單中每個元素為 dict_foreign_t

以下連結清單記錄該表被哪些表引用,連結清單中每個元素為 dict_foreign_t

dict_foreign_t 結構如下

對于上面的測例,t1為被引用表,t2為引用表。

對于t1

table->foreign_list 為null

table->referenced_list 則記錄了引用關系

對于t2

table->foreign_list 記錄了引用關系

table->referenced_list 則為null

對于删除索引操作,如果索引涉及到引用關系,那麼對應引用關系中的索引也應該做相應調整。對于測例中的删索引操作 <code>alter table t1 drop key idx1;</code> 應該做如下調整

修改t1表的引用關系

table-&gt;referenced_list中dict_foreign_t-&gt;referenced_index 置為null

修改t2表的引用關系

table-&gt;foreign_list中dict_foreign_t-&gt;referenced_index 置為null

而此bug修複之前, 并沒有修改t2表的引用關系, 進而導緻後面對t2表進行dml操作時,如果通路了無效的dict_foreign_t-&gt;referenced_index就會導緻crash。

删除被引用的索引後,應修改引用表的引用關系,即應修改table-&gt;referenced_list。

如果删除引用表對應的外鍵時,mysql如何處理的呢?同删除被引用表的索引一樣,都需要調整引用表和被引用表的關系。

實際上,當删除引用表對應的外鍵時,如果存在和此外鍵相似(這裡的相似是指索引列數和列順序相同)的索引時,會用相似的索引代替删除的外鍵,進而保持原有的引用限制關系。

例如,在下面的例子中,原來t2存在有兩個索引idx1和idx2,都可以作為外鍵,函數<code>dict_foreign_find_index</code>選擇了idx1作為外鍵。當删除idx1後,同樣通過dict_foreign_find_index選擇了idx2做為了外鍵。

同樣,此bug修複後,删除被引用表的索引時,如果存在相似的索引會用相似的索引代替。

例如,在下面的例子中,原來t1存在有兩個索引idx1和idx2,都可以作為被引用索引,函數dict_foreign_find_index選擇了idx1作為被引用索引。當删除idx1後,同樣通過dict_foreign_find_index選擇了idx2做為了新的被引用索引。

其實,相似索引的存在是完全沒有必要的。如果禁止建立相似的索引,那麼引用限制這塊的處理也不會這麼複雜了。

繼續閱讀