Sharding-JDBC初步认识
# Sharding-JDBC初步认识
# 它是什么
Sharding-JDBC是当当网研发的开源分布式数据库中间件,从3.0 开始Sharding-JDBC被包含在 ShardingSphere中,之后该项目进入进入Apache孵化器,4.0版本之后的版本为Apache版本。
官网链接-Sharding-JDBC (opens new window)
- https://shardingsphere.apache.org/document/4.1.0/cn/manual/sharding-jdbc/
Shardingsphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由ShardingJDBCSharding-Proxy和Sharding-Sidecar(计划中)这3款相互独立的产品组成。 他们均提供标准化的数据分片、分布式事务和数据库治理功能,可适用于如|ava同构、异构语言、容器、云原生等各种多样化的应用场景。
Sharding-JDBC 是一个开源的 Java 库,它通过 JDBC 接口提供数据库分片(Sharding)功能。它可以在应用程序层面透明地处理水平分库分表、读写分离等分布式数据库操作,而无需对现有代码进行大量修改。Sharding-JDBC 支持多种数据库类型,并提供了灵活的配置选项和强大的扩展能力。
你需要记住的是,也是描述它的作用的关键语句是:它可以在应用程序层面透明地处理水平分库分表、读写分离等分布式数据库操作,而无需对现有代码进行大量修改。Sharding-JDBC 支持多种数据库类型,并提供了灵活的配置选项和强大的扩展能力。
关于“透明”这里再做进一步的解释:这里的透明指的是程序层面。开发者不需要直接处理或感知到数据分片的具体细节。也就是说,应用程序仍然像操作单一数据库那样编写代码和执行查询,而底层的分库分表逻辑由中间件(如 Sharding-JDBC)自动管理和处理。
你可以这样理解,Sharding-JDBC 它帮我们自动管理着不同库中的不同表,我们只需要对它说,我们需要根据什么条件寻找什么数据,它就会自动的去不同的库或者便利分表之后的数据。然后返回给我们。
# 优劣
当然,它带来便利的同时,也会带来新的挑战,这里先简单列举下:
优势:
- 无缝集成:作为 JDBC 的增强版,Sharding-JDBC 可以直接替换原有的 JDBC 驱动,几乎不需要修改现有代码,降低了迁移成本。
- 灵活性:
- 提供了丰富的配置选项和扩展点,用户可以根据具体需求定制分片策略、路由规则等。
- 支持多种数据库类型,包括 MySQL、PostgreSQL、Oracle 等。
- 高性能:
- 通过优化 SQL 执行路径和减少网络开销,提升了查询和写入性能。
- 支持批量插入和更新操作,进一步提高了效率。
不足之处:
- 复杂的应用层逻辑:尽管 Sharding-JDBC 力求简化分片操作,但仍然需要开发者理解其工作原理和配置方法,增加了学习曲线。
- 潜在的性能瓶颈:
- 在某些极端情况下,如涉及大量跨分片的联合查询,可能会引入额外的性能开销。
- 如果分片键选择不当,可能导致数据分布不均,造成热点问题。
- 依赖于特定技术栈:
- 主要面向 Java 生态系统,对于其他编程语言的支持有限。
更多的会在后续的讲解说,慢慢展示。
# 快速上手
关于快速上手的demo,官网有详细的例子,这里就不一一展开了,下面只展示核心的一些配置或概念、注意事项。
以下是官网给出的示例:
官网示例代码-github地址 (opens new window)
- https://github.com/apache/shardingsphere-example
掘金-分库分表Sharding-讲解文章 (opens new window)
- https://juejin.cn/post/7095558089327247367#heading-17
# 引入依赖
对于Java或者springboot项目来说,一定要记住引入对应依赖,同时还需要注意各个版本之间是否兼容。
下面介绍一个版本关系:
操作系统:Win10
数据库:MySQL-5.7.25
JDK:64位 jdk1.8.0_201
应用框架:spring-boot-2.1.3.RELEASE,Mybatis3.5.0
Sharding-JDBC:sharding-jdbc-spring-boot-starter-4.0.0-RC1
2
3
4
5
以下是Sharding-jdbc对应pom:
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding‐jdbc‐spring‐boot‐starter</artifactId>
<version>4.0.0‐RC1</version>
</dependency>
2
3
4
5
# 编写配置-水平分表
引入对应依赖后,就是编写对应配置,告诉框架,我们的分库分表策略是什么样子,数据库地址再哪里,这样它才能帮我们完成在不同数据库执行sql,帮我们把不同表的结果集汇总起来,按照事先给的函数进行处理比如:排序、限制数量、分组,最后再返还给我们。
以下是一个简单的配置示例:
server.port=56081
spring.application.name = sharding‐jdbc‐simple‐demo
server.servlet.context‐path = /sharding‐jdbc‐simple‐demo
spring.http.encoding.enabled = true
spring.http.encoding.charset = UTF‐8
spring.http.encoding.force = true
spring.main.allow‐bean‐definition‐overriding = true
mybatis.configuration.map‐underscore‐to‐camel‐case = true
# 分片规则配置是sharding-jdbc进行对分库分表操作的重要依据,
# 配置内容包括:数据源、主键生成策略、分片策略等。
# 在application.properties中进行配置
# 以下是分片规则配置
# 定义数据源
spring.shardingsphere.datasource.names = m1
spring.shardingsphere.datasource.m1.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver‐class‐name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m1.url = jdbc:mysql://localhost:3306/order_db?useUnicode=true
spring.shardingsphere.datasource.m1.username = root
spring.shardingsphere.datasource.m1.password = root
# 指定t_order表的数据分布情况,配置数据节点 采用Groovy表达式
spring.shardingsphere.sharding.tables.t_order.actual‐data‐nodes = m1.t_order_$‐>{1..2}
# 指定t_order表的主键生成策略为SNOWFLAKE
# 根据表的order_id进行 分库分表
spring.shardingsphere.sharding.tables.t_order.key‐generator.column=order_id
# 使用雪花算法生成 主键id
spring.shardingsphere.sharding.tables.t_order.key‐generator.type=SNOWFLAKE
# 指定t_order表的分片策略,分片策略包括分片键和分片算法
spring.shardingsphere.sharding.tables.t_order.table‐strategy.inline.sharding‐column = order_id
spring.shardingsphere.sharding.tables.t_order.table‐strategy.inline.algorithm‐expression =
t_order_$‐>{order_id % 2 + 1}
# 根据order_id 的奇偶分别存储到 t_order_1 和 t_order_2
# 以上为sharding-jdbc的配置
# 打开sql输出日志
spring.shardingsphere.props.sql.show = true
swagger.enable = true
logging.level.root = info
logging.level.org.springframework.web = info
logging.level.com.itheima.dbsharding = debug
logging.level.druid.sql = debug
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 一些问题
# 数据节点
上述配置是水平分表
的一个配置,对于其中的数据节点,虽然是一个新的概念,但看配置相信你也能猜出来。
数据节点(Data Node)是指逻辑上代表一个分片表的物理存储位置。每个数据节点对应于一个具体的数据库实例中的表。通过定义数据节点,Sharding-JDBC 可以知道如何将查询路由到正确的数据库和表,从而实现水平分库分表。
数据节点由两部分组成:
- 数据源名称(DataSource Name):指向实际的数据库实例,例如
ds_0
、ds_1
等。 - 表名(Table Name):指定具体的数据表,例如
t_order_0
、t_order_1
等。
在上述配置文件中:
spring.shardingsphere.sharding.tables.t_order.actual‐data‐nodes = m1.t_order_$‐>{1..2}
sharding.tables.t_order
中的 t_order
代表的是逻辑表名。这个逻辑表名是应用程序用来引用物理分片表的一个抽象概念。因此,t_order
本身并不是直接对应到具体的物理表,而是作为一个占位符,指向多个实际的数据节点,它的值才对应真正的数据表。
而值的表达,可以使用准确的值,或者是内联表达式。上述的值表示,t_order
被分成了2个表,在m1数据库中,分别为t_order_1
和t_order_2
。
当然,分库分表规则除了可以直接写在配置文件中,也支持通过Java配置类的方式实现。欢迎大家自定查阅资料
介绍完水平分表,那如何实现水平分库呢?
# 完整的分库分表
以下是使用yaml格式进行配置的,格式更标准的写法,这里的更标准是因为用上了rules。
spring:
shardingsphere:
datasource:
names: ds_0,ds_1
ds_0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/db_0?serverTimezone=UTC&useSSL=false
username: root
password: password
ds_1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/db_1?serverTimezone=UTC&useSSL=false
username: root
password: password
rules:
sharding:
tables:
t_order:
actual-data-nodes: ds_${0..1}.t_order_${0..7}
database-strategy:
inline:
sharding-column: order_id
algorithm-expression: ds_${order_id % 2}
table-strategy:
inline:
sharding-column: order_id
algorithm-expression: t_order_${order_id % 8}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
这样来,你的感受可能更直接,配置主要分2部分,一个是数据源,一个是配置规则。强烈推荐用这种方式进行配置。
这里还有一些小点需要进行解释下:
- rules是必须的吗?
- inline使用后就是表示————水平分库分表吗?
下列是问题的解答:
inline
是指一种表达式:用于简洁地定义分片规则,通过模运算等简单运算符动态生成数据节点名称。如果不使用它,你的分片策略可能会复杂一些。
rules不是必须的,当你需要使用 Sharding-JDBC 提供的高级功能时,如水平分库分表、读写分离、分布式事务等,通常需要显式地定义 rules
。这些规则告诉 Sharding-JDBC 如何处理数据路由、分片策略等。
第一个例子的情况,
rules
节点实际上是隐含在sharding.tables.t_order
的配置中。ShardingSphere 允许通过这种方式简化配置文件,特别是在只有一个逻辑表需要分片的情况下。这种配置方式使得配置文件更简洁,易于理解和维护。但是,如果你的应用中有多个逻辑表或复杂的分片规则,或者需要启用其他高级功能(如读写分离、分布式事务等),则建议使用显式的
rules
节点来组织和管理这些规则。
# 垂直分表
垂直分库,这里就不展示了,垂直分开,原理是根据业务,把不同表放到不同的库,编写其实跟水平分开类似。所以这里主要介绍的是垂直分表。
垂直分表
(Vertical Sharding),即根据字段或业务逻辑将一个大表拆分成多个小表,每个新表包含原表的一部分列。垂直分表可以提高查询性能,减少单表的数据量,并简化某些复杂查询。
应用场景:适用于字段较多、查询频繁但只涉及部分字段的场景,或者为了分离冷热数据。
假设我们有一个用户信息表 t_user
,现在我们把它拆成2个,t_user_basic
跟t_user_detail
.
spring:
shardingsphere:
datasource:
names: ds_0
ds_0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/db?serverTimezone=UTC&useSSL=false
username: root
password: password
rules:
sharding:
tables:
t_user_basic:
actual-data-nodes: ds_0.t_user_basic
key-generate-strategy:
column: user_id
type: SNOWFLAKE
t_user_detail:
actual-data-nodes: ds_0.t_user_detail
key-generate-strategy:
column: user_id
type: SNOWFLAKE
binding-tables:
- t_user_basic,t_user_detail
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
似乎跟前面的例子差不多,定义数据源然后定义分片规则,不过它好像多了一个字段binding-tables
,它起着什么作用呢?
- binding-定义绑定表(可选)
它是一个可选字段,作用:为了简化跨表查询,告诉sharding相关联的表在同一个数据库实例中进行查询。
加上与不加的影响?
不加也可以使用,但如果你要使用,请一定确保你拆分后的表存在同一个数据库中。
- 绑定表(Binding Table):确保一组相关的表在同一个数据节点上,避免跨库关联查询带来的性能问题。
- 不绑定表:各个表可以独立分片,可能会分布在不同的数据节点上,这可能导致跨库关联查询。
那加了有何作用?
性能优化:Sharding-JDBC 知道这些表是绑定的,会自动优化联合查询,确保它们在同一数据节点上执行,避免跨库关联查询。
减少错误风险:Sharding-JDBC 会强制保证绑定表在同一数据节点上,减少了因分片策略不同步导致的错误风险。
如果拆分后的表存在不同库,你却使用它(banding),那会发生什么呢?
- 可能导致,预期存在的结果查询不到
- 可能导致异常
# 进行业务代码
如果是分库,在进行编写代码时,继续以往的风格就好
@Insert("insert into t_user(user_id, fullname) value(#{userId},#{fullname})")
int insertUser(@Param("userId")Long userId,@Param("fullname")String fullname);
2
如果是分表,则要分情况了,若是水平分表,因为表结构是相同的,因此你按照逻辑表来进行业务编写即可。
@Insert("insert into t_user(user_id, fullname) value(#{userId},#{fullname})")
int insertUser(@Param("userId")Long userId,@Param("fullname")String fullname);
2
如果是垂直分表,按照实际表明来编写即可。比如上述的例子,分为了2个表,那就按照2张表来编写。
原始的 t_user
表已经被拆分为两个或多个表(如 t_user_basic
和 t_user_detail
)。直接在业务层代码中继续使用 t_user
表进行 SQL 编写是不可行的,因为物理上这个表已经不存在了。