简介
我们知道,当前的应用都离不开数据库,随着数据库中的数据越来越多,单表突破性能上限记录时,如MySQL单表上线估计在近千万条内,当记录数继续增长时,从性能考虑,则需要进行拆分处理。而拆分分为横向拆分和纵向拆分。一般来说,采用横向拆分较多,这样的表结构是一致的,只是不同的数据存储在不同的数据库表中。其中横向拆分也分为分库和分表。
为什么决定进行分库分表
- 根据业务类型,和业务容量的评估,来选择和判断是否使用分库分表。
- 当前数据库本事具有的能力,压力的评估。
- 数据库的物理隔离,例如减少锁的争用、资源的消耗和隔离等。
- 热点表较多,并且数据量大,可能会导致锁争抢,性能下降。
- 数据库的高并发,数据库的读写压力过大,可能会导致数据库或系统宕机。
- 数据库连接数过高,会增加系统压力。
- 单表数据量大,如SQL使用不当,会导致io随机读写比例高。查询慢(大表上的B+树太大,扫描太慢,甚至可能需要4层B+树)
- 备份和恢复时间比较长。
遇到什么问题
- 全局pk(主键和唯一索引)的冲突检测不准确,全局的自增主键支持不够好。
- 分片键的选择。如没有选择好,可能会影响SQL执行效率。
- 分布式事物,中间价产品对分布式事物的支持力度。
- 对于开发来说,需要进行业务的拆分
- 对于开发来说,部分SQL不兼容则需要代码重构,工作量的评估
- 对于开发来说,跨库join,跨库查询
如何解决
- 使用全局分号器。或者使用全局唯一id,(应用生成顺序唯一int类型做为全局主键)。
- 应用层来判断唯一索引。
- 配合应用选择合适的分片键,并加上索引。
- 配合应用,配合开发,对不兼容SQL的进行整改。
分库分表实现
在Java语言下的框架中,有众多的开源框架,其中关于分库分表的框架,可以选择Apache ShardingSphere
官网地址为: https://shardingsphere.apache.org/index_zh.html
ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由 JDBC、Proxy 和 Sidecar(规划中)这 3 款既能够独立部署,又支持混合部署配合使用的产品组成。 它们均提供标准化的基于数据库作为存储节点的增量功能,可适用于如 Java 同构、异构语言、云原生等各种多样化的应用场景
ShardingSphere定位为关系型数据库中间件,旨在充分合理地在分布式的场景下利用关系型数据库的计算和存储能力,而并非实现一个全新的关系型数据库。 它与NoSQL和NewSQL是并存而非互斥的关系。NoSQL和NewSQL作为新技术探索的前沿,放眼未来,拥抱变化,是非常值得推荐的。反之,也可以用另一种思路看待问题,放眼未来,关注不变的东西,进而抓住事物本质。 关系型数据库当今依然占有巨大市场,是各个公司核心业务的基石,未来也难于撼动,我们目前阶段更加关注在原有基础上的增量,而非颠覆
一、介绍
1. ShardingSphere-JDBC
定位为轻量级Java框架,在Java的JDBC层提供的额外服务。 它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。
- 适用于任何基于Java的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。
- 基于任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。
- 支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer和PostgreSQL。
2. ShardingSphere-Proxy
定位为透明化的数据库代理端,提供封装了数据库二进制协议的服务端版本,用于完成对异构语言的支持。 目前提供 MySQL 和 PostgreSQL(兼容 openGauss 等基于 PostgreSQL 的数据库)版本,它可以使用任何兼容 MySQL/PostgreSQL 协议的访问客户端(如:MySQL Command Client, MySQL Workbench, Navicat 等)操作数据,对 DBA 更加友好。
-
向应用程序完全透明,可直接当做MySQL使用。
-
适用于任何兼容MySQL/PostgreSQL协议的客户端。
3. ShardingSphere-Sidecar(TODO)
定位为 Kubernetes 的云原生数据库代理,以 Sidecar 的形式代理所有对数据库的访问。 通过无中心、零侵入的方案提供与数据库交互的啮合层,即 Database Mesh
,又可称数据库网格。
Database Mesh 的关注重点在于如何将分布式的数据访问应用与数据库有机串联起来,它更加关注的是交互,是将杂乱无章的应用与数据库之间的交互进行有效地梳理。 使用 Database Mesh,访问数据库的应用和数据库终将形成一个巨大的网格体系,应用和数据库只需在网格体系中对号入座即可,它们都是被啮合层所治理的对象。
Sharding-JDBC | Sharding-Proxy | Sharding-Sidecar | |
---|---|---|---|
数据库 | 任意 | MySQL | MySQL |
连接消耗数 | 高 | 低 | 高 |
异构语言 | 仅Java | 任意 | 任意 |
性能 | 损耗低 | 损耗略高 | 损耗低 |
无中心化 | 是 | 否 | 是 |
静态入口 | 无 | 有 | 无 |
4. 混合架构
ShardingSphere-JDBC 采用无中心化架构,与应用程序共享资源,适用于 Java 开发的高性能的轻量级 OLTP 应用; ShardingSphere-Proxy 提供静态入口以及异构语言的支持,独立于应用程序部署,适用于 OLAP 应用以及对分片数据库进行管理和运维的场景。
Apache ShardingSphere 是多接入端共同组成的生态圈。 通过混合使用 ShardingSphere-JDBC 和 ShardingSphere-Proxy,并采用同一注册中心统一配置分片策略,能够灵活的搭建适用于各种场景的应用系统,使得架构师更加自由地调整适合于当前业务的最佳系统架构。
二、功能列表
1. 数据分片
- 分库 & 分表
- 读写分离
- 分片策略定制化
- 无中心化分布式主键
2. 分布式事务
- 标准化事务接口
- XA强一致事务
- 柔性事务
3. 数据库治理
- 配置动态化
- 编排 & 治理
- 数据脱敏
- 可视化链路追踪
- 弹性伸缩
三、搭建
1. ShardingSphere-JDBC
- 引入Maven依赖:
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere</artifactId>
<version>5.1.0</version>
</dependency>
- 举例说明:
数据源:6个数据源,两个集群,分别为1主2从;
主1:master0;从1:master0salve0;从2:master0slave1
主2:master1;从1:master1slave0;从2:master1slave1
作为demo演示,将表进行分库和分表;每个库里有两张表:mc_member0和mc_member1;
分库规则:gender=1(男)时 gender%2 -> master1库; gender=2(女)时 gender%2 -> master0库;
分表规则:id偶数时,id%2->mc_member0表;id为奇数时,id%2->mc_member1表;
注意事项:对逻辑表配置分片策略时,数据库分片策略设置逻辑数据源,不使用物理库名;表分片策略内,使用物理表名;
配置读写分离:master-slave-rules下的rule逻辑名可以自定义如 ms1和ds1等。
- yml配置:
sharding:
jdbc:
datasource:
#数据源名称,多数据源以逗号分隔
names: master0,master0salve0,master0slave1,master1,master1slave0,master1slave1
master0:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/mcspcsharding0?useUnicode=true&character_set_server=utf8mb4&useSSL=false&serverTimezone=GMT%2B8
username: ***
password: ***
master0salve0:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/mcspcsharding0s0?useUnicode=true&character_set_server=utf8mb4&useSSL=false&serverTimezone=GMT%2B8
username: ***
password: ***
master0slave1:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/mcspcsharding0s1?useUnicode=true&character_set_server=utf8mb4&useSSL=false&serverTimezone=GMT%2B8
username: ***
password: ***
master1:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/mcspcsharding1?useUnicode=true&character_set_server=utf8mb4&useSSL=false&serverTimezone=GMT%2B8
username: ***
password: ***
master1slave0:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/mcspcsharding1s0?useUnicode=true&character_set_server=utf8mb4&useSSL=false&serverTimezone=GMT%2B8
username: ***
password: ***
master1slave1:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/mcspcsharding1s1?useUnicode=true&character_set_server=utf8mb4&useSSL=false&serverTimezone=GMT%2B8
username: ***
password: ***
#分库策略,缺省表示使用默认分库策略,以下的分片策略只能选其一
config:
sharding:
tables:
mc_member:
#由数据源名 + 表名组成,以小数点分隔。多个表以逗号分隔,支持inline表达式。缺省表示使用已知数据源与逻辑表名称生成数据节点。
#用于广播表(即每个库中都需要一个同样的表用于关联查询,多为字典表)或只分库不分表且所有库的表结构完全一致的情况
actual-nodes: mcspcsharding$->{0..1}.mc_member$->{0..1}
#分库规则配置
database-strategy:
inline:
#分片列名称
sharding-column: gender
#分片算法行表达式,需符合groovy语法
algorithm-expression: master$->{gender % 2}
#分表规则配置
table-strategy:
inline:
#分片列名称
sharding-column: id
#分片算法行表达式,需符合groovy语法
algorithm-expression: mc_member$->{id % 2}
#绑定表规则列表,多个时逗号隔开
binding-tables: mc_member
#广播表规则列表,多个时逗号隔开
broadcast-tables: mc_master
master-slave-rules:
ms0:
master-data-source-name: master0
slave-data-source-names: master0salve0,master0slave1
ms1:
master-data-source-name: master1
slave-data-source-names: master1slave0,master1slave1
#是否开启SQL显示,默认值: false
props:
sql:
show: true
- Sharding-JDBC的基本概念:转载自 https://zhuanlan.zhihu.com/p/267513756
2.ShardingSphere-Proxy
- 下载:
wget https://downloads.apache.org/shardingsphere/5.1.0/apache-shardingsphere-5.1.0-shardingsphere-proxy-bin.tar.gz
- 解压:
tar zxvf apache-shardingsphere-5.1.0-shardingsphere-proxy-bin.tar.gz
- 进入配置文件:
cd apache-shardingsphere-5.1.0-shardingsphere-proxy-bin/conf/
-
配置文件简介
config-master_slave.yaml 用来配置读写分离
config-sharding.yaml 用来配置分库分表(也可以在该文件中配置读写分离+分库分表)
config-shadow.yaml 用来配置影子库
config-encrypt.yaml 用来配置数据加密
server.yaml Sharding-Proxy的一些基础配置,比如:账号、密码、注册中心等
logback.xml 日志的配置 -
修改server.yaml:
rules:
- !AUTHORITY
users:
- ***@:***# 用于登录计算节点的用户名,授权主机和密码的组合。格式:<username>@<hostname>:<password>,hostname 为 % 或空字符串表示不限制授权主机
provider:
type: NATIVE # 存储节点数据授权的权限提供者类型
- 修改config-sharding.yaml文件
schemaName: sharding_db # 逻辑数据源名称
dataSources: # 数据源配置,可配置多个 <data-source-name>
ds_0: # 与 ShardingSphere-JDBC 配置不同,无需配置数据库连接池
url: jdbc:mysql://127.0.0.1:3306/demo_ds_0?useSSL=false #数据库 URL 连接
username: *** # 数据库用户名
password: *** # 数据库密码
connectionTimeoutMilliseconds: 30000 # 连接超时毫秒数
idleTimeoutMilliseconds: 60000 # 空闲连接回收超时毫秒数
maxLifetimeMilliseconds: 1800000 # 连接最大存活时间毫秒数
maxPoolSize: 50 # 最大连接数
minPoolSize: 1 # 最小连接数
ds_1:
url: jdbc:mysql://127.0.0.1:3306/demo_ds_1?useSSL=false
username: ***
password: ***
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
rules: # 与 ShardingSphere-JDBC 配置一致
- !SHARDING
tables:
t_order:
actualDataNodes: ds_${0..1}.t_order_${0..1}
tableStrategy:
standard:
shardingColumn: order_id
shardingAlgorithmName: t_order_inline
keyGenerateStrategy:
column: order_id
keyGeneratorName: snowflake
t_order_item:
actualDataNodes: ds_${0..1}.t_order_item_${0..1}
tableStrategy:
standard:
shardingColumn: order_id
shardingAlgorithmName: t_order_item_inline
keyGenerateStrategy:
column: order_item_id
keyGeneratorName: snowflake
bindingTables:
- t_order,t_order_item
defaultDatabaseStrategy:
standard:
shardingColumn: user_id
shardingAlgorithmName: database_inline
defaultTableStrategy:
none:
shardingAlgorithms:
database_inline:
type: INLINE
props:
algorithm-expression: ds_${user_id % 2}
t_order_inline:
type: INLINE
props:
algorithm-expression: t_order_${order_id % 2}
t_order_item_inline:
type: INLINE
props:
algorithm-expression: t_order_item_${order_id % 2}
keyGenerators:
snowflake:
type: SNOWFLAKE
props:
worker-id: 123
- 如果后端连接 PostgreSQL 数据库,不需要引入额外依赖。
- 如果后端连接 MySQL 数据库,请下载
mysql-connector-java-5.1.47.jar
或``mysql-connector-java-8.0.11.jar,并将其放入 %SHARDINGSPHERE_PROXY_HOME%/lib
目录 - 启动服务:
启动:
./apache-shardingsphere-5.1.0-shardingsphere-proxy-bin/bin/start.sh 3388
查看日志:
tail -f /root/shardingSphere-proxy/shardingsphere-5.1.0/logs/stdout.log
-
测试:
连接数据库
在sharding_db数据库中创建t_order和t_order_item表
CREATE TABLE t_order ( ORDER_id BIGINT(20) NOT NULL AUTO_INCREMENT, user_id INT(11) NOT NULL, STATUS VARCHAR(50) COLLATE utf8_bin DEFAULT NULL, PRIMARY KEY (order_id) )ENGINE=INNODB DEFAULT CHARSET=utf8 COLLATE utf8_bin; CREATE TABLE t_order_item( order_item_id BIGINT(20) NOT NULL, order_id BIGINT(20) NOT NULL, user_id INT(11) NOT NULL, content VARCHAR(255) COLLATE utf8_bin DEFAULT NULL, STATUS VARCHAR(50) COLLATE utf8_bin DEFAULT NULL, PRIMARY KEY (order_item_id) )ENGINE=INNODB DEFAULT CHARSET=utf8 COLLATE utf8_bin;
创建完成后:
demo_ds_0和demo_ds_1自动创建一些表
测试插入:
INSERT INTO t_order (user_id, STATUS) VALUES(1,1); INSERT INTO t_order (user_id, STATUS) VALUES(2,1); INSERT INTO t_order (user_id, STATUS) VALUES(2,2);
查看是否按分片规则执行到对应的库、表中:
后续可优化为:
ShardingSphere-Proxy + ShardingSphere-ui + zookeeper 可以实现服务治理以及页面化配置