一、ShardingSphere
1、ShardingSphere介绍
Apache Shardingsphere是一款开源的分布式数据库中间件组成的生态圈。它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(规划中)这3款相互独立的产品组成。他们均提供标准化的数据分片、分布式事务和数据库治理功能,可适用于如ava同构、异构语言、容器、云原生等各种多样化的应用场景。
ShardingSphere项目状态如下:
ShardingSphere定位为关系型数据库中间件,旨在充分合理地在分布式的场景下利用关系型数据库的计算和存储能力,而并非实现一个全新的关系型数据库。
- Sharding-IDBC:被定位为轻量级Java框架,在JavaIDBC层提供的额外服务,以jar包形式使用。
- Sharding-Proxy:被定位为透明化的数据库代理端,提供封装了数据库二进制协议的服务端版本,用于完成对异构语言的支持。
- Sharding-Sidecar:被定位为Kubernetes或Mesos的云原生数据库代理,以DaemonSet的形式代理所有对数据库的访问。
Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar三者区别如下:
二、Sharding-JDBC
Sharding-JDBC定位为轻量级Java框架,在JavaJDBC层提供的额外服务。它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版IDBC驱动,完全兼容lDBC和各种ORM框架。|
- 适用于任何基于Java的QRM框架,如:IPA,Hibernate,Mybatis,Spring JDBC Template或直接使用IDBC。
- 基于任何第三方的数据库连接池,如:DBCP,C3PO,BoneCP,Druid,HikariCP等。
- 支持任意实IDBC规范的数据库。目前支持MySQL,Oracle,SQLServer和PostgresQL。
1、Sharding-JDBC主要功能
Sharding-JDBC主要功能:
- 数据分片
分库、分表
读写分离
分片策略
分布式主键
- 分布式事务
标准化的事务接口
XA强一致性事务
柔性事务
- 数据库治理
- 配置动态化
编排和治理
数据脱敏
可视化链路追踪
Sharding-JDBC内部结构:
- 图中黄色部分表示的是ShardingJDBC的入口API,采用工厂方法的形式提供。目前有ShardingDataSourceFactory和MasterSlaveDataSourceFactory两个工厂类。
ShardingDataSourceFactory支持分库分表、读写分离操作MasterSlaveDataSourceFactory支持读写分离操作
- 图中蓝色部分表示的是Sharding-JDBC的配置对象,提供灵活多变的配置方式。ShardingRuleConfiguration是分库分表配置的核心和入口,它可以包含多个TableRuleConfiguration和MasterSlaveRuleConfiguration。
TableRuleConfiguration封装的是表的分片配置信息,有5种配置形式对应不同的Configuration类型。
MasterSlaveRuleConfiguration封装的是读写分离配置信息。
- 图中红色部分表示的是内部对象,由Sharding-JDBC内部使用,应用开发者无需关注。Sharding-JDBC通过ShardingRuleConfiguration和MasterSlaveRuleConfiguration生成真正供ShardingDataSource和MasterSlaveDataSource使用的规则对象。ShardingDataSource和MasterSlaveDataSource实现了Datasource接口,是JDBC的完整实现方案。
2、Sharding-JDBC初始化流程:
- 根据配置的信息生成Configuration对象
- 通过Factory会将Configuration对象转化为Rule对象
- 通过Factory会将Rule对象与Datasource对象封装
- Sharding-JDBC使用Datasource进行分库分表和读写分离操作
3、Sharding-JDBC使用过程
- 引入maven依赖
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-core</artifactId>
<version>${latest.release.version}</version>
</dependency>
- 规则配置
ShardingJDBC可以通java,YAML,Spring命名空间和Spring Boot Starter四种方式配置,开发者可根据场景选择适合的配置方式。
- 创建DataSource通过ShardingDataSourceFactory工厂和规则配置对象获取ShardingDataSource,然后即可通过DataSource选择使用原生JDBC开发,或者使用JPA,MyBatis等ORM工具。
DataSource datasource=ShardingpDataSourceFactory.createDataSource(datasourceMap,shardingRuleconfig,props);
三、数据分片
1、数据分片概念
- 表概念
- 真实表
数据库中真实存在的物理表。例如b_order0、b_order1
- 逻辑表
在分片之后,同一类表结构的名称(总成)。例如b_order。
- 数据节点
在分片之后,有数据源和数据表组成。例如ds0.b_order1
- 绑定表
指的是分片规则一致的关系表(主表、子表),例如b_order和b_order_item,均按照order_id分片,则此两个表互为绑定表关系。绑定表之间的多表关联查询不会出现笛卡尔积关联,可以提升关联查询效率。
b_order:b_order0、b_order1
b_order_item:b_order_item0、b_order_item1
select * from b_order o join b_order_item i on(o.order_id=i.order_id)
where o.order_id in(10,11);
如果不配置绑定表关系,采用笛卡尔积关联,会生成4个SQL
select * from b_order0 o join b_order_item0 i on(o.order_id=i.order_id)
where o.order_id in(10,11);
select * from b_order0 o join b_order_item1 i on(o.order_id=i.order_id)
where o.order_id in(10,11);
select * from b_order1 o join b_order_item0 i on(o.order_id=i.order_id)
where o.order_id in(10,11);
select * from b_order1 o join b_order_item1 i on(o.order_id=i.order_id)
where o.order_id in(10,11);
如果配置绑定表关系,生成2个SQL
select * from b_order0 o join b_order_item0 i on(o.order_id=i.order_id)
where o.order_id in(10,11);
select * from b_order1 o join b_order_item1 i on(o.order_id=i.order_id)
where o.order_id in(10,11);
- 广播表
在使用中,有些表没必要做分片,例如字典表、省份信息等,因为他们数据量不大,而且这种表可能需要与海量数据的表进行关联查询。广播表会在不同的数据节点上存储,存储的表结构和数据完全相同。
- 分片算法
由于分片算法和业务实现紧密相关,因此并未提供内置分片算法,而是通过分片策略将各种场景提炼出来,提供更高层级的抽象,并提供接口让应用开发者自行实现分片算法。目前提供4种分片算法。
- 精确分片算法 PreciseshardingAlgorithm
用于处理使用单一键作为分片键的=与IN进行分片的场景。
- 范围分片算法RangeShardingAlgorithm
用于处理使用单一键作为分片键的BETWEEN、AND、>、<、>=、<=进行分片的场景。
- 复合分片算法ComplexKeysShardingAlgorithm
用于处理使用多键作为分片键进行分片的场景,多个分片键的逻辑较复杂,需要应用开发者自行处理其中的复杂度。
- Hint分片算法HintShardingAlgorithm
用于处理使用Hint行分片的场景。
- 分片策略
分片策略包含分片键和分片算法,真正可用于分片操作的是分片键+分片算法,也就是分片策略。目前提供5种分片策略。
- 标准分片策略StandardShardingstrategy
只支持单分片键,提供对SQL语句中的=,>,<,>=,<=,IN和BETWEENAND的分片操作支持。提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法。
PreciseShardingAlgorithm是必选的,RangeShardingAlgorithm是可选的。但是SQL中使用了范围操作,如果不配置RangeShardingAlgorithm会采用全库路由扫描,效率低。
- 复合分片策略ComplexshardingStrategy
支持多分片键。提供对SQL语句中的=,>,<,>=,<=,IN和BETWEENAND的分片操作支持。由于多分片键之间的关系复杂,因此并未进行过多的封装,而是直接将分片键值组合以及分片操作符透传至分片算法,完全由应用开发者实现,提供最大的灵活度。
- 行表达式分片策略InlineShardingStrategy
只支持单分片键。使用Groovy的表达式,提供对SQL语句中的=和IN的分片操作支持,对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐Java代码开发。如:t_user_$->{u_id%8}表示t_user表根据uid模8,而分成8张表,表名称为t_user_0到t_user_7。
- Hint分片策略HintshardingStrategy
通过Hint指定分片值而非从SQL中提取分片值的方式进行分片的策略。对于分片字段非SQL决定,而由其他外置条件决定的场景,可使用SQL Hint灵活的注入分片字段。例:内部系统,按照员工登录主键分库,而数据库中并无此字段。SQL Hint支持通过java APl和SQL注释两种方式使用。
- 不分片策略NoneShardingStrategy
不分片的策略
- 分片策略配置
对于分片策略存有数据源分片策略和表分片策略两种维度,两种策略的API完全相同。
- 数据源分片策略
用于配置数据被分配的目标数据源。
- 表分片策略
用于配置数据被分配的目标表,由于表存在与数据源内,所以表分片策略是依赖数据源分片策略结果的。
2、数据分片流程分析
Shardingsphere3个产品的数据分片功能主要流程是完全一致的,如下图所示。
- SQL解析
SQL解析分为词法解析和语法解析。先通过词法解析器将SQL拆分为一个个不可再分的单词。再使用语法解析器对SQL进行理解,并最终提炼出解析上下文。
Sharding-JDBC采用不同的解析器对SQL进行解析,解析器类型如下:
MySQL解析器
Oracle解析器
SQLServer解析器
PostgreSQL解析器
默认SQL解析器
- 查询优化
合并和优化分片条件,如OR等。
- SQL路由
根据解析上下文匹配用户配置的分片策略,并生成路由路径。目前支持分片路由和广播路由。
- SQL改写
改写成真实的数据源与真实的表名称
- SQL执行
多线程异步执行SQL
- 结果归并
将多个执行结果集归并以便于通过统一JDBC接口输出。结果归并包括流式归并、内存归并和使用装饰者模式的追加归并这几种方式。
3、数据分片SQL使用规范
- SQL使用规范
- 支持项
路由至单数据节点时,目前MySQL数据库100%全兼容,其他数据库完善中。
路由至多数据节点时,全面支持DQL、DML、DDL、DCL、TCL。支持分页、去重、排序、分组、聚合、关联查询(不支持跨库关联)。以下用最为复杂的查询为例:
SELECT select_expr I,select_expr...]
FROM table_reference I,table_reference...]
[WHERE predicates]
[GROUP BY{col_name I position}[ASC IDESC],...][ORDER BY{col_name I position}[ASC IDESC],...]
[LIMIT{[offset,]row_count I row_count OFFSET offset}]
- 不支持项(路由至多数据节点)不支持CASE WHEN、HAVING、UNION(ALL)
支持分页子查询,但其他子查询有限支持,无论嵌套多少层,只能解析至第一个包含数据表的子查询,一旦在下层嵌套中再次找到包含数据表的子查询将直接抛出解析异常。
例如,以下子查询可以支持:
SELECT COUNT(*)FROM(SELECT * FROM b_order o)
以下子查询不支持:
SELECT COUNT(*)FROM(SELECT*FROM b_order o WHERE o.id IN(SELECT id FROM b_order WHERE status=? ))
通过子查询进行非功能需求,在大部分情况下是可以支持的。比如分页、统计总数等;而通过子查询实现业务查询当前并不能支持。
由于归并的限制,子查询中包含聚合函数目前无法支持。
不支持包含schema的SQL。因为ShardingSphere的理念是像使用一个数据源一样使用多数据源,因此对SQL的访问都是在同一个逻辑schema之上。
当分片键处于运算表达式或函数中的SQL时,将采用全路由的形式获取结果。
例如下面SQL,create_time为分片键:
SELECT*FROM b_order WHERE to_date(create_time,'yyyy-mm-dd')='2020-05-05';
由于ShardingSphere只能通过SQL字面提取用于分片的值,因此当分片键处于运算表达式或函数中时,ShardingSphere无法提前获取分片键位于数据库中的值,从而无法计算出真正的分片值。
不支持的SQL示例:
INSERT INTO tb1_name(co11,col2,…)VALUES(1+2,?,…)
INSERT INTO tb1_name(col1,col2, …)SELECT col1,col2 FROM tb1_name WHERE col3=?
SELECT COUNT(col1)as count_alias FROM tb1_name GROUP BY col1 HAVING count_alias >?
SELECT * FROM tb1_name1 UNION SELECT * FROM tb1_name2
SELECT * FROM tb1_name1 UNION ALL SELECT * FROM tb1_name2
SELECT * FROM ds.tb1_name1
SELECT SUM(DISTINCT col1),SUM(col1)FROM tb1_name
SELECT * FROM tb1_name WHERE to_date(create_time,'yyyy-mm-dd')=?
SELECT * FROM b_order ORDER BY id LIMIT 1000000,10
这句SQL会使得MySQL在无法利用索引的情况下跳过1000000条记录后,再获取10条记录,其性能可想而知。而在分库分表的情况下(假设分为2个库),为了保证数据的正确性,SQL会改写为:
SELECT * FROM b_order ORDER BY id LIMIT 0,1000010
即将偏移量前的记录全部取出,并仅获取排序后的最后10条记录。这会在数据库本身就执行很慢的情况下,进一步加剧性能瓶颈。因为原SQL仅需要传输10条记录至客户端,而改写之后的SQL则会传输1000010*2的记录至客户端。
SELECT * FROM b_order WHERE id>100000 AND id<=100010 ORDER BY id
或通过记录上次查询结果的最后一条记录的ID进行下一页的查询:
SELECT * FROM b_order WHERE id>100000 LIMIT 10
4、其他功能
- Inline行表达式
InlineShardingStrategy:采用lnline行表达式进行分片的配置。
Inline是可以简化数据节点和分片算法配置信息。主要是解决配置简化、配置一体化。
语法格式:
行表达式的使用非常直观,只需要在配置中使用 e x p r e s s i o n 或 {expression}或 expression或->{expression)标识行表达式即可。
例如:
${begin..end}表示范围区间
${[unit1,unit2,unit_×]}表示枚举值
例如,以下行表达式:
${['online','offline']}-tables{1..3}
最终会解析为:
online_table1,online_table2,online_table3,offline_table1,offline_table2,offline_table3
数据节点配置:
对于均匀分布的数据节点,如果数据结构如下:
用行表达式可以简化为:
对于自定义的数据节点,如果数据结构如下:
用行表达式简化为
-
分片算法配置:
行表达式内部的表达式本质上是一段Groovy代码,可以根据分片键进行计算的方式,返回相应的真实数据源或真实表名称。
结果为:ds0、ds1、ds2…ds9
-
分布式主键
ShardingSphere不仅提供了内置的分布式主键生成器,例如UUID、SNOWFLAKE,还抽离出分布式主键生成器的接口,方便用户自行实现自定义的自增主键生成器。
内置主键生成器:
UUID采用UUID.randomUUID()的方式产生分布式主键。
SNOWFLAKE在分片规则配置模块可配置每个表的主键生成策略,默认使用雪花算法,生成64bit的长整型数据。
自定义主键生成器:
自定义主键类,实现ShardingKeyGenerator接口
按SPl规范配置自定义主键类
在Apache ShardingSphere中,很多功能实现类的加载方式是通过SPI注入的方式完成的。
注意:在resources目录下新建META-INF文件夹,再新建services文件夹,然后新建文件的名字为org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator,打开文件,复制自定义主键类全路径到文件中保存。
自定义主键类应用配置
#对应主键字段名
spring.shardingsphere.sharding.tables.t_book.key-generator.column=id
#对应主键类getType返回内容
spring.shardingsphere.sharding.tables.t_book.key-generator.type=CKWKEY