接上一篇 Nginx 和 PHP-FPM 許可權安全配置最後一段關於資料庫連接失敗的處理。
之前配置WordPress時, 當時想讓mysql串連走tcp, google一下就得到答案:
define('DB_HOST', '127.0.0.1');
但是, 當時沒注意看wp-config.php配置, 原先已經有一條配置了:
define('DB_HOST', 'localhost');
後來在針對Discuz論壇做許可權處理時, 因為mysql也是走的unix domain socket, 所以想看看如何改為走tcp, 當時看到是配了localhost, 總想著是不是配一個連接埠上去就強制走tcp了。
折騰了半天, 還是不行, 直接通過報錯定位PHP代碼, 最終定位到 mysql_connect()函數上。
看了文檔也沒看出哪裡有問題, 隨手把 localhost改為 127.0.0.1, 突然發現行了, 興奮之餘也感覺很莫名其妙。
初以為是PHP對 localhost作了特殊處理, 直接解析為mysql sock路徑了。
今天折騰完手頭事情再回頭看看這個問題。
依然在本地作了一個PHP-FPM chroot的隔離環境, 測試指令碼:
root@gentoo-local /var/www/test % cat index.php
和昨天的情況一樣, 如果是 localhost, 則失敗; localhost:3306也失敗; 127.0.0.1則OK。
因為知道是哪塊的問題, 所以搜尋起來離正確的答案也就越來越近。
於是搜到了這篇回答 Warning: mysql_connect(): [2002] No such file or directory, 裡面有一句話:
The reason is that "localhost" is a special name for the mysql driver making it use the unix socket to connect to mysql instead of the a tcp socket.
這裡說是因為mysql對localhost的解析問題。和我之前猜測是PHP解析的不一致。
本地mysql命令列用戶端測試, 正常情況下下面兩者都OK ( -h localhost是預設, 可不指定):
$ mysql -h localhost -u root -p # OK$ mysql -h 127.0.0.1 -u root -p # OK
因為 localhost 是 /etc/hosts中定義的, 所以臨時注釋掉這塊, 自然localhost就變成無意義的字串了, 再次嘗試:
$ mysql -h localhost -u root -p # OK$ mysql -h 127.0.0.1 -u root -p # OK
兩者依然OK, 所以可以確定原因應該是mysql driver對 db host 的解析問題了。
另外, 通過 lsof也可以看到串連進程的通訊:
# localhostmysql 27971 tankywoo 3u unix 0xffff880078d25540 0t0 371911 type=STREAM# 127.0.0.1mysql 28249 tankywoo 3u IPv4 373797 0t0 TCP localhost:50187->localhost:mysql (ESTABLISHED)
按照經驗來說, localhost和 127.0.0.1是等價的, 如果主機是非IP地址, 都會嘗試去做解析操作。MySQL可能是考慮到unix domain socket效率更高, 所以預設對localhost作了這個處理。
但是這種處理太不明顯, 或者說模淩兩可, 所以給我的感覺是「坑」的性質更大一些。
後續針對這塊又搜了下, 發現遇到這個問題的人真不少, 就比如上面StackOverflow的那個回答, 有接近300的Up vote就能看出來。
:(