mysqlClient进行一次查询需要三个核心操作,获得连接、测试连是否可用、发送请求:conn,:client。Connect(127。0。0。1:3306,root,,test)conn。Ping()r,:conn。Execute(insertintotable(id,name)values(1,abc)) 首先我们看下获取连接的过程:github。comgomysqlorggomysqlv1。7。0clientconn。gofuncConnect(addrstring,userstring,passwordstring,dbNamestring,options。。。func(Conn))(Conn,error){dialer:net。Dialer{}returnConnectWithDialer(ctx,,addr,user,password,dbName,dialer。DialContext,options。。。)funcConnectWithDialer(ctxcontext。Context,networkstring,addrstring,userstring,passwordstring,dbNamestring,dialerDialer,options。。。func(Conn))(Conn,error){c:new(Conn)conn,err:dialer(ctx,network,addr)iferrc。handshake();err!nil{ 握手过程中需要进行身份认证:func(cConn)handshake()error{iferrc。readInitialHandshake();err!nil{iferr:c。writeAuthHandshake();err!nil{iferr:c。handleAuthResult();err!nil{ 标识连接的过程如下:typeConnstruct{packet。ConnuserstringpasswordstringdbstringtlsConfigtls。Configprotostringservercapabilitiescapabilityuint32clientsetcapabilitiesonlyccapsuint32attributesmap〔string〕stringstatusuint16charsetstringsalt〔〕byteauthPluginNamestringconnectionIDuint32} 认证完成后就可以获得server端分配的本次连接的连接ID:github。comgomysqlorggomysqlv1。7。0clientauth。gofunc(cConn)readInitialHandshake()error{data,err:c。ReadPacket()returnerrors。Annotate(c。handleErrorPacket(data),readinitialhandshakeerror)c。connectionIDbinary。LittleEndian。Uint32(data〔pos:pos4〕)pos4 一般通过conn。Ping()命令测试连接的可用性func(cConn)Ping()error{iferr:c。writeCommand(COMPING);err!nil{if,err:c。readOK();err!nil{ 可以看到它是一个单独的命令,mysql的命令列表如下:const(COMSLEEPbyteiotaCOMQUITCOMINITDBCOMQUERYCOMFIELDLISTCOMCREATEDBCOMDROPDBCOMREFRESHCOMSHUTDOWNCOMSTATISTICSCOMPROCESSINFOCOMCONNECTCOMPROCESSKILLCOMDEBUGCOMPINGCOMTIMECOMDELAYEDINSERTCOMCHANGEUSERCOMBINLOGDUMPCOMTABLEDUMPCOMCONNECTOUTCOMREGISTERSLAVECOMSTMTPREPARECOMSTMTEXECUTECOMSTMTSENDLONGDATACOMSTMTCLOSECOMSTMTRESETCOMSETOPTIONCOMSTMTFETCHCOMDAEMONCOMBINLOGDUMPGTIDCOMRESETCONNECTION) 执行请求的场景可以分为两种:不带参数的请求和带参数的请求:github。comgomysqlorggomysqlv1。7。0clientconn。go,不带参数的请求直接执行,带参数的需要先预编译,然后执行。func(cConn)Execute(commandstring,args。。。interface{})(Result,error){iflen(args)0{returnc。exec(command)ifs,err:c。Prepare(command);err!nil{r,errs。Execute(args。。。) 执行的过程就是向server发送COMQUERY命令func(cConn)exec(querystring)(Result,error){iferr:c。writeCommandStr(COMQUERY,query);err!nil{returnc。readResult(false) github。comgomysqlorggomysqlv1。7。0clientreq。gofunc(cConn)writeCommandStr(commandbyte,argstring)error{returnc。writeCommandBuf(command,utils。StringToByteSlice(arg))}func(cConn)writeCommandBuf(commandbyte,arg〔〕byte)error{err:c。WritePacket(data。B) github。comgomysqlorggomysqlv1。7。0packetconn。gofunc(cConn)WritePacket(data〔〕byte)error{ 预编译的请求发送在github。comgomysqlorggomysqlv1。7。0clientstmt。go,对应命令:COMSTMTPREPAREfunc(cConn)Prepare(querystring)(Stmt,error){iferr:c。writeCommandStr(COMSTMTPREPARE,query);err!nil{data,err:c。ReadPacket()s:new(Stmt)func(cConn)writeCommandStr(commandbyte,argstring)error{returnc。writeCommandBuf(command,utils。StringToByteSlice(arg))func(sStmt)Execute(args。。。interface{})(Result,error){iferr:s。write(args。。。);err!nil{returns。conn。readResult(true) 发送命令的过程如下,它先填充字段类型信息,然后填充字段的值,然后进行发送:func(sStmt)write(args。。。interface{})error{fori:rangeargs{switchv:args〔i〕。(type){caseint8:paramTypes〔i1〕MYSQLTYPETINYparamValues〔i〕〔〕byte{byte(v)}s。conn。ResetSequence()returns。conn。WritePacket(data)} 每次发生之前都会重置序列号。func(cConn)ResetSequence(){c。Sequence0} mysql的类型信息定义如下:const(MYSQLTYPEDECIMALbyteiotaMYSQLTYPETINYMYSQLTYPESHORTMYSQLTYPELONGMYSQLTYPEFLOATMYSQLTYPEDOUBLEMYSQLTYPENULLMYSQLTYPETIMESTAMPMYSQLTYPELONGLONGMYSQLTYPEINT24MYSQLTYPEDATEMYSQLTYPETIMEMYSQLTYPEDATETIMEMYSQLTYPEYEARMYSQLTYPENEWDATEMYSQLTYPEVARCHARMYSQLTYPEBITmysql5。6MYSQLTYPETIMESTAMP2MYSQLTYPEDATETIME2MYSQLTYPETIME2)const(MYSQLTYPEJSONbyteiota0xf5MYSQLTYPENEWDECIMALMYSQLTYPEENUMMYSQLTYPESETMYSQLTYPETINYBLOBMYSQLTYPEMEDIUMBLOBMYSQLTYPELONGBLOBMYSQLTYPEBLOBMYSQLTYPEVARSTRINGMYSQLTYPESTRINGMYSQLTYPEGEOMETRY) 总结一下,mysqlclient的实现起来很简单,建立连接,按照mysql协议编码,发送请求到服务端。