数据库系列——基于MySQL主从同步实现读写分离,2024年最新靠着这份900多页的PDF面试整理

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

a、编写配置文件,配置两个数据源信息

只有必填信息,其他都用默认设置

application.properties

mysql.datasource.config-location=classpath:/mybatis-config.xml

mysql.datasource.mapper-locations=classpath:/mapper/*.xml

mysql.datasource.num=2

mysql.datasource.read1.driverClass=com.mysql.jdbc.Driver

mysql.datasource.read1.password=123456

mysql.datasource.read1.url=jdbc:mysql://127.0.0.1:3307/test?serverTimezone=Asia/Shanghai

mysql.datasource.read1.username=root

mysql.datasource.read2.driverClass=com.mysql.jdbc.Driver

mysql.datasource.read2.password=123456

mysql.datasource.read2.url=jdbc:mysql://127.0.0.1:3308/test?serverTimezone=Asia/Shanghai

mysql.datasource.read2.username=root

mysql.datasource.write.driverClass=com.mysql.jdbc.Driver

mysql.datasource.write.password=123456

mysql.datasource.write.url=jdbc:mysql://127.0.0.1:3306/test?serverTimezone=Asia/Shanghai

mysql.datasource.write.username=root

b、编写DbContextHolder类

这个类用来设置数据库类别,其中有一个ThreadLocal用来保存每个线程的是使用读库,还是写库。代码如下:

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

/**

  • Description 这里切换读/写模式

  • 原理是利用ThreadLocal保存当前线程是否处于读模式(通过开始READ_ONLY注解在开始操作前设置模式为读模式,

  • 操作结束后清除该数据,避免内存泄漏,同时也为了后续在该线程进行写操作时任然为读模式

  • @author 杜康

  • @date 2020-08-31

*/

public class DbContextHolder {

private static Logger log = LoggerFactory.getLogger(DbContextHolder.class);

public static final String WRITE = “write”;

public static final String READ = “read”;

private static ThreadLocal contextHolder= new ThreadLocal<>();

public static void setDbType(String dbType) {

if (dbType == null) {

log.error(“dbType为空”);

throw new NullPointerException();

}

log.info(“设置dbType为:{}”,dbType);

contextHolder.set(dbType);

}

public static String getDbType() {

return contextHolder.get() == null ? WRITE : contextHolder.get();

}

public static void clearDbType() {

contextHolder.remove();

}

}

c、重写determineCurrentLookupKey方法

spring在开始进行数据库操作时会通过这个方法来决定使用哪个数据库,因此我们在这里调用上面DbContextHolder类的getDbType()方法获取当前操作类别,同时可进行读库的负载均衡,代码如下

package com.gary.dbrw.datachange;

import com.gary.dbrw.util.NumberUtil;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class MyAbstractRoutingDataSource extends AbstractRoutingDataSource {

@Value(“${mysql.datasource.num}”)

private int num;

private final Logger log = LoggerFactory.getLogger(this.getClass());

@Override

protected Object determineCurrentLookupKey() {

String typeKey = DbContextHolder.getDbType();

if (typeKey.equals(DbContextHolder.WRITE)) {

log.info(“使用了写库w”);

return typeKey;

}

//使用随机数决定使用哪个读库(可写负载均衡算法)

int sum = NumberUtil.getRandom(1, num);

log.info(“使用了读库r{}”, sum);

return DbContextHolder.READ + sum;

}

}

d、编写配置类

由于要进行读写分离,不能再用springboot的默认配置,我们需要手动来进行配置。首先生成数据源,使用@ConfigurProperties自动生成数据源:

package com.gary.dbrw.config;

import com.alibaba.druid.pool.DruidDataSource;

import com.gary.dbrw.common.Properties;

import com.gary.dbrw.datachange.DbContextHolder;

import com.gary.dbrw.datachange.MyAbstractRoutingDataSource;

import org.apache.ibatis.session.SqlSessionFactory;

import org.mybatis.spring.SqlSessionFactoryBean;

import org.mybatis.spring.annotation.MapperScan;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.context.annotation.Primary;

import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import org.springframework.core.io.support.ResourcePatternResolver;

import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.sql.DataSource;

import java.sql.SQLException;

import java.util.HashMap;

import java.util.Map;

@MapperScan(basePackages = “com.gary.dbrw.mapper”, sqlSessionFactoryRef = “sqlSessionFactory”)

@Configuration

public class DataSourceConfig {

// @Value(“${mysql.datasource.type-aliases-package}”)

// private String typeAliasesPackage;

//

// @Value(“${mysql.datasource.mapper-locations}”)

// private String mapperLocation;

//

// @Value(“${mysql.datasource.config-location}”)

// private String configLocation;

/**

  • 写数据源

  • @Primary 标志这个 Bean 如果在多个同类 Bean 候选时,该 Bean 优先被考虑。

  • 多数据源配置的时候注意,必须要有一个主数据源,用 @Primary 标志该 Bean

*/

@Primary

@Bean

@ConfigurationProperties(prefix = “mysql.datasource.write”)

public DataSource writeDataSource() {

return new DruidDataSource();

}

/**

  • 读数据源

*/

@Bean

@ConfigurationProperties(prefix = “mysql.datasource.read1”)

public DataSource read1() {

return new DruidDataSource();

}

/**

  • 多数据源需要自己设置sqlSessionFactory

*/

@Bean

public SqlSessionFactory sqlSessionFactory() throws Exception {

SqlSessionFactoryBean bean = new SqlSessionFactoryBean();

bean.setDataSource(routingDataSource());

ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();

// 实体类对应的位置

bean.setTypeAliasesPackage(Properties.typeAliasesPackage);

// mybatis的XML的配置

bean.setMapperLocations(resolver.getResources(Properties.mapperLocation));

bean.setConfigLocation(resolver.getResource(Properties.configLocation));

return bean.getObject();

}

/**

  • 设置事务,事务需要知道当前使用的是哪个数据源才能进行事务处理

*/

@Bean

public DataSourceTransactionManager dataSourceTransactionManager() {

return new DataSourceTransactionManager(routingDataSource());

}

/**

  • 设置数据源路由,通过该类中的determineCurrentLookupKey决定使用哪个数据源

*/

@Bean

public AbstractRoutingDataSource routingDataSource() {

MyAbstractRoutingDataSource proxy = new MyAbstractRoutingDataSource();

Map<Object, Object> targetDataSources = new HashMap<>(2);

targetDataSources.put(DbContextHolder.WRITE, writeDataSource());

targetDataSources.put(DbContextHolder.READ + “1”, read1());

proxy.setDefaultTargetDataSource(writeDataSource());

proxy.setTargetDataSources(targetDataSources);

return proxy;

}

}

注意有多少个读库就要设置多少个读数据源,Bean名为read+序号。

最后设置数据源,使用的是我们之前写的MyAbstractRoutingDataSource类

/**

  • 设置数据源路由,通过该类中的determineCurrentLookupKey决定使用哪个数据源

*/

@Bean

public AbstractRoutingDataSource routingDataSource() {

MyAbstractRoutingDataSource proxy = new MyAbstractRoutingDataSource();

Map<Object, Object> targetDataSources = new HashMap<>(2);

targetDataSources.put(DbContextHolder.WRITE, writeDataSource());

targetDataSources.put(DbContextHolder.READ + “1”, read1());

proxy.setDefaultTargetDataSource(writeDataSource());

proxy.setTargetDataSources(targetDataSources);

return proxy;

}

接着需要设置sqlSessionFactory,配置MyBatis的sqlSessionFactoryRef 引用

/**

  • 多数据源需要自己设置sqlSessionFactory

*/

总结

在这里,由于面试中MySQL问的比较多,因此也就在此以MySQL为例为大家总结分享。但是你要学习的往往不止这一点,还有一些主流框架的使用,Spring源码的学习,Mybatis源码的学习等等都是需要掌握的,我也把这些知识点都整理起来了

面试真题

Spring源码笔记

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 多数据源需要自己设置sqlSessionFactory

*/

总结

在这里,由于面试中MySQL问的比较多,因此也就在此以MySQL为例为大家总结分享。但是你要学习的往往不止这一点,还有一些主流框架的使用,Spring源码的学习,Mybatis源码的学习等等都是需要掌握的,我也把这些知识点都整理起来了

[外链图片转存中…(img-Q7EWBjne-1713711165330)]

[外链图片转存中…(img-tDS9y2HM-1713711165331)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-rtHemn29-1713711165331)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 27
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值