文章标题:
Sharding-JDBC:分布式数据库的中间件解决之道
文章内容
文章目录
- Sharding-JDBC
- Sharding-JDBC概览
- Sharding-JDBC的功用
- 分库分表的含义
- 分库分表的类型
- 分库分表引发的问题
- 事务一致性难题
- 跨节点关联查询问题
- 跨节点分页与排序函数困境
- 主键重复隐患
- Sharding-JDBC入门(水平分表)
- 需求阐述
- 环境搭建步骤
- 代码编写流程
- 流程剖析
- 其他配置途径
- 概念术语解读
- 执行原理剖析
- 水平分库
- 执行流程详解
- 垂直分库(补充)
- 分片策略配置
- 用户数据操作
Sharding-JDBC
Sharding-JDBC概览
Sharding-JDBC是由当当网开发的开源分布式数据库中间件,属于轻量级Java框架,在Java的JDBC层提供附加功能。它以客户端直接连接数据库的方式工作,通过jar包提供服务,无需额外部署,可视为增强的JDBC驱动,完全兼容JDBC和多种ORM框架,从3.0版本起,Sharding-JDBC被纳入Sharding-Sphere中。
ShardingSphere是一套开源的分布式数据库中间件解决方案集合,由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(规划中)这三款相互独立的产品构成。它们均提供标准化的数据分片、分布式事务和数据库治理功能,适用于Java同构、异构语言、云原生等多样的应用场景。

Sharding-JDBC的功用
Sharding-JDBC的核心功能为数据分片与读写分离,借助Sharding-JDBC,应用能够透明地通过Jdbc访问已实现分库分表、读写分离的多个数据源,无需关注数据源的数量及数据的分布情况。
- 适用于任何基于JDBC的ORM框架,如JPA、Hibernate、Mybatis、Spring JDBC Template或直接使用JDBC。
- 支持任何第三方的数据库连接池,如DBCP、C3P0、BoneCP、Druid、HikariCP等。
- 支持任意实现JDBC规范的数据库。目前支持MySQL、Oracle、SQLServer、PostgreSQL以及任何遵循SQL92标准的数据库。
官网地址:概览 :: ShardingSphere (apache.org)

分库分表的含义
随着用户规模的扩大和业务的快速发展,数据库中的数据量急剧增加。然而,关系型数据库自身的单机存储容量、连接数、处理能力有限,会导致系统出现瓶颈,访问性能大幅下降。即便通过增加从库、优化索引等方式,也难以有效解决单表数据量过大带来的性能问题。
解决办法之一是提升服务器硬件能力,但成本较高;另一种办法是将数据分散到不同的数据库中,降低单一数据库的数据量,从而提升性能。例如,将数据库拆分为若干独立的数据库,同时将大表拆分为若干小表,以此缓解性能压力。
分库分表就是为了解决数据量过大导致的数据库性能降低问题,将原本独立的数据库拆分成多个数据库,将大表拆分成多个数据表,使单一数据库、单一数据表的数据量减少,进而提升数据库性能。
分库分表的类型
分库分表包含分库和分表两部分,实际生产中通常有垂直分库、水平分库、垂直分表、水平分表四种方式。
垂直分表:将一个表按字段拆分为多个表,每个表存储部分字段。例如,在商品信息表中,访问频次高的基本信息和访问频次低的详细描述可分别存于不同表,避免IO争抢和锁表,提升热门数据的操作效率。
大字段IO效率低的原因:一是数据量大读取时间长;二是大字段占用空间大,单页存储行数少,IO效率低;三是短字段且高频访问的数据能更快加载到内存,减少磁盘IO,提升性能。锁表是数据库中保证数据一致性和完整性的机制,事务修改表时会锁定该表,阻止其他事务修改,直到事务完成释放锁。
垂直分库:按业务对表分类,分布到不同数据库,各库可部署在不同服务器,实现专库专用。它能解决业务耦合问题,实现业务分级管理等,但未解决单表数据量过大的问题。
水平分库:将同一表的数据按规则拆到不同数据库,各库可在不同服务器。例如,根据类别ID的奇偶性将商品信息分别存储到不同数据库,解决单库大数据、高并发的性能瓶颈,提高系统稳定性和可用性。
水平分表:在同一数据库内,将同一表的数据按规则拆到多个表。与水平分库类似,解决单表数据量大的问题,提升性能和可用性。
分库分表引发的问题
分库分表虽能缓解性能瓶颈,但也带来一些问题。
事务一致性难题
因分库分表将数据分布在不同库甚至服务器,易引发分布式事务问题。
跨节点关联查询问题
分库分表后,商品和文章可能不在同一数据库或服务器,无法直接关联查询。可通过两次查询,先获取关联数据id,再发起第二次请求获取关联数据,最后拼装结果。
跨节点分页、排序函数问题
跨节点多库查询时,limit分页、order by排序等较复杂。需先在各分片节点排序返回,再汇总排序。请求页数越大,性能越差。使用Max、Min等函数时,需先在各分片执行,再汇总计算。
主键重复问题
分库分表后,自增长主键无法保证全局唯一,需设计全局主键避免跨库主键重复。
Sharding-JDBC入门(水平分表)
需求阐述
手动创建两张表mall_order_1
和mall_order_2
,作为订单表拆分后的表。通过Sharding-Jdbc向订单表插入数据,按分片规则,主键为偶数的进入mall_order_1
,另一部分进入mall_order_2
,并通过Sharding-Jdbc查询数据。
环境搭建
主机名 | IP地址 |
---|---|
mysql | 192.168.8.100/24 |
[root@mysql ~]# dnf -y install mysql-server mysql
[root@mysql ~]# systemctl start mysqld
[root@mysql ~]# systemctl enable mysqld
[root@mysql ~]# ss -nutlp | grep :3306
[root@mysql ~]# mysqladmin -uroot password "123qqq...A" #修改root用户密码
创建数据库:tmall_server-mall
mysql> CREATE DATABASE `tmall_server-mall`;
授权root@'%'用户
mysql> CREATE USER root@'%' IDENTIFIED BY '123qqq...A';
mysql> GRANT ALL ON *.* TO root@'%';
在tmall_server-mall
数据库下创建mall_order_1
表和mall_order_2
表
USE `tmall_server-mall`;
CREATE TABLE mall_order_1
(
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '数据ID',
buyer_id BIGINT UNSIGNED DEFAULT 0 COMMENT '用户ID',
buyer_username VARCHAR(50) DEFAULT '' COMMENT '用户名',
order_no VARCHAR(50) DEFAULT '' COMMENT '订单编号',
receiver_name VARCHAR(32) DEFAULT '' COMMENT '收货人',
receiver_phone VARCHAR(32) DEFAULT '' COMMENT '收货电话',
receiver_address VARCHAR(255) DEFAULT '' COMMENT '收货地址',
goods_num INT UNSIGNED DEFAULT 0 COMMENT '商品数量',
total_price DECIMAL(10, 2) DEFAULT 0 COMMENT '商品销售总价',
logistics_no VARCHAR(50) DEFAULT '' COMMENT '物流单号',
pay_channel INT UNSIGNED DEFAULT 0 COMMENT '支付渠道:1=支付宝,2=微信',
pay_method INT UNSIGNED DEFAULT 0 COMMENT '支付方式:1=在线支付,2=货到付款',
order_state TINYINT UNSIGNED DEFAULT 0 COMMENT '订单状态: 0=待支付,1=已支付,待发货, 2=已发货/待收货,3=确认收货/已完成,4=用户关闭,5=平台关闭(商家),6=系统调度关闭',
gmt_pay DATETIME DEFAULT NULL COMMENT '支付时间',
gmt_create DATETIME DEFAULT NULL COMMENT '数据创建时间',
gmt_modified DATETIME DEFAULT NULL COMMENT '数据最后修改时间',
PRIMARY KEY (id)
) COMMENT '商城-订单' CHARSET = utf8mb4;
CREATE TABLE mall_order_2
(
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '数据ID',
buyer_id BIGINT UNSIGNED DEFAULT 0 COMMENT '用户ID',
buyer_username VARCHAR(50) DEFAULT '' COMMENT '用户名',
order_no VARCHAR(50) DEFAULT '' COMMENT '订单编号',
receiver_name VARCHAR(32) DEFAULT '' COMMENT '收货人',
receiver_phone VARCHAR(32) DEFAULT '' COMMENT '收货电话',
receiver_address VARCHAR(255) DEFAULT '' COMMENT '收货地址',
goods_num INT UNSIGNED DEFAULT 0 COMMENT '商品数量',
total_price DECIMAL(10, 2) DEFAULT 0 COMMENT '商品销售总价',
logistics_no VARCHAR(50) DEFAULT '' COMMENT '物流单号',
pay_channel INT UNSIGNED DEFAULT 0 COMMENT '支付渠道:1=支付宝,2=微信',
pay_method INT UNSIGNED DEFAULT 0 COMMENT '支付方式:1=在线支付,2=货到付款',
order_state TINYINT UNSIGNED DEFAULT 0 COMMENT '订单状态: 0=待支付,1=已支付,待发货, 2=已发货/待收货,3=确认收货/已完成,4=用户关闭,5=平台关闭(商家),6=系统调度关闭',
gmt_pay DATETIME DEFAULT NULL COMMENT '支付时间',
gmt_create DATETIME DEFAULT NULL COMMENT '数据创建时间',
gmt_modified DATETIME DEFAULT NULL COMMENT '数据最后修改时间',
PRIMARY KEY (id)
) COMMENT '商城-订单' CHARSET = utf8mb4;
创建Spring Boot工程
name: spring-boot-sharding-demo
package name: cn.tedu.springboot.sharding.demo



使用IDEA连接数据库,用于查看数据库

引入maven依赖**
<!--引入sharding-jdbc和Spring Boot整合的Jar包-->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC1</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.16</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
点击右上角m标志,下载依赖,下载完毕之后,查看依赖是否下载成功

代码编写
分片规则配置
分片规则配置是sharding-jdbc进行分库分表操作的重要依据,包含数据源、主键生成策略、分片策略等配置。在application.properties中配置
创建资源文件:application.properties




server.port=9090
spring.application.name =sharding-demo
spring.main.allow-bean-definition-overriding = true
mybatis.configuration.map-underscore-to-camel-case=true
#配置数据源,名字可以自行定义,需要与下方配置保持一致
spring.shardingsphere.datasource.names=m1
#配置druid的连接池
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://192.168.8.100:3306/tmall_server-mall?useUnicode=true
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=123qqq...A
#指定mall_order表的数据分布情况,配置数据节点
spring.shardingsphere.sharding.tables.mall_order.actual-data-nodes=m1.mall_order_$->{1..2}
#配置表的主键生成策略,使用雪花算法可以自动解决主键冲突的问题
spring.shardingsphere.sharding.tables.mall_order.key-generator.column=id
spring.shardingsphere.sharding.tables.mall_order.key-generator.type=SNOWFLAKE
#配置表的分片策略,分片策略包含了分片健和分片算法
spring.shardingsphere.sharding.tables.mall_order.table-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.mall_order.table-strategy.inline.algorithm-expression=mall_order_$->{id % 2 + 1}
#打开SQL输出日志
spring.shardingsphere.props.sql.show=true
通过MyBatis新增数据
创建持久层接口: 在mapper包下创建OrderMapper




实现插入功能
package cn.tedu.springboot.sharding.demo.mapper;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface OrderMapper {
//编写新增方法,通过insert注解,指定新增的SQL语句
//当调用insertOrder方法的时候,等于执行上方注解指定的SQL语句
//Param注解