Atlas Sharding

Sharding当前是Atlas的分布式分支, 是Atlas最近重点开发的功能. [source code](https://github.com/Qihoo360/Atlas/tree/sharding) ## rpm 包安装(推荐) 请到 https://github.com/Qihoo360/Atlas/releases/ 下载相应的rpm包(Sharding2.2.1)直接安装即可. ## 源码安装 sharding是以sharding分支发布的: ``` git clone https://github.com/Qihoo360/Atlas.git git checkout sharding git pull origin sharding ``` 编译sharding分支的Atlas还需要安装lemon: ``` git clone https://github.com/winkyao/lemon cd lemon mkdir build && cd build && cmake .. make sudo make install ``` 之后就可以编译了(glib jemalloc libffi libevent等依赖请自行安装): ``` ./bootstrap.sh make sudo make install ``` ## Atlas sharding 简介 Sharding的基本思想就是把一个数据表中的数据切分成多个部分, 存放到不同的主机上去(切分的策略有多种), 从而缓解单台机器的性能跟容量的问题. sharding是一种水平切分, 适用于单表数据庞大的情景. 目前atlas支持静态的sharding方案, 暂时不支持数据的自动迁移以及数据组的动态加入. Atlas以表为单位sharding, 同一个数据库内可以同时共有sharding的表和不sharding的表, 不sharding的表数据存在未sharding的数据库组中. 目前Atlas sharding支持insert, delete, select, update语句, 只支持不跨shard的事务. 所有的写操作如insert, delete, update只能一次命中一个组, 否则会报"ERROR 1105 (HY000):write operation is only allow to one dbgroup!"错误. 由于sharding取替了Atlas的分表功能, 所以在Sharding分支里面, Atlas单机分表的功能已经移除, 配置tables将不会再有效. 关于垂直切分与水平切分的区别与优缺点, 在这里就不详细解释了. ### 兼容性 Atlas支持非sharding跟sharding的表共存在同一个Atlas中, 2.2.1之前的配置可以直接运行. 之前的配置如 ``` ... proxy-backend-addresses = 192.168.0.12:3306 proxy-read-only-backend-addresses = 192.168.0.13:3306,192.168.0.14:3306 ... ``` 这配置了一个master和两个slave, 这属于非sharding的组, 所有非sharding的表跟语句都会发往这个组内. 所以之前没有Sharding的Atlas的表可以无缝的在新版上使用, 注意: 非Sharding的组只能配置一个, 而sharding的组可以配置多个. 下面的配置, 配置了Sharding的组, 注意与上面的配置区分 ``` ... [group-0] proxy-backend-addresses=192.168.0.15:3306 proxy-read-only-backend-addresses=192.168.0.16:3306 [group-1] proxy-backend-addresses=192.168.0.17:3306 proxy-read-only-backend-addresses=192.168.0.18:3306 ... ``` ### Sharding 架构 ![](/uploads/article/2018/12/06/20181206181715_7747.jpg) Atlas是无状态的, 对于后端的多个组, 可以配置任意多个Atlas实例, 这一点与MongoDB的mongos类似. #### Sharding数据库组 在Atlas中, 将一个组看做是数据存储的单位, 一个组由一台master, 零台或者多台slave组成(mysql主从同步需要由用户自己配置). 每个组之间的数据独立, 没有关系, 表的数据的各个部分存储在各个组中. ##### 组内读写分离 与非sharding的方案一样, Atlas sharding也支持组内的读写分离, 也就是说Atlas在命中了某个组之后, 还是会对这个组内的master和slave执行读写分离(读发送到slave, 写发送到master). ### Sharding 数据切分策略 #### shard key 每一个shard table都有一个shard key, 其可以是主键, 也可以是非主键, 但是这个列必须是一个整数. Atlas会利用这个shard key来判断应该把这条记录存放到哪一个数据库组中. 现在Atlas Shardingh支持两种类型的数据切分: Range方式和Hash方式. #### Range 方式 ![](/uploads/article/2018/12/06/20181206181743_1581.jpg) 如上图中, shard Key范围在0-1000的数据存放在DbGroup0中, 范围在1000-2000的数据存放在DbGroup1中, 2000-MaxInt 的数据存放在DbGroup2 中. 这些范围的大小不需要相同.比如id为shard key的话, sql: "select * from test where id = 1500;", Atlas会将此语句发往DbGroup1. 暂时Atlas的range是静态的, 不支持动态的增加范围. ##### 优点: 对于range的sql查询如(where id > 100 or id < 1000), range方式的sharding可以精确的命中后端的数据组, 不需要将sql发到各个mysql去请求数据, 节约了网络传输的消耗. ##### 缺点 如果shard key是递增的, 那么可能会在一段时间内的所有sql都命中到同一个数据组, 没有体现出sharding的优势, range不适用于这种场景. ##### 适用场景 range适用于对范围查询有大量需求, 并且shard key相对离散插入的情景 #### hash 方式 ![](/uploads/article/2018/12/06/20181206181819_8853.jpg) 目前Atlas使用取模的方式实现Hash, 也就是说Hash(id) = id % dbgroup_count, 如id = 10, id % 3 = 1, 所以会命中到DbGroup1中. ##### 优缺点 hash跟range方式是恰好相反的, hash 可以应对数据递增的情景, 即使是在递增的情况下, sharding的数据也是均匀分布在各个数据组内的, 但是其缺点就是对于范围的查询通常都需要查询所有的dbgroup, 网络的消耗比较大. ##### 适用场景 hash 适用于shard key顺序增长, 并对范围查询的需求比较小的情景 ### 关于支持的语句 Atlas sharding只对sql语句提供有限的支持, 目前支持基本的Select, insert/replace, delete, update语句, 支持全部的Where语法(SQL-92标准), 不支持DDL(create drop alter)以及一些管理语句, DDL请直连MYSQL执行, 请只在Atlas上执行Select, insert, delete, update(CRUD)语句, 对于以下语句, 如果语句命中了多台dbgroup, Atlas均未做支持(如果语句只命中了一个dbgroup, 如select count(*) from test where id < 1000, 其中dbgroup0范围是0 - 1000, 那么这些特性都是支持的) * Limit Offset(支持Limit) * Order by * Group by * Join * ON * Count, Max, Min等函数 请不要在Sharding的表上使用这些特性, 如果对这种特性有需求请不要让此表sharding. #### 注意: 1. 子查询在Sharding中可能会返回不正确的结果, 也请不要使用子查询. 请把语句拆分成多句执行 2. 对于写操作, 如果写操作命中了多个数据库组, 由于部分成功(某个组执行失败)需要回滚的问题, 暂时不支持写操作命中多个数据组的语句.请拆分成多个sql语句执行. 3. Atlas可能会在接下来的版本中对其中的一些特性中做出支持. ### 关于事务支持 事务在Atlas的非sharding的表是完全支持的, 但是对于sharding的表, Atlas只能提供部分的支持(不支持跨dbgroup的事务). Atlas只支持事务中涉及单个dbgroup的语句, 例如有两个dbgroup0, dbgroup1, 其切分方式是range, 规则是dbgroup0: 0 - 999, dbgroup1: 1000 - 2000, ``` mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> insert into sharding_test(id, name, age) values(1, 'test', 0); Query OK, 1 row affected (0.00 sec) mysql> insert into sharding_test(id, name, age) values(1500, 'test', 0); ERROR 1179 (sqlst): Proxy Warning - sharding dbgroup is in trans, transaction will not work across multi dbgroup mysql> /*master*/select * from sharding_test where id < 1000; +----+------+------+----------+----------+ | id | name | age | birthday | nickname | +----+------+------+----------+----------+ | 1 | test | 0 | NULL | NULL | +----+------+------+----------+----------+ 1 row in set (0.00 sec) mysql> /*master*/select * from sharding_test; ERROR 1179 (sqlst): Proxy Warning - sharding dbgroup is in trans, transaction will not work across multi dbgroup mysql> commit; Query OK, 0 rows affected (0.00 sec) mysql> /*master*/select * from sharding_test; +----+------+------+----------+----------+ | id | name | age | birthday | nickname | +----+------+------+----------+----------+ | 1 | test | 0 | NULL | NULL | +----+------+------+----------+----------+ 1 row in set (0.00 sec) ``` 请注意第二条语句, 由于之前将insert与dbgroup0绑定了, 所以从此之后Atlas在此事务中只接受涉及dbgroup0的语句, 其他语句将会执行失败. ```"/*master*/select * from sharding_test;"``` 执行失败是因为, 这个语句会命中所有的dbgroup, 也是同理, 不支持这种语句. 在commit之后, sharding dbgroup不再处于事务状态, 就可以执行跨shard的操作了 换句话说, 如果是hash方式sharding的表, 基本上事务是无法支持的, 因为hash的表, 大部分操作都是会涉及多个dbgroup的. ## 增加节点 **注意**: 暂时只支持range方式的节点扩展, hash方式由于需要数据迁移, 暂时未做支持. 扩展节点在保证原来节点的范围不改变的情况下, 如已有dbgroup0为范围0 - 999, dbgroup1为范围 1000 - 1999, 这个时候可以增加范围>2000的节点. 如增加一个节点为2000 - 2999, 修改配置文件, 重启Atlas即可. ## 示例 假设我们有以下一个sharding的表, 建表语句如下: ``` DROP TABLE IF EXISTS `sharding_test`; CREATE TABLE `sharding_test` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` char(50) COLLATE utf8_bin NOT NULL, `age` int(11) DEFAULT NULL, `birthday` date DEFAULT NULL, `nickname` char(50) COLLATE utf8_bin DEFAULT NULL, PRIMARY KEY (`id`) ) ``` 有两个dbgroup(数据库组), 每个dbgroup有一个master, 一个slave, sharding_test使用range的方式, 以id作为shard key, 属于test数据库, dbgroup0属于范围0 - 999, dbgroup1 属于范围 1000 - 1999, 数据库用户为root, 密码为mysqltest dbgroup0 有一主一从, 分别在localhost:3307 (master)和localhost:3308 (slave)端口 dbgroup1 有一主一从, 分别为localhost:3309 (master)和localhost:3310 (slave)端口 配置文件如下 ``` [mysql-proxy] admin-username = user admin-password = pwd admin-lua-script = /usr/local/mysql-proxy/lib/mysql-proxy/lua/admin.lua proxy-backend-addresses = 127.0.0.1:3306 daemon = true keepalive = false event-threads = 4 log-level = debug log-path = /usr/local/mysql-proxy/log sql-log = realtime proxy-address = 0.0.0.0:1234 admin-address = 0.0.0.0:2345 charset = UTF8 wait-timeout = 3600 pwds = root:S4HJu78/H/6I/aYp2Xdb8Q== [shardrule-0] table = test.sharding_test type = range shard-key = id groups = 0:0-999,1:1000-1999 [group-0] # master proxy-backend-addresses=127.0.0.1:3307 # slave proxy-read-only-backend-addresses=127.0.0.1:3308 [group-1] proxy-backend-addresses=127.0.0.1:3309 proxy-read-only-backend-addresses=127.0.0.1:3310 ``` Atlas sharding部分新增配置项,包含两个部分: * shardrule. 一个shardrule对应一个分表规则,不同的shardrule通过下划线后面的数字区分。例如shardrule-0, shardrule-1....。一个shardrule里面有以下几项: ``` [shardrule-0] table = test.sharding_test #分表名,有数据库+表名组成 type = range #sharding类型:range 或 hash shard-key = id #sharding 字段 groups = 0:0-999,1:1000-1999 #分片的group,如果是range类型的sharding,则groups的格式是:group_id:id范围。如果是hash类型的sharding,则groups的格式是:group_id。例如groups = 0, 1 ``` * group. 一个group一般包含一主多从,由master(proxy-backend-addresses)和slave(proxy-read-only-backend-addresses)组成。group之间的区别也是通过通过下划线后面的数字区分。 在本地启动好这些后端mysql实例(主从同步为了演示方便, 我们先不配置). 我们用Atlas插入几条数据: ``` $ mysql -h127.0.0.1 -P1234 -uroot -pmysqltest -c mysql> use test; Database changed mysql> insert into sharding_test(id, name, age) values(1, 'test', 0); Query OK, 1 row affected (0.00 sec) mysql> insert into sharding_test(id, name, age) values(50, 'test', 0), (999, 'test', 0); Query OK, 2 rows affected (0.00 sec) ``` 以上几条数据都插入到了dbgroup0, 请注意第二条多值插入的语句, 因为50和999都命中了dbgroup0, 所以其执行成功, 但是如果执行以下的语句: ``` mysql> insert into sharding_test(id, name, age) values(100, 'test', 0), (1500, 'test', 0); ERROR 1105 (HY000): Proxy Warning - write operation is only allow to one dbgroup! ``` 在sharding的表中, 这是不允许的, 因为id为100命中了dbgroup0, 而id为1500 命中了dbgroup1, 由于分布式的多值插入可能导致部分成功, 需要回滚, 这个Atlas暂不支持. update, delete, replace同理. 我们再插几条数据到dbgroup1: ``` mysql> insert into sharding_test(id, name, age) values(1000, 'test', 0), (1999, 'test', 0); Query OK, 2 rows affected (0.00 sec) ``` 我们现在用直连mysql来检验一下: ``` $ mysql -uroot -pmysqltest -h 127.0.0.1 -P3307 mysql> use test; Database changed mysql> select * from sharding_test; +-----+------+------+----------+----------+ | id | name | age | birthday | nickname | +-----+------+------+----------+----------+ | 1 | test | 0 | NULL | NULL | | 50 | test | 0 | NULL | NULL | | 999 | test | 0 | NULL | NULL | +-----+------+------+----------+----------+ 3 rows in set (0.00 sec) mysql> exit Bye $ mysql -uroot -pmysqltest -h 127.0.0.1 -P3309 mysql> use test; Database changed mysql> select * from sharding_test; +------+------+------+----------+----------+ | id | name | age | birthday | nickname | +------+------+------+----------+----------+ | 1000 | test | 0 | NULL | NULL | | 1999 | test | 0 | NULL | NULL | +------+------+------+----------+----------+ 2 rows in set (0.00 sec) ``` 可以看到结果都正确的插入到了各个dbgroup的master中(注意现在由于没有配置主从, 所以在从库中都是没有数据的). 我们现在在Atlas中执行一下select: ``` $ mysql -h127.0.0.1 -P1234 -uroot -pmysqltest -c mysql> use test; Database changed mysql> /*master*/ select * from sharding_test; +------+------+------+----------+----------+ | id | name | age | birthday | nickname | +------+------+------+----------+----------+ | 1 | test | 0 | NULL | NULL | | 50 | test | 0 | NULL | NULL | | 999 | test | 0 | NULL | NULL | | 1000 | test | 0 | NULL | NULL | | 1999 | test | 0 | NULL | NULL | +------+------+------+----------+----------+ 5 rows in set (0.00 sec) ``` 可以看到, 所有的数据都取出来了, 不管是dbgroup0或是dbgroup1的. (加上```/*master*/```的目的是因为现在从库中没有数据, 将语句强制发往主库) 我们再来在Atlas中执行一些语句: ``` mysql> /*master*/select * from sharding_test where id >= 1000 and id <= 1999; +------+------+------+----------+----------+ | id | name | age | birthday | nickname | +------+------+------+----------+----------+ | 1000 | test | 0 | NULL | NULL | | 1999 | test | 0 | NULL | NULL | +------+------+------+----------+----------+ 2 rows in set (0.00 sec) mysql> /*master*/select * from sharding_test where id < 1999; +------+------+------+----------+----------+ | id | name | age | birthday | nickname | +------+------+------+----------+----------+ | 1 | test | 0 | NULL | NULL | | 50 | test | 0 | NULL | NULL | | 999 | test | 0 | NULL | NULL | | 1000 | test | 0 | NULL | NULL | +------+------+------+----------+----------+ 4 rows in set (0.00 sec) mysql> /*master*/select * from sharding_test where id IN (1, 1999); +------+------+------+----------+----------+ | id | name | age | birthday | nickname | +------+------+------+----------+----------+ | 1 | test | 0 | NULL | NULL | | 1999 | test | 0 | NULL | NULL | +------+------+------+----------+----------+ 2 rows in set (0.00 sec) mysql> /*master*/select * from sharding_test where id between 1 and 1999; +------+------+------+----------+----------+ | id | name | age | birthday | nickname | +------+------+------+----------+----------+ | 1 | test | 0 | NULL | NULL | | 50 | test | 0 | NULL | NULL | | 999 | test | 0 | NULL | NULL | | 1000 | test | 0 | NULL | NULL | | 1999 | test | 0 | NULL | NULL | +------+------+------+----------+----------+ 5 rows in set (0.00 sec) mysql> /*master*/select count(*) from sharding_test; ERROR 1105 (HY000): Proxy Warning - Sharing Hit Multi Dbgroup Not Support SQL mysql> /*master*/select count(*) from sharding_test where id <= 999; +----------+ | count(*) | +----------+ | 3 | +----------+ 1 row in set (0.00 sec) mysql> /*master*/select * from sharding_test where id <= 999 limit 1; +----+------+------+----------+----------+ | id | name | age | birthday | nickname | +----+------+------+----------+----------+ | 1 | test | 0 | NULL | NULL | +----+------+------+----------+----------+ 1 row in set (0.00 sec) mysql> /*master*/select * from sharding_test limit 1; +----+------+------+----------+----------+ | id | name | age | birthday | nickname | +----+------+------+----------+----------+ | 1 | test | 0 | NULL | NULL | +----+------+------+----------+----------+ 1 row in set (0.00 sec) mysql> /*master*/select * from sharding_test where id <= 999 order by id desc; +-----+------+------+----------+----------+ | id | name | age | birthday | nickname | +-----+------+------+----------+----------+ | 999 | test | 0 | NULL | NULL | | 50 | test | 0 | NULL | NULL | | 1 | test | 0 | NULL | NULL | +-----+------+------+----------+----------+ 3 rows in set (0.00 sec) ``` [mysql-proxy] #管理接口的用户名 admin-username = user #管理接口的密码 admin-password = pwd #Atlas后端连接的MySQL主库的IP和端口,可设置多项,用逗号分隔 proxy-backend-addresses = 192.168.1.1:3306 #从库 proxy-read-only-backend-addresses = 192.168.1.2:3306@1 #用户名和密码配置项,需要和主从复制配置的用户名和密码配置一样 r1:+jKsgB3YAG8=, user2:GS+tr4TPgqc= #后台运行 daemon = true keepalive = false #工作线程数,对Atlas的性能有很大影响,可根据情况适当设置 event-threads = 4 #日志级别,分为message、warning、critical、error、debug五个级别 log-level = error #日志存放的路径 log-path = ./log #SQL日志的开关,可设置为OFF、ON、REALTIME,OFF代表不记录SQL日志,ON代表记录SQL日志,REALTIME代表记录SQL日>志且实时写入磁盘,默认为OFF #sql-log = OFF #慢日志输出设置。当设置了该参数时,则日志只输出执行时间超过sql-log-slow(单位:ms)的日志>记录。不设置该参数则输出全部日志。 sql-log-slow = 1000 #实例名称,用于同一台机器上多个Atlas实例间的区分 instance = web #Atlas监听的工作接口IP和端口 proxy-address = 0.0.0.0:13470 #Atlas监听的管理接口IP和端口 admin-address = 0.0.0.0:23470 #分表设置,此例中person为库名,mt为表名,id为分表字段,3为子表数量,可设置多项,以逗号分>隔,若不分表则不需要设置该项 #tables = person.mt.id.3 #默认字符集,设置该项后客户端不再需要执行SET NAMES语句 charset = utf8 #允许连接Atlas的客户端的IP,可以是精确IP,也可以是IP段,以逗号分隔,若不设置该项则允许所>有IP连接,否则只允许列表中的IP连接 #client-ips = 127.0.0.1, 192.168.1 #Atlas前面挂接的LVS的物理网卡的IP(注意不是虚IP),若有LVS且设置了client-ips则此项必须设置>,否则可以不设置 #lvs-ips = 192.168.1.1
联系我们

邮箱 626512443@qq.com
电话 18611320371(微信)
QQ群 235681453

Copyright © 2015-2022

备案号:京ICP备15003423号-3