php mysqli MySQL server has gone away 問題分析

結論

mysqli沒有處理對端(MySQL server)的斷開請求。在send時,收到對端(MySQL server)的RST,會打印MySQL server has gone away。php

環境

xxxx@xxxx:~$ lsb_release -a 
No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux 8.4 (jessie)
Release:        8.4
Codename:       jessie
xxxx@xxxx:~$ php --version
PHP 5.6.30 (cli) (built: Oct 27 2017 11:18:47) (DEBUG)
Copyright (c) 1997-2016 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2016 Zend Technologies
    with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2016, by Zend Technologies

MySQL server的官方解釋

https://dev.mysql.com/doc/refman/5.7/en/gone-away.html
官方文檔很是羅嗦,簡單說來就是:html

  • 無效連接
    1.關閉鏈接後仍然發起查詢如超時斷開、主動斷開
    2.fork子進程使用父進程的連接)。
  • 查詢太大。
  • 無訪問權限。
  • 網絡問題(防火牆等)。

均可能形成MySQL server has gone away。
而我遇到的問題屬於無效連接。mysql

抓包分析


能夠看到。
No.469 是 MySQL server的tcp斷開請求,apache的工做進程並無返回FIN。
netstat的結果也證實了這一點:sql

tcp        0      0 0.0.0.0:3306            0.0.0.0:*               LISTEN      11439/mysqld    
tcp        1      0 127.0.0.1:56236         127.0.0.1:3306          CLOSE_WAIT  21523/apache2   
tcp        1      0 127.0.0.1:56196         127.0.0.1:3306          CLOSE_WAIT  21466/apache2   
tcp        1      0 127.0.0.1:56191         127.0.0.1:3306          CLOSE_WAIT  21523/apache2   
tcp        0      0 127.0.0.1:3306          127.0.0.1:56247         FIN_WAIT2   -               
tcp        0      0 127.0.0.1:3306          127.0.0.1:56236         FIN_WAIT2   -               
tcp        1      0 127.0.0.1:56247         127.0.0.1:3306          CLOSE_WAIT  21466/apache2

No.2113 是 MySQL client 的 Change User 請求,值得注意的是,仍然用原端口(連接)和MySQL server通訊。被MySQL server RST,觸發MySQL server has gone away
開始我認爲這是一個BUG,因而開始從源碼中尋找答案。apache

源碼分析

在腳本層定位到連接、報錯發生在:api

$this->_mysqli->real_connect

對應的c函數:網絡

mysqli_noapi.c:mysqli_common_connect

由於連接由apache工做進程持有,mysqli拓展做爲動態庫載入,若apache須要對MySQL server的FIN包作出處理,mysqli 拓展必須在創建連接時,將連接ID傳給apache。
但我沒找到這樣的邏輯。
結論是:
實現就是如此。MySQL斷開連接時,mysqli對失效連接無任何處理。
mysqli在send時才能發現連接失效,若連接失敗,會重連一次。tcp

調試證明

No.2113 被RST

mysqlnd_auth.c:226,單步執行後,tcpdump抓到函數

15:21:20.300274 IP localhost.i.nease.net.56230 > localhost.i.nease.net.mysql: Flags [P.], seq 2540548209:2540548279, ack 3116587272, win 86, options [nop,nop,TS val 888121188 ecr 888020891], length 70
15:21:20.300305 IP localhost.i.nease.net.mysql > localhost.i.nease.net.56230: Flags [R], seq 3116587272, win 0, length 0

錯誤信息來源:
mysqlnd_auth.c:234源碼分析

(gdb) n
234             COPY_CLIENT_ERROR(*conn->error_info, chg_user_resp->error_info);

(gdb) p conn->error_info.error
$14 = "MySQL server has gone away", '\000' <repeats 486 times>

mysqli使用失效連接時,被MySQL server RST。

重連

mysqli_nonapi.c

244             if (mysqlnd_connect(mysql->mysql, hostname, username, passwd, passwd_len, dbname, dbname_len,
(gdb) n

tcpdump 結果

15:27:33.105716 IP localhost.i.nease.net.56247 > localhost.i.nease.net.mysql: Flags [S], seq 842214159, win 43690, options [mss 65495,sackOK,TS val 888214389 ecr 0,nop,wscale 9], length 0
15:27:33.105738 IP localhost.i.nease.net.mysql > localhost.i.nease.net.56247: Flags [S.], seq 3337503184, ack 842214160, win 43690, options [mss 65495,sackOK,TS val 888214389 ecr 888214389,nop,wscale 9], length 0
15:27:33.105771 IP localhost.i.nease.net.56247 > localhost.i.nease.net.mysql: Flags [.], ack 1, win 86, options [nop,nop,TS val 888214389 ecr 888214389], length 0
15:27:33.106227 IP localhost.i.nease.net.mysql > localhost.i.nease.net.56247: Flags [P.], seq 1:79, ack 1, win 86, options [nop,nop,TS val 888214389 ecr 888214389], length 78
15:27:33.106241 IP localhost.i.nease.net.56247 > localhost.i.nease.net.mysql: Flags [.], ack 79, win 86, options [nop,nop,TS val 888214389 ecr 888214389], length 0
15:27:33.106362 IP localhost.i.nease.net.56247 > localhost.i.nease.net.mysql: Flags [P.], seq 1:122, ack 79, win 86, options [nop,nop,TS val 888214389 ecr 888214389], length 121
15:27:33.106373 IP localhost.i.nease.net.mysql > localhost.i.nease.net.56247: Flags [.], ack 122, win 86, options [nop,nop,TS val 888214389 ecr 888214389], length 0
15:27:33.106479 IP localhost.i.nease.net.mysql > localhost.i.nease.net.56247: Flags [P.], seq 79:90, ack 122, win 86, options [nop,nop,TS val 888214389 ecr 888214389], length 11
15:27:33.142634 IP localhost.i.nease.net.56247 > localhost.i.nease.net.mysql: Flags [.], ack 90, win 86, options [nop,nop,TS val 888214399 ecr 888214389], length 0

能夠看到。因mysqli_common_connect在連接失效時,會重試一次。 項目使用了pconnect,大量長連接因空閒超時被斷開,下次請求時,連接失效會致使gone away,由於有重連。該報錯可忽略。

相關文章
相關標籤/搜索