
其中1,2相对较容易实现,本文重点讲讲水平拆表和水平拆库,以及基于mybatis插件方式实现水平拆分方案落地。
在 《聊一聊扩展字段设计》 一文中有讲解到基于KV水平存储扩展字段方案,这就是非常典型的可以水平分表的场景。主表和kv表是一对N关系,随着主表数据量增长,KV表最大N倍线性增长。
这里我们以分KV表水平拆分为场景
对于kv扩展字段查询,只会根据id + key 或者 id 为条件的方式查询,所以这里我们可以按照id 分片即可
分512张表(实际场景具体分多少表还得根据字段增加的频次而定)
分表后表名为kv_000 ~ kv_511
id % 512 = 1 .... 分到 kv_001,
id % 512 = 2 .... 分到 kv_002
依次类推!
水平分表相对比较容易,后面会讲到基于mybatis插件实现方案
场景:以下我们基于博客文章表分库场景来分析
目标:
表结构如下(节选部分字段):
按照user_id sharding
假如分1024个库,按照user_id % 1024 hash
user_id % 1024 = 1 分到db_001库
user_id % 1024 = 2 分到db_002库
依次类推
目前是2个节点,假如后期达到瓶颈,我们可以增加至4个节点
最多可以增加只1024个节点,性能线性增长
对于水平分表/分库后,非shardingKey查询首先得考虑到
基于mybatis分库分表,一般常用的一种是基于spring AOP方式, 另外一种基于mybatis插件。其实两种方式思路差不多。
为了比较直观解决这个问题,我分别在Executor 和StatementHandler阶段2个拦截器
实现动态数据源获取接口
测试结果如下
由此可知,我们需要在Executor阶段 切换数据源
对于分库:
原始sql:
目标sql:
其中定义了三个注解
@useMaster 是否强制读主
@shardingBy 分片标识
@DB 定义逻辑表名 库名以及分片策略
1)编写entity
Insert
select
以上顺利实现mysql分库,同样的道理实现同时分库分表也很容易实现。
此插件具体实现方案已开源: https://github.com/bytearch/mybatis-sharding
目录如下:
mysql分库分表,首先得找到瓶颈在哪里(IO or CPU),是分库还是分表,分多少?不能为了分库分表而拆分。
原则上是尽量先垂直拆分 后 水平拆分。
以上基于mybatis插件分库分表是一种实现思路,还有很多不完善的地方,
例如:
7.1 范围分片(range)比如说t3表
(1)行数非常多,2000w(1-1000w:sh1 1000w01-2000w:sh2)
(2)访问非常频繁,用户顺序访问较多
cp schema.xml schema.xml.1
vim schema.xml
添加:
<table name="t3" dataNode="sh1,sh2" rule="auto-sharding-long" />
vim rule.xml
<tableRule name="auto-sharding-long">
<rule>
<columns>id</columns>
<algorithm>rang-long</algorithm>
</rule>
<function name="rang-long"
class="io.mycat.route.function.AutoPartitionByLong">
<property name="mapFile">autopartition-long.txt</property>
</function>
vim autopartition-long.txt
0-10=0
10-20=1
[root@db01 conf]# mysql -S /data/3308/mysql.sock -e "select * from taobao.t3"
[root@db01 conf]# mysql -S /data/3307/mysql.sock -e "select * from taobao.t3"
7.2 取模分片
1%3 1
2%3 2
3%3 0
4%3 1
5%3 2
任何正整数数字和N(正整数)取模,得的值永远都是 0~N-1
id % 分片数量取模
N % 5 = 0-4 idx
取余分片方式:分片键(一个列)与节点数量进行取余,得到余数,将数据写入对应节点
vim schema.xml
<table name="t4" dataNode="sh1,sh2" rule="mod-long" />
vim rule.xml
<property name="count">2</property>
创建测试表:
mysql -S /data/3307/mysql.sock -e "use taobaocreate table t4 (id int not null primary key auto_increment,name varchar(20) not null)"
mysql -S /data/3308/mysql.sock -e "use taobaocreate table t4 (id int not null primary key auto_increment,name varchar(20) not null)"
重启mycat
mycat restart
[root@db01 ~]# mysql -uroot -p123456 -h 10.0.0.52 -P8066 --default-auth=mysql_native_password
use TESTDB
insert into t4(id,name) values(1,'a')
insert into t4(id,name) values(2,'b')
insert into t4(id,name) values(3,'c')
insert into t4(id,name) values(4,'d')
insert into t4(id,name) values(6,'x'),(8,'y'),(10,'z')
分别登录后端节点查询数据
mysql -S /data/3308/mysql.sock -e "select * from taobao.t4"
mysql -S /data/3307/mysql.sock -e "select * from taobao.t4"
7.3 枚举分片 (区域、zone)
t5 表
id name telnum
1 bj 1212
2 sh 22222
3 bj 3333
4 sh 44444
5 bj 5555
sharding-by-intfile
vim schema.xml
<table name="t5" dataNode="sh1,sh2" rule="sharding-by-intfile" />
vim rule.xml
<function name="hash-int" class="org.opencloudb.route.function.PartitionByFileMap">
<property name="mapFile">partition-hash-int.txt</property>
<property name="type">1</property>
</function>
vim partition-hash-int.txt 配置:
bj=0
sh=1
DEFAULT_NODE=1
mysql -S /data/3307/mysql.sock -e "use taobaocreate table t5 (id int not null primary key auto_increment,name varchar(20) not null)"
mysql -S /data/3308/mysql.sock -e "use taobaocreate table t5 (id int not null primary key auto_increment,name varchar(20) not null)"
重启mycat
mycat restart
mysql -uroot -p123456 -h10.0.0.52 -P8066 --default-auth=mysql_native_password
use TESTDB
insert into t5(id,name) values(1,'bj')
insert into t5(id,name) values(2,'sh')
insert into t5(id,name) values(3,'bj')
insert into t5(id,name) values(4,'sh')
insert into t5(id,name) values(5,'tj')
mysql -S /data/3308/mysql.sock -e "select * from taobao.t5"
mysql -S /data/3307/mysql.sock -e "select * from taobao.t5"
7.4 Mycat全局表
a b c d .....
join
t
a
id name age
1zs 18 sh1
idaddr aid
1001 bj1
1002 sh2
2ls 19 sh2
idaddr aid
1001 bj1
1002 sh2
t
idaddr aid
1001 bj1
1002 sh2
使用场景:
如果你的业务中有些数据类似于数据字典,比如配置文件的配置,
常用业务的配置或者数据量不大很少变动的表,这些表往往不是特别大,
而且大部分的业务场景都会用到,那么这种表适合于Mycat全局表,无须对数据进行切分,
要在所有的分片上保存一份数据即可,Mycat 在Join *** 作中,业务表与全局表进行Join聚合会优先选择相同分片内的全局表join,
避免跨库Join,在进行数据插入 *** 作时,mycat将把数据分发到全局表对应的所有分片执行,在进行数据读取时候将会随机获取一个节点读取数据。
vim schema.xml
<table name="t_area" primaryKey="id" type="global" dataNode="sh1,sh2" />
mysql -S /data/3307/mysql.sock -e "use taobaocreate table t_area (id int not null primary key auto_increment,name varchar(20) not null)"
mysql -S /data/3308/mysql.sock -e "use taobaocreate table t_area (id int not null primary key auto_increment,name varchar(20) not null)"
重启mycat
mycat restart
mysql -uroot -p123456 -h10.0.0.52 -P8066 --default-auth=mysql_native_password
mysql -S /data/3308/mysql.sock -e "select * from taobao.t_area"
mysql -S /data/3307/mysql.sock -e "select * from taobao.t_area"
7.5 E-R分片
a
join
b
on a.xx =b.yy
a
id name
1 a sh1
3 c
2 b sh2
4 d
b
id addr aid
1001 bj 1sh1
1002 sh 2
1003 tj 3sh2
1004 wh 4
为了防止跨分片join,可以使用E-R模式
<table name="a" dataNode="sh1,sh2" rule="mod-long">
<childTable name="b" joinKey="aid" parentKey="id" />
</table>
select * from a join b on a.id = b.aid
例子:
mysql -S /data/3308/mysql.sock -e "use taobaocreate table a (id int not null primary key auto_increment,name varchar(20) not null)"
mysql -S /data/3308/mysql.sock -e "use taobaocreate table b (id int not null primary key auto_increment,addr varchar(20) not null ,aid int )"
insert into b(id,addr,aid) values(1001,'bj',1)
insert into b(id,addr,aid) values(1002,'sj',3)
insert into b(id,addr,aid) values(1003,'sd',4)
insert into b(id,addr,aid) values(1004,'we',2)
insert into b(id,addr,aid) values(1005,'er',5)
========
后端数据节点数据分布:
mysql -S /data/3307/mysql.sock -e "select * from taobao.a"
mysql -S /data/3307/mysql.sock -e "select * from taobao.b"
mysql -S /data/3308/mysql.sock -e "select * from taobao.a"
mysql -S /data/3308/mysql.sock -e "select * from taobao.b"
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)