通过分区字段(shardkey)把一个大表水平拆分到多个数据库,下面给大家介绍下分表的方法:
如何选择分区字段
一旦定好分区字段,就不能轻易修改分区字段,因此开发人员需要提前评估。选择分区字段的时候主要考虑两个维度:
- 通过该字段能否对数据进行均衡的存储和访问
- 多个相关联的表能否使用同一个字段。(相同分区字段数据会存储在同一个物理分片中,大多数的业务逻辑需要进行join时,可无需走分布式事务逻辑而直接在单节点内执行,效率大大提示)
举个例子,如果业务有两张表,一个用于记录用户的基本信息,一个用于用户的订单信息,此时如果选取用户ID作为shardkey,则理论数据分布和访问都会比较均衡,同时单个用户对应的基本信息和订单信息都会在一个后端数据库中,方便后续的join等操作。
shardkey选择的限制
普通的分表创建时在最后面指定shardkey的值,该值为表中的一个字段名字,会用于后续sql的路由选择:
shardkey字段有限定要求如下:
1.如存在主键或者唯一索引,则shardkey字段必须是主键以及所有唯一索引的一部分
2.shardkey字段的类型必须是int,bigint,smallint/char/varchar
3.shardkey字段的值尽量使用ascii码,网关不会转换字符集,所以不同字符集可能会路由到不同的分区(且尽量不要有中文)
4.不要update shardkey字段的值,如必须则先delete,再insert。
5.`shardkey=` 放在create语句的最后面,如下示例
6.访问数据尽量都能带上shardkey字段
创建一张分表
mysql> create table test.right ( a int not null,b int not null, c char(20) not null,primary key(a,b) ,unique key(a,c)) shardkey=a;
Query OK, 0 rows affected (0.12 sec)
shardkey'
是系统标记分片字段的关键字,不可占用。
shardkey=noshardkey_allset
定义该表为广播表的关键字,广播表表示该表不分表,但会在每个物理分片中都存储一份。
常见DML操作
使用分表时,对DML有一定的要求,具体如下(下面的例子中a为shardkey):
SELECT最好带上shardkey
select最好带上shardkey字段,由于分布式路由默认采用hash方式。
- 若是
=
或者in
,路由将自动跳转到对应分片,此时效率最高。 - 若无
=
或者in
,分布式系统会自动全表扫描,然后在网关进行结果集聚合,此时效率较低:
例如:下面两条sql根据shardkey的值直接可以发送到对应的数据库,通常5ms内可以处理完成。
mysql> select a,b,c from test.test1 where a=2 order by b;
mysql> select a,b,c from test.test1 where a in (2) order by b;
例如:如下的sql都会发到所有的后端数据库,然后需要对数据进行额外的汇总排序,通常要5~20ms才能处理完成。
mysql> select a,b,c from test.test1 where a>2 order by b;
mysql> select a,b,c from test.test1 where c=2 order by b;
insert/replace字段必须包含shardkey
insert/replace字段必须包含shardkey,否则路由不知道应该将数据插入到哪个物理分片,会拒绝执行该sql;
mysql> insert into test.test1 (b,c) values(4,"record3");
ERROR 1105 (07000): Proxy Warning - sql have no shardkey
mysql> insert into test.test1 (a,c) values(4,"record3");
Query OK, 1 row affected (0.01 sec)
使用广播表或单表时除外。
delete/update字段必须包含shardkey
delete/update时为了安全考虑,执行该类sql的时候必须带有where条件,系统拒绝执行该sql命令,where条件最好也和select一样带上shardkey:
mysql> delete from test.test1;
ERROR 1005 (07000): Proxy Warning - sql is not legal,tokenizer_gram went wrong
mysql> delete from test.test1 where a=1;
Query OK, 1 row affected (0.01 sec)
使用广播表或单表时除外。
修改shardkey字段值
同时update不能修改shardkey的字段的值,需要的话先insert再delete
mysql> update test.test1 set a=10 where d=1;
ERROR 7013 (HY000): Proxy ERROR:combine_sql_key return null,something went wrong
mysql> update test.test1 set d=1 where a=1;
Query OK, 0 rows affected (0.00 sec)
不能更换shardkey字段类型、修改字段名称、删除shardkey字段、或更换shardkey字段,除非您新建一个表。
常见问题:
表没有主键:
mysql> create table test.e1 ( a int ,b int) shardkey=a;
ERROR 1105 (HY000): This table type requires a primary key
主键或者唯一键没有包含shardkey:
mysql> create table test.e2 ( a int not null,b int not null, c char(20) not null,primary key(a,b) ) shardkey=c;
ERROR 1105 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function
shardkey的拼写错误或者列名错误:
mysql> create table test.e3 ( a int key,b int,c char(20)) shardkey1=d;
ERROR 1911 (HY000): Unknown option 'shardkey1'
mysql> create table test.e4 ( a int key,b int,c char(20)) shardkey=d;
ERROR 7008 (HY000): Proxy ERROR:shardkey must be one of the column