Skip to content

Commit

Permalink
optimize the route rule of unshard sql
Browse files Browse the repository at this point in the history
  • Loading branch information
flike committed Sep 9, 2015
1 parent f417357 commit 8c4e39a
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 65 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ kingshard is a high-performance proxy for MySQL powered by Go. Just like other m
6. set the config file (etc/multi.yaml)
7. run kingshard (./bin/kingshard -config=etc/multi.yaml)

# Licence

kingshard is under the MIT license. See the [LICENSE](./doc/License) directory for details.

#Other language version

[简体中文](README_ZH.md)
4 changes: 4 additions & 0 deletions README_ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,9 @@ kingshard是一个由Go开发高性能MySQL Proxy项目,kingshard在满足基

[8.ChangeLog](./doc/KingDoc/change_log_CN.md)

## Licence

kingshard采用MIT协议,相关协议请参看[目录](./doc/License)

## 反馈
目前kingshard还是1.0版本,比较核心的功能已经实现了。但还有很多地方不完善。如果您在使用kingshard的过程中发现BUG或者有新的功能需求,非常欢迎您发邮件至flikecn#126.com与作者取得联系,或者加入QQ群(147926796)交流。
5 changes: 3 additions & 2 deletions core/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ var (
ErrMasterDown = errors.New("master is down")
ErrSlaveDown = errors.New("slave is down")

ErrAddressNull = errors.New("address is nil")
ErrCmdUnsupport = errors.New("command unsupport")
ErrAddressNull = errors.New("address is nil")
ErrInvalidArgument = errors.New("argument is invalid")
ErrCmdUnsupport = errors.New("command unsupport")

ErrLocationsCount = errors.New("locations count are not equal")
ErrNoCriteria = errors.New("plan have no criteria")
Expand Down
2 changes: 1 addition & 1 deletion doc/KingDoc/how_to_use_kingshard.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

```
# kingshard的地址和端口
addr : 127.0.0.1:9696
addr : 0.0.0.0:9696
# 连接kingshard的用户名和密码
user : kingshard
Expand Down
2 changes: 1 addition & 1 deletion doc/KingDoc/kingshard_install_document.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

```
# kingshard的地址和端口
addr : 127.0.0.1:9696
addr : 0.0.0.0:9696
# 连接kingshard的用户名和密码
user : kingshard
Expand Down
2 changes: 1 addition & 1 deletion etc/ks.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# server listen addr
addr : 127.0.0.1:9696
addr : 0.0.0.0:9696

# server user and password
user : kingshard
Expand Down
49 changes: 27 additions & 22 deletions mysql/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,33 +145,38 @@ const (
)

var (
KS_TK_INSERT = 1
KS_TK_UPDATE = 1
KS_TK_DELETE = 1
KS_TK_REPLACE = 1
KS_TK_SET = 1
KS_TK_BEGIN = 1
KS_TK_COMMIT = 1
KS_TK_ROLLBACK = 1
KS_TK_ADMIN = 1
KS_TK_USE = 1
TK_ID_INSERT = 1
TK_ID_UPDATE = 2
TK_ID_DELETE = 3
TK_ID_REPLACE = 4
TK_ID_SET = 5
TK_ID_BEGIN = 6
TK_ID_COMMIT = 7
TK_ID_ROLLBACK = 8
TK_ID_ADMIN = 9
TK_ID_USE = 10

KS_TK_SELECT = 2
TK_ID_SELECT = 11

WHITE_TOKEN_MAP = map[string]int{
"insert": KS_TK_INSERT,
"update": KS_TK_UPDATE,
"delete": KS_TK_DELETE,
"replace": KS_TK_REPLACE,
"set": KS_TK_SET,
"begin": KS_TK_BEGIN,
"commit": KS_TK_COMMIT,
"rollback": KS_TK_ROLLBACK,
"admin": KS_TK_ADMIN,
"select": KS_TK_SELECT,
"use": KS_TK_USE,
"insert": TK_ID_INSERT,
"update": TK_ID_UPDATE,
"delete": TK_ID_DELETE,
"replace": TK_ID_REPLACE,
"set": TK_ID_SET,
"begin": TK_ID_BEGIN,
"commit": TK_ID_COMMIT,
"rollback": TK_ID_ROLLBACK,
"admin": TK_ID_ADMIN,
"select": TK_ID_SELECT,
"use": TK_ID_USE,
}
// '/'
COMMENT_PREFIX uint8 = 47
COMMENT_STRING = "/*"

//
TK_STR_FROM = "from"
TK_STR_INTO = "into"
TK_STR_SET = "set"
)
89 changes: 58 additions & 31 deletions proxy/server/conn_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func (c *ClientConn) handleQuery(sql string) (err error) {
}()

sql = strings.TrimRight(sql, ";") //删除sql语句最后的分号
hasHandled, err := c.handleUnShard(sql)
hasHandled, err := c.preHandleShard(sql)
if err != nil {
golog.Error("server", "parse", err.Error(), 0, "hasHandled", hasHandled)
return err
Expand Down Expand Up @@ -315,7 +315,7 @@ func (c *ClientConn) newEmptyResultset(stmt *sqlparser.Select) *mysql.Resultset
return r
}

func (c *ClientConn) GetTransNode(tokens []string, sql string) (*backend.Node, error) {
func (c *ClientConn) GetTransExecNode(tokens []string, sql string) (*backend.Node, error) {
var execNode *backend.Node
var err error

Expand All @@ -330,7 +330,7 @@ func (c *ClientConn) GetTransNode(tokens []string, sql string) (*backend.Node, e
}

if execNode == nil {
execNode, _, err = c.GetNotransNode(tokens, sql)
execNode, _, err = c.GetExecNode(tokens, sql)
if err != nil {
return nil, err
}
Expand All @@ -342,39 +342,66 @@ func (c *ClientConn) GetTransNode(tokens []string, sql string) (*backend.Node, e
return execNode, nil
}

func (c *ClientConn) GetNotransNode(tokens []string,
func (c *ClientConn) GetExecNode(tokens []string,
sql string) (*backend.Node, bool, error) {

var execNode *backend.Node
var TK_FROM string = "from"
var fromSlave bool

tokensLen := len(tokens)
if 0 < tokensLen {
//token is in WHITE_TOKEN_MAP
if 0 < mysql.WHITE_TOKEN_MAP[tokens[0]] {
//select
if 1 < mysql.WHITE_TOKEN_MAP[tokens[0]] {
for i := 1; i < tokensLen; i++ {
if tokens[i] == TK_FROM {
return nil, false, nil
schema := c.proxy.schemas[c.proxy.db]
rules := schema.rule.Rules
if 0 < len(rules) {
tokensLen := len(tokens)
if 0 < tokensLen {
tokenId, ok := mysql.WHITE_TOKEN_MAP[tokens[0]]
if ok == true {
switch tokenId {
case mysql.TK_ID_SELECT, mysql.TK_ID_DELETE:
for i := 1; i < tokensLen; i++ {
if tokens[i] == mysql.TK_STR_FROM {
if i+1 < tokensLen {
tableName := sqlparser.GetTableName(tokens[i+1])
if _, ok := rules[tableName]; ok {
return nil, false, nil
}
}
}
}
case mysql.TK_ID_INSERT, mysql.TK_ID_REPLACE:
for i := 0; i < tokensLen; i++ {
if tokens[i] == mysql.TK_STR_INTO {
if i+1 < tokensLen {
tableName := sqlparser.GetTableName(tokens[i+1])
if _, ok := rules[tableName]; ok {
return nil, false, nil
}
}
}
}
case mysql.TK_ID_UPDATE:
for i := 0; i < tokensLen; i++ {
if tokens[i] == mysql.TK_STR_SET {
tableName := sqlparser.GetTableName(tokens[i-1])
if _, ok := rules[tableName]; ok {
return nil, false, nil
}
}
}
default:
return nil, false, nil
}
} else {
return nil, false, nil
}
}
}
//get node
if 2 <= tokensLen {
if tokens[0][0] == mysql.COMMENT_PREFIX {
nodeName := strings.Trim(tokens[0], mysql.COMMENT_STRING)
if c.schema.nodes[nodeName] != nil {
execNode = c.schema.nodes[nodeName]
}
//select
if mysql.WHITE_TOKEN_MAP[tokens[1]] == 2 {
fromSlave = true
//get node
if 2 <= tokensLen {
if tokens[0][0] == mysql.COMMENT_PREFIX {
nodeName := strings.Trim(tokens[0], mysql.COMMENT_STRING)
if c.schema.nodes[nodeName] != nil {
execNode = c.schema.nodes[nodeName]
}
//select
if mysql.WHITE_TOKEN_MAP[tokens[1]] == mysql.TK_ID_SELECT {
fromSlave = true
}
}
}
}
Expand All @@ -391,7 +418,7 @@ func (c *ClientConn) GetNotransNode(tokens []string,
}

//返回true表示已经处理,false表示未处理
func (c *ClientConn) handleUnShard(sql string) (bool, error) {
func (c *ClientConn) preHandleShard(sql string) (bool, error) {
var rs []*mysql.Result
var err error

Expand All @@ -408,9 +435,9 @@ func (c *ClientConn) handleUnShard(sql string) (bool, error) {
}

if c.needBeginTx() {
execNode, err = c.GetTransNode(tokens, sql)
execNode, err = c.GetTransExecNode(tokens, sql)
} else {
execNode, fromSlave, err = c.GetNotransNode(tokens, sql)
execNode, fromSlave, err = c.GetExecNode(tokens, sql)
}

if err != nil {
Expand Down
18 changes: 11 additions & 7 deletions sqlparser/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ package sqlparser

import (
"fmt"
"strings"

"github.com/flike/kingshard/sqltypes"
)

Expand All @@ -31,14 +33,16 @@ func GetDBName(sql string) (string, error) {
return "", fmt.Errorf("statement '%s' is not a dml", sql)
}

// GetTableName returns the table name from the SimpleTableExpr
// only if it's a simple expression. Otherwise, it returns "".
func GetTableName(node SimpleTableExpr) string {
if n, ok := node.(*TableName); ok && n.Qualifier == nil {
return string(n.Name)
func GetTableName(token string) string {
if len(token) == 0 {
return ""
}
vec := strings.SplitN(token, ".", 2)
if len(vec) == 2 {
return vec[1]
} else {
return vec[0]
}
// sub-select or '.' expression
return ""
}

// GetColName returns the column name, only if
Expand Down

0 comments on commit 8c4e39a

Please sign in to comment.