数据库的主从复制,读写分离

本文探讨了数据库优化的两种策略:读写分离和分库分表。通过将写操作集中在master服务器,读操作分散到多个slave服务器,可以提高并发读取效率并确保数据一致性。同时,分库分表能降低服务器压力,提升执行效率,实现数据库的高效管理。

目的:都是为了优化数据库

        比如现在直有一台数据库服务器,若某个写的操作会给表加锁,此时其他所有并发操作的效率都会受到影响,采用读写分离,master用于写操作,多台salve用于读操作,在写的同时不会影响读操作,且多台slave用于读,可以大大提高并发读取的雄安率,在读写分离的情况必须保证master和salve上的数据一致,所以需要主从复制

分库分表也是优化数据库的措施,可以降低服务器的压力,提供执行效率

<think>我们使用若依框架(一个基于SpringBoot和MyBatis的快速开发框架)来实现数据库主从复制读写分离主从复制读写分离通常包括以下步骤:1.配置主库(Master)和从库(Slave)的数据源。2.配置动态数据源,使得应用在执行读操作时使用从库,写操作时使用主库。3.使用AOP或拦截器在业务逻辑层进行读写分离的路由。在若依框架中,我们可以通过以下方式实现:步骤1:配置多个数据源(主库和从库)步骤2:使用AbstractRoutingDataSource来动态切换数据源步骤3:通过自定义注解和AOP来根据方法类型(读/写)选择数据源下面是一个详细的实现步骤和代码示例:1.首先,在application.yml中配置主库和从库的连接信息,例如:```yamlspring:datasource:master:driver-class-name:com.mysql.cj.jdbc.Driverurl:jdbc:mysql://master-host:3306/db_name?useUnicode=true&characterEncoding=utf8&useSSL=falseusername:rootpassword:master-passwordslave:driver-class-name:com.mysql.cj.jdbc.Driverurl:jdbc:mysql://slave-host:3306/db_name?useUnicode=true&characterEncoding=utf8&useSSL=falseusername:rootpassword:slave-password```2.创建数据源配置类,配置主从数据源,并初始化动态数据源。3.定义动态数据源类,继承AbstractRoutingDataSource,并重写determineCurrentLookupKey方法。4.使用ThreadLocal来保存当前线程需要使用的数据源标识(master或slave)。5.定义一个切面,在Service层的方法执行前,根据方法名或自定义注解来切换数据源。通常,写方法(insert,update,delete)使用主库,读方法(select)使用从库。下面是一个代码示例:首先,定义数据源上下文持有者(用于存储当前线程的数据源类型):```javapublicclassDynamicDataSourceContextHolder{privatestaticfinalThreadLocal<String>CONTEXT_HOLDER=newThreadLocal<>();publicstaticvoidsetDataSourceType(StringdataSourceType){CONTEXT_HOLDER.set(dataSourceType);}publicstaticStringgetDataSourceType(){returnCONTEXT_HOLDER.get();}publicstaticvoidclearDataSourceType(){CONTEXT_HOLDER.remove();}}```然后,定义动态数据源类:```javapublicclassDynamicDataSourceextendsAbstractRoutingDataSource{@OverrideprotectedObjectdetermineCurrentLookupKey(){returnDynamicDataSourceContextHolder.getDataSourceType();}}```接着,配置数据源:```java@ConfigurationpublicclassDataSourceConfig{@Bean(name="masterDataSource")@ConfigurationProperties(prefix="spring.datasource.master")publicDataSourcemasterDataSource(){returnDataSourceBuilder.create().build();}@Bean(name="slaveDataSource")@ConfigurationProperties(prefix="spring.datasource.slave")publicDataSourceslaveDataSource(){returnDataSourceBuilder.create().build();}@Bean(name="dynamicDataSource")publicDataSourcedynamicDataSource(){DynamicDataSourcedynamicDataSource=newDynamicDataSource();//配置默认数据源(主库)DataSourcemaster=masterDataSource();//配置从库数据源DataSourceslave=slaveDataSource();//设置数据源映射Map<Object,Object>dataSourceMap=newHashMap<>();dataSourceMap.put("master",master);dataSourceMap.put("slave",slave);dynamicDataSource.setDefaultTargetDataSource(master);//设置默认数据源dynamicDataSource.setTargetDataSources(dataSourceMap);returndynamicDataSource;}//配置SqlSessionFactory,使用动态数据源@BeanpublicSqlSessionFactorysqlSessionFactory()throwsException{SqlSessionFactoryBeansessionFactoryBean=newSqlSessionFactoryBean();sessionFactoryBean.setDataSource(dynamicDataSource());//其他配置,如mapper.xml位置等returnsessionFactoryBean.getObject();}//配置事务管理器,使用动态数据源@BeanpublicPlatformTransactionManagertransactionManager(){returnnewDataSourceTransactionManager(dynamicDataSource());}}```然后,定义数据源切换的AOP切面:```java@Aspect@ComponentpublicclassDataSourceAspect{/***切点:所有service包下的方法*/@Pointcut("execution(*com.ruoyi..service..*.*(..))")publicvoiddataSourcePointCut(){}@Before("dataSourcePointCut()")publicvoidbefore(JoinPointjoinPoint){StringmethodName=joinPoint.getSignature().getName();if(StringUtils.startsWithAny(methodName,"get","select","find","query","count")){//读操作使用从库DynamicDataSourceContextHolder.setDataSourceType("slave");}else{//写操作使用主库DynamicDataSourceContextHolder.setDataSourceType("master");}}@After("dataSourcePointCut()")publicvoidafter(JoinPointjoinPoint){DynamicDataSourceContextHolder.clearDataSourceType();}}```注意:以上代码只是一个简单的示例,实际应用中可能需要更精细的控制,例如使用自定义注解来明确指定数据源,或者处理事务中的数据源切换问题(在同一个事务中,即使有读操作也应该使用主库,因为从库可能有延迟)。此外,若依框架本身已经集成了多数据源功能,可以参考其官方文档或者多数据源模块(ruoyi-common中的datasource包)。相关问题:§§相关问题§§:1.在读写分离架构中,如何解决主从同步延迟的问题?2.动态数据源切换时,如何保证在事务中的一致性?3.若依框架中如何配置多个从库并实现负载均衡?4.除了AOP切面,还有哪些方式可以实现读写分离?</think>### 若依框架实现数据库主从复制读写分离 若依框架基于 Spring Boot + MyBatis Plus 实现,配置主从复制读写分离的核心步骤如下: #### 1. 数据库主从复制配置(MySQL 示例) **主库配置 (my.cnf):** ```ini [mysqld] server-id=1 log-bin=mysql-bin binlog-do-db=your_db ``` **从库配置 (my.cnf):** ```ini [mysqld] server-id=2 relay-log=mysql-relay ``` **执行主从同步命令:** ```sql -- 主库创建同步账号 CREATE USER 'repl'@'%' IDENTIFIED BY 'password'; GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%'; -- 从库配置同步 CHANGE MASTER TO MASTER_HOST='master_ip', MASTER_USER='repl', MASTER_PASSWORD='password', MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=154; START SLAVE; ``` #### 2. 若依框架动态数据源配置 **application.yml 配置:** ```yaml spring: datasource: dynamic: primary: master # 默认数据源 strict: true # 严格匹配数据源 datasource: master: url: jdbc:mysql://master_ip:3306/your_db username: root password: master_pass driver-class-name: com.mysql.cj.jdbc.Driver slave1: url: jdbc:mysql://slave_ip:3306/your_db username: root password: slave_pass driver-class-name: com.mysql.cj.jdbc.Driver ``` #### 3. 数据源路由配置(关键代码) ```java public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceType(); } } public class DynamicDataSourceContextHolder { private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>(); public static void setDataSourceType(String dsType) { CONTEXT_HOLDER.set(dsType); } public static String getDataSourceType() { return CONTEXT_HOLDER.get(); } public static void clearDataSourceType() { CONTEXT_HOLDER.remove(); } } ``` #### 4. AOP 切面实现读写分离 ```java @Aspect @Component public class DataSourceAspect { // 切点:Mapper 包下所有方法 @Pointcut("execution(* com.ruoyi.*.mapper..*.*(..))") public void mapperPointcut() {} @Before("mapperPointcut()") public void before(JoinPoint point) { String methodName = point.getSignature().getName(); // 写操作使用主库 if (methodName.startsWith("insert") || methodName.startsWith("update") || methodName.startsWith("delete")) { DynamicDataSourceContextHolder.setDataSourceType("master"); } // 读操作使用从库 else { DynamicDataSourceContextHolder.setDataSourceType("slave1"); } } @After("mapperPointcut()") public void after(JoinPoint point) { DynamicDataSourceContextHolder.clearDataSourceType(); } } ``` #### 5. 事务管理配置 ```java @Configuration public class TransactionConfig { @Bean public PlatformTransactionManager transactionManager(DynamicDataSource dataSource) { return new DataSourceTransactionManager(dataSource); } } ``` ### 关键点说明: 1. **动态数据源切换** - 通过 `ThreadLocal` 保存当前线程的数据源标识 - `AbstractRoutingDataSource` 根据标识路由到具体数据源 2. **AOP 切面规则** - 根据方法名前缀自动路由(insert/update/delete → 主库,其他 → 从库) - 支持自定义注解实现更精细控制 3. **事务处理** - 使用 `DataSourceTransactionManager` 确保事务内使用同一数据源 - 事务方法中所有操作默认使用主库 4. **主从同步监控** - 定期检查 `SHOW SLAVE STATUS` 确保同步正常 - 从库故障时自动降级到主库 ### 注意事项: 1. 主从延迟问题:重要业务操作建议强制走主库 2. 多从库负载均衡:可通过轮询/随机算法扩展 3. 连接池配置:建议使用 HikariCP 并设置不同连接参数 4. 健康检查:集成 Spring Boot Actuator 监控数据源状态 ### 验证读写分离: ```java // Service 层方法示例 public void testReadWriteSplit() { // 写操作(自动路由到主库) userMapper.insert(new User("master_user")); // 读操作(自动路由到从库) List<User> users = userMapper.selectList(null); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Liamlf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值