Sharding-JDBC 实现分库分表
时间:2022-07-25
本文章向大家介绍Sharding-JDBC 实现分库分表,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
一、概述
- 分库分表介绍:当数据量变大以后,单库单表已经不能满足需求。此时就需要进行拆分,拆分纬度分为垂直拆分和水平拆分。
- 水平拆分:比如 服务器1 上有 user_0, order_0; 服务器2 上有 user_1, order_1。此时 user_0 和 user_1 一起组成了用户表。
- 垂直拆分:用户表 放在服务器1上,订单表 放在服务器2上。
- 此处模拟使用两个数据库,每个数据库建两张表。库的拆分使用 city 字段(按城市存不同的库),表的拆分使用 id 取模。
二、数据准备
- 引入 pom :
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1.tmp</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<dependency>
<groupId>com.dangdang</groupId>
<artifactId>sharding-jdbc-config-spring</artifactId>
<version>1.5.4.1</version>
</dependency>
- 新建两个数据库(可以同一个服务器上,也可以两个服务器上)。
CREATE DATABASE `xjf_0` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
CREATE DATABASE `xjf_1` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
- 在两个库中分别建如下两张表:
CREATE TABLE `user_0` (
`id` BIGINT(64) NOT NULL,
`city` VARCHAR(20) NOT NULL,
`name` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
CREATE TABLE `user_1` (
`id` BIGINT(64) NOT NULL,
`city` VARCHAR(20) NOT NULL,
`name` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
三、分库分表配置
- 在 resource 目录下新建 sharding.xml。配置如下,记得数据库连接修改为你自己的:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:rdb="http://www.dangdang.com/schema/ddframe/rdb"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.dangdang.com/schema/ddframe/rdb
http://www.dangdang.com/schema/ddframe/rdb/rdb.xsd
">
<!--======================================================分库分表===开始=================================================-->
<!-- inline 表达式报错解决:在 Spring 的配置文件中,由于 inline 表达式使用了 Groovy 语法, Groovy 语法的变量符与 Spring 默认占位符
同为 ${} ,因此需要在配置文件中增加下面这行来解决解析报错问题-->
<context:property-placeholder ignore-unresolvable="true" />
<!-- 第一个数据库 -->
<bean id="ds_0" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close" primary="true">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/xjf_0?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&useSSL=false" />
<property name="username" value="root" />
<property name="password" value="123456" />
</bean>
<!-- 第二个数据库 -->
<bean id="ds_1" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/xjf_1?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&useSSL=false" />
<property name="username" value="root" />
<property name="password" value="123456" />
</bean>
<!-- 配置分库规则: 根据 city 来分库,同一个城市的数据存同一个数据库中 -->
<rdb:strategy id="databaseShardingStrategy" sharding-columns="city"
algorithm-class="com.xjf.sharding.algorithm.MySingleKeyDbShardingAlgorithm" />
<!-- 配置分表规则 -->
<rdb:strategy id="tableShardingStrategy" sharding-columns="id"
algorithm-class="com.xjf.sharding.algorithm.MyUserSingleKeyTableShardingAlgorithm" />
<!-- 配置分库分表数据源 -->
<rdb:data-source id="dataSource">
<rdb:sharding-rule data-sources="ds_0, ds_1">
<rdb:table-rules>
<rdb:table-rule logic-table="user" actual-tables="user_${0..1}"
database-strategy="databaseShardingStrategy" table-strategy="tableShardingStrategy">
<!-- 使用 Sharding-JDBC 的默认 ID 生成器,基于雪花算法。-->
<rdb:generate-key-column column-name="id" />
</rdb:table-rule>
</rdb:table-rules>
</rdb:sharding-rule>
</rdb:data-source>
<!--======================================================分库分表===结束=================================================-->
<!-- 给 MyBatis-Plus 配置数据源 -->
<bean id="mybatisSqlSessionFactoryBean" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
- 在启动类上添加对应注解,引入 sharding.xml。
@ImportResource(locations = "classpath:sharding.xml")
@MapperScan("com.xjf.sharding.mapper")
@SpringBootApplication
public class ShardingApplication {
public static void main(String[] args) {
SpringApplication.run(ShardingApplication.class, args);
}
}
四、自定义分库和分表算法
- 分库算法,使用 city 来区分:
public class MySingleKeyDbShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm<String> {
private static Map<String, List<String>> shardingMap = new ConcurrentHashMap<>();
static {
shardingMap.put("ds_0", Arrays.asList("上海"));
shardingMap.put("ds_1", Arrays.asList("杭州"));
}
@Override
public String doEqualSharding(Collection<String> collection, ShardingValue<String> shardingValue) {
for (String each : collection) {
System.err.println("数据库:" + each);
System.err.println("添加数据的城市:" + shardingValue.getValue());
if (shardingMap.get(each).contains(shardingValue.getValue())){
return each;
}
}
// 默认保存在数据库 "ds_0" 中
return "ds_0";
}
@Override
public Collection<String> doInSharding(Collection<String> collection, ShardingValue<String> shardingValue) {
Collection<String> result = new LinkedHashSet<>(collection.size());
for (String each : collection) {
if (shardingMap.get(each).contains(shardingValue.getValue())){
result.add(each);
}else {
result.add("ds_0");
}
}
return result;
}
@Override
public Collection<String> doBetweenSharding(Collection<String> collection, ShardingValue<String> shardingValue) {
Collection<String> result = new LinkedHashSet<>(collection.size());
for (String each : collection) {
if (shardingMap.get(each).contains(shardingValue.getValue())){
result.add(each);
}else {
result.add("ds_0");
}
}
return result;
}
}
- 分表算法,id 取模:
public class MyUserSingleKeyTableShardingAlgorithm implements SingleKeyTableShardingAlgorithm<Long> {
/**
* 在 where 使用 = 作为条件分片键
*/
@Override
public String doEqualSharding(Collection<String> collection, ShardingValue<Long> shardingValue) {
System.err.println("运行方法: doEqualSharding");
for (String each : collection) {
System.err.println("表:" + each);
System.err.println("shardingValue.getValue: " + shardingValue.getValue());
// 配合测试分库分表,取模是只有 2 张表。在测试不分库只分表时是 4 张表。分别对应使用
// if (each.endsWith(shardingValue.getValue() % 4 +"")){
if (each.endsWith(shardingValue.getValue() % 2 +"")){
return each;
}
}
throw new IllegalArgumentException();
}
/**
* 在 where 使用 in 作为条件分片键
*/
@Override
public Collection<String> doInSharding(Collection<String> collection, ShardingValue<Long> shardingValue) {
System.err.println("运行方法: doInSharding");
Collection<String> result = new LinkedHashSet<>(collection.size());
for (Long value : shardingValue.getValues()) {
for (String tableName : collection) {
if (tableName.endsWith(value % 4 + "")){
result.add(tableName);
}
}
}
return result;
}
/**
* 在 where 使用 between 作为条件分片键
*/
@Override
public Collection<String> doBetweenSharding(Collection<String> collection, ShardingValue<Long> shardingValue) {
System.err.println("运行方法: doBetweenSharding");
Collection<String> result = new LinkedHashSet<>(collection.size());
Range<Long> range = shardingValue.getValueRange();
for (Long i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) {
for (String each : collection) {
if (each.endsWith( i % 4 + "")){
result.add(each);
}
}
}
return result;
}
}
五、测试
- 在 controller 中添加插入数据的方法。id 的生成使用 Sharding-JDBC 默认的分布式主键(基于雪花算法),当程序在多个服务器上时,需要分别为机器在系统环境变量中设置 sharing-jdbc.default.key.generator.worker.id :
@GetMapping("/add3")
public String add3(){
for (int i = 0; i < 100; i++) {
User user = new User();
// 不设置 ID,在 sharding.xml 配置了 Sharding-JDBC 的默认分布式主键生成,是采用雪花算法实现的。
// 在类 com.dangdang.ddframe.rdb.sharding.keygen.DefaultKeyGenerator 中
// user.setId(Long.valueOf(i));
// 随机设置城市
int random = new Random().nextInt();
if (random % 2 == 0){
user.setCity("上海");
}else {
user.setCity("杭州");
}
user.setName("嘉文四世");
userMapper.insert(user);
}
return "success";
}
- 调用方法,可以在分别的两个数据库,四张表中查看数据。其中数据总数加起来刚好 100 条。
看《Spring Cloud微服务入门、实战与进阶》
- 基于JQuery EasyUI的WebMVC控件封装(含源码)
- Android系统源码分析-JNI
- EntityFrameWork实现部分字段获取和修改(含源码)
- 基于Ado.Net的日志组件
- Do you kown Asp.Net Core -- 配置Kestrel端口
- 【翻译】在Visual Studio中使用Asp.Net Core MVC创建第一个Web Api应用(二)
- 微信快速开发框架(一)-- 对微信公众平台开发的消息处理
- 微信快速开发框架(二) -- 快速开发微信公众平台框架---简介
- LayoutInflater 布局渲染工具原理分析
- 使用Keras在训练深度学习模型时监控性能指标
- 微信快速开发框架(四)-- 体验微信公众平台快速开发框架
- AsyncTask源码解析
- 微信快速开发框架(五)-- 利用快速开发框架,快速搭建微信浏览博客园首页文章
- 对于小批量梯度下降以及如何配置批量大小的入门级介绍
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Linux下docker制作springboot应用镜像
- 行为型设计模式:备忘录模式
- 行为型设计模式:访问者模式
- redis实战第十篇 集群收缩
- redis实战第九篇 集群扩容自动迁移槽(redis-cli)
- 自动化运维| Ansible playbook的逻辑控制语句
- Windows通用应用平台UWP持久化
- redis实战第八篇 集群扩容 手动迁移槽
- Xinetd服务的安装与配置详解
- redis实战第七篇 使用redis工具(redis-cli)搭建redis cluster
- 使用ROS2机器人操作系统进行多机器人编程技术实践(Multi-Robot Programming Via ROS2 )
- 自动化运维实践 | Ansible playbook重用
- 手把手教你打造高效的 Kubernetes 命令行终端
- docker save load export import的区别
- 使用ABAC控制数据访问