上次写了一篇有关kingshard(https://github.com/flike/king... ) 架构设计的文章,获得了不少热心网友的关注。其中有网友提到:但愿再写一篇关于如何利用kingshard搭建一个可扩展的MySQL集群的文档。利用假期时间,写了一篇kingshard使用指南,在这篇文章中,我将结合本身对MySQL Proxy的理解,为你们讲述如何正确使用kingshard数据库中间件。前端
如今不少互联网公司仍是在大量使用MySQL来存储各类类型的关系型数据。随着访问量和数据量的增加,开发者不得不考虑一些MySQL相关的新问题:node
面对这些问题,咱们能够在客户端代码中逐一实现。但这样也会使得客户端愈来愈重,不那么灵活。做者一直从事数据库相关工做的开发,正是基于数据库开发的痛点,设计和实现了kingshard数据库中间件。kingshard对上述5类问题都有比较合适的解决方案。下面对kingshard的主要功能,逐个介绍并演示一下。mysql
下面给出一个配置文件范例,用户能够自行按照本身的需求逐项配置:git
# kingshard的地址和端口 addr : 127.0.0.1:9696 # 链接kingshard的用户名和密码 user : kingshard password : kingshard # log级别,[debug|info|warn|error],默认是error log_level : debug #日志文件路径,若是不配置则会输出到终端。 log_path : /Users/flike/log # 只容许下面的IP列表链接kingshard,若是不配置则对链接kingshard的IP不作限制。 allow_ips: 127.0.0.1 # 一个node节点表示mysql集群的一个数据分片,包括一主多从(能够不配置从库) nodes : #node节点名字 name : node1 # 链接池中默认的空闲链接数 idle_conns : 16 # kingshard链接该node中mysql的用户名和密码,master和slave的用户名和密码必须一致 user : kingshard password : kingshard # master的地址和端口 master : 127.0.0.1:3306 # slave的地址和端口,可不配置 #slave : 192.168.0.12@2,192.168.0.13@3 #kingshard在300秒内都链接不上mysql,kingshard则会下线该mysql down_after_noalive : 300 - name : node2 idle_conns : 16 rw_split: true user : kingshard password : kingshard master : 192.168.59.103:3307 slave : down_after_noalive: 100 # 分表规则 schemas : - #分表使用的db,全部的分表必须都在这个db中。 db : kingshard #分表分布的node名字 nodes: [node1,node2] rules: #全部未分表的SQL,都会发往默认node。 default: node1 shard: - #分表名字 table: test_shard_hash #分表字段 key: id #分表分布的node nodes: [node1, node2] #分表类型 type: hash #子表个数分布,表示node1有4个子表, #node2有4个子表。 locations: [4,4] - table: test_shard_range key: id type: range nodes: [node1, node2] locations: [4,4] #表示每一个子表包含的最大记录数,也就是说每 #个子表最多包好10000条记录。 table_row_limit: 10000
这里着重说一下分表的配置规则:github
table_name_%4d
,也就是说子表下标由4位数组成。例如:table_name_0000,table_name_0102
。因为做者的只有两台MySQL,因此搭建了两个节点,这两个节点都只有一台Master 角色的MySQL数据库,具体的拓扑图以下所示:web
分表操做有hash和range两种类型,在这里只演示hash类型的分表操做,range类型的分表相似,就再也不赘述了。sql
在node1和node2上各建立4张子表,下面只给出在node1上test_shard_hash_0000
的建表SQL语句,其余子表的建表SQL语句相似。node1包含:test_shard_hash_0000, test_shard_hash_0001, test_shard_hash_0002, test_shard_hash_0003
。node2包含:test_shard_hash_0004, test_shard_hash_0005, test_shard_hash_0006, test_shard_hash_0007
。数据库
CREATE TABLE `test_shard_hash_0000` ( `id` bigint(64) unsigned NOT NULL, `str` varchar(256) DEFAULT NULL, `f` double DEFAULT NULL, `e` enum('test1','test2') DEFAULT NULL, `u` tinyint(3) unsigned DEFAULT NULL, `i` tinyint(4) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
执行下面SQL语句,根据查询的结果能够看出SQL语句根据分表规则落到不一样的子表。查询操做(select)能够跨多个node,当更新操做涉及到多个node时,kingshard会返回错误。为了保证数据一致性,kingshard不容许同时更新多个node上的子表(由于kingshard还未实现分布式事务)。但能够更新单个node上的多个子表,由单node上的事务保证。后端
mysql> insert into test_shard_hash(id,str,f,e,u,i) values(15,"flike",3.14,'test2',2,3); Query OK, 1 row affected (0.01 sec) mysql> mysql> insert into test_shard_hash(id,str,f,e,u,i) values(7,"chen",2.1,'test1',32,3); Query OK, 1 row affected (0.01 sec) mysql> insert into test_shard_hash(id,str,f,e,u,i) values(17,"github",2.5,'test1',32,3); Query OK, 1 row affected (0.00 sec) mysql> insert into test_shard_hash(id,str,f,e,u,i) values(18,"kingshard",7.3,'test1',32,3); Query OK, 1 row affected (0.01 sec)
对应的SQL日志以下所示:数组
2015/09/02 18:48:24 - INFO - 127.0.0.1:55003->192.168.59.103:3307:insert into test_shard_hash_0007(id, str, f, e, u, i) values (15, 'flike', 3.14, 'test2', 2, 3) 2015/09/02 18:49:05 - INFO - 127.0.0.1:55003->192.168.59.103:3307:insert into test_shard_hash_0007(id, str, f, e, u, i) values (7, 'chen', 2.1, 'test1', 32, 3) 2015/09/02 18:49:51 - INFO - 127.0.0.1:55003->127.0.0.1:3306:insert into test_shard_hash_0001(id, str, f, e, u, i) values (17, 'github', 2.5, 'test1', 32, 3) 2015/09/02 18:50:21 - INFO - 127.0.0.1:55003->127.0.0.1:3306:insert into test_shard_hash_0002(id, str, f, e, u, i) values (18, 'kingshard', 7.3, 'test1', 32, 3)
能够看到前两条SQL发送到了node2的master上了,后两条SQL发送到node1上的master了。
而后咱们能够用select语句查看数据,且select支持跨node查询。
mysql> select * from test_shard_hash where id < 18; +----+--------+------+-------+------+------+ | id | str | f | e | u | i | +----+--------+------+-------+------+------+ | 17 | github | 2.5 | test1 | 32 | 3 | | 7 | chen | 2.1 | test1 | 32 | 3 | | 15 | flike | 3.14 | test2 | 2 | 3 | +----+--------+------+-------+------+------+ 3 rows in set (0.02 sec)
由于是hash类型的分表,因此对于select范围类型的查询,必须查询每个子表。对应的SQL日志以下所示:
2015/09/02 18:55:01 - INFO - 127.0.0.1:55003->127.0.0.1:3306:select * from test_shard_hash_0000 where id < 18 2015/09/02 18:55:01 - INFO - 127.0.0.1:55003->127.0.0.1:3306:select * from test_shard_hash_0001 where id < 18 2015/09/02 18:55:01 - INFO - 127.0.0.1:55003->127.0.0.1:3306:select * from test_shard_hash_0002 where id < 18 2015/09/02 18:55:01 - INFO - 127.0.0.1:55003->127.0.0.1:3306:select * from test_shard_hash_0003 where id < 18 2015/09/02 18:55:01 - INFO - 127.0.0.1:55003->192.168.59.103:3307:select * from test_shard_hash_0004 where id < 18 2015/09/02 18:55:01 - INFO - 127.0.0.1:55003->192.168.59.103:3307:select * from test_shard_hash_0005 where id < 18 2015/09/02 18:55:01 - INFO - 127.0.0.1:55003->192.168.59.103:3307:select * from test_shard_hash_0006 where id < 18 2015/09/02 18:55:01 - INFO - 127.0.0.1:55003->192.168.59.103:3307:select * from test_shard_hash_0007 where id < 18
对应等值的select查询,kingshard会计算出具体命中的子表,而后只会在相应的子表中查询。对应的SQL以下所示:
mysql> select * from test_shard_hash where id = 18; +----+-----------+------+-------+------+------+ | id | str | f | e | u | i | +----+-----------+------+-------+------+------+ | 18 | kingshard | 7.3 | test1 | 32 | 3 | +----+-----------+------+-------+------+------+ 1 row in set (0.00 sec)
对应的SQL日志以下所示:
2015/09/02 18:59:37 - INFO - 127.0.0.1:55003->127.0.0.1:3306:select * from test_shard_hash_0002 where id = 18
当更新的记录落在同一个子表时,kingshard支持这类操做。在上面插入的记录中,id为7和15的记录都落在test_shard_hash_0007
中,因此能够成功地执行下面的SQL:
mysql> update test_shard_hash set u=123 where id = 15 or id = 7; Query OK, 2 rows affected (0.01 sec)
对应的SQL日志是:
2015/09/02 19:17:27 - INFO - 127.0.0.1:55003->192.168.59.103:3307:update test_shard_hash_0007 set u = 123 where id = 15 or id = 7
当更新的记录落在不一样的子表,只有当这些子表在同一个node中,kingshard才支持。kingshard是经过单node事务实现的,也就是说将发往同一个node的SQL都放在一个事务中执行,这些操做正确性由MySQL保证。在上述记录中,咱们能够看出id为17和18的记录都在node1中,因此kingshard是能够执行下列SQL:
mysql> update test_shard_hash set i=23 where id = 17 or id = 18; Query OK, 2 rows affected (0.00 sec)
对应的SQL日志是:
2015/09/02 19:24:46 - INFO - 127.0.0.1:55003->127.0.0.1:3306:update test_shard_hash_0001 set i = 23 where id = 17 or id = 18 2015/09/02 19:24:46 - INFO - 127.0.0.1:55003->127.0.0.1:3306:update test_shard_hash_0002 set i = 23 where id = 17 or id = 18
可是若是更新的记录落在不一样的node时,kingshard则会报告错误:
例如:
mysql> update test_shard_hash set i=23 where id = 15 or id = 18; ERROR 1105 (HY000): no route node
对应的SQL日志是:
2015/09/02 19:24:24 - ERROR - router.go:[483] - [Router] "generateUpdateSql" "update in multi node" "RouteNodeIndexs=[0 1]" conn_id=0
有时候咱们须要操做的表,不在default node中。在kingshard中容许用户将特定的sql路由到指定的node上。只须要在sql语句前面加上包含node名称的注释。
mysql> /*node2*/show tables; +-----------------------+ | Tables_in_kingshard | +-----------------------+ | kingshard_test_conn | | test_shard_hash_0004 | | test_shard_hash_0005 | | test_shard_hash_0006 | | test_shard_hash_0007 | | test_shard_range_0004 | | test_shard_range_0005 | | test_shard_range_0006 | | test_shard_range_0007 | +-----------------------+ 9 rows in set (0.03 sec) mysql> /*node2*/select * from kingshard_test_conn; Empty set (0.01 sec) mysql> /*node2*/desc kingshard_test_conn; +-------+-----------------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-----------------------+------+-----+---------+-------+ | id | bigint(20) unsigned | NO | PRI | NULL | | | str | varchar(256) | YES | | NULL | | | f | double | YES | | NULL | | | e | enum('test1','test2') | YES | | NULL | | | u | tinyint(3) unsigned | YES | | NULL | | | i | tinyint(4) | YES | | NULL | | +-------+-----------------------+------+-----+---------+-------+ 6 rows in set (0.00 sec) mysql> /*node2*/insert into kingshard_test_conn values(10,"hello",10.2,'test1',1,1); Query OK, 1 row affected (0.00 sec) mysql> /*node2*/select * from kingshard_test_conn; +----+-------+------+-------+------+------+ | id | str | f | e | u | i | +----+-------+------+-------+------+------+ | 10 | hello | 10.2 | test1 | 1 | 1 | +----+-------+------+-------+------+------+ 1 row in set (0.00 sec)
有时候在主库中插入数据后,但愿当即从主库读出来。在kingshard中因为读写分离的缘由,select默认会发送到相应node的从库上。可是只须要在select语句中加入相应的注释项(/*master*/
),就能够将select语句发送到主库。
mysql> select/*master*/ * from kingshard_test_conn; +----+----------+------+-------+------+------+ | id | str | f | e | u | i | +----+----------+------+-------+------+------+ | 1 | a | 3.14 | test1 | NULL | NULL | | 5 | ""''\abc | NULL | NULL | NULL | NULL | | 6 | 中国 | NULL | NULL | NULL | NULL | +----+----------+------+-------+------+------+ 3 rows in set (0.01 sec)
在kingshard中,支持sum和count函数,kingshard会将相应的SQL发生到正确的DB,并将结果合并起来再返回给客户的。例如:
mysql> select count(id) from test_shard_hash where id > 1; +-----------+ | count(id) | +-----------+ | 4 | +-----------+ 1 row in set (0.02 sec) mysql> select sum(id) from test_shard_hash where id > 1; +---------+ | sum(id) | +---------+ | 57 | +---------+ 1 row in set (0.02 sec)
相应的SQL日志以下所示:
2015/09/03 14:49:01 - INFO - 127.0.0.1:55768->127.0.0.1:3306:select count(id) from test_shard_hash_0000 where id > 1 2015/09/03 14:49:01 - INFO - 127.0.0.1:55768->127.0.0.1:3306:select count(id) from test_shard_hash_0001 where id > 1 2015/09/03 14:49:01 - INFO - 127.0.0.1:55768->127.0.0.1:3306:select count(id) from test_shard_hash_0002 where id > 1 2015/09/03 14:49:01 - INFO - 127.0.0.1:55768->127.0.0.1:3306:select count(id) from test_shard_hash_0003 where id > 1 2015/09/03 14:49:01 - INFO - 127.0.0.1:55768->192.168.59.103:3307:select count(id) from test_shard_hash_0004 where id > 1 2015/09/03 14:49:01 - INFO - 127.0.0.1:55768->192.168.59.103:3307:select count(id) from test_shard_hash_0005 where id > 1 2015/09/03 14:49:01 - INFO - 127.0.0.1:55768->192.168.59.103:3307:select count(id) from test_shard_hash_0006 where id > 1 2015/09/03 14:49:01 - INFO - 127.0.0.1:55768->192.168.59.103:3307:select count(id) from test_shard_hash_0007 where id > 1 2015/09/03 14:49:14 - INFO - 127.0.0.1:55768->127.0.0.1:3306:select sum(id) from test_shard_hash_0000 where id > 1 2015/09/03 14:49:14 - INFO - 127.0.0.1:55768->127.0.0.1:3306:select sum(id) from test_shard_hash_0001 where id > 1 2015/09/03 14:49:14 - INFO - 127.0.0.1:55768->127.0.0.1:3306:select sum(id) from test_shard_hash_0002 where id > 1 2015/09/03 14:49:14 - INFO - 127.0.0.1:55768->127.0.0.1:3306:select sum(id) from test_shard_hash_0003 where id > 1 2015/09/03 14:49:14 - INFO - 127.0.0.1:55768->192.168.59.103:3307:select sum(id) from test_shard_hash_0004 where id > 1 2015/09/03 14:49:14 - INFO - 127.0.0.1:55768->192.168.59.103:3307:select sum(id) from test_shard_hash_0005 where id > 1 2015/09/03 14:49:14 - INFO - 127.0.0.1:55768->192.168.59.103:3307:select sum(id) from test_shard_hash_0006 where id > 1 2015/09/03 14:49:14 - INFO - 127.0.0.1:55768->192.168.59.103:3307:select sum(id) from test_shard_hash_0007 where id > 1
kingshard支持跨node的select操做使用order by,kingshard先将合适的SQL发生到对应的node,而后将结果集在内存中排序,从而实现select的order by操做。示例以下所示:
mysql> select * from test_shard_hash where id > 1 order by id; +----+-----------+------+-------+------+------+ | id | str | f | e | u | i | +----+-----------+------+-------+------+------+ | 7 | chen | 2.1 | test1 | 123 | 3 | | 15 | flike | 3.14 | test2 | 123 | 3 | | 17 | github | 2.5 | test1 | 32 | 23 | | 18 | kingshard | 7.3 | test1 | 32 | 23 | +----+-----------+------+-------+------+------+ 4 rows in set (0.02 sec)
对应的SQL日志为:
2015/09/03 14:54:11 - INFO - 127.0.0.1:55768->127.0.0.1:3306:select * from test_shard_hash_0000 where id > 1 order by id asc 2015/09/03 14:54:11 - INFO - 127.0.0.1:55768->127.0.0.1:3306:select * from test_shard_hash_0001 where id > 1 order by id asc 2015/09/03 14:54:11 - INFO - 127.0.0.1:55768->127.0.0.1:3306:select * from test_shard_hash_0002 where id > 1 order by id asc 2015/09/03 14:54:11 - INFO - 127.0.0.1:55768->127.0.0.1:3306:select * from test_shard_hash_0003 where id > 1 order by id asc 2015/09/03 14:54:11 - INFO - 127.0.0.1:55768->192.168.59.103:3307:select * from test_shard_hash_0004 where id > 1 order by id asc 2015/09/03 14:54:11 - INFO - 127.0.0.1:55768->192.168.59.103:3307:select * from test_shard_hash_0005 where id > 1 order by id asc 2015/09/03 14:54:11 - INFO - 127.0.0.1:55768->192.168.59.103:3307:select * from test_shard_hash_0006 where id > 1 order by id asc 2015/09/03 14:54:11 - INFO - 127.0.0.1:55768->192.168.59.103:3307:select * from test_shard_hash_0007 where id > 1 order by id asc
kingshard支持在单个node上执行事务,也就是说同一个事务不能跨多个node,当出现跨node的状况时,kingshard会返回错误给客户端。能够跨同node上的不一样子表。示例以下所示:
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> insert into test_shard_hash(id,str,f,e,u,i) values(23,'proxy',9.2,'test1',12,3); Query OK, 1 row affected (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.01 sec)
当在一个事务中,出现跨node的SQL语句时,kingshard会返回错误:
#SQL语句在node2中执行 mysql> insert into test_shard_hash(id,str,f,e,u,i) values(31,'proxy',9.2,'test1',12,3); Query OK, 1 row affected (0.01 sec) #SQL语句在须要在node1执行,跨node了。 mysql> insert into test_shard_hash(id,str,f,e,u,i) values(40,'proxy',9.2,'test1',12,3); ERROR 1105 (HY000): transaction in multi node
kingshard的管理接口,目前仍是命令行的方式。后续有时间打算将其改为web方式。管理端具体的命令能够参考文档。管理端的命令格式,能够分为两类:
admin server(opt,k,v) values(action,k1,v1)
。这种命令是操做整个kingshard的,其中opt表示这个操做的动做;k表示操做的对象,v表示给对象的赋值。admin node(opt,node,k,v) values(action,nodeName,k1,v1)
,这类命令表示操做node。其中opt表示这个操做的动做;node表示操做哪一个node;k表示操做的对象,v表示给对象的赋值。kingshard开源两个月以来,获得了不少开发者的关注。这足以证实,你们对数据库中间件是有需求的,但愿出现一款简单好用的MySQL Proxy。kingshard通过这两个月的迭代开发,也比较稳定了。据了解,有几个公司正在对其进行尝试。后续做者的主要精力会放在优化kingshard的性能上,同时完善kingshard已有的功能。若是你们对kingshard有什么想法或建议,能够发邮件联系我(flikecn#126.com),很是乐意和你们交流。
github: https://github.com/flike/king...
欢迎关注后端技术快讯公众号,有关kingshard的最新消息与后端架构设计类的文章,都会在这个公众号分享。