当前位置: 首页 > news >正文

开发经验总结: 读写分离简单实现

背景

file

使用mysql的代理中间件,某些接口如果主从同步延迟大,容易出现逻辑问题。所以程序中没有直接使用这个中间件。

依赖程序逻辑,如果有一些接口可以走读库,需要一个可以显示指定读库的方式来连接读库,降低主库的压力。

所以需要一个能配置多个数据源,通过注解或者代码方式设置读还是写数据源的方法。

实现方式

使用MybatisPlus的多数据源切换的能力。

github地址: https://github.com/baomidou/dynamic-datasource

已经封装好了。直接使用就行。

介绍

dynamic-datasource-spring-boot-starter 是一个基于springboot的快速集成多数据源的启动器。

其支持 Jdk 1.7+, SpringBoot 1.5.x 2.x.x 3.x.x。 JPA用户不建议使用,JPA自带事务,无法连续切库。

特性:

  • 支持 数据源分组 ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。
  • 支持数据库敏感配置信息 加密(可自定义) ENC()。
  • 支持每个数据库独立初始化表结构schema和数据库database。
  • 支持无数据源启动,支持懒加载数据源(需要的时候再创建连接)。
  • 支持 自定义注解 ,需继承DS(3.2.0+)。
  • 提供并简化对Druid,HikariCp,BeeCp,Dbcp2的快速集成。
  • 提供对Mybatis-Plus,Quartz,ShardingJdbc,P6sy,Jndi等组件的集成方案。
  • 提供 自定义数据源来源 方案(如全从数据库加载)。
  • 提供项目启动后 动态增加移除数据源 方案。
  • 提供Mybatis环境下的 纯读写分离 方案。
  • 提供使用 spel动态参数 解析数据源方案。内置spel,session,header,支持自定义。
  • 支持 多层数据源嵌套切换 。(ServiceA >>> ServiceB >>> ServiceC)。
  • 提供 基于seata的分布式事务方案
  • 提供 本地多数据源事务方案。

我们需要的是纯读写分离 方案,它已经内置了。

功能范围:

  1. 本框架只做 切换数据源 这件核心的事情,并不限制你的具体操作,切换了数据源可以做任何CRUD。
  2. 配置文件所有以下划线 分割的数据源 首部 即为组的名称,相同组名称的数据源会放在一个组下。
  3. 切换数据源可以是组名,也可以是具体数据源名称。组名则切换时采用负载均衡算法切换。
  4. 默认的数据源名称为 master ,你可以通过 spring.datasource.dynamic.primary 修改。
  5. 方法上的注解优先于类上注解。
  6. DS支持继承抽象类上的DS,暂不支持继承接口上的DS。

使用步骤

1.引入依赖。

<dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>${version}</version>
</dependency>

2.配置数据源

spring:datasource:dynamic:primary: master #设置默认的数据源或者数据源组,默认值即为masterstrict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源datasource:master:url: jdbc:mysql://xx.xx.xx.xx:3306/dynamicusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置slave_1:url: jdbc:mysql://xx.xx.xx.xx:3307/dynamicusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driverslave_2:url: ENC(xxxxx) # 内置加密,使用请查看详细文档username: ENC(xxxxx)password: ENC(xxxxx)driver-class-name: com.mysql.jdbc.Driver#......省略#以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2

3.使用注解切换数据源。

@DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解

file

@Service
@DS("slave")
public class UserServiceImpl implements UserService {@Autowiredprivate JdbcTemplate jdbcTemplate;public List selectAll() {return  jdbcTemplate.queryForList("select * from user");}@Override@DS("slave_1")public List selectByCondition() {return  jdbcTemplate.queryForList("select * from user where age >10");}
}

业务集成

我们的程序使用的经典的三层结构,即MVC .

画板

看到我们的框架是已经集成了dynamicSource的。如果没有,则加一下依赖即可。

以crm服务为例子。写了一些测试代码,进行验证测试。

package com.pig4cloud.pigx.crm.controller;import com.baomidou.dynamic.datasource.annotation.DS;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.security.annotation.Inner;
import com.pig4cloud.pigx.crm.mapper.AiAgentMapper;
import com.pig4cloud.pigx.crm.service.AiAgentService;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;@RestController
@RequiredArgsConstructor
@Tag(description = "test read write", name = "测试读写分离")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
@Valid
@Slf4j
public class TestDsController {private final AiAgentMapper aiAgentMapper;private final AiAgentService aiAgentService;@GetMapping("/testD")@ResponseBody//@Inner(value = false)public R testD() {return R.ok(aiAgentMapper.selectList(null));}@GetMapping("/testRead")@ResponseBody//@Inner(value = false)@DS("read")public R testRead() {return R.ok(aiAgentMapper.selectList(null));}@GetMapping("/testMaster")@ResponseBody//@Inner(value = false)@DS("master")public R testMaster() {return R.ok(aiAgentMapper.selectList(null));}@GetMapping("/testHe")@ResponseBody//@Inner(value = false)@DS("master")public R testHe() {return R.ok(aiAgentService.listTest());}@GetMapping("/testHe2")@ResponseBody//@Inner(value = false)@DS("read")public R testHe2() {return R.ok(aiAgentService.listTest2());}@GetMapping(value = "/testHe3",produces = "application/json")@ResponseBody//@Inner(value = false)public R testHe3() {Map<String, Object> dataMap = new HashMap<>();dataMap.put("read", aiAgentService.listTest());dataMap.put("master", aiAgentService.listTest2());return R.ok(dataMap);}}

对应的Service实现代码:

@Service
@Slf4j
@RequiredArgsConstructor
public class AiAgentServiceImpl extends ServiceImpl<AiAgentMapper, AiAgentEntity> implements AiAgentService {@Override@DS("read")public List<AiAgentEntity> listTest() {return baseMapper.selectList(null);}@Override@DS("master")public List<AiAgentEntity> listTest2() {return baseMapper.selectList(null);}
}

测试用例和结果:

测试接口和场景测试说明预期结果测试结果
/testD不加任何注解走主库- [x] 走主库 ![]file
/testRead添加了读库注解走读库- [x] 走读库file
/testMaster添加了主库注解走主库- [x] 走主库file
/testHe上层加主库注解,下层加读库注解走读库- [x] 走读库file
/testHe2上层加读库注解,下层加写库注解走主库- [x] 走主库 file
/testHe3在中层的方法分别加读库和写库注解分别走读库和主库- [x] 分别走读库和读库file

目前需要增加读写分离的服务。

服务增加读写分离的原因是否完成增加
crm后台列表查询页面, openAPI的读接口,直接走读库;- [x]
email后台列表查询页面, openAPI的读接口,直接走读库;- [ ]
phone后台列表查询页面, openAPI的读接口,直接走读库;- [ ]
ssm后台列表查询页面, openAPI的读接口,直接走读库;- [ ]
upms后台列表查询页面, openAPI的读接口,直接走读库;- [ ]

源码研究

3.6.0版本。基于这个进行分析。

1 自动装配类

file

配置属性:DynamicDataSourceProperties

属性说明
primary默认的库
strict严格检查
p6spy日志输出
seata是否开启seata
lazy是否懒加载数据源
setaModeAT模式
publicKey加密key
datasource数据源,可以单独配置;
strategy负载均衡策略
druid数据源
hikari数据源
beecp数据源
dbcp2数据源
aopaop配置,有默认值

DataSourceProperty

属性说明说明
poolName连接池名称
type连接类型
driverClassName驱动名称
url连接url
username用户名
password密码
jndiNamejndi中的名称
init初始化配置
p6spy日志输出
seata是否开启seata
lazy是否懒加载数据源
publicKey加密key
druid数据源
hikari数据源
beecp数据源
dbcp2数据源

DatasourceInitProperties

属性名说明
schema建表脚本
data数据脚本
continueOnError错误是否继续
separator分隔符

2 自动装配逻辑

file

基于切面实现。

小结

集成使用了mybatisPlus自带的多数据源实现了读写分离,并探究了源码实现过程。

详细过程可以看我的视频号。

原创不易,关注诚可贵,转发价更高!转载请注明出处,让我们互通有无,共同进步,欢迎沟通交流。

相关文章:

开发经验总结: 读写分离简单实现

背景 使用mysql的代理中间件&#xff0c;某些接口如果主从同步延迟大&#xff0c;容易出现逻辑问题。所以程序中没有直接使用这个中间件。 依赖程序逻辑&#xff0c;如果有一些接口可以走读库&#xff0c;需要一个可以显示指定读库的方式来连接读库&#xff0c;降低主库的压力…...

MySQL(面试题 - 同类型归纳面试题)

目录 一、MySQL 数据类型 1. 数据库存储日期格式时&#xff0c;如何考虑时区转换问题&#xff1f; 2. Blob和text有什么区别&#xff1f; 3. mysql里记录货币用什么字段类型比较好&#xff1f; 4. MySQL如何获取当前日期&#xff1f; 5. 你们数据库是否支持emoji表情存储&…...

【C++ Primer Plus习题】17.7

问题: 解答: #include <iostream> #include <vector> #include <string> #include <fstream> #include <algorithm>using namespace std;const int LIMIT 50;void ShowStr(const string& str); void GetStrs(ifstream& fin, vector<…...

vue3(整合版)

创建第一个vue项目 1.安装node.js cmd输入node查看是否安装成功 2.vscode开启一个终端&#xff0c;配置淘宝镜像 # 修改为淘宝镜像源 npm config set registry https://registry.npmmirror.com 输入如下命令创建第一个Vue项目 3.下载依赖&#xff0c;启动项目 访问5173端口 …...

复制他人 CSDN 文章到自己的博客

文章目录 0.前言步骤 0.前言 在复制别人文章发布时&#xff0c;记得表明转载哦 步骤 在需要复制的csdn 文章页面&#xff0c;打开浏览器开发者工具&#xff08;F12&#xff09;Ctrl F 查找"article_content"标签头 右键“Copy”->“Copy element”新建一个 tx…...

【算法——二分查找】

理论基础&#xff1a; 程序员面试经典题&#xff0c;二分搜索一个区间&#xff0c;区间查找 (LeetCode 34)_哔哩哔哩_bilibili 手把手带你撕出正确的二分法 | 二分查找法 | 二分搜索法 | LeetCode&#xff1a;704. 二分查找_哔哩哔哩_bilibili 这个是红蓝法&#xff0c;很牛…...

Cisco Packet Tracer的安装加汉化

这个工具学计算机网络的同学会用到 1.下载安装 网盘链接&#xff1a;https://pan.baidu.com/s/1CmnxAD9MkCtE7pc8Tjw0IA 提取码&#xff1a;frkb 点击第一个进行安装&#xff0c;按步骤来即可。 2.汉化 &#xff08;1&#xff09;复制chinese.ptl文件 &#xff08;2&…...

MMain函数定义为WinMain函数看port1632.h和pwin32.h文件

编译win2k3的源代码的时候有时候看到MMain函数 ..//public/sdk/inc/port1632.h #if defined(WIN16) /* ---------------- Maps to windows 3.0 and 3.1 16-bit APIs ----------------*/ #include "ptypes16.h" #include "pwin16.h" #include "plan16.…...

单词搜索问题(涉及递归等)

目录 一题目&#xff1a; 二思路解释&#xff1a; 三解答代码&#xff1a; 一题目&#xff1a; newcode题目链接&#xff1a; 单词搜索_牛客题霸_牛客网 二思路解释&#xff1a; 思路&#xff1a;个人理解是找到word中的第一个元素&#xff0c;然后去递归的上下左右查找&am…...

Redis的一些通用指令

首先我们需要先连接客户端服务器&#xff0c;此时我们需要通过redis-cli和redis服务器进行交互&#xff0c;输入ping来确保通路的流畅 &#xff08;一&#xff09;get和set redis中最核心的两个命令就是get和set&#xff0c;get就是根据key来取出对应value&#xff0c;set就是把…...

C++中vector类的使用

目录 1.vector类常用接口说明 1.1默认成员函数 1.1.1构造函数(constructor) 1.1.2 赋值运算符重载(operator()) 2. vector对象的访问及遍历操作(Iterators and Element access) 3.vector类对象的容量操作(Capacity) 4. vector类对象的修改及相关操作(Modifiers and Stri…...

cmaklist流程控制——调试及发布

cmaklist流程控制 目前只会配置-编译调试-打包发布&#xff0c;并且不会workflow控制 后续学习配置-编译调试-测试-打包发布&#xff0c;workflow控制&#xff0c;理解整个流程&#xff0c;目前对流程控制理解也不够。 1.CMake Presets 先于Cmakelist文件&#xff0c;指导项…...

制作一个能对话能跳舞的otto机器人

OTTO机器人是一个开源外壳&#xff0c;硬件和软件的桌面机器人项目&#xff0c;非常适合新手研究和拓展。记住&#xff0c;他是一个能移动有表情能声音的机器人。 b站有很多演示和组装的视频&#xff0c;我就不多说了&#xff0c;照着做就好&#xff0c;因为硬件我也是刚入门&…...

git配置SSH

1 打开cmd窗口 2 在窗口中输入如下命令&#xff1a; 配置用户名&#xff1a; git config --global user.name “gyk” 配置邮箱&#xff1a; git config --global user.email “247929163qq.com” 继续在Git命令窗口中输入如下命令&#xff0c;即可生成SSH公钥和私钥 ss…...

mozilla/pdf.js view.html加载指定页码

mozilla/pdf.js view.html加载指定页码 在Mozilla’s PDF.js中&#xff0c;如果你想要在viewer.html加载时直接跳转到指定的页码&#xff0c;你可以通过修改URL来实现。 PDF.js使用查询参数来处理URL&#xff0c;其中page参数用于指定页码。你可以通过修改URL的查询字符串来设…...

Qt之QFuture理解

结构 #mermaid-svg-J9J683RG8QjtEqoM {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-J9J683RG8QjtEqoM .error-icon{fill:#552222;}#mermaid-svg-J9J683RG8QjtEqoM .error-text{fill:#552222;stroke:#552222;}#merm…...

求二叉树的高度(递归和非递归)

假设二叉树采用二叉链表存储结构&#xff0c;设计一个算法求二叉树的高度。 递归&#xff1a; int getTreeHight(BiTree T){if(TNULL){return 0;}else {int lh getTreeHight(T->lchild);int rh getTreeHight(T->rchild);return (lh>rh?lh:rh)1;}}时间复杂度O(n)&a…...

Java查找算法——(四)分块查找(完整详解,附有代码+案例)

文章目录 分块查找1.1普通分块查找 分块查找 1.1普通分块查找 分块原则&#xff1a; 块内无序&#xff0c;块间有序:前一块中的最大数据&#xff0c;小于后一块中所有的数据&#xff0c;块与块之间不能有数据重复的交集。块的数量一般等于数字个数开根号 核心思路&#xff…...

进制数知识(2)—— 浮点数在内存中的存储 和 易混淆的二进制知识总结

目录 1. 浮点数在内存中的存储 1.1 浮点数的大V表示法 1.2 浮点数的存储格式 1.3 浮点数的存入规则 1.4 浮点数的读取规则 1.5 补充&#xff1a;移码与掩码 1.6 题目解析 2. 易错的二进制知识 2.0 符号位到底会不会参与运算&#xff1f; 2.0.1 存储前的编码变化运算 …...

类似QQ聊天功能的Java程序

实现一个类似QQ聊天功能的Java程序需要考虑以下几个关键点&#xff1a; 用户界面&#xff1a;用于展示消息和输入消息。网络通信&#xff1a;用于客户端之间的信息传输。用户管理&#xff1a;用于管理用户的登录、注册和状态。消息存储&#xff1a;用于存储聊天记录。 这里提…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

逻辑回归:给不确定性划界的分类大师

想象你是一名医生。面对患者的检查报告&#xff08;肿瘤大小、血液指标&#xff09;&#xff0c;你需要做出一个**决定性判断**&#xff1a;恶性还是良性&#xff1f;这种“非黑即白”的抉择&#xff0c;正是**逻辑回归&#xff08;Logistic Regression&#xff09;** 的战场&a…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

以光量子为例,详解量子获取方式

光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学&#xff08;silicon photonics&#xff09;的光波导&#xff08;optical waveguide&#xff09;芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中&#xff0c;光既是波又是粒子。光子本…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...

【JVM】Java虚拟机(二)——垃圾回收

目录 一、如何判断对象可以回收 &#xff08;一&#xff09;引用计数法 &#xff08;二&#xff09;可达性分析算法 二、垃圾回收算法 &#xff08;一&#xff09;标记清除 &#xff08;二&#xff09;标记整理 &#xff08;三&#xff09;复制 &#xff08;四&#xff…...

解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist

现象&#xff1a; android studio报错&#xff1a; [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决&#xff1a; 不要动CMakeLists.…...

基于Java+VUE+MariaDB实现(Web)仿小米商城

仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意&#xff1a;运行前…...

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分&#xff1a; 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...