Spring Cloud 微服务系列文章,点击上方合集↑
ShardingSphere 是国产的、开源的、配置简单的分布式数据库解决方案。可以通过简单的配置实现分库分表和读写分离。
ShardingSphere 提供了两种分布式数据库解决方案:ShardingSphere-JDBC和ShardingSphere-Proxy。
ShardingSphere-JDBC是一个基于JDBC协议的中间件,JDBC API是Java访问关系型数据库的标准API,因此ShardingSphere-JDBC适用于JDBC API连接的各种关系型数据库。它通过JDBC驱动程序来拦截应用程序发出的数据库访问请求,并根据事先配置的分片规则将请求路由到正确的数据库分片中。
ShardingSphere-Proxy则是一个基于数据库协议的中间件,主要用于对数据库协议的拦截和处理。它是一个代理数据库,对于开发人员完全透明无感知。
官网地址:https://shardingsphere.apache.org
本文先讲解ShardingSphere-JDBC。
创建sharding_0
和sharding_1
两个数据库。两个数据库完全一样,包含如下数据表:
company
企业表,根据id
分库
product
商品表,根据企业idcompany_id
分库
permission
权限表,广播表不分库
CREATE DATABASE sharding_0 CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE DATABASE sharding_1 CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE TABLE `company` (
`id` bigint(20) NOT NULL COMMENT '主键id',
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '名称',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
CREATE TABLE `permission` (
`id` bigint(20) NOT NULL COMMENT '主键id',
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '名称',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
CREATE TABLE `product` (
`id` bigint(20) NOT NULL COMMENT '主键id',
`company_id` bigint(20) NULL DEFAULT NULL COMMENT '公司id',
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '名称',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
添加shardingsphere-jdbc-core
包依赖,版本是5.4.0
。
<dependency>
<groupId>org.apache.shardingspheregroupId>
<artifactId>shardingsphere-jdbc-coreartifactId>
<version>5.4.0version>
dependency>
如果出现如下问题在properties
节点加上
。
java.lang.NoSuchMethodError: org.apache.shardingsphere.infra.util.yaml.constructor.ShardingSphereYamlConstructor$1.setCodePointLimit(I)V
配置数据库驱动为org.apache.shardingsphere.driver.ShardingSphereDriver
数据库路径指向sharding.yaml
配置文件。
spring.datasource.driver-class-name=org.apache.shardingsphere.driver.ShardingSphereDriver
spring.datasource.url=jdbc:shardingsphere:classpath:sharding.yaml
resources
目录下创建sharding.yaml
配置文件,主要配置如下内容:
ds_0
指向 jdbc:mysql://127.0.0.1:3306/sharding_0
。ds_1
指向 jdbc:mysql://127.0.0.1:3306/sharding_1
。company
表的分片规则是id_inline
,根据id
取模。product
表没有配置分片规则,用默认分配规则,根据company_id
取模。permission
表是广播表,插入(更新)数据的时候每张表都会插入(更新),读取的时候随机一张表读取。ds_$->{id % 2}
偶数在ds_0
,奇数在ds_1
。dataSources:
ds_0:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://127.0.0.1:3306/sharding_0?serverTimezone=UTC&useSSL=false
username: root
password: 123456
ds_1:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://127.0.0.1:3306/sharding_1?serverTimezone=UTC&useSSL=false
username: root
password: 123456
rules:
- !SHARDING
tables:
company:
actualDataNodes: ds_$->{0..1}.company
databaseStrategy:
standard:
shardingColumn: id
shardingAlgorithmName: id_inline
product:
actualDataNodes: ds_$->{0..1}.product
defaultDatabaseStrategy:
standard:
shardingColumn: company_id
shardingAlgorithmName: database_inline
shardingAlgorithms:
database_inline:
type: INLINE
props:
algorithm-expression: ds_$->{company_id % 2}
id_inline:
type: INLINE
props:
algorithm-expression: ds_$->{id % 2}
- !BROADCAST
tables: # 广播表规则列表
- permission
props:
sql-show: true
新增企业的接口,通过OpenFeign
调用id-service
服务递增生成企业id,企业名称通过UUID
随机生成。
@RestController
@RequestMapping("company")
public class CompanyController {
@Resource
private CompanyService companyService;
@Resource
private IdService idService;
@GetMapping("add")
public Boolean add() {
Company company = new Company();
company.setId(idService.generateId("company"));
company.setName("企业名称;" + UUID.randomUUID());
company.setCreateTime(LocalDateTime.now());
company.setUpdateTime(LocalDateTime.now());
return companyService.save(company);
}
}
新增权限的接口,通过OpenFeign
调用id-service
服务递增生成权限id,权限名称通过UUID
随机生成。
@RestController
@RequestMapping("permission")
public class PermissionController {
@Resource
private PermissionService permissionService;
@Resource
private IdService idService;
@GetMapping("add")
public Boolean add() {
Permission permission = new Permission();
permission.setId(idService.generateId("permission"));
permission.setName("权限名称:" + UUID.randomUUID());
permission.setCreateTime(LocalDateTime.now());
permission.setUpdateTime(LocalDateTime.now());
return permissionService.save(permission);
}
}
新增商品的接口,传入参数companyId
表明是新增那个企业的商品,通过OpenFeign
调用id-service
服务递增生成商品id,
商品名称通过UUID
随机生成。
@RestController
@RequestMapping("product")
public class ProductController {
@Resource
private ProductService productService;
@Resource
private IdService idService;
@GetMapping("add")
public Boolean add(@RequestParam Long companyId) {
Product product = new Product();
product.setId(idService.generateId("product"));
product.setCompanyId(companyId);
product.setName("商品名称:" + UUID.randomUUID());
product.setCreateTime(LocalDateTime.now());
product.setUpdateTime(LocalDateTime.now());
return productService.save(product);
}
}
这里需要启动id-service
。(具体可以看之前分布式ID那一篇文章)。
在leaf_alloc
数据表中插入三条记录,
INSERT INTO `leaf_alloc` VALUES ('company', 1, 2000, '企业表', '2023-08-30 11:26:23');
INSERT INTO `leaf_alloc` VALUES ('permission', 1, 2000, '权限表', '2023-08-30 11:26:27');
INSERT INTO `leaf_alloc` VALUES ('product', 1, 2000, '商品表', '2023-08-30 11:26:33');
leaf_alloc
数据表使用不同的biz_tag
,比如商品表就在leaf_alloc
表中插入biz_tag
为product
的一条记录,获取id传入参数就是key=product
。通过OpenFeign
调用id-service
服务获取id。
@FeignClient(name = "id-service")
public interface IdService {
@GetMapping("/id/generate")
Long generateId(@RequestParam String key);
}
浏览器访问新增企业接口地址:http://localhost:8091/company/add
多次请求后发现:id
为偶数的企业记录在sharding_0
数据库,id
为奇数的企业记录在sharding_1
数据库。
浏览器访问新增商品接口地址:
id为1的企业新增商品:http://localhost:8091/product/add?companyId=1
id为2的企业新增商品:http://localhost:8091/product/add?companyId=2
company_id
为偶数的商品记录在sharding_0
数据库,company_id
为奇数的商品记录在sharding_1
数据库。
注意跟
company
表的区别,company
表是通过id
取模,product
表是通过company_id
取模。
浏览器访问新增权限接口地址:
http://localhost:8091/permission/add
发现sharding_0
和sharding_1
两个数据库都有数据记录。
本文讲解了如何通过ShardingSphere-JDBC进行分库分表,通过ShardingSphere-JDBC分库分表需要在代码中引入相关依赖包并且在配置文件中进行相关配置。
ShardingSphere-JDBC的不方便之处就是需要开发人员在每个应用服务都去配置,对于开发人员是不透明的。但是由于是应用直连数据库,它避免了代理数据库崩溃的风险。
下文将讲解如何通过ShardingSphere-Proxy
代理数据库来实现分布式数据库。
Spring Cloud 微服务系列 完整的代码在仓库的sourcecode/spring-cloud-demo
目录下。
gitee(推荐):https://gitee.com/cunzaizhe/xiaohuge-blog
github:https://github.com/tigerleeli/xiaohuge-blog
关注微信公众号:“小虎哥的技术博客”,让我们一起成为更优秀的程序员❤️!