設定 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->referenced_list中dict_foreign_t->referenced_index 置為null
修改t2表的引用關系
table->foreign_list中dict_foreign_t->referenced_index 置為null
而此bug修複之前, 并沒有修改t2表的引用關系, 進而導緻後面對t2表進行dml操作時,如果通路了無效的dict_foreign_t->referenced_index就會導緻crash。
删除被引用的索引後,應修改引用表的引用關系,即應修改table->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做為了新的被引用索引。
其實,相似索引的存在是完全沒有必要的。如果禁止建立相似的索引,那麼引用限制這塊的處理也不會這麼複雜了。