liangsen | 2023年3月
本文主要介绍分库分表的基本概念、优缺点,以及常用的数据库分库分表中间件:Mycat2和Atlas。
我们想一个问题,为什么要对于数据库进行分库分表?我们不妨分析下数据库架构的演变:
我们试想,随着业务的扩展,用户越来越多,数据增长速度越来越快,数据库压力越来越大,对数据库而言,读操作的频率远大于写操作,读操作带来的压力成为了数据库的瓶颈,这时候首先会采取数据库读写分离技术,来提升业务系统的读性能。将数据库分为主从库,主库用于写数据,多个从库用于完成读数据。但是在高并发的情况下,主从库之间的数据同步时存在一定的延迟的。
我们继续思考,在数据库数据急剧增加的情况下,数据库仍是整个系统的瓶颈。例如电商系统中的用户、商品、订单等等模块,放在一个库中不仅十分臃肿,且难以维护,这时候我们不妨将一些根本互不相关、彼此独立的数据,分别放在不同的数据库中,这样可以将压力分散开来,且提高了系统的吞吐量。
再继续去看,是不是有时候仍会面临单表的数据量庞大,极大的影响的SQL的执行性能,在高并发的查询场景下,性能十分低下,此时,我们可以将原有数据库水平分成多个数据库,再将表记录分布存储在不同的数据库节点上,这种方式我们称为水平分库分表。
垂直分:表数量多造成海量数据,各个业务逻辑划分明确、耦合度低。
水平分:表数据少,但单表数据大造成海量数据。
以上讲述的其实都是分库分表带来的优点,下面一起来看下其缺点:
这里对这些不做过多的阐述,我们主要介绍工具的使用及对比。
官网:http://mycatone.top/
仓库地址:
https://github.com/MyCATApache/Mycat2
https://gitee.com/MycatOne/Mycat2
Mycat是由阿里团队基于Cobar进行的二次开发,Mycat2相对于Mycat1.6使用更加便捷,配置更加方便。
Mycat的原理其实是“拦截”,拦截用户发送的SQL请求,做特定分析后转发给后端真实的数据库,并将结果做适当处理后再返回给用户。
更多:数据源映射关系。
这里主要介绍一些常见的概念:
注意:mycat需要java1.8环境,请提前准备好。
# 1、下载模板
wget http://dl.mycat.org.cn/2.0/install-template/mycat2-install-template-1.21.zip
# 2、下载jar包
wget http://dl.mycat.org.cn/2.0/1.21-release/mycat2-1.21-release-jar-with-dependencies-2022-5-9.jar
# 3、解压模板,将jar放至模板的 lib目录下
unzip mycat2-install-template-1.21.zip
mv mycat2-1.21-release-jar-with-dependencies-2022-5-9.jar ./mycat
# 4、添加权限
chmod 777 ./bin/mycat
chomd 777 ./bin/wrapper-linux-*
# 5、安装java
yum install java-1.8.0-openjdk
# 6、JAVA环境变量设置
JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-3.b13.el7_5.x86_64
JRE_HOME=$JAVA_HOME/jre
CLASS_PATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib
PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
export JAVA_HOME JRE_HOME CLASS_PATH PATH
mycat2有一款自己的UI工具,本质是java的一个jar包:
下载链接:http://dl.mycat.org.cn/2.0/ui/
本地有JAVA环境,直接双击jar包就可以打开
看着上去UI比较简陋,但不影响我们的使用。
目录如下:
在mycat2中,cluster、datasources、schemas、users有了各自的文件夹,其内的文件名称形式:{cluster/datasources/schemas/users}.{cluster/datasources/schemas/users}.josn,且可通过mycat在线输入命令进行更改,不用再像mycat1.6提前进行配置。
在启动Mycat之前我们需要设置一个数据源:
vim ./conf/datasources/prototypeDs.datasource.json
是的,初始配置很简单,只要你的机器有一套JAVA1.8环境、一套MySQL以及mycat及相关的权限设置好,下面我们来看一下mycat的管理:
cd mycat/bin
./mycat start
./mycat status
./mycat start 启动
./mycat stop 停止
./mycat console 前台运行
./mycat restart 重启服务
./mycat pause 暂停
./mycat status 查看启动状态
eg:
前台运行成功如下:
登录查看,mycat的端口为8066:
你会发现是不是和我们普通的mysql没有什么区别看上去。
mycat的初始账户密码为:root/123456
想要修改,可在 ./conf/users/root.user.json中进行:
看到这里,我们会发现对于mycat2 的配置其实很简单,初始的启动我们只设置了一个数据源就OK了,其次就是mycat的账户密码,可以自行设置。
下面我们简单举两个实例让大家体会下mycat2分库分表。
还记得我们在启动mycat前设置了一个数据源了嘛,我们可以使用mysql客户端进行查询:
/*+ mycat:showDataSources{} */;
在这里我们就对这个数据库进行分库分表的实战。
假设背景,现有如下表:
#客户表 rows:20万
CREATE TABLE customer(
id INT AUTO_INCREMENT,
NAME VARCHAR(200),
PRIMARY KEY(id)
);
#订单表 rows:600万
CREATE TABLE orders(
id INT AUTO_INCREMENT,
order_type INT,
customer_id INT,
amount DECIMAL(10,2),
PRIMARY KEY(id)
);
#订单详细表 rows:600万
CREATE TABLE orders_detail(
id INT AUTO_INCREMENT,
detail VARCHAR(2000),
order_id INT,
PRIMARY KEY(id)
);
#订单状态字典表 rows:20
CREATE TABLE dict_order_type(
id INT AUTO_INCREMENT,
order_type VARCHAR(200),
PRIMARY KEY(id)
);
注意:以下操作均是在Mycat客户端上进行的,不需要在数据源操作。
step1:创建逻辑库
create database eg1;
可以在conf/schemas中看到,自动生成了eg1.schema.json。
step2:创建orders表(分库分表),插入数据,进行查询
#在 Mycat 终端直接运行建表语句进行数据分片
CREATE TABLE eg1.orders(
id BIGINT NOT NULL AUTO_INCREMENT,
order_type INT,
customer_id INT,
amount DECIMAL(10,2),
PRIMARY KEY(id),
KEY `id` (`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
dbpartition BY mod_hash(customer_id) tbpartition BY mod_hash(customer_id)
tbpartitions 10 dbpartitions 5;
最下面这句话的意思是,以customer_id 为分片键,采取MOD_HASH分片策略,分5个库,每个库10个表。
分片效果如下:
插入数据,进行查询
INSERT INTO orders(id,order_type,customer_id,amount)
VALUES(1,101,100,100100);
INSERT INTO orders(id,order_type,customer_id,amount)
VALUES(2,101,100,100300);
INSERT INTO orders(id,order_type,customer_id,amount)
VALUES(3,101,101,120000);
INSERT INTO orders(id,order_type,customer_id,amount)
VALUES(4,101,101,103000);
INSERT INTO orders(id,order_type,customer_id,amount)
VALUES(5,102,101,100400);
INSERT INTO orders(id,order_type,customer_id,amount)
VALUES(6,102,100,100020);
step3:创建orders_detail表,插入数据,进行查询
CREATE TABLE orders_detail(
`id` BIGINT NOT NULL AUTO_INCREMENT,
detail VARCHAR(2000),
order_id INT,
PRIMARY KEY(id)
)ENGINE=INNODB DEFAULT CHARSET=utf8
dbpartition BY mod_hash(order_id) tbpartition BY mod_hash(order_id)
tbpartitions 10 dbpartitions 5;
同样在上述的5个分库中,每个库创建10个订单详情表,效果如下:
插入数据,进行查询
INSERT INTO orders_detail(id,detail,order_id) VALUES(1,'detail1',1);
INSERT INTO orders_detail(id,detail,order_id) VALUES(2,'detail1',2);
INSERT INTO orders_detail(id,detail,order_id) VALUES(3,'detail1',3);
INSERT INTO orders_detail(id,detail,order_id) VALUES(4,'detail1',4);
INSERT INTO orders_detail(id,detail,order_id) VALUES(5,'detail1',5);
INSERT INTO orders_detail(id,detail,order_id) VALUES(6,'detail1',6);
step4:查看ER关系
mycat2无需指定ER
表,是自动识别的,具体看分片算法的接口
查看配置的表是否具有ER关系,使用/*+ mycat:showErGroup{}*/
查看,结果:
group_id 表示相同的组,该组中的表具有相同的存储分布
且 mycat2在涉及这两个表的join
分片字段等价关系的时候可以完成join
的下推,结果:
GitHub:https://github.com/Qihoo360/Atlas
Atlas是由Qihoo 360,Web平台基础架构图队维护的一个基于MySQL协议的数据库中间件,是在mysql-proxy 0.8.2版本之上的优化。
名字来源:在希腊神话中,阿特拉斯是支撑天球的原始泰坦。他还是天文学和航海学的巨人。
话不多说,开搞直接。
wget https://github.com/Qihoo360/Atlas/releases/download/2.2.1/Atlas-2.2.1.el6.x86_64.rpm
rpm -ivh ./Atlas-2.2.1.el6.x86_64.rpm
成功如下:
# 1、进入配置目录
cd /usr/local/mysql-proxy/conf
# 2、将test.cnf 备份一下,防止出错
mv ./test.cnf ./test.cnf.bak
# 3、我们可以利用Atlas提供的密码加密功能,后面会用到我们加密的密码
/usr/local/mysql-proxy/bin/encrypt your_password
# 4、编辑配置文件
vim ./test.cnf
[mysql-proxy]
# Atlasd的工作IP和端口
proxy-address = 0.0.0.0:33060
# Atlasd的管理IP和端口
admin-address = 0.0.0.0:2345
# 默认字符集
charset=utf8
# 允许登录管理接口的用户名
admin-username = user
# 管理接口的密码
admin-password = pwd
# 主库的IP和端口号,可设置多个,使用逗号分隔
proxy-backend-addresses = master_host:3306
# 从库的IP和端口号,可设置多个,使用逗号分隔
proxy-read-only-backend-addresses = slave_host:3306
# 允许登录工作接口的用户名与其对应的加密后的密码
pwds = root:ljFlIKX3mx5KsA0UOm9vDA==
# 是否已守护线程运行
daemon = true
# 开启监控进程和工作进程
keepalive = true
# 工作线程数
event-threads = 8
# 日志记录级别
log-level = message
# 日志存放路径
log-path = /usr/local/mysql-proxy/log
# SQL日志开关
sql-log=ON
cd /usr/local/mysql-proxy/bin
./mysql-proxyd test start # 开启
./mysql-proxyd test status # 查看状态
./mysql-proxyd test stop # 关闭
./mysql-proxyd test restart # 重启
# 登录Atlas的管理端口
mysql -uuser -ppwd -P 2345 -h127.0.0.1
VS mysql-prox:
在Altas中,将数据库可以分为多个组,一个组由一台master,0个或多个slave组成,组之间数据相互独立。
在命中某个组后,会存在组内的读写分离,master负责写,slave负责读。
shardrule: 一个shardrule对应一个分表规则,不同的shardrule通过下划线后面的数字区分。
根据数据的范围(范围大小不需要相同)进行切分,例如,可以将share key 范围在 0-999的数据存放在Group-0 ,范围在1000-1999的数据放在Group-1中。range是静态的,不支持动态增加范围。
举个栗子:
[shardrule-0]
table = test.sharding_test
type = range
shard-key = id
groups = 0:0-1000,1:1000-1999
使用取模的方式实现Hash。
举个栗子:
[shardrule-1]
table = test.sharding_hash
type = hash
shard-key = id
groups = 0,1
Atlas:不能实现分布式分表,所有的子表必须在同一台DB的同一个database里且所有的子表必须事先建好,Atlas没有自动建表的功能。最新的一次维护已经是4年前了。
Mycat2:配置相对简单,国内开源,最近的一次维护是11个月前。