ShardingSphere5学习笔记
一、简介
1、介绍
本文属于改参考文章的升级版,部分内容可以参考:ShardingSphere数据库中间件基础学习
互联网业务兴起之后,海量用户加上海量数据的特点,单个数据库服务器已经难以满足业务需要,必须考虑数据库集群的方式来提升性能。高性能数据库集群的第一种方式是"读写分离
",第二种方式是"数据库分片
";
读写分离和数据分片具体的实现方式一般有两种: 程序代码封装
和中间件封装
,中间件软件有**Apache ShardingSphere(程序级别和中间件级别)**和MyCat(数据库中间件)
2、读写分离架构
2.1 理论介绍
**读写分离原理:**读写分离的基本原理是将数据库读写操作分散到不同的节点上
读写分离的基本实现:
-
主库负责处理事务性的增删改操作,从库负责处理查询操作
,能够有效的避免由数据更新导致的行锁,使得整个系统的查询性能得到极大的改善。 -
读写分离是
根据 SQL 语义的分析
,将读操作和写操作分别路由至主库与从库
。 -
通过
一主多从
的配置方式,可以将查询请求均匀的分散到多个数据副本,能够进一步的提升系统的处理能力。 -
使用
多主多从
的方式,不但能够提升系统的吞吐量,还能够提升系统的可用性,可以达到在任何一个数据库宕机,甚至磁盘物理损坏的情况下仍然不影响系统的正常运行。
2.2 CAP理论
CAP 定理(CAP theorem)又被称作布鲁尔定理(Brewer’s theorem),是加州大学伯克利分校的计算机科学家埃里克·布鲁尔(Eric Brewer)在 2000 年的 ACM PODC 上提出的一个猜想。在一个分布式系统中
,当涉及读写操作时,只能保证**一致性(Consistence)、可用性(Availability)、分区容错性(Partition Tolerance)**三者中的两个,另外一个必须被牺牲。
-
C 一致性(Consistency):对某个指定的客户端来说,读操作保证能够返回最新的写操作结果
-
A 可用性(Availability):非故障的节点在合理的时间内返回合理的响应
(不是错误和超时的响应)
-
P 分区容忍性(Partition Tolerance):当出现网络分区后
(可能是丢包,也可能是连接中断,还可能是拥塞)
,系统能够继续“履行职责”
CAP特点:
-
CP:
为了保证一致性
,当发生分区现象后,N1 节点上的数据已经更新到 y,但由于 N1 和 N2 之间的复制通道中断,数据 y 无法同步到 N2,N2 节点上的数据还是 x。这时客户端 C 访问 N2 时,N2 需要返回 Error,提示客户端 C“系统现在发生了错误”,
这种处理方式违背了可用性
(Availability)的要求,因此 CAP 三者只能满足 CP -
AP:
为了保证可用性
,当发生分区现象后,N1 节点上的数据已经更新到 y,但由于 N1 和 N2 之间的复制通道中断,数据 y 无法同步到 N2,N2 节点上的数据还是 x。这时客户端 C 访问 N2 时,N2 将当前自己拥有的数据 x 返回给客户端 C 了
,而实际上当前最新的数据已经是 y 了,这就不满足一致性
(Consistency)的要求了,因此 CAP 三者只能满足 AP。注意:这里 N2 节点返回 x,虽然不是一个“正确”的结果,但是一个“合理”的结果,因为 x 是旧的数据,并不是一个错乱的值,只是不是最新的数据而已
CAP 理论中的 C 在实践中是不可能完美实现的
,在数据复制的过程中,节点N1 和节点 N2 的数据并不一致(强一致性)。即使无法做到强一致性
,但应用可以采用适合的方式达到最终一致性
。具有如下特点:
-
基本可用(Basically Available):分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。
-
软状态(Soft State):允许系统存在中间状态,而该中间状态不会影响系统整体可用性。这里的中间状态就是 CAP 理论中的数据不一致。
-
最终一致性(Eventual Consistency):系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。
3、数据库分片架构
-
读写分离的问题
读写分离分散了数据库读写操作的压力,但没有分散存储压力,为了满足业务数据存储的需求,就需要
将存储分散到多台数据库服务器上
。 -
数据分片
将存放在单一数据库中的数据分散地存放至多个数据库或表中,以达到提升性能瓶颈以及可用性的效果。 数据分片的有效手段是对关系型数据库进行
分库和分表
。数据分片的拆分方式又分为垂直分片和水平分片
3.1 垂直分片
-
垂直分库
按照业务拆分的方式称为垂直分片,又称为纵向拆分
,它的核心理念是专库专用。 在拆分之前,一个数据库由多个数据表构成,每个表对应着不同的业务。而拆分之后,则是按照业务将表进行归类,分布到不同的数据库中,从而将压力分散至不同的数据库。 垂直拆分可以缓解数据量和访问量带来的问题,但无法根治。如果垂直拆分之后,表中的数据量依然超过单节点所能承载的阈值,则需要水平分片来进一步处理
-
垂直分表
垂直分表适合将表中某些不常用的列,或者是占了大量空间的列拆分出去。
假设我们是一个婚恋网站,用户在筛选其他用户的时候,主要是用 age 和 sex 两个字段进行查询,而 nickname 和 description 两个字段主要用于展示,一般不会在业务查询中用到。description 本身又比较长,因此我们可以将这两个字段独立到另外一张表中,这样在查询 age 和 sex 时,就能带来一定的性能提升。垂直分表引入的复杂性主要体现在表操作的数量要增加。
3.2 水平分片
阿里巴巴Java开发手册:【推荐】单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。说明:如果预计三年后的数据量根本达不到这个级别,
请不要在创建表时就分库分表
水平分片又称为横向拆分。
相对于垂直分片,它不再将数据根据业务逻辑分类,而是通过某个字段(或某几个字段),根据某种规则将数据分散至多个库或表中,每个分片仅包含数据的一部分。 例如:根据主键分片,偶数主键的记录放入 0 库(或表),奇数主键的记录放入 1 库(或表)。单表进行切分后,是否将多个表分散在不同的数据库服务器中,可以根据实际的切分效果来确定。
-
水平分表:单表切分为多表后,新的表即使在同一个数据库服务器中,也可能带来可观的性能提升,如果性能能够满足业务要求,可以不拆分到多台数据库服务器,毕竟业务分库也会引入很多复杂性;
-
水平分库:如果单表拆分为多表后,单台服务器依然无法满足性能要求,那就需要将多个表分散在不同的数据库服务器中。
4、ShardingSphere介绍
4.1 简介
官网:https://shardingsphere.apache.org/index_zh.html
文档:https://shardingsphere.apache.org/document/5.1.1/cn/overview/
Apache ShardingSphere 由 JDBC、Proxy 和 Sidecar(规划中)这 3 款既能够独立部署,又支持混合部署配合使用的产品组成。
4.2 ShardingSphere-JDBC
程序代码封装。定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务
。 它使用客户端直连数据库,以 jar 包形式提供服务
,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架
4.3 ShardingSphere-Proxy
**中间件封装。**定位为透明化的数据库代理端
,提供封装了数据库二进制协议的服务端版本,用于完成对异构语言的支持。 目前提供 MySQL 和 PostgreSQL版本,它可以使用任何兼容 MySQL/PostgreSQL 协议的访问客户端(如:MySQL Command Client, MySQL Workbench, Navicat 等)操作数据,对 DBA 更加友好
二、MySQL主从配置
1、MySQL主从同步原理
详细也可以参考上一篇ShardingSphere文章
Docker参考文章:docker学习笔记
基本原理:
slave会从master读取binlog来进行数据同步
具体步骤:
-
step1:
master将数据改变记录到二进制日志(binary log)
中。 -
step2:
当slave上执行start slave
命令之后,slave会创建一个IO 线程
用来连接master,请求master中的binlog。 -
step3:
当slave连接master时,master会创建一个log dump 线程
,用于发送 binlog 的内容。在读取 binlog 的内容的操作中,会对主节点上的 binlog 加锁,当读取完成并发送给从服务器后解锁。 -
step4:
IO 线程接收主节点 binlog dump 进程发来的更新之后,保存到中继日志(relay log)
中。 -
step5:
slave的SQL线程
,读取relay log日志,并解析成具体操作,从而实现主从操作一致,最终数据一致。
2、主服务器准备
step1:docker中创建并启动MySQL主服务器:端口3306
1 | docker run -d \ |
step2:创建MySQL主服务器配置文件,默认情况下MySQL的binlog日志是自动开启的,可以通过如下配置定义一些可选配置
1 | vim /data/mysql/master/conf/my.cnf |
最后重启
1 | docker restart mysql-master |
binlog格式说明:
-
binlog_format=STATEMENT:日志记录的是主机数据库的
写指令
,性能高,但是now()之类的函数以及获取系统参数的操作会出现主从数据不同步的问题。 -
binlog_format=ROW(默认):日志记录的是主机数据库的
写后的数据
,批量操作时性能较差,解决now()或者 user()或者 @@hostname 等操作在主从机器上不一致的问题。 -
binlog_format=MIXED:是以上两种level的混合使用,有函数用ROW,没函数用STATEMENT,但是无法识别系统变量
step3:使用命令行登录MySQL主服务器
1 | #进入容器:env LANG=C.UTF-8 避免容器中显示中文乱码 |
step4:主机中创建slave用户
1 | -- 创建slave用户 |
step5:主机中查询master状态
执行完此步骤后不要再操作主服务器MYSQL
,防止主服务器状态值变化,记下File
和Position
的值
1 | SHOW MASTER STATUS; |
3、从服务器准备
这里可以配置多台从机slave1、slave2…
step1:在docker中创建并启动MySQL从服务器:端口3307
1 | docker run -d \ |
step2:创建MySQL从服务器配置文件,最后重启MySQL容器
1 | vim /data/mysql/slave1/conf/my.cnf |
step3:使用命令行登录MySQL从服务器
1 | #进入容器: |
step4:在从机上配置主从关系,在从机上执行以下SQL操作
1 | CHANGE MASTER TO MASTER_HOST='192.168.249.135',MASTER_USER='slave',MASTER_PASSWORD='123456', MASTER_PORT=3306,MASTER_LOG_FILE='binlog.000003',MASTER_LOG_POS=1616; |
4、启动主从同步
启动从机的复制功能,执行SQL;当Slave_IO_Running: Yes
和Slave_SQL_Running: Yes
都为Yes时启动成功
1 | START SLAVE; |
停止与重置
1 | -- 在从机上执行。功能说明:停止I/O 线程和SQL线程的操作。 |
测试,在主机中执行以下SQL,在从机中查看数据库、表和数据是否已经被同步
1 | CREATE DATABASE db_user; |
5、常见问题
5.1 示例一
启动主从同步后,常见错误是Slave_IO_Running: No 或者 Connecting
的情况,此时查看下方的 Last_IO_ERROR
错误日志,根据日志中显示的错误信息在网上搜索解决方案即可
典型的错误例如:Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'Client requested master to start replication from position > file size'
解决方案:
1 | -- 在从机停止slave |
5.2 示例二
启动docker容器后提示 WARNING: IPv4 forwarding is disabled. Networking will not work.
此错误,虽然不影响主从同步的搭建,但是如果想从远程客户端通过以下方式连接docker中的MySQL则没法连接
解决方案:
1 | #修改配置文件,这是Centos的解决方案,原理是这样 |
三、ShardingSphere-JDBC
1、ShardingSphere-JDBC读写分离
1.1 环境与项目创建
添加依赖
1 | <dependencies> |
创建实体类
1 | "t_user") ( |
创建Mapper
1 |
|
1.2 配置文件
1 | # 应用名称 |
1.3 测试
- 读写分离测试
测试插入,通过日志可以看到Logic SQL逻辑SQl,因为应用程序将请求发送给ShardingSphere
后并不关心是插入到哪台服务器,比如这里只是单纯把请求发送给myds的服务器组,由ShardingSphere
的配置决定哪台是写服务器,最终进行SQL插入
1 |
|
- 事务测试
为了保证主从库间的事务一致性,避免跨服务的分布式事务,ShardingSphere-JDBC的主从模型中,事务中的数据读写均用主库
。
-
不添加@Transactional:insert对主库操作,select对从库操作
-
添加@Transactional:则insert和select均对主库操作
-
**注意:**在JUnit环境下的@Transactional注解,默认情况下就会对事务进行回滚(即使在没加注解@Rollback,也会对事务回滚)
1 | /** |
- 负载均衡测试
1 | /** |
2、ShardingSphere-JDBC垂直分片
2.1 环境准备
创建server-user容器和server-order容器
1 | docker run -d \ |
登录MySQL服务器修改一下密码
1 | #进入容器: |
在两个库分布创建数据表
1 | -- 这是server-user |
2.2 程序实现
创建实体类
1 | "t_order") ( |
创建Mapper
1 |
|
配置垂直分片
1 | # 应用名称 |
2.3 测试垂直分片
测试通过日志可以发现程序写的是逻辑表名,会通过Sharding配置文件自动路由到对应的数据源下的数据表
1 |
|
如果运行报错Public Key Retrieval is not allowed
,是因为ShardingSphere-JDBC远程连接的方式默认的密码加密规则是:mysql_native_password。因此需要在服务器端修改服务器的密码加密规则,如下:
1 | ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456'; |
四、ShardingSphere-JDBC水平分片(常用)
1、环境准备
创建两个Mysql容器
1 | docker run -d \ |
登录MySQL服务器操作
1 | #进入容器: |
创建数据库:和server-order1相同注意:
水平分片的id需要在业务层实现,不能依赖数据库的主键自增
1 | CREATE DATABASE db_order; |
2、基本水平分片
2.1 基本配置
1 | #========================基本配置 |
2.2 数据源配置
1 | #========================数据源配置 |
2.3 标椎分片表配置
1 | #========================标准分片表配置(数据节点配置) |
修改Order实体类的主键策略:
1 | //@TableId(type = IdType.AUTO)//依赖数据库的主键自增策略 |
测试:保留上面配置中的一个分片表节点分别进行测试,检查每个分片节点是否可用
1 | /** |
2.4 行表达式
https://shardingsphere.apache.org/document/5.1.1/cn/features/sharding/concept/inline-expression/
1 | #========================标准分片表配置(数据节点配置) |
2.5 分片算法配置
- 水平分库
分片规则:order表中user_id
为偶数时,数据插入server-order0服务器
,user_id
为奇数时,数据插入server-order1服务器
。这样分片的好处是,同一个用户的订单数据,一定会被插入到同一台服务器上,查询一个用户的订单时效率较高。
1 | #------------------------分库策略 |
因为只是暂时设置了分库,但未分表,先设置只在 t_order0
表上进行测试
1 | xxx.actual-data-nodes=server-order$->{0..1}.t_order0 |
测试:可以分别测试行表达式分片算法和取模分片算法
1 | /** |
水平分表
分片规则:order表中order_no的哈希值为偶数时
,数据插入对应服务器的t_order0表
,order_no的哈希值为奇数时
,数据插入对应服务器的t_order1表
。因为order_no是字符串形式,因此不能直接取模。
1 | #------------------------分表策略 |
测试前不要忘记将如下节点改回原来的状态
1 | xxx.actual-data-nodes=server-order$->{0..1}.t_order$->{0..1} |
测试
1 | /** |
查询测试
1 | /** |
2.6 分布式序列算法
https://shardingsphere.apache.org/document/5.1.1/cn/features/sharding/concept/key-generator/
水平分片需要关注全局序列,因为不能简单的使用基于数据库的主键自增。这里有两种方案:一种是基于MyBatisPlus的id策略;一种是ShardingSphere-JDBC的全局序列配置。
基于MyBatisPlus的id策略,
将Order类的id设置成如下形式
1 | (type = IdType.ASSIGN_ID) |
基于ShardingSphere-JDBC的全局序列配置
,和前面的MyBatisPlus的策略二选一
1 | #------------------------分布式序列策略配置 |
此时,需要将实体类中的id策略修改成以下形式:
1 | //当配置了shardingsphere-jdbc的分布式序列时,自动使用shardingsphere-jdbc的分布式序列 |
3、多表关联
创建关联表,在server-order0、server-order1
服务器中分别创建两张订单详情表t_order_item0、t_order_item1
。我们希望同一个用户的订单表和订单详情表中的数据都在同一个数据源中,避免跨库关联
,因此这两张表我们使用相同的分片策略。那么在t_order_item
中我们也需要创建order_no
和user_id
这两个分片键
1 | CREATE TABLE t_order_item0( |
创建实体类
1 | "t_order_item") ( |
创建Mapper
1 |
|
配置关联表
1 | #------------------------标准分片表配置(数据节点配置) |
测试插入数据
1 | /** |
4、绑定表
创建VO对象
1 |
|
添加Mapper方法
1 |
|
测试关联查询
1 | /** |
配置绑定表,在原来水平分片配置的基础上添加如下配置
1 | #------------------------绑定表 |
配置完绑定表后再次进行关联查询的测试:
-
**如果不配置绑定表:测试的结果为8个SQL。**多表关联查询会出现笛卡尔积关联。
-
如果配置绑定表:测试的结果为4个SQL。 多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升。
绑定表:
指分片规则一致的一组分片表。 使用绑定表进行多表关联查询时,必须使用分片键进行关联,否则会出现笛卡尔积关联或跨库关联,从而影响查询效率。
5、广播表
5.1 广播表介绍
指所有的分片数据源中都存在的表,表结构及其数据在每个数据库中均完全一致。 适用于数据量不大且需要与海量数据的表进行关联查询的场景,例如:字典表。
广播具有以下特性:
-
插入、更新操作会实时在所有节点上执行,保持各个分片的数据一致性
-
查询操作,只从一个节点获取
-
可以跟任何一个表进行 JOIN 操作
5.2 创建广播表
在server-order0、server-order1和server-user服务器中分别创建t_dict表
1 | CREATE TABLE t_dict( |
5.3 程序实现
创建实体类
1 | "t_dict") ( |
创建Mapper
1 |
|
配置广播表
1 | #数据节点可不配置,默认情况下,向所有数据源广播 |
5.4 测试
1 |
|
五、ShardingSphere-Proxy
1、环境搭建与安装
https://shardingsphere.apache.org/document/5.1.1/cn/quick-start/shardingsphere-proxy-quick-start/
1.1 获取包
目前 ShardingSphere-Proxy 提供了 3 种获取方式:
1.2 使用二进制发布包安装
二进制包既可以Linux系统运行,又可以在windows系统运行
step1:解压二进制包
apache-shardingsphere-5.1.1-shardingsphere-proxy-bin.tar.gz
windows:使用解压软件解压文件
Linux:将文件上传至/opt目录,并解压
1 | tar -zxvf apache-shardingsphere-5.1.1-shardingsphere-proxy-bin.tar.gz |
step2:MySQL驱动
mysql-connector-java-8.0.22.jar
,将MySQl驱动放至解压目录中的ext-lib
目录
spte3:修改配置conf/server.yaml
1 | rules: |
spte4:启动ShardingSphere-Proxy
Linux 操作系统请运行 bin/start.sh
;Windows 操作系统请运行 bin/start.bat
;指定端口号和配置文件目录:bin/start.bat ${proxy_port} ${proxy_conf_directory}
step5:远程连接ShardingSphere-Proxy
1 | mysql -h192.168.100.1 -P3307 -uroot -p |
step6:访问测试
1 | show databases; |
1.3 使用Docker安装
step1:启动Docker容器,若容器内存太小,创建容器的时候使用JVM参数限制
1 | docker run -d \ |
step2:上传MySQL驱动
将MySQL驱动上传至/data/server/proxy-a/ext-lib
目录
spte3:修改配置server.yaml
1 | rules: |
将配置文件上传至/data/server/proxy-a/conf
目录
spte4:重启容器
1 | docker restart server-proxy-a |
step5:远程连接ShardingSphere-Proxy
ShardingSphere-Proxy容器中默认情况下没有mysql命令行客户端的安装,因此需要远程访问
1 | mysql -h192.168.249.135 -P3321 -uroot -p |
step6:访问测试
1 | show databases; |
2、ShardingSphere-Proxy读写分离
2.1 修改配置文件
修改配置config-readwrite-splitting.yaml,ShardingSphere-JDBC的配置是ShardingSphere-Proxy的子集,因此可以复用
1 | # 逻辑名字 |
将配置文件上传至/data/server/proxy-a/conf
目录,最后重启容器
2.2 实时查看日志
可以通过这种方式查看服务器中输出的SQL语句
1 | docker exec -it server-proxy-a env LANG=C.UTF-8 /bin/bash |
2.3 远程访问测试
1 | mysql> show databases; |
2.4 应用程序访问Proxy
添加依赖
1 | <dependencies> |
创建实体类
1 | "t_user") ( |
创建Mapper
1 |
|
配置数据源
1 | # 应用名称 |
测试
1 |
|
3、ShardingSphere-Proxy垂直分片
修改配置config-sharding.yaml
1 | schemaName: sharding_db |
实时查看日志,可以通过这种方式查看服务器中输出的SQL语句
1 | docker exec -it server-proxy-a env LANG=C.UTF-8 /bin/bash |
远程访问测试
1 | mysql> show databases; |
4、ShardingSphere-Proxy水平分片
修改配置config-sharding.yaml
1 | schemaName: sharding_db |
实时查看日志,可以通过这种方式查看服务器中输出的SQL语句
1 | docker exec -it server-proxy-a env LANG=C.UTF-8 /bin/bash |
远程访问测试
1 | mysql> show databases; |
参考
https://shardingsphere.apache.org/document/5.1.1/cn/overview/