引言
在 MySQL 分库分表理论基础中,分库分表的不同粒度充分还原了不同的应用场景,并预演了不同粒度下所带来的问题的解决方案。接着引出了分库后如何访问数据库的问题,在代理层,所有使用数据库的业务服务都连接代理中间件,由中间件来决定落库位置,如 MyCat、Sharding-Sphere 实现;而在驱动层,在 JDBC 驱动层拦截 SQL 语句,然后改写 SQL 实现,如 Sharding-JDBC 框架的原理就是如此。
ShardingSphere 介绍
Apache ShardingSphere 是一款分布式 SQL 事务和查询引擎,可通过数据分片、弹性伸缩、加密等能力对任意数据库进行增强。
ShardingSphere 目前包括两大产品:ShardingSphere-JDBC 和 ShardingSphere-Proxy。
ShardingSphere-JDBC
ShardingSphere-JDBC 定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。
它会以POM
依赖的形式嵌入程序,运行期间会和 Java 应用共享资源,这款框架的本质可以理解成是JDBC
的增强版,只不过 Java 原生的 JDBC 仅支持单数据源的连接。
ORM
框架在执行 SQL 语句时,Sharding-JDBC 会以切面的形式拦截发往数据库的语句,接着根据配置好的数据源、分片规则和路由键,为 SQL 选择一个目标数据源,然后再发往对应的数据库节点处理。
ShardingSphere-Proxy
ShardingSphere-Proxy 定位为透明化的数据库代理端,通过实现数据库二进制协议,对异构语言提供支持。
Sharding-JDBC 在整个业务系统中对性能损耗极低,但由于 Sharding-JDBC 配置较为麻烦,比如在分布式系统中,任何使用分库分表的服务都需要单独配置多数据源地址、路由键、分片策略等等信息,同时它也仅支持 Java 语言,当一个系统是用多语言异构的,此时其他语言开发的子服务,则无法使用分库分表策略。
Sharding-Proxy 可以将其理解成一个伪数据库,对于应用程序而言是完全透明的,它会以中间件的形式独立部署在系统中。
使用 Sharding-Proxy 的子服务都会以连接数据库的形式,与其先建立数据库连接,然后将 SQL 发给它执行,Sharding-Proxy 会根据分片规则和路由键,将 SQL 语句发给具体的数据库节点处理,数据库节点处理完成后,又会将结果集返回给 Sharding-Proxy,最终再由它将结果集返回给具体的子服务。因此相较于原本的 Sharding-JDBC 来说,Sharding-Proxy 性能、资源开销更大,响应速度也会变慢。
ShardingSphere 核心概念
分片
- 分片键:用于将数据库(表)水平拆分的数据库字段。 例:将订单表中的订单主键的尾数取模分片,则订单主键为分片字段。 SQL 中如果无分片字段,将执行全路由,性能较差。 除了对单分片字段的支持,Apache ShardingSphere 也支持根据多个字段进行分片。
- 分片算法:用于将数据分片的算法,支持
=
、>=
、<=
、>
、<
、BETWEEN
和IN
进行分片。 分片算法可由开发者自行实现,也可使用 Apache ShardingSphere 内置的分片算法语法糖,灵活度非常高。 - 自动化分片算法:分片算法语法糖,用于便捷的托管所有数据节点,使用者无需关注真实表的物理分布。 包括取模、哈希、范围、时间等常用分片算法的实现。
- 自定义分片算法:提供接口让应用开发者自行实现与业务实现紧密相关的分片算法,并允许使用者自行管理真实表的物理分布。 自定义分片算法又分为:
- 标准分片算法:用于处理使用单一键作为分片键的
=
、IN
、BETWEEN AND
、>
、<
、>=
、<=
进行分片的场景。 - 复合分片算法:用于处理使用多键作为分片键进行分片的场景,包含多个分片键的逻辑较复杂,需要应用开发者自行处理其中的复杂度。
- Hint 分片算法:用于处理使用 Hint 行分片的场景。对于分片字段并非由 SQL 而是其他外置条件决定的场景,可使用 SQL Hint 注入分片值。 例:按照员工登录主键分库,而数据库中并无此字段。 SQL Hint 支持通过 Java API 和 SQL 注释两种方式使用。 详情请参见强制分片路由。
- 标准分片算法:用于处理使用单一键作为分片键的
逻辑表&真实表
- 逻辑表:相同结构的水平拆分数据库(表)的逻辑名称,是 SQL 中表的逻辑标识。 例:订单数据根据主键尾数拆分为 10 张表,分别是
t_order_0
到t_order_9
,他们的逻辑表名为t_order
。 - 真实表:在水平拆分的数据库中真实存在的物理表。 即上个示例中的
t_order_0
到t_order_9
。
数据节点
数据分片的最小单元,由数据源名称和真实表组成。 例:ds_0.t_order_0
。 逻辑表与真实表的映射关系,可分为均匀分布和自定义分布两种形式。
- 均匀分布:指数据表在每个数据源内呈现均匀分布的态势, 例如:
数据节点的配置为:db0.t_order0, db0.t_order1, db1.t_order0, db1.t_order1
db0
├── t_order0
└── t_order1
db1
├── t_order0
└── t_order1
- 自定义分布:指数据表呈现有特定规则的分布, 例如:
数据节点的配置为:db0.t_order0, db0.t_order1, db1.t_order2, db1.t_order3, db1.t_order4
db0
├── t_order0
└── t_order1
db1
├── t_order2
├── t_order3
└── t_order4
分库分表工作流程
以一个分库分表的工作流程示例图了解上述概念是如何应用的。
假定有一张逻辑表t_order
,以order_id
作为分片键,并以Key % DBCount
分片算法将数据分发到三个数据库db_0
、db_1
和db_2
,并自定义数据节点分布为db_0.order_01
、db_1.order_01
、db_1.order_02
和db_2.order_01
。
绑定表&广播表&单表&动态表
- 绑定表:指分片规则一致的一组分片表。 使用绑定表进行多表关联查询时,必须使用分片键进行关联,否则会出现笛卡尔积关联或跨库关联,从而影响查询效率。假定有
t_order
表和t_order_item
表,它们之间以t_order.id
和t_order_item.order_id
作为逻辑上的主外键关系。其中t_order
表由于指定了分片条件,ShardingSphere 将会以它作为整个绑定表的主表。 所有路由计算将会只使用主表的策略,那么t_order_item
表的分片计算将会使用t_order
的条件。其配置如下:
rules:
- !SHARDING
tables:
t_order:
actualDataNodes: ds_${0..1}.t_order_${0..1}
t_order_item:
actualDataNodes: ds_${0..1}.t_order_item_${0..1}
- 广播表:指所有的数据源中都存在的表,表结构及其数据在每个数据库中均完全一致。 适用于数据量不大且需要与海量数据的表进行关联查询的场景,例如:字典表。
- 单表:指所有的分片数据源中仅唯一存在的表。 适用于数据量不大且无需分片的表。也就是说不需要分库分表的数据表无需分片,这样所有的读写操作最终都会落入这一张单表中处理。
- 动态表:动态表的概念在 ShardingSphere 最新的 5.x 文档中已经移除了,但也可以基于分片算法去实现,所以虽然移除了动态表的概念,但也可以实现相同的效果,动态表的概念是指表会随着数据增长、或随着时间推移,不断的去创建新表,如下:
数据节点的配置为:db0.t_order202301, db0.t_order202302, db1.t_order202303...
db0
└── t_order202301
db1
└── t_order202302
db2
└── t_order202303
行表达式
行表达式是为了解决配置的简化与一体化这两个主要问题。在繁琐的数据分片规则配置中,随着数据节点的增多,大量的重复配置使得配置本身不易被维护。 通过行表达式可以有效地简化数据节点配置工作量。
行表达式的使用非常直观,只需要在配置中使用${ expression }
或$->{ expression }
标识行表达式即可。 目前支持数据节点和分片算法这两个部分的配置。行表达式的内容使用的是Groovy
的语法,Groovy
能够支持的所有操作,行表达式均能够支持。 例如:${begin..end}
表示范围区间,${[unit1, unit2, unit_x]}
表示枚举值。在Spring Boot
中因为属性配置会使用${}
,为与之区分建议使用$->{ expression }
。
例如,以下行表达式:
$->{['online', 'offline']}_table$->{1..3}
最终解析为:
online_table1, online_table2, online_table3, offline_table1, offline_table2, offline_table3
总结
ShardingSphere 作为 MySQL 分库分表的解决方案,是目前主流的方式之一。学习 ShardingSphere 的原理及核心概念有助于理解实际开发的业务编码,这是前往实战的必修课之一。之后我们将基于 Spring Boot 应用 ShardingSphere,完成实际开发中的分库分表。