MySQLClient/Server协议解读:Connection&ReplicationMySQL客户端和服务器之间的通信是基于特定的TCP协议。本文将详细讲解Connection和Replication部分。每个部分对应建立客户端与服务端的连接,完成鉴权认证,将客户端注册为slave,获取master的binlog日志。ConnetcionPhaseMySQL客户端如果要与服务器通信,第一步是要成功建立连接。整个过程如下图所示:客户端发起一个TCP连接。服务器以包含默认身份验证方法的初始握手数据包进行响应。该步骤是可选的,双方建立SSL加密连接。客户端响应一个握手响应包,内容需要按照指定的方式包含用户名和加密后的密码数据。服务器响应OK_Packet以确认身份验证成功,或响应ERR_Packet以指示身份验证失败并关闭连接。Packet一个Packet其实就是一个TCP数据包,所有的数据包都有一个基本的结构:如上图所示,所有的数据包都可以看成由两部分组成:header和body:header的第一部分一共有4个bytes,3个字节用于标识body的大小,即payload,1个字节记录sequenceID;第二部分,body,是payload的实际加载数据。由于payloadlength只有3个字节来记录,所以一个packet的payloadsize不能超过2^24=16MB,例子:Packet:当数据不超过16MB时,payloadsizedoesnotexceed2^tobeprecise24-1Byte(三个字节可以表示的最大整数0xFFFFFF),发送一个数据包就够了。当数据大小超过16MB时,需要将数据分成多个包进行传输。当datapayload刚好是2^24-1Byte时,一个packet就够了,但是为了表示数据传输完成,还是会多传一个payload为空的packet。SequenceID:包的序号,从0开始递增。在一个完整的session中,每个packet的sequencenumber加1,当新的session开始时,sequencenumber又从0开始。例如:在建立连接阶段,服务器发送初始握手包(SequenceID为0),客户端响应握手响应包(SequenceID为1),服务器响应OK_Packet或ERR_Packet(SequenceID为2),则建立连接阶段结束,如果有后续的命令数据,数据包的SequenceID会重新从0开始;在命令阶段(客户端向服务器端发送增删改查,属于命令阶段),一个命令的请求和响应可以看作是一个完整的会话过程,例如客户端首先向服务器发送查询请求,然后服务器响应查询请求,然后本次session结束,下一个命令是一个新的session,SequenceID从0开始再次递增。InitialHandshakePacket当连接建立后,当客户端发起TCP连接时,MySQL服务器会回应一个InitialHandshakePacket。初始化握手包的数据格式如下图所示:本图从上到下:1字节整数,表示握手协议的版本,现在是10。一个以NULL结尾的字符串(即一个字节0x00),表示MySQL服务器的版本,例如5.7.18-log。表示线程id的4字节整数,也是本次连接的id。8字节字符串,auth-plugin-data-part-1后续密码加密时使用的随机数的前8位。1个字节的填充。2字节整数,capability_flags_1是CapabilitiesFlags的低2字节。一个1字节的整数,表示服务器默认的字符编码格式,例如utf8_general_ci。标识服务器状态的2字节整数。2字节整数,capability_flags_2是CapabilitiesFlags的高2位字节。1字节整数,如果服务端有CLIENT_PLUGIN_AUTH的能力(其实是有客户端认证能力,基本支持),则传auth_plugin_data_len,即加密随机数的长度,否则传0x00。10个字节的填充,全部为0x00。auth_plugin_data_len指定长度的字符串,auth-plugin-data-part-2加密随机数的后13位。如果服务端有CLIENT_PLUGIN_AUTH的能力(其实是有client认证的能力,基本支持),那么传入auth_plugin_name,就是用户认证方式的名称。对于MySQL5.x版本,默认的用户认证方式叫做mysql_native_password(对应上面的auth_plugin_name),这种认证方式的算法是:SHA1(password)XORSHA1("20-bytesrandomdatafromserver"SHA1(SHA1(password))加密所需的20字节随机数为auth-plugin-data-part-1(8位)和auth-plugin-data-part-2(12位前的13位)。注意:MySQL使用little-endianbyteordering,看到这里,你可能还会对CapabilitiesFlags感到困惑,CapabilitiesFlags其实是一个featureflag,用来表示server端和client端支持和想要使用哪些features。为什么需要这个功能标志?因为首先MySQL有很多版本,每个版本可能支持不同的功能,所以服务器端需要注明支持哪些功能;其次,对于服务器来说,可以有各种各样的客户端连接到它。端想使用哪些功能也需要指明。CapabilitiesFlags一般是4字节的整数:如上图所示,每个feature占用一个bit。CapabilitiesFlags通常是多个函数的组合。例如表示CLIENT_PROTOCOL_41、CLIENT_PLUGIN_AUTH、CLIENT_SECURE_CONNECTION这三个函数,然后分别对它们对应的0x00000200、0x00080000、0x00008000进行按位或运算,得到最终值0x00088200,即FinalCapabilitiesFlags。根据CapabilitiesFlags判断是否支持某个功能。例如,CapabilitiesFlags的值为0x00088200。判断是否支持CLIENT_SECURE_CONNECTION功能,可以直接按位与,即CapabilitiesFlags&CLIENT_SECURE_CONNECTION==CLIENT_SECURE_CONNECTION。HandshakeResponsePacket在连接建立过程中,当客户端收到服务器的InitialHandshakePacket后,需要向服务器响应一个HandshakeResponsePacket。数据包的数据格式如下图所示显示:依次为:4字节整数,CapabilitiesFlags,必须设置CLIENT_PROTOCOL_41,对于MySQL5.x版本,使用默认的认证方式,还需要设置相应的CLIENT_PLUGIN_AUTH和CLIENT_SECURE_CONNECTION.4字节整数,最大包大小,这里指的是命令包的大小,比如一条SQL的最大大小。1字节整数,字符编码。23个字节的填充,全部为0x00。以NUL(0x00)结尾的字符串,登录的用户名。CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA一般不用。1字节整数,auth_response_length,密码的加密长度。auth_response_length指定字符串的长度、密码的加密数据和随机数。如果CLIENT_CONNECT_WITH_DB直接指定要连接的数据库,则需要传递一个包含数据库名称的以NUL(0x00)结尾的字符串。CLIENT_PLUGIN_AUTH一般需要,默认传值是mysql_native_password。可以看出HandshakeResponsePacket和InitialHandshakePacket其实是对应的。OK_Packet&ERR_PacketOK_Packet和ERR_Packet是MySQL服务器常见的响应包。MySQL5.7.5之后,OK_Packet还包括EOF_Packet(用于显示警告和状态信息)。OK_Packet和EOF_Packet的区别:OK:header=0x00andlengthofpacket>7EOF:header=0xfeandlengthofpacket<9在MySQL5.7.5之前,EOF_Packet是一个单独格式的包:如果认证通过,连接成功established,returnOK_Packet会是:0x070x000x000x020x000x000x000x020x000x000x00如果连接失败或者出错,会返回一个ERR_Packet格式的包:DoesReplicationwanttogetthebinlogofmaster?只要你连接执行复制协议。客户端与服务端的连接建立成功,身份认证完成。这个过程就是上面说的连接阶段。客户端向服务器发送COM_REGISTER_SLAVE包,表示要注册为slave,服务器响应OK_Packet或ERR_Packet,成功后才能进行下一步。客户端向服务器发送COM_BINLOG_DUMP包,表示要开始获取binlog的内容。服务器响应数据,可能是:二进制日志网络流(binlognetworkstream)。ERR_Packet,表示发生错误。EOF_Packet,如果COM_BINLOG_DUMP中的flags设置为0x01,那么当binlog中不再有新的事件时发送EOF_Packet,而不是阻塞连接继续等待后续的binlog事件。COM_REGISTER_SLAVE客户端向MySQL发送COM_REGISTER_SLAVE,表示要注册为slave。包格式如下图所示:除1字节固定内容0x15和4字节server-id外,其他内容通常为空或忽略。注意这里的user和password并不是登录MySQL的用户名和密码,只是slave的一个标识。COM_BINLOG_DUMP注册为slave后,发送COM_BINLOG_DUMP开始接收binlog事件。具有固定内容0x12的1字节整数。4字节整数,binlog-pos为binlog文件的起始位置。2字节整型,flags,正常情况下slave会保持连接等待接受binlog事件,但是当flags设置为0x01时,如果当前binlog全部收到,server会发送EOF_Packet结束整个process而不是保持连接,继续等待后续的binlog事件。4字节整数,server-id,slave标识,MySQL可以同时有多个slave,每个slave必须有不同的server-id。变长字符串,binlog-filename,起始binlog文件名。查看当前的binlog文件名和pos位置可以执行SQL语句showmasterstatus,查看所有的binlog文件可以执行SQL语句showbinarylogs。BinlogEventclient注册slave成功,并正确发送COM_BINLOG_DUMP,MySQL会向client发送binlognetworkstream,即binlognetworkstream。所谓binlog网络流,其实就是源源不断的binlogevent包(对MySQL进行操作,如inset、update、delete等,以一个或多个binlogevent的形式存在于binlog中)。Replication的两种方式:异步,默认方式,master不断的向slave发送binlog事件,不需要slave的ack确认。半同步,master每次向slave发送binlog事件都需要等待ack确认回复。Binlog有三种模式:statement,binlog存储的是原始的SQL语句。row,binlog存储的是每一行的实际变化前后。mixed,混合模式,binlog存储一部分是SQL语句,一部分是每一行的变化。BinlogEvent的封装格式如下:每个BinlogEvent封装都有一定的事件头,根据事件类型的不同可能会有post头和payload。BinlogEvent有多种类型:BinlogManagement:START_EVENT_V3FORMAT_DESCRIPTION_EVENT:MySQL5.x及以上版本binlog文件中的第一个事件,内容为binlog的基本描述信息。STOP_EVENTROTATE_EVENT:binlog文件发生切换,binlog文件中的最后一个事件。SLAVE_EVENTINCIDENT_EVENTHEARTBEAT_EVENT:心跳信息,表示slave落后master多少秒(执行SQL语句SHOWSLAVESTATUS输出的Seconds_Behind_Master字段)。StatementBasedReplicationEvents(binlog为statement模式时的相关事件):QUERY_EVENT:原始SQL语句,如insert,update...。INTVAR_EVENT:基于会话变量的整数。比如主键设置为auto_increment自增整数,那么在插入的时候,这个字段实际写入的值会被记录到这个事件中。RAND_EVENT:内部RAND()函数的状态。USER_VAR_EVENT:用户变量事件。XID_EVENT:记录事务ID,提交事务commit后才会写入。RowBasedReplicationEvents(binlog为行模式时的相关事件):TABLE_MAP_EVENT:记录后续事件涉及的表结构的映射关系。v0事件对应MySQL5.1.0到5.1.15版本DELETE_ROWS_EVENTv0:记录行数据的删除。UPDATE_ROWS_EVENTv0:记录行数据的更新。WRITE_ROWS_EVENTv0:记录行数据的增加。v1事件对应MySQL5.1.15到5.6.x版本DELETE_ROWS_EVENTv1:记录行数据的删除。UPDATE_ROWS_EVENTv1:记录行数据的更新。WRITE_ROWS_EVENTv1:记录行数据的增加。v2事件对应MySQL5.6.x及以上版本。DELETE_ROWS_EVENTv2:记录行数据的删除。UPDATE_ROWS_EVENTv2:记录行数据的更新。WRITE_ROWS_EVENTv2:记录行数据的增加。LOADINFILEreplication(加载文件的特殊场景,本文不做介绍):LOAD_EVENTCREATE_FILE_EVENTAPPEND_BLOCK_EVENTEXEC_LOAD_EVENTDELETE_FILE_EVENTNEW_LOAD_EVENTBEGIN_LOAD_QUERY_EVENTEXECUTE_LOAD_QUERY_EVENT解析具体binlogevent的内容,对比官方文档数据包的格式即可。结论MySQLClient/ServerProtocol协议实际上非常简单。就是按照约定的格式向对方发送合约。了解协议后,相信你可以实现一个lib注册为slave,然后解析binlog。