記一次php mysqli出現No such file or directory的錯誤排查 原 薦

最近公司的一臺開發機器壞了,需要把部分工作相關的系統轉移到另一臺機器上,在轉移mantis的過程中發現mysql連不上了,而且錯誤居然是No such file or directory,這個錯誤信息很明顯告訴我文件不存在,但是我是通過網絡連mysql,何故會提示文件不存在?於是寫了一段測試代碼。

$mysqli = new mysqli('localhost', 'root', '', 'bugtracker');
if ($mysqli->connect_error) {
    die('Connect Error (' . $mysqli->connect_errno . '): '. $mysqli->connect_error);
}

果然,還是報錯!!!!

PHP Warning:  mysqli::mysqli(): (HY000/2002): No such file or directory in /root/test.php on line 2

爲了確保參數沒錯,使用mysql客戶端進行測試

mysql -uroot -hlocalhost

嗯,連上了,完全沒有任何錯誤,那說明參數是沒有問題的,可爲什麼相同的參數,mysql客戶端連接正常,php連接不上?爲了一探究竟,於是使用strace再次跑了一下這段代碼,發現系統調用的connect居然不是使用tcp,而是unixdomain,文件路徑是/tmp/mysql.sock,而這個文件確實不存在

connect(3, {sa_family=AF_FILE, path="/tmp/mysql.sock"}, 17) = -1 ENOENT (No such file or directory)

到這裏,我已經知道錯誤的來源,是使用unixdomain去連接了一個不存的地址,導致報錯,可爲什麼會使用unixdomain連接mysql呢?而這個地址又從哪裏來的呢?於是又查閱了相關資(Bai)料(Du),根據史書記載,當使用localhost進行連接的時候,會使用unixdomain的方式,那這麼說來mysql客戶端也是使用unixdomain了?於是又對mysql使用了一次strace

connect(3, {sa_family=AF_FILE, path="/var/lib/mysql/mysql.sock"}, 110) = 0

發現connect確實是使用unixdomain,但是.......這個文件路徑跟上面的完全不一樣,而這個路徑是存在的。。可上面/tmp/mysql.sock到底哪裏來的?於是又馬不停蹄的去翻閱php文檔,文檔明確指出,mysqli的構造函數socket參數(也就是上面提到的unixdomain的文件路徑)默認從php.ini的mysqli.default_socket中取,那麼再次檢查參數

php -i|grep 'mysqli.default_socket'
mysqli.default_socket => no value => no value

嗯哼?沒有值,鬧呢這是。那這個貨到底從哪裏來的,爲了探明事情真相,開始翻閱mysqli的源代碼,果然,從源代碼中找出了這麼一段

if (host_len == sizeof("localhost") - 1 && !strncasecmp(host, "localhost", host_len)) {
    DBG_INF_FMT("socket=%s", socket_or_pipe? socket_or_pipe:"n/a");
    if (!socket_or_pipe) {
        socket_or_pipe = "/tmp/mysql.sock";
    }
    transport_len = mnd_sprintf(&transport, 0, "unix://%s", socket_or_pipe);
    unix_socket = TRUE;
}

如果使用localhost,就使用unixdomain,文件路徑就是參數中的socket,但如果這個參數是空,那麼奏寫死/tmp/mysql.sock。這麼說來上面報錯不存在的地址就是在源代碼中給寫死了,所以需要自己手動設置爲正確的值。於是修改mysqli的參數再次測試。

$mysqli = new mysqli('localhost', 'root', '', 'bugtracker', 3306, "/var/lib/mysql/mysql.sock");

果然好了,到這裏,已經查明事情真相,連接不上就是因爲使用了localhost並且mysqli.default_socket的值爲空,而源碼中提供的默認路徑又不存在。到這裏我不經要問,可否加兩個判斷呢?使用localhost並且socket不爲空才使用unixdomain,而不提供默認路徑,因爲如果先裝php後裝mysql,那麼這個default_socket就會是個空值。

當然解決這個問題其實很簡單,網上有非常多的文章都有寫如何解決,比如將mysqli.default_socket的值改爲正確的路徑,或者將localhost改成127.0.0.1等。主要還是想知道爲什麼不配置socket會出現錯誤,要找到問題的根源纔好對症下藥。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章