Sharding-JDBC 是 Apache ShardingSphere 的一个模块,它是一个 轻量级的 Java 数据访问框架,通过增强 JDBC 驱动实现了分库分表、分布式事务、读写分离和数据加密等功能。它适用于任何基于 JDBC 的应用程序。
支持根据分片键(如主键、用户 ID 等)将数据分布到不同的数据库和表中。
提供多种分片策略(标准分片、复杂分片、行表达式分片等)。
自动路由查询、更新到正确的数据库和表。
读写分离:
支持主从数据库架构,自动将写操作路由到主库,读操作路由到从库。
提供动态负载均衡策略。
支持柔性事务(两阶段提交事务或 TCC 模型)。
保证分布式场景下的数据一致性。
数据加密:
支持字段级别的加密和解密。
提供 AES、MD5 等多种加密算法。
高扩展性:
提供 SPI 接口,支持自定义分片算法和加密算法。
架构特点
Sharding-JDBC 不依赖任何中间代理服务,直接嵌入应用程序,适用于无中间件、灵活部署的场景。
通过拦截和增强 JDBC 请求,实现了透明化的分库分表和其他功能。
无侵入性:
对应用程序几乎无侵入,开发者只需按照 Sharding-JDBC 的配置规则配置数据源和分片规则,无需修改业务代码。
分库分表:数据库表随着业务增长越来越大时,通过分片将数据分散到多个表或数据库中。
读写分离:在读多写少的场景下,减轻主库的压力,提高性能。
多租户系统:不同租户的数据分布在不同的数据库中。
分布式事务:需要在多个数据库之间实现事务管理。
数据安全:需要对敏感字段(如密码、银行卡号等)进行加密。
ShardingSphere-JDBC:
嵌入式的分布式数据库中间件,直接集成在应用程序中。
轻量、灵活,适用于任何基于 JDBC 的项目。
ShardingSphere-Proxy:
独立的数据库代理服务,支持多语言访问。
类似 MySQL Proxy,适用于多种编程语言场景。
ShardingSphere-UI:
提供友好的 Web 界面,方便配置和管理。
框架 SpringBoot + mybatiesPlus + sharding-jdbc
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.1.1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
# 打印SQl
spring.shardingsphere.props.sql-show=true
spring.autoconfigure.exclude=com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
# 定义多个数据源
spring.shardingsphere.datasource.names = db1,db2
#数据源1
spring.shardingsphere.datasource.db1.type = com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.db1.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.db1.url = jdbc:mysql://127.0.0.1:3306/fee?useUnicode=true&characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false&useSSL=false
spring.shardingsphere.datasource.db1.username = acc_admin
spring.shardingsphere.datasource.db1.password = B6nr4ql
#数据源2
spring.shardingsphere.datasource.db2.type = com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.db2.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.db2.url = jdbc:mysql://127.0.0.1:3306/book?useUnicode=true&characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false&useSSL=false
spring.shardingsphere.datasource.db2.username = acc_admin
spring.shardingsphere.datasource.db2.password = B6nr4ql
# 标准分片表配置
# 由数据源名 + 表名组成,以小数点分隔。多个表以逗号分隔,支持 inline 表达式。缺省表示使用已知数据源与逻辑表名称生成数据节点,用于广播表(即每个库中都需要一个同样的表用于关联查询,多为字典表)或只分库不分表且所有库的表结构完全一致的情况
spring.shardingsphere.rules.sharding.tables.pay_order.actual-data-nodes=db1.pay_order
spring.shardingsphere.rules.sharding.tables.users.actual-data-nodes=db2.users
#1.配置数据节点 指定customer_order逻辑表表的真实分布情况 分片算法
# 数据节点定义:db0 和 db1 中的 customer_order_0 和 customer_order_1
spring.shardingsphere.rules.sharding.tables.customer_order.actual-data-nodes=db$->{1..2}.customer_order_$->{0..1}
## 数据库分片策略:基于 user_id
spring.shardingsphere.rules.sharding.tables.customer_order.database-strategy.standard.sharding-column=user_id
spring.shardingsphere.rules.sharding.tables.customer_order.database-strategy.standard.sharding-algorithm-name=table-inline
# 数据库分片算法:user_id % 2
spring.shardingsphere.rules.sharding.sharding-algorithms.table-inline.type=INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.table-inline.props.algorithm-expression=db$->{user_id % 2+1}
## 分布式序列配置
spring.shardingsphere.rules.sharding.tables.customer_order.key-generate-strategy.column=cid
spring.shardingsphere.rules.sharding.tables.customer_order.key-generate-strategy.key-generator-name=alg-snowflake
spring.shardingsphere.rules.sharding.key-generators.alg-snowflake.type=SNOWFLAKE
#
## 表分片策略:基于 cid
spring.shardingsphere.rules.sharding.tables.customer_order.table-strategy.standard.sharding-column=cid
spring.shardingsphere.rules.sharding.tables.customer_order.table-strategy.standard.sharding-algorithm-name=inline-hash-mod
#
## 表分片算法:cid.hashCode() % 2
spring.shardingsphere.rules.sharding.sharding-algorithms.inline-hash-mod.type=INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.inline-hash-mod.props.algorithm-expression=customer_order_$->{Math.abs(cid.hashCode()) % 2}
#
CREATE TABLE customer_order_0 (
cid BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
user_id BIGINT NOT NULL COMMENT '用户ID',
corder_no BIGINT NOT NULL COMMENT '订单编号',
cname VARCHAR(255) NOT NULL COMMENT '订单名称',
brief TEXT COMMENT '订单简介',
price DOUBLE NOT NULL COMMENT '订单价格',
status INT NOT NULL COMMENT '订单状态',
PRIMARY KEY (cid),
UNIQUE KEY uq_corder_no (corder_no) -- 为订单编号添加唯一约束
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='客户订单表';
CREATE TABLE customer_order_1 (
cid BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
user_id BIGINT NOT NULL COMMENT '用户ID',
corder_no BIGINT NOT NULL COMMENT '订单编号',
cname VARCHAR(255) NOT NULL COMMENT '订单名称',
brief TEXT COMMENT '订单简介',
price DOUBLE NOT NULL COMMENT '订单价格',
status INT NOT NULL COMMENT '订单状态',
PRIMARY KEY (cid),
UNIQUE KEY uq_corder_no (corder_no) -- 为订单编号添加唯一约束
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='客户订单表';
@Data
@TableName("customer_order")
@ToString
public class Course {
@TableId(type = IdType.AUTO)
private Long cid;
private Long userId;
private Long corderNo;
private String cname;
private String brief;
private double price;
private int status;
}
package com.umpay.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.umpay.bean.Course;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface CourseMapper extends BaseMapper<Course> {
}
package com.umpay.test;
import com.umpay.bean.Course;
import com.umpay.bean.PayOrder;
import com.umpay.bean.User;
import com.umpay.mapper.CourseMapper;
import com.umpay.mapper.PayOrderMapper;
import com.umpay.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.graphql.GraphQlAutoConfiguration;
import org.springframework.boot.autoconfigure.graphql.data.GraphQlReactiveQuerydslAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class Test {
@Autowired
private UserMapper userMapper;
@Autowired
private PayOrderMapper payOrderMapper;
@Autowired
private CourseMapper courseMapper;
@org.junit.jupiter.api.Test
public void testInsert(){
User user = new User();
user.setUser_id(10089L);
user.setUsername("大远哥3");
user.setPhone("15612344321");
user.setStatus("1");
user.setEmail("12535352");
userMapper.insert(user);
PayOrder payOrder = new PayOrder();
payOrder.setOrder_id(12345L);
payOrder.setAmount("100");
payOrder.setUser_id(user.getUser_id());
payOrder.setStatus(1);
payOrderMapper.insert(payOrder);
}
@org.junit.jupiter.api.Test
public void coreTest(){
/**
* CREATE TABLE customer_order (
* cid BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
* user_id BIGINT NOT NULL COMMENT '用户ID',
* corder_no BIGINT NOT NULL COMMENT '订单编号',
* cname VARCHAR(255) NOT NULL COMMENT '订单名称',
* brief TEXT COMMENT '订单简介',
* price DOUBLE NOT NULL COMMENT '订单价格',
* status INT NOT NULL COMMENT '订单状态',
* PRIMARY KEY (cid),
* UNIQUE KEY uq_corder_no (corder_no) -- 为订单编号添加唯一约束
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='客户订单表';
*/
for (int i = 45; i < 60; i++) {
Course course=new Course();
// course.setCid(10010L + i);
course.setUserId(1L+i);
course.setCname("Java面试题详解");
course.setBrief("经典的10000道面试题");
course.setPrice(100.00);
course.setStatus(1);
course.setCorderNo(1l+i);
courseMapper.insert(course);
}
}
}