Kingshard数据库中间件
miproxy就是基于kingshard进行开发的,该中间件是用Go语言写的,今天就简单看了一下代码。我主要是想看看它是怎么实现读写分离,如何强制读主库的。因为有了主从库,必然就有延时性的问题,一致性的问题。
看了代码感觉挺简单的,主要是下面的代码:
入口就是HTTP server的监听端口,Kingshard写了一个dispatch分发器:
func (c *ClientConn) Run() {
for {
data, err := c.readPacket()
if err != nil {
return
}
if err := c.dispatch(data); err != nil {
c.proxy.counter.IncrErrLogTotal()
golog.Error("ClientConn", "Run",
err.Error(), c.connectionId,
)
c.writeError(err)
if err == mysql.ErrBadConn {
c.Close()
}
}
if c.closed {
return
}
c.pkg.Sequence = 0
}
}
分发器会根据不同的连接类型,进行分发:
func (c *ClientConn) dispatch(data []byte) error {
//原子操作,QPS统计
c.proxy.counter.IncrClientQPS()
cmd := data[0]
data = data[1:]
switch cmd {
case mysql.COM_QUIT:
c.handleRollback()
c.Close()
return nil
case mysql.COM_QUERY:
return c.handleQuery(hack.String(data))
case mysql.COM_PING:
return c.writeOK(nil)
case mysql.COM_INIT_DB:
return c.handleUseDB(hack.String(data))
case mysql.COM_FIELD_LIST:
return c.handleFieldList(data)
case mysql.COM_STMT_PREPARE:
return c.handleStmtPrepare(hack.String(data))
case mysql.COM_STMT_EXECUTE:
return c.handleStmtExecute(data)
case mysql.COM_STMT_CLOSE:
return c.handleStmtClose(data)
case mysql.COM_STMT_SEND_LONG_DATA:
return c.handleStmtSendLongData(data)
case mysql.COM_STMT_RESET:
return c.handleStmtReset(data)
case mysql.COM_SET_OPTION:
return c.writeEOF(0)
default:
msg := fmt.Sprintf("command %d not supported now", cmd)
golog.Error("ClientConn", "dispatch", msg, 0)
return mysql.NewError(mysql.ER_UNKNOWN_ERROR, msg)
}
return nil
}
handQuery是处理查询语句的,在解析sql前,会先获得数据库连接,调用下面的函数。
func (c *ClientConn) getBackendConn(n *backend.Node, fromSlave bool) (co *backend.BackendConn, err error) { if !c.isInTransaction() { if fromSlave { co, err = n.GetSlaveConn() if err != nil { co, err = n.GetMasterConn() } } else { co, err = n.GetMasterConn() } if err != nil { golog.Error("server", "getBackendConn", err.Error(), 0) return } } else { var ok bool co, ok = c.txConns[n] if !ok { if co, err = n.GetMasterConn(); err != nil { return } if !c.isAutoCommit() { if err = co.SetAutoCommit(0); err != nil { return } } else { if err = co.Begin(); err != nil { return } } c.txConns[n] = co } } if err = co.UseDB(c.db); err != nil { //reset the database to null c.db = "" return } if err = co.SetCharset(c.charset, c.collation); err != nil { return } return }
如果是事务,直接建立Master连接;
对于非事务的:
如果fromslave=false,就选择主库,否则选择从库。那么这个fromslave从哪来呢?
往上找:
//处理select语句 func (c *ClientConn) handleSelect(stmt *sqlparser.Select, args []interface{}) error { var fromSlave bool = true plan, err := c.schema.rule.BuildPlan(c.db, stmt) if err != nil { return err } if 0 < len(stmt.Comments) { comment := string(stmt.Comments[0]) if 0 < len(comment) && strings.ToLower(comment) == MasterComment { fromSlave = false } }
const ( MasterComment = "/*master*/"
)
如果在sql语句中,加入了上面的文本,就可以直接选择主库。
其实除了上述的方法,还可以根据账号来决定。
2020.10.20补充。借着学习Mysql的机会,又看了一遍Kingshard代码,觉得开发得真挺好的。我也希望我未来自己一个人也可以开发初类似的系统。
--------EOF---------
微信分享/微信扫码阅读
微信分享/微信扫码阅读