【服务实现读写分离】

文章目录

  • 什么是读写分离
  • 基于Spring实现实现读写分离
  • 项目中常用的数据源切换依赖包

什么是读写分离

服务读写分离(Service Read-Write Splitting)是一种常见的数据库架构设计模式,旨在提高系统的性能和可扩展性。通过将读操作和写操作分离到不同的数据库实例上,可以减轻单个数据库实例的负载,提高整体系统的响应速度和可靠性。
核心思想
写操作:所有的写操作(插入、更新、删除)都发送到主数据库(Master)。
读操作:所有的读操作(查询)都发送到从数据库(Slave)。
主要步骤
主从复制:配置一个主数据库和一个或多个从数据库,从数据库实时同步主数据库的数据更新。
路由层(Routing Layer):在应用程序层或通过中间件(如代理服务器)实现读写请求的路由。写请求路由到主数据库,读请求路由到从数据库。
数据一致性:保证数据在主数据库和从数据库之间的一致性,通常使用同步或异步复制策略。

基于Spring实现实现读写分离

在这里插入图片描述

Spring实现应用层实现读写分离,是基于AbstractRoutingDataSource
来实现。
AbstractRoutingDataSource是基于特定的查找key路由到特定的数据源。它内部维护了一组目标数据源,并且做了路由key与目标数据源之间的映射,提供基于key查找数据源的方法。

代码实现

//实现数据源动态切换的核心代码
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.lang.Nullable;
public class MyRoutingDataSource extends AbstractRoutingDataSource {@Nullable@Overrideprotected Object determineCurrentLookupKey() {return DBContextHolder.get();}
}

利用ThreadLocal获取存储或获取数据源

package com.zqtest.config;import java.util.concurrent.atomic.AtomicInteger;public class DBContextHolder {private static final ThreadLocal<DBTypeEnum> contextHolder=new ThreadLocal<DBTypeEnum>();private static final AtomicInteger counter=new AtomicInteger(-1);public static void set(DBTypeEnum dbTypeEnum){contextHolder.set(dbTypeEnum);}public static DBTypeEnum get(){return contextHolder.get();}public static void master(){set(DBTypeEnum.MASTER);System.out.println("切换到Master");}public static void slave(){//从节点进行轮训int index=counter.getAndIncrement()%2;if(counter.get()>9999){counter.set(-1);}if(index==0){set(DBTypeEnum.SLAVE1);System.out.println("切换到slave1");}if(index==1){set(DBTypeEnum.SLAVE2);System.out.println("切换到slave2");}}
}

数据源注册

package com.zqtest.config;import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;@Configuration
public class DataSourceConfig {@Bean@ConfigurationProperties("spring.datasource.master")public DataSource masterDataSource(){return DataSourceBuilder.create().build();}@Bean@Primary@ConfigurationProperties("spring.datasource.slave1")public DataSource slave1DataSource() {return DataSourceBuilder.create().build();}@Bean@ConfigurationProperties("spring.datasource.slave2")public DataSource slave2DataSource() {return DataSourceBuilder.create().build();}@Beanpublic DataSource myRoutingDataSource( DataSource masterDataSource,DataSource slave1DataSource,DataSource slave2DataSource) {Map<Object, Object> targetDataSources = new HashMap<Object, Object>();targetDataSources.put(DBTypeEnum.MASTER, masterDataSource);targetDataSources.put(DBTypeEnum.SLAVE1, slave1DataSource);targetDataSources.put(DBTypeEnum.SLAVE2, slave2DataSource);MyRoutingDataSource myRoutingDataSource = new MyRoutingDataSource();myRoutingDataSource.setDefaultTargetDataSource(slave1DataSource);myRoutingDataSource.setTargetDataSources(targetDataSources);return myRoutingDataSource;}}
spring:application:name: testdatasource:master:url: jdbc:mysql://ip1:3306/databaseusername: usernamepassword: passworddriver-class-name: com.mysql.jdbc.Driverslave1:url: jdbc:mysql://ip2:3306/databaseusername: username   # 只读账户password: passworddriver-class-name: com.mysql.jdbc.Driverslave2:url: jdbc:ip3:3306/databaseusername: username   # 只读账户password: passworddriver-class-name: com.mysql.jdbc.Driver
server:port: 8080

java枚举类


public enum DBTypeEnum {MASTER,SLAVE1,SLAVE2,
}

注意这里一定要加事务管理,防止代码出现多数据源问题。

import javax.annotation.Resource;
import javax.sql.DataSource;
@Configuration
@EnableTransactionManagement
public class MyBatiesConfig {@Resource(name="myRoutingDataSource")private DataSource myRoutingDataSource;@Beanpublic SqlSessionFactory sqlSessionFactory ()throws Exception{SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(myRoutingDataSource);sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));return sqlSessionFactoryBean.getObject();}@Beanpublic PlatformTransactionManager platformTransactionManager(){return new DataSourceTransactionManager(myRoutingDataSource);}
}

AOP的实现
注解实现

public @interface Master {
}

核心点

package com.zqtest.aop;import com.zqtest.config.DBContextHolder;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;@Aspect
@Component
public class DataSourceAop {@Pointcut("!@annotation(com.zqtest.annotation.Master) " +"&& (execution(* com.zqtest.service..*.select*(..)) " +"|| execution(* com.zqtest.service..*.get*(..)))")public void readPointcut() {}@Pointcut("@annotation(com.zqtest.annotation.Master) " +"|| execution(* com.zqtest.service..*.insert*(..)) " +"|| execution(* com.zqtest.service..*.add*(..)) " +"|| execution(* com.zqtest.service..*.update*(..)) " +"|| execution(* com.zqtest.service..*.edit*(..)) " +"|| execution(* com.zqtest.service..*.delete*(..)) " +"|| execution(* com.zqtest.service..*.remove*(..))")public void writePointcut() {}@Before("readPointcut()")public void read(){DBContextHolder.slave();}@Before("writePointcut()")public void write(){DBContextHolder.master();}
}

项目中常用的数据源切换依赖包

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

dynamic-datasource-spring-boot-starter 是一个基于springboot的快速集成多数据源的启动器。
其支持 Jdk 1.7+, SpringBoot 1.4.x 1.5.x 2.x.x。
特性
1.支持 数据源分组 ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。
2.支持数据库敏感配置信息 加密 ENC()。
3.支持每个数据库独立初始化表结构schema和数据库database。
4.支持无数据源启动,支持懒加载数据源(需要的时候再创建连接)。
支持 自定义注解 ,需继承DS(3.2.0+)。
5.提供并简化对Druid,HikariCp,BeeCp,Dbcp2的快速集成。
6.提供对Mybatis-Plus,Quartz,ShardingJdbc,P6sy,Jndi等组件的集成方案。
7.提供 自定义数据源来源 方案(如全从数据库加载)。
8.提供项目启动后 动态增加移除数据源 方案。
9.提供Mybatis环境下的 纯读写分离 方案。
10.提供使用 spel动态参数 解析数据源方案。内置spel,session,header,支持自定义。
支持 多层数据源嵌套切换 。(ServiceA >>> ServiceB >>> ServiceC)。
11.提供 基于seata的分布式事务方案。
12.提供 本地多数据源事务方案。 附:不能和原生spring事务混用。
约定
1.本框架只做 切换数据源 这件核心的事情,并不限制你的具体操作,切换了数据源可以做任何CRUD。
2.配置文件所有以下划线 _ 分割的数据源 首部 即为组的名称,相同组名称的数据源会放在一个组下。
3.切换数据源可以是组名,也可以是具体数据源名称。组名则切换时采用负载均衡算法切换。
4.默认的数据源名称为 master ,你可以通过 spring.datasource.dynamic.primary 修改。
5.方法上的注解优先于类上注解。DS支持继承抽象类上的DS,暂不支持继承接口上的DS。
快速配置数据源:
1.引入dynamic-datasource-spring-boot-starter。

<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 切换数据源。
没有@DS 默认数据源
@DS(“dsName”) dsName可以为组名也可以为具体某个库的名称

@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");}
}

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

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

相关文章

javaspringbootmysql小程序的竞赛管理系统71209-计算机毕业设计项目选题推荐(附源码)

摘 要 随着社会的发展,社会的方方面面都在利用信息化时代的优势。互联网的优势和普及使得各种系统的开发成为必需。 本文以实际运用为开发背景, 运用软件工程原理和开发方法,它主要是采用java语言技术和mysql数库来完成对系统的设计。整个开发过程首先对竞赛管理系统进行需求分…

OceanBase 4.3 特性解析:列存技术

在涉及大规模数据的复杂分析或即时查询时&#xff0c;列式存储是支撑业务负载的关键技术之一。相较于传统的行式存储&#xff0c;列式存储采用了不同的数据文件组织方式&#xff0c;它将表中的数据以列为单位进行物理排列。这种存储模式允许在分析过程中&#xff0c;查询计算仅…

mmdetection使用未定义backbone训练

首先找到你需要用到的 backbone&#xff0c;一般有名的backbone 都会在github有相应的代码开源和预训练权重提供 本文以mobilenetv3 fastercnn 作为举例&#xff0c;在mmdetection中并未提供 mobilenetv3&#xff0c;提供的仅有 mobilenetv2&#xff1b; 在github上找到 mobil…

高性能MySQL(第3版)电子书笔记

Mysql官方文档&#xff1a;https://dev.mysql.com/doc/refman/5.7/en/ 高性能MySQL&#xff08;第3版&#xff09;&#xff1a;百度网盘&#xff0c;基于Mysql5.1和Mysql5.5 本机版本 mysql> select version(); ------------ | version() | ------------ | 5.7.32-log |…

Linux 网络设置

Linux 网络设置 查看及测试网络查看网络配置测试网络连接 设置网络地址参数使用网络配置命令修改网络配置文件 查看及测试网络 查看及测试网络配置是管理 Linux 网络服务的第一步,本节将学习 Linux 操作系统中的网络查看及测试命令。其中讲解的大多数命令以普通用户权限就可以…

【ppyoloe+】19届智能车完全模型组非官方基线

基于十九届智能车百度完全模型组线上赛baseline修改 调整参数最高能到0.989吧 一、环境准备 1.安装PaddleDetection In [1] # 解压PaddleDetection压缩包 %cd /home/aistudio/data/data267567 !unzip -q PaddleDetection-release-2.6.zip -d /home/aistudio /home/aistud…

初识C++ · 反向迭代器简介

目录 前言 反向迭代器的实现 前言 继模拟实现了list和vector之后&#xff0c;我们对迭代器的印象也是加深了许多&#xff0c;但是我们实现的都是正向迭代器&#xff0c;还没有实现反向迭代器&#xff0c;那么为什么迟迟不实现呢&#xff1f;因为难吗&#xff1f;实际上还好。…

stm32MP135裸机编程:修改官方GPIO例程在DDR中点亮第一颗LED灯

0 参考资料 轻松使用STM32MP13x - 如MCU般在cortex A核上裸跑应用程序.pdf 正点原子stm32mp135开发板&原理图 STM32Cube_FW_MP13_V1.1.0 STM32CubeIDE v1.151 需要修改那些地方 1.1 修改LED引脚 本例使用开发板的PI3引脚链接的LED作为我们点亮的第一颗LED灯&#xff0c;…

AC/DC电源模块的原理、特点以及其在实际应用中的重要性

BOSHIDA AC/DC电源模块的原理、特点以及其在实际应用中的重要性 AC/DC电源模块是一种用于将交流电转换为直流电的设备&#xff0c;广泛应用于各种电子设备中。这种电源模块可以有效地将电力从电网中提取出来&#xff0c;并将其转换为稳定的直流电源&#xff0c;供给各种不同功…

容器(Docker)安装

centos安装Docker sudo yum remove docker* sudo yum install -y yum-utils#配置docker的yum地址 sudo yum-config-manager \ --add-repo \ http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo#安装指定版本 - 可以根据实际安装版本 sudo yum install -y docke…

12个精选Prompt框架,快速提升你写Prompt的能力,内附实例(上篇)

前言 想要熟练驾驭大模型&#xff0c;除了掌握Prompt的原则和技巧外&#xff0c;我们还可以参考一些成熟的Prompt框架&#xff0c;这样能快速提升我们写Prompt的能力&#xff0c;我从网上搜集到了12个精选Prompt框架&#xff0c;并为每一个框架附上一个实际的例子&#xff0c;…

何为屎山代码?

在编程界&#xff0c;有一种代码被称为"屎山代码"。这并非指某种编程语言或方法&#xff0c;而是对那些庞大而复杂的项目的一种形象称呼。屎山代码&#xff0c;也被称为"祖传代码"&#xff0c;是历史遗留问题&#xff0c;是前人留给我们的"宝藏"…

性能测试2【搬代码】

1.性能测试脚本完善以及增强 2.jmeter插件安装以及监控使用 3.性能压测场景设置&#xff08;基准、负载、压力、稳定性&#xff09; 4. 无界面压测场景详解 一、性能测试脚本完善以及增强 使用控制器的目的是使我们的脚本更加接近真实的场景 1.逻辑控制器: 【事务控制器】&…

电商API接口接入||电商比价项目比价系统搭建需要注意哪些?

在搭建一个淘宝/京东比价系统时&#xff0c;需要注意以下几个方面&#xff0c;以确保系统的有效性、准确性和用户友好性&#xff1a; 确定平台和商品范围&#xff1a; 明确系统覆盖的电商平台&#xff0c;如淘宝、京东等。确定要比较的商品类别和范围&#xff0c;以确保数据的…

Maven环境搭建

&#x1f4bb;博主现有专栏&#xff1a; C51单片机&#xff08;STC89C516&#xff09;&#xff0c;c语言&#xff0c;c&#xff0c;离散数学&#xff0c;算法设计与分析&#xff0c;数据结构&#xff0c;Python&#xff0c;Java基础&#xff0c;MySQL&#xff0c;linux&#xf…

【Vue】核心概念 - module

目标 掌握核心概念 module 模块的创建 问题 由于使用单一状态树&#xff0c;应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时&#xff0c;store 对象就有可能变得相当臃肿。 这句话的意思是&#xff0c;如果把所有的状态都放在state中&#xff0c;当项目变得…

表的设计与查询

目录 一、表的设计 1.第一范式&#xff08;一对一&#xff09; 定义&#xff1a; 示例&#xff1a; 2.第二范式&#xff08;一对多&#xff09; 定义&#xff1a; 要求&#xff1a; 示例&#xff1a; 3.第三范式&#xff08;多对多&#xff09; 定义&#xff1a; 要求…

Selenium三种等待方式的使用!

UI自动化测试&#xff0c;大多都是通过定位页面元素来模拟实际的生产场景操作。但在编写自动化测试脚本中&#xff0c;经常出现元素定位不到的情况&#xff0c;究其原因&#xff0c;无非两种情况&#xff1a;1、有frame&#xff1b;2、没有设置等待。 因为代码运行速度和浏览器…

如何有效释放Docker占用的存储空间

随着Docker的广泛应用&#xff0c;我们经常会遇到Docker占用过多存储空间的问题。这可能是由于频繁的镜像拉取、容器创建和删除等操作导致的。本文将介绍几种方法来有效释放Docker占用的存储空间&#xff0c;特别是docker system prune命令的使用。 Docker的存储机制 Docker使…

体验SmartEDA:颠覆传统,设计流程更流畅,超越Multisim与Proteus!

在电子设计自动化&#xff08;EDA&#xff09;领域&#xff0c;传统软件如Multisim和Proteus一直是工程师们的得力助手。然而&#xff0c;随着科技的飞速发展和用户需求的不断升级&#xff0c;一个全新的EDA平台——SmartEDA正崭露头角&#xff0c;凭借其更为流畅的设计流程&am…