产生原因之一

该连接超过空闲等待时间(wait_timeout,默认8小时),服务端主动关闭该连接,此时客户端再执行query,则mysql client返回该错误。

研究

  1. 设置mysql wait_timeout为5s,方便复现

    set global wait_timeout=5;
    
  2. 客户端首先建立好连接

    18:07:19.831124 IP localhost.46740 > localhost.mysql: Flags [S], seq 424450064, win 43690, options [mss 65495,sackOK,TS val 357860862 ecr 0,nop,wscale 7], length 0
    18:07:19.831158 IP localhost.mysql > localhost.46740: Flags [S.], seq 2970334829, ack 424450065, win 43690, options [mss 65495,sackOK,TS val 357860862 ecr 357860862,nop,wscale 7], length 0
    18:07:19.831179 IP localhost.46740 > localhost.mysql: Flags [.], ack 1, win 342, options [nop,nop,TS val 357860862 ecr 357860862], length 0
    ...
    
  3. 5s后,发现服务端主动断开连接(关闭了),服务端关闭了写通道

    18:07:24.902120 IP localhost.mysql > localhost.46740: Flags [F.], seq 233, ack 704, win 350, options [nop,nop,TS val 357865933 ecr 357860968], length 0
    18:07:24.943661 IP localhost.46740 > localhost.mysql: Flags [.], ack 234, win 342, options [nop,nop,TS val 357865974 ecr 357865933], length 0
    
    [I] wallace@centos:/m/s/W/p/B/p/b/B/judge_server> ss -aon|grep 3306
    tcp    CLOSE-WAIT 1      0      127.0.0.1:46739              127.0.0.1:3306                timer:(keepalive,120min,0)
    tcp    LISTEN     0      80       :::3306                 :::*                  
    tcp    FIN-WAIT-2 0      0      ::ffff:127.0.0.1:3306               ::ffff:127.0.0.1:46739               timer:(timewait,56sec,0)
    
    观看连接状态,发现服务端socket状态已变为`FIN-WAIT-2`,60s后超时关闭,服务端socket关闭连接后;
    此后客户端进入了半连接状态,若客户端继续发送数据,则会收到`RST`,客户端读返回-1,errno=ECONNRESET,
    `mysql connector c`会将该错误作为`CR_SERVER_LOST`
    
  4. 客户端发送数据

18:13:23.289923 IP localhost.46740 > localhost.mysql: Flags [P.], seq 704:737, ack 234, win 342, options [nop,nop,TS val 358224321 ecr 357865933], length 33
18:13:23.289935 IP localhost.mysql > localhost.46740: Flags [R], seq 2970335063, win 0, length 0
  1. gdb源码跟踪(mysql-connector-c-6.1.5-src)

    sql-common/client.c:957

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    ulong
    cli_safe_read_with_ok(MYSQL *mysql, my_bool read_ok)
    {

    NET *net= &mysql->net;
    ulong len=0;

    MYSQL_TRACE(READ_PACKET, mysql, ());

    if (net->vio != 0)
    len=my_net_read(net); // <= 此调用失败

    if (len == packet_error || len == 0) // len == packet_error 成立
    {
    DBUG_PRINT("error",("Wrong connection or packet. fd: %s len: %lu",
    vio_description(net->vio),len));
    #ifdef MYSQL_SERVER
    if (net->vio && (net->last_errno == ER_NET_READ_INTERRUPTED))
    return (packet_error);
    #endif /*MYSQL_SERVER*/
    end_server(mysql);
    set_mysql_error(mysql, net->last_errno == ER_NET_PACKET_TOO_LARGE ?
    CR_NET_PACKET_TOO_LARGE: CR_SERVER_LOST, unknown_sqlstate); // 设置错误代码为:CR_SERVER_LOST
    return (packet_error);
    }
    Breakpoint 3, cli_safe_read_with_ok (mysql=0xb99e80, read_ok=0 '\000') at /media/sf_E_DRIVE/Soft/mysql-connector/mysql-connector-c-6.1.5-src/sql-common/client.c:976
    976     set_mysql_error(mysql, net->last_errno == ER_NET_PACKET_TOO_LARGE ?
    (gdb) bt
    #0  cli_safe_read_with_ok (mysql=0xb99e80, read_ok=0 '\000') at /media/sf_E_DRIVE/Soft/mysql-connector/mysql-connector-c-6.1.5-src/sql-common/client.c:976
    #1  0x00000000004183da in cli_safe_read (mysql=0xb99e80) at /media/sf_E_DRIVE/Soft/mysql-connector/mysql-connector-c-6.1.5-src/sql-common/client.c:1047
    #2  0x0000000000421f0b in cli_read_query_result (mysql=0xb99e80) at /media/sf_E_DRIVE/Soft/mysql-connector/mysql-connector-c-6.1.5-src/sql-common/client.c:4711
    #3  0x000000000042275c in mysql_real_query (mysql=0xb99e80, query=0x59c2db "SELECT age, name FROM test.a", length=28)
        at /media/sf_E_DRIVE/Soft/mysql-connector/mysql-connector-c-6.1.5-src/sql-common/client.c:4802
    #4  0x000000000040ad7b in mysql::Connection::Query (this=0xba01d0, sql=...) at Beme4wdServer/lily/mysql/connection.cpp:89
    

参考

  1. http://dev.mysql.com/doc/refman/5.6/en/mysql-real-query.html
  2. https://dev.mysql.com/doc/refman/5.6/en/gone-away.html

留言

2016-08-26