My.cnf 增加一個(gè)配置項(xiàng),MySQL 不能啟動(dòng)了
有一天,同事問(wèn)了我一個(gè)來(lái)自客戶的問(wèn)題:在 my.cnf 中增加系統(tǒng)變量 foreign_key_checks 之后,MySQL 就啟動(dòng)不了了。
我們嘗試通過(guò) SET 命令修改 foreign_key_checks,成功了。查看官方文檔,明明也是有這個(gè)系統(tǒng)變量的:
圖片
為什么把 foreign_key_checks 加到配置文件里就不行了呢?
這就觸及到我的盲區(qū)了,同時(shí)也勾起了我的好奇心,搞清楚這個(gè)問(wèn)題勢(shì)在必行。
研究一番之后,發(fā)現(xiàn)并不是只有 foreign_key_checks 存在這種情況,還有一些系統(tǒng)變量也是這樣的,不能加到配置文件里,只能通過(guò) SET 命令修改。
如果你也對(duì)這個(gè)問(wèn)題感到好奇,我們就一起來(lái)探個(gè)究竟吧。
本文基于 MySQL 8.0.32 源碼。
1、問(wèn)題復(fù)現(xiàn)
在 my.cnf 中增加系統(tǒng)變量:
foreign_key_checks = 0
然后,啟動(dòng) MySQL,結(jié)果:?jiǎn)?dòng)失敗。查看 error.log,能看到如下錯(cuò)誤信息:
unknown variable 'foreign_key_checks = 0'
官方文檔里說(shuō) MySQL 支持這個(gè)系統(tǒng)變量,MySQL 說(shuō)我不認(rèn)識(shí)它,是不是有點(diǎn)奇怪?
不過(guò),不管怎樣,我們已經(jīng)復(fù)現(xiàn)了這個(gè)問(wèn)題,接下來(lái),繼續(xù)追根溯源。
2、原理介紹
MySQL 中,每個(gè)系統(tǒng)變量都會(huì)在代碼里定義,foreign_key_checks 定義如下:
圖片
我最開(kāi)始懷疑是 SESSION_VAR 的問(wèn)題,調(diào)試了一遍,發(fā)現(xiàn) SESSION_VAR 是無(wú)辜的,它并不是導(dǎo)致 MySQL 無(wú)法識(shí)別配置文件中 foreign_key_checks 的元兇。
調(diào)試過(guò)程中,發(fā)現(xiàn)了另一個(gè)疑似兇手,就是紅框里的 NO_CMD_LINE。
為了驗(yàn)證我的推測(cè),又在代碼中搜索了其它帶有 NO_CMD_LINE
標(biāo)志的系統(tǒng)變量,在其中發(fā)現(xiàn)了一個(gè)比較眼熟的家伙(autocommit
):
圖片
為了確認(rèn) autocommit 是否也會(huì)導(dǎo)致 MySQL 啟動(dòng)失敗,修改了配置文件:
# foreign_key_checks = 0
autocommit = 0
注釋掉配置文件中的 foreign_key_checks,加上了 autocommit,結(jié)果 MySQL 啟動(dòng)成功了。
完了,NO_CMD_LINE 看起來(lái)也不像兇手,探索之路就此進(jìn)入了尷尬的局面。
又經(jīng)過(guò)一番漫長(zhǎng)的東調(diào)西試,發(fā)現(xiàn)了 autocommit 的秘密:autocommit 有兩處定義,上面截圖是 sql/sys_vars.cc 里的定義,還有一處位于 sql/mysqld.cc 文件。
圖片
為了驗(yàn)證 MySQL 能識(shí)別配置文件中的 autocommit,是因?yàn)?my_long_options 中增加了 autocommit 的定義,我把 my_long_options 中的 autocommit 刪掉了,然后啟動(dòng) MySQL,結(jié)果失敗。
不過(guò),遺憾的是,并沒(méi)有報(bào) unknown variable 錯(cuò)誤,而是觸發(fā)了一個(gè)斷言錯(cuò)誤,和配置文件無(wú)關(guān),這條路徑驗(yàn)證失敗。
為了繼續(xù)驗(yàn)證我的推測(cè),在 sql/sys_vars.cc 中增加了一個(gè)帶有 NO_CMD_LINE 標(biāo)志的自定義系統(tǒng)變量,并加到配置文件中,然后,啟動(dòng) MySQL,結(jié)果:?jiǎn)?dòng)失敗,報(bào)錯(cuò):unknown variable xxx。
接著,我又在 sql/mysqld.cc 的 my_long_options 中增加了這個(gè)自定義的系統(tǒng)變量,然后,啟動(dòng) MySQL,結(jié)果:?jiǎn)?dòng)成功。
這就證明了我的推測(cè):sql/sys_vars.cc 中定義的系統(tǒng)變量,如果包含了 NO_CMD_LINE,MySQL 啟動(dòng)過(guò)程中不能識(shí)別。
這種系統(tǒng)變量加入 sql/mysqld.cc 的 my_long_options 數(shù)組之后,MySQL 啟動(dòng)過(guò)程中就能識(shí)別了,autocommit 就是這么干的。
另外,調(diào)試過(guò)程中還有另一個(gè)發(fā)現(xiàn):sql/mysqld.cc 的 my_long_early_options 數(shù)組,也具有和 my_long_options 一樣的功能。
概括來(lái)說(shuō),NO_CMD_LINE 是一扇門(mén),my_long_options、my_long_early_options 是兩扇窗,某個(gè)系統(tǒng)變量被 NO_CMD_LINE 關(guān)起來(lái)閉門(mén)思過(guò)之后,如果開(kāi)了其中一扇窗,這個(gè)系統(tǒng)變量就可以從窗戶上逃出來(lái)了。
3、簡(jiǎn)單的分辨方法
有些系統(tǒng)變量能在配置文件中配置,有些變量又不能,我們想知道哪些變量能,哪些變量不能,除了擼代碼還有別的方法嗎?
當(dāng)然是有的,用這個(gè)命令就可以:
/path/mysqld --verbose --help | grep "xxx"
例如,查看 foreign_key_checks 是否能通過(guò)配置文件配置:
./mysqld --verbose --help | grep "foreign-key-checks"
執(zhí)行命令沒(méi)有任何輸出,說(shuō)明 foreign_key_checks 不能通過(guò)配置文件配置。
查看 autocommit 是否能通過(guò)配置文件配置:
./mysqld --verbose --help | grep "autocommit"
# 輸出如下
--autocommit Set default value for autocommit (0 or 1)
(Defaults to on; use --skip-autocommit to disable.)
輸出結(jié)果中有 --autocommit balabala,說(shuō)明 autocommit 能通過(guò)配置文件配置。
注意:grep 后面系統(tǒng)變量名中的下劃線需要替換為中劃線,例如 foreign-key-checks。
4、總結(jié)
對(duì)于官方文檔里說(shuō)明 MySQL 支持的系統(tǒng)變量,如果我們?cè)谂渲梦募╩y.cnf)中增加了一個(gè)系統(tǒng)變量,MySQL 啟動(dòng)時(shí)報(bào) unknown variable xxx 錯(cuò)誤,說(shuō)明這個(gè)系統(tǒng)變量不支持通過(guò)配置文件配置。
另一種分辨方法是執(zhí)行 /path/mysqld --verbose --help | grep "xxx" 命令,如果輸出結(jié)果中沒(méi)有 --xxx balabala 這樣的信息,也可以說(shuō)明這個(gè)系統(tǒng)變量不支持通過(guò)配置文件配置。
本文轉(zhuǎn)載自微信公眾號(hào)「一樹(shù)一溪」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系一樹(shù)一溪公眾號(hào)。