Spring Boot + ShardingSphere 实现分库分表 + 读写分离实战

🚀 Spring Boot + ShardingSphere 实现分库分表 + 读写分离(涵盖99%真实场景)

🏷️ 标签:ShardingSphere、分库分表、读写分离、MySQL 主从、Spring Boot 实战

分库分表 vs 读写分离 vs 主从配置与数据库高可用架构区别


📚 目录导航

  • 🔍 一、场景说明
  • 🧱 二、架构图
  • ⚙️ 三、核心配置
  • 🗃️ 四、数据库建表SQL
  • 👨‍💻 五、关键代码
  • 🧪 六、测试验证
  • 🧠 七、总结与建议

🔍 一、场景说明

🚨 实际项目中,数据库面临两类瓶颈:

  • 📌 数据量太大 → 单库单表撑不住 → 使用 分库分表 拆解压力
  • 📌 读操作压力太大 → 单库处理不过来 → 使用 读写分离 转移压力

⚡ 本项目整合了这两类方案,构建如下特性系统:

  • 两个逻辑库 ds0ds1
  • ds0 搭建一主两从(主库:ds0,从库:ds0_slave1,ds0_slave2)
  • 表按照用户 ID 分片(user_id % 2)
  • 主写从读,轻松实现读写分离

🧱 二、架构图

在这里插入图片描述

📝 说明:

  • 用户通过 Controller 发起请求
  • ShardingSphere JDBC 根据操作类型选择库、表
  • 如果是写入操作,走 ds0ds1 的主库
  • 如果是读取操作,优先走 ds0_slave1ds0_slave2 从库,减轻主库压力

⚙️ 三、核心配置(application.yml)

🧩 我们通过配置 ShardingSphere 的两类规则:shardingreadwrite-splitting 实现业务目标。

spring:shardingsphere:datasource:names: ds0, ds1, ds0_slave1, ds0_slave2ds0:type: com.zaxxer.hikari.HikariDataSourcejdbc-url: jdbc:mysql://mysql-master:3307/testdb?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=trueusername: rootpassword: rootds0_slave1:type: com.zaxxer.hikari.HikariDataSourcejdbc-url: jdbc:mysql://mysql-slave1:3308/testdb?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=trueusername: rootpassword: rootds0_slave2:type: com.zaxxer.hikari.HikariDataSourcejdbc-url: jdbc:mysql://mysql-slave2:3309/testdb?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=trueusername: rootpassword: rootds1:type: com.zaxxer.hikari.HikariDataSourcejdbc-url: jdbc:mysql://mysql-master:3306/testdb?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=trueusername: rootpassword: root123456rules:sharding:tables:t_user:# 注意:此处使用物理库名(ds0、ds1)来定义 actual-data-nodesactual-data-nodes: ds$->{0..1}.t_user_$->{0..1}database-strategy:standard:sharding-column: user_idsharding-algorithm-name: database_inlinetable-strategy:standard:sharding-column: user_idsharding-algorithm-name: table_inlinekey-generate-strategy:column: idkey-generator-name: snowflakesharding-algorithms:database_inline:type: INLINEprops:algorithm-expression: ds${user_id % 2}table_inline:type: INLINEprops:algorithm-expression: t_user_${user_id % 2}key-generators:snowflake:type: SNOWFLAKEreadwrite-splitting:data-sources:rw_ds0:static-strategy:write-data-source-name: ds0read-data-source-names: [ds0_slave1,ds0_slave2]load-balancer-name: round_robinrw_ds1:static-strategy:write-data-source-name: ds1read-data-source-names: [ds1]load-balancer-name: round_robinload-balancers:round_robin:type: ROUND_ROBINprops:sql-show: truelogging:level:org.apache.shardingsphere: DEBUG

1. 数据源配置 (datasource)

定义了 4 个 MySQL 数据源(2 个主库 ds0/ds1 和 2 个从库 ds0_slave1/ds0_slave2)。

配置项说明
spring.shardingsphere.datasource.names数据源名称列表:ds0, ds1, ds0_slave1, ds0_slave2
ds0.jdbc-url主库 ds0 的 JDBC 连接 URL(端口 3307)
ds0.username主库 ds0 用户名(root
ds0.password主库 ds0 密码(root
ds0_slave1.jdbc-url从库 ds0_slave1 的 JDBC URL(端口 3308)
ds0_slave2.jdbc-url从库 ds0_slave2 的 JDBC URL(端口 3309)
ds1.jdbc-url主库 ds1 的 JDBC URL(端口 3306,与 ds0 不同实例)

2. 分片规则 (sharding)

配置表 t_user 的分片策略和分布式 ID 生成。

分片表配置
配置项作用示例值说明
tables.t_user.actual-data-nodes定义物理节点ds$->{0..1}.t_user_$->{0..1}表达式生成所有物理表,如 ds0.t_user_0ds1.t_user_1
tables.t_user.database-strategy.standard.sharding-column分库列user_id根据 user_id 计算数据存储的库。
tables.t_user.database-strategy.standard.sharding-algorithm-name分库算法名称database_inline引用 sharding-algorithms 中定义的算法。
tables.t_user.table-strategy.standard.sharding-column分表列user_id根据 user_id 计算数据存储的表。
tables.t_user.table-strategy.standard.sharding-algorithm-name分表算法名称table_inline引用 sharding-algorithms 中定义的算法。
tables.t_user.key-generate-strategy.column主键列id指定自动生成主键的列。
tables.t_user.key-generate-strategy.key-generator-name主键生成器名称snowflake使用 Snowflake 算法生成分布式 ID。
分片算法
配置项作用示例值说明
sharding-algorithms.database_inline.type算法类型INLINE使用行表达式(Inline)分片算法。
sharding-algorithms.database_inline.props.algorithm-expression分库表达式ds${user_id % 2}根据 user_id % 2 计算库索引(0 或 1)。
sharding-algorithms.table_inline.type算法类型INLINE使用行表达式分片算法。
sharding-algorithms.table_inline.props.algorithm-expression分表表达式t_user_${user_id % 2}根据 user_id % 2 计算表索引(0 或 1)。
分布式 ID
配置项作用示例值说明
key-generators.snowflake.type主键生成器类型SNOWFLAKE使用 Snowflake 算法生成分布式唯一 ID。

3. 读写分离规则 (readwrite-splitting)

配置读写分离数据源和负载均衡策略。

数据源 rw_ds0
配置项说明
write-data-source-name写库数据源:ds0(主库)
read-data-source-names读库数据源列表:[ds0_slave1, ds0_slave2](两个从库)
load-balancer-name负载均衡算法:round_robin(轮询)
数据源 rw_ds1
配置项说明
write-data-source-name写库数据源:ds1(主库)
read-data-source-names读库数据源列表:([ds1]),表示仅使用写库读
load-balancer-name负载均衡算法:round_robin(未实际生效)
负载均衡器
配置项说明
round_robin.type算法类型:ROUND_ROBIN(轮询调度)

4. 属性配置 (props)

配置项说明
sql-show: true打印 SQL 日志(便于调试)

配置逻辑总结

spring:shardingsphere:datasource:names: ds0, ds1, ds0_slave1, ds0_slave2
  • 定义三个数据源

    • ds0: 主库
    • ds0_slave: 从库(ds0 复制)
    • ds1: 第二个分片主库
    rules:sharding:tables:t_user:actual-data-nodes: rw_ds$->{0..1}.t_user_$->{0..1}
  • t_user 分片规则:共有 2 库 x 2 表 结构
            table-strategy:standard:sharding-column: user_idsharding-algorithm-name: user_inline
  • 按照 user_id 做分片(水平拆表)
        sharding-algorithms:user_inline:type: INLINEprops:algorithm-expression: t_user_${user_id % 2}
  • 表名后缀 = user_id % 2,如 t_user_0t_user_1

      readwrite-splitting:data-sources:rw_ds0:static-strategy:write-data-source-name: ds0read-data-source-names: [ds0_slave1,ds0_slave1]
  • 配置 rw_ds0 为读写分离库
  • ds0 负责写入,ds0_slave1,ds0_slave2 负责读取
          rw_ds1:static-strategy:write-data-source-name: ds1read-data-source-names: [ds1]
  • ds1 暂无从库,只支持主库写读

潜在问题

  1. rw_ds1 无读库

    • 配置中 rw_ds1.read-data-source-namesds1,可能导致读请求全部发往主库 ds1,增加压力。
  2. 分片与读写分离结合

    • 实际数据节点 rw_ds0.t_user_0rw_ds1.t_user_1 的分片逻辑需确保数据均匀分布。

🗃️ 四、数据库建表SQL

db0db1 上执行以下语句,创建两个分表:

CREATE TABLE t_user_0 (id BIGINT PRIMARY KEY,username VARCHAR(100),user_id INT
);CREATE TABLE t_user_1 LIKE t_user_0;

📌 ds0_slave1, ds0_slave2 是主库 ds0 的复制库,MySQL 自动同步,无需手动建表。


👨‍💻 五、关键代码

✅ 项目概览

sharding-demo/
├── src/
│   ├── main/
│   │   ├── java/com/example/shardingdemo/
│   │   │   ├── controller/
│   │   │   ├── entity/
│   │   │   ├── mapper/
│   │   │   ├── ShardingDemoApplication.java
│   └── resources/
│       ├── application.yml
├── pom.xml

✅ User 实体类

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_user")
public class User {private Long id;private String username;private Integer userId; // 分片键
}

✅ Mapper 接口

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.shardingdemo.entity.User;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface UserMapper extends BaseMapper<User> {}

✅ Controller 控制器

import com.example.shardingdemo.entity.User;
import com.example.shardingdemo.mapper.UserMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;import java.util.List;@RestController
@RequestMapping("/user")
@RequiredArgsConstructor
public class UserController {private final UserMapper userMapper;@PostMapping("/add")public String addUser(@RequestParam String name, @RequestParam Integer userId) {User user = new User(System.currentTimeMillis(), name, userId);userMapper.insert(user);return "User added.";}@GetMapping("/list")public List<User> selectList() {return userMapper.selectList(null);}@GetMapping("/selectById")public User selectById(Long id){return userMapper.selectById(id);}}

🧪 六、测试验证

✅ 添加用户

http://localhost:8080/user/add?name=Alice&userId=11

控制台输出:

Actual SQL: rw_ds1 ::: INSERT INTO t_user_1 ...

✅ 说明:

  • userId = 11,落到 rw_ds1(ds1)
  • 且表名为 t_user_1,符合 % 2 = 1 的路由逻辑

🧠 七、总结与建议

特性说明
💡 分库分表扩展写能力,解决单表瓶颈
💡 读写分离减轻主库压力,提高系统吞吐
✅ 可扩展性新增库或表只需扩展路由规则,无需修改业务代码
✅ 高性能多线程批量插入、查询等场景提升明显
🔒 安全性通过主从架构,规避读阻塞或死锁导致系统不可用

📢 建议配合 Docker + MySQL 主从配置部署验证,效果最佳。


❤️ 如果你觉得这篇文章有帮助:

  • 点赞 👍
  • 收藏 ⭐
  • 留言 💬

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/92449.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/92449.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

将普通用户添加到 Docker 用户组

这样可以避免每次使用 Docker 命令时都需要 sudo。以下是具体步骤&#xff1a;1. 创建 Docker 用户组&#xff08;如果尚未存在&#xff09; 默认情况下&#xff0c;安装 Docker 时会自动创建 docker 用户组。可以通过以下命令检查&#xff1a; groupadd docker&#xff08;如果…

Scrapy(一):轻松爬取图片网站内容​

目录 一、CrawlSpider 简介​ 二、实战案例&#xff1a;图片网站爬取​ 三、代码解析&#xff1a;核心组件详解​ 类定义&#xff1a; 2.核心属性&#xff1a;​ 3.爬取规则&#xff08;Rules&#xff09;&#xff1a;​ 4.数据提取方法&#xff08;parse_item&#xff09;…

使用 systemd 的原生功能来实现 Redis 的自动监控和重启,而不是依赖额外的脚本最佳实践方案

使用 systemd 的原生功能来实现 Redis 的自动监控和重启&#xff0c;而不是依赖额外的脚本最佳实践方案方案 1&#xff1a;配置 systemd 服务文件&#xff08;推荐&#xff09;1. 检查/创建 Redis 的 systemd 服务文件2. 配置关键参数&#xff08;覆盖配置示例&#xff09;3. 重…

Eclipse 代码模板

Eclipse 代码模板 引言 Eclipse 作为一款功能强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;深受广大开发者的喜爱。在编程过程中&#xff0c;使用代码模板可以大大提高开发效率&#xff0c;减少重复劳动。本文将详细介绍 Eclipse 代码模板的配置、使用方法以及一…

输电线路防外破声光预警装置 | 防山火/防钓鱼/防施工安全警示系统

在输电网络的安全保障中&#xff0c;外力破坏是一个不容忽视的问题&#xff0c;各类隐患可能对电力系统造成严重影响。TLKS-PMG-WP 输电线路声光防外破警示装置在应对这类挑战时&#xff0c;有着独特的技术表现&#xff0c;下面从功能和技术参数两方面进行详细介绍。核心功能解…

STM32——STM32CubeMX

总&#xff1a;STM32——学习总纲 一、简介 注意&#xff0c;非逻辑代码。 可兼容不同系列的STM32Cube固件包。 STM32Cube前置知识链接&#xff1a; STM32——HAL库 不可过多依赖&#xff0c;此工具只针对STM32芯片&#xff0c;类似英飞凌芯片无法配置。主要用于参考。 二、安…

Java NIO 核心原理与秋招高频面试题解析

一、NIO 概述Java NIO&#xff08;New I/O 或 Non-blocking I/O&#xff09;是 Java 1.4 引入的一套全新 I/O API&#xff0c;位于 java.nio 包下。NIO 提供了与传统 BIO&#xff08;Blocking I/O&#xff09;完全不同的 I/O 处理方式&#xff0c;通过非阻塞模式、缓冲区&#…

vue3+element-plus,el-popover实现筛选弹窗的方法

实现一个筛选框&#xff0c;点击筛选按钮出现弹窗&#xff0c;弹窗内有选择框/输入框/单选框等等&#xff0c;底部有重置/确定两个按钮。需求&#xff1a;点击筛选外部其他位置可以关闭弹窗&#xff0c;关闭弹窗后已编辑的数据不保存&#xff0c;点击确定按钮关闭弹窗&#xff…

python每日一题 贪心算法练习

在一条环路上有 n 个加油站&#xff0c;其中第 i 个加油站有汽油 gas[i] 升。你有一辆油箱容量无限的的汽车&#xff0c;从第 i 个加油站开往第 i1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发&#xff0c;开始时油箱为空。给定两个整数数组 gas 和 cost &…

Python + Pika RabbitMQ集群压测完整方案

一、最近搭建了个rabbitmq集群 三个磁盘节点&#xff0c;上生产环境之前想做个压测&#xff0c;测试下稳定性&#xff0c;参考Deepseek做了如下测试方案二、核心代码实现&#xff1a; 配置文件 (config.py) import os RABBITMQ_NODES [amqp://admin:123456192.168.0.175:8101,…

【第7话:相机模型3】自动驾驶IPM图像投影拼接技术详解及代码示例

IPM图像投影拼接技术详解 IPM&#xff08;逆透视映射&#xff09;图像投影拼接技术是一种在计算机视觉中广泛应用的图像处理方法&#xff0c;主要用于将多个透视视图的图像转换为鸟瞰视图并拼接成一个无缝的大场景图像。该技术特别适用于自动驾驶、机器人导航和监控系统等领域&…

【测试工程思考】测试自动化基础能力建设

1 回顾 传统软件研发体系下定义的软件测试是从用户视角设计的。测试是试图穷尽用户行为的工程&#xff0c;从测试用例&#xff08;use case&#xff09;的英文定义就可见一般。测试的逻辑资产就是用自然语言去描述用户的操作行为或路径。 但随着软件工程向分布式架构和敏捷交付…

进阶向:AI聊天机器人(NLP+DeepSeek API)

什么是AI聊天机器人? AI聊天机器人是一种通过自然语言处理(NLP)技术模拟人类对话的智能程序系统。其核心是建立在机器学习算法和大型语言模型基础上的对话引擎,能够理解用户的自然语言输入,分析语境和意图,并生成符合上下文的相关回复。 这类机器人系统通常包含以下几个…

一个C#的段子

猜猜按钮的结果是啥。 public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { } public static bool flag true; privat…

使用 gptqmodel 量化 Qwen3-Coder-30B-A3B-Instruct

代码部分 : quantize_qwen3_coder_30b_a3b_instruct_gptq.py import os########## 环境变量设置 ########## # 当前可用的 CUDA 编号 os.environ["CUDA_VISIBLE_DEVICES"] "1" # GPU 显存资源片段优化 os.environ["PYTORCH_CUDA_ALLOC_CONF"] …

基于python、django的疫苗接种管理系统

基于python、django的疫苗接种管理系统

Go语言实战案例:使用sync.Map构建线程安全map

在并发编程中&#xff0c;共享资源的访问是一个绕不开的问题。Go 中的 map 在并发读写时是不安全的&#xff0c;直接使用可能导致程序 panic。因此&#xff0c;在多协程同时访问 Map 的场景下&#xff0c;必须采取有效的同步措施。本篇将通过一个实战案例&#xff0c;介绍 Go 的…

关于vue2中对接海康摄像头以及直播流rtsp或rtmp,后台ffmpeg转码后通过ws实现

最近项目中需要对接摄像头监控&#xff0c;海康摄像头为rtsp流格式有一个软件VLC media player&#xff0c;可以在线进行rtsp或者rtmp流播放&#xff0c;可用来测试流地址是否可用功能实现思路为后台通过fmpeg把rtsp流进行转码&#xff0c;然后通过ws方式进行一帧一帧推送。&am…

Docker容器强制删除及文件系统修复完整指南

Docker容器强制删除及文件系统修复完整指南 故障现象与原因分析 ​故障表现​&#xff1a; ERROR: for c9ca40be974d_OpIsosMD_OB unable to remove filesystem unlinkat /data/docker/storage/containers/c9ca40be974d...: structure needs cleaning​根本原因​&#xff1a;…

Matplotlib 知识点总结

1. 基础绘图&#xff08;plot函数&#xff09;基本语法&#xff1a;plot([x], y, [fmt], [x2], y2, [fmt2], ..., **kwargs)功能特点&#xff1a;可绘制点、线和组合图形自动生成x轴&#xff08;0-N-1&#xff09;当x未指定时示例&#xff1a;绘制两点连线、多点不规则线等代码…