springboot、mysql实现读写分离

1、首先在springcloud config中配置读写数据库

mysql:
datasource:
readSize: 1 #读库个数
type: com.alibaba.druid.pool.DruidDataSource
write:
url: jdbc:mysql://200.200.4.34:3306/quote?characterEncoding=utf8&useSSL=false
username: root
password: 123123
driver-class-name: com.mysql.cj.jdbc.Driver
minIdle: 5
maxActive: 100
initialSize: 10
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 50
removeAbandoned: true
filters: stat
read01:
url: jdbc:mysql://200.200.4.34:3306/quote?characterEncoding=utf8&useSSL=false
username: root
password: 123123
driver-class-name: com.mysql.cj.jdbc.Driver
minIdle: 5
maxActive: 100
initialSize: 10
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 50
removeAbandoned: true
filters: stat
read02:
url: jdbc:mysql://200.200.4.34:3306/quote?characterEncoding=utf8&useSSL=false
username: root
password: 123123
driver-class-name: com.mysql.cj.jdbc.Driver
minIdle: 5
maxActive: 100
initialSize: 10
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 50
removeAbandoned: true
filters: stat

2、编写读库注解

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public [@interface](https://my.oschina.net/u/996807) ReadDataSource { }

3、增加数据源初始化配置

import java.util.ArrayList;
import java.util.List; import javax.sql.DataSource; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
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; /**
* name:DataSourceConfiguration
* <p></p>
* @author:lipeng
* @data:2018年6月27日 下午5:55:35
* @version 1.0
*/
@Configuration
public class DataSourceConfiguration { private static Logger log = LoggerFactory.getLogger(DataSourceConfiguration.class); @Value("${mysql.datasource.type}")
private Class<? extends DataSource> dataSourceType; /**
* 写库 数据源配置
* @return
*/
@Bean(name = "writeDataSource")
@Primary
@ConfigurationProperties(prefix = "mysql.datasource.write")
public DataSource writeDataSource() {
log.info("-------------------- writeDataSource init ---------------------");
return DataSourceBuilder.create().type(dataSourceType).build();
} /**
* 有多少个从库就要配置多少个
* @return
*/
@Bean(name = "readDataSource01")
@ConfigurationProperties(prefix = "mysql.datasource.read01")
public DataSource readDataSourceOne() {
log.info("-------------------- read01 DataSourceOne init ---------------------");
return DataSourceBuilder.create().type(dataSourceType).build();
} @Bean(name = "readDataSource02")
@ConfigurationProperties(prefix = "mysql.datasource.read02")
public DataSource readDataSourceTwo() {
log.info("-------------------- read01 DataSourceOne init ---------------------");
return DataSourceBuilder.create().type(dataSourceType).build();
} @Bean("readDataSources")
public List<DataSource> readDataSources(){
List<DataSource> dataSources=new ArrayList<>();
dataSources.add(readDataSourceOne());
dataSources.add(readDataSourceTwo());
return dataSources;
} }

4、增加主从配置常量

/**
* name:DataSourceType
* <p></p>
* @author:lipeng
* @data:2018年6月28日 上午9:25:44
* @version 1.0
*/
public enum DataSourceType { read("read", "从库"),
write("write", "主库"); private String type; private String name; DataSourceType(String type, String name) {
this.type = type;
this.name = name;
} public String getType() {
return type;
} public void setType(String type) {
this.type = type;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} }

5、事务内读写配置

由于涉及到事务处理,可能会遇到事务中同时用到读库和写库,可能会有延时造成脏读,所以增加了线程变量设置,来保证一个事务内读写都是同一个库

/**
* name:DataSourceContextHolder
* <p></p>
* @author:lipeng
* @data:2018年6月27日 下午5:57:39
* @version 1.0
*/
public class DataSourceContextHolder { private static Logger log = LoggerFactory.getLogger(DataSourceContextHolder.class); //线程本地环境
private static final ThreadLocal<String> local = new ThreadLocal<String>(); public static ThreadLocal<String> getLocal() {
return local;
} /**
* 读库
*/
public static void setRead() {
local.set(DataSourceType.read.getType());
log.info("数据库切换到读库...");
} /**
* 写库
*/
public static void setWrite() {
local.set(DataSourceType.write.getType());
log.info("数据库切换到写库...");
} public static String getReadOrWrite() {
return local.get();
} public static void clear(){
local.remove();
}
}

如果在注解在service层并且声明式事务也在service层,这个得保证拦截器优先级在声明式事务前面

/**
* name:DataSourceAopInService
* 在service层觉得数据源
* 必须在事务AOP之前执行,所以实现Ordered,order的值越小,越先执行
* 如果一旦开始切换到写库,则之后的读都会走写库
*
* @author:lipeng
* @data:2018年6月27日 下午5:59:17
* @version 1.0
*/
@Aspect
@EnableAspectJAutoProxy(exposeProxy=true,proxyTargetClass=true)
@Component
public class DataSourceAopInService implements PriorityOrdered{ private static Logger log = LoggerFactory.getLogger(DataSourceAopInService.class); @Before("@annotation(com.sangfor.quote.datasource.annotation.ReadDataSource) ")
public void setReadDataSourceType() {
//如果已经开启写事务了,那之后的所有读都从写库读
if(!DataSourceType.write.getType().equals(DataSourceContextHolder.getReadOrWrite())){
DataSourceContextHolder.setRead();
} } @Before("@annotation(com.sangfor.quote.datasource.annotation.WriteDataSource) ")
public void setWriteDataSourceType() {
DataSourceContextHolder.setWrite();
} @Override
public int getOrder() {
/**
* 值越小,越优先执行
* 要优于事务的执行
* 在启动类中加上了@EnableTransactionManagement(order = 10)
*/
return 1;
} }

并且在启动类或者配置类中增加注解order配置 @EnableTransactionManagement(order = 10)

6、增加mybatis相关配置类

mybatis配置

@Configuration
@AutoConfigureAfter(DataSourceConfiguration.class)
@MapperScan(basePackages = "com.sangfor.springboot")
public class MybatisConfiguration { private static Logger log = LoggerFactory.getLogger(MybatisConfiguration.class); @Value("${mysql.datasource.readSize}")
private String readDataSourceSize;
@Autowired
@Qualifier("writeDataSource")
private DataSource writeDataSource;
@Autowired
@Qualifier("readDataSources")
private List<DataSource> readDataSources; @Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(roundRobinDataSouceProxy());
sqlSessionFactoryBean.setTypeAliasesPackage("com.sangfor.quote.model");
//设置mapper.xml文件所在位置
Resource[] resources = new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml");
sqlSessionFactoryBean.setMapperLocations(resources);
sqlSessionFactoryBean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
return sqlSessionFactoryBean.getObject();
} /**
* 有多少个数据源就要配置多少个bean
*
* @return
*/
@Bean
public AbstractRoutingDataSource roundRobinDataSouceProxy() {
int size = Integer.parseInt(readDataSourceSize);
MyAbstractRoutingDataSource proxy = new MyAbstractRoutingDataSource(size);
Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
// DataSource writeDataSource = SpringContextHolder.getBean("writeDataSource");
// 写
targetDataSources.put(DataSourceType.write.getType(), writeDataSource);
// targetDataSources.put(DataSourceType.read.getType(),readDataSource);
// 多个读数据库时
for (int i = 0; i < size; i++) {
targetDataSources.put(i, readDataSources.get(i));
}
proxy.setDefaultTargetDataSource(writeDataSource);
proxy.setTargetDataSources(targetDataSources);
return proxy;
} }

多数据源切换

/**
* 多数据源切换
* name:MyAbstractRoutingDataSource
* <p></p>
* @author:lipeng
* @data:2018年6月27日 下午6:57:34
* @version 1.0
*/
public class MyAbstractRoutingDataSource extends AbstractRoutingDataSource {
private final int dataSourceNumber;
private AtomicInteger count = new AtomicInteger(0); public MyAbstractRoutingDataSource(int dataSourceNumber) {
this.dataSourceNumber = dataSourceNumber;
} @Override
protected Object determineCurrentLookupKey() {
String typeKey = DataSourceContextHolder.getReadOrWrite();
if(StringUtils.isBlank(typeKey)||typeKey.equals(DataSourceType.write.getType())) {
return DataSourceType.write.getType();
}
// 读 简单负载均衡
int number = count.getAndAdd(1);
int lookupKey = number % dataSourceNumber;
return new Integer(lookupKey);
}
}

事务管理配置

@Configuration
@EnableTransactionManagement(order = 10)
@Slf4j
@AutoConfigureAfter({ MybatisConfiguration.class })
public class TransactionConfiguration extends DataSourceTransactionManagerAutoConfiguration { @Bean
@Autowired
public DataSourceTransactionManager transactionManager(MyAbstractRoutingDataSource roundRobinDataSouceProxy) {
log.info("事物配置");
return new DataSourceTransactionManager(roundRobinDataSouceProxy);
}
}

mysql读写分离之springboot集成的更多相关文章

  1. mysql读写分离(PHP类)

    mysql读写分离(PHP类) 博客分类: php mysql   自己实现了php的读写分离,并且不用修改程序 优点:实现了读写分离,不依赖服务器硬件配置,并且都是可以配置read服务器,无限扩展 ...

  2. amoeba实现MySQL读写分离

    amoeba实现MySQL读写分离 准备环境:主机A和主机B作主从配置,IP地址为192.168.131.129和192.168.131.130,主机C作为中间件,也就是作为代理服务器,IP地址为19 ...

  3. PHP代码实现MySQL读写分离

    关于MySQL的读写分离有几种方法:中间件,Mysql驱动层,代码控制 关于中间件和Mysql驱动层实现Mysql读写分离的方法,今天暂不做研究, 这里主要写一点简单的代码来实现由PHP代码控制MyS ...

  4. 转:Mysql读写分离实现的三种方式

    1 程序修改mysql操作类可以参考PHP实现的Mysql读写分离,阿权开始的本项目,以php程序解决此需求.优点:直接和数据库通信,简单快捷的读写分离和随机的方式实现的负载均衡,权限独立分配缺点:自 ...

  5. 使用Atlas实现MySQL读写分离+MySQL-(Master-Slave)配置

    参考博文: MySQL-(Master-Slave)配置  本人按照博友北在北方的配置已成功  我使用的是 mysql5.6.27版本. 使用Atlas实现MySQL读写分离 数据切分——Atlas读 ...

  6. MySQL读写分离技术

    1.简介 当今MySQL使用相当广泛,随着用户的增多以及数据量的增大,高并发随之而来.然而我们有很多办法可以缓解数据库的压力.分布式数据库.负载均衡.读写分离.增加缓存服务器等等.这里我们将采用读写分 ...

  7. php实现MySQL读写分离

    MySQL读写分离有好几种方式 MySQL中间件 MySQL驱动层 代码控制 关于 中间件 和 驱动层的方式这里不做深究  暂且简单介绍下 如何通过PHP代码来控制MySQL读写分离 我们都知道 &q ...

  8. [记录]MySQL读写分离(Atlas和MySQL-proxy)

    MySQL读写分离(Atlas和MySQL-proxy) 一.阿里云使用Atlas从外网访问MySQL(RDS) (同样的方式修改配置文件可以实现代理也可以实现读写分离,具体看使用场景) 1.在跳板机 ...

  9. docker环境 mysql读写分离 mycat maxscale

    #mysql读写分离测试 环境centos 7.4 ,docker 17.12 ,docker-compose mysql 5.7 主从 mycat 1.6 读写分离 maxscale 2.2.4 读 ...

  10. mysql读写分离总结

    随着一个网站的业务不断扩展,数据不断增加,数据库的压力也会越来越大,对数据库或者SQL的基本优化可能达不到最终的效果,我们可以采用读写分离的策略来改变现状.读写分离现在被大量应用于很多大型网站,这个技 ...

随机推荐

  1. VUE CLI中使用Jquery无法获取到dom节点

    mounted 类型:Function 详细: 实例被挂载后调用,这时 el 被新创建的 vm.$el 替换了.如果根实例挂载到了一个文档内的元素上,当 mounted 被调用时 vm.$el 也在文 ...

  2. 纯代码搭建iOS三级结构(UITabbarController+UINavigationController+UIViewController)

    声明:这里所指的三级结构不是网上百度中所经常提及的三级框架或者MVC模式,而是指UITabbarController+UINavigationController+UIViewController. ...

  3. 利用 device_map、torch.dtype、bitsandbytes 压缩模型参数控制使用设备

    为了更好的阅读体验,请点击这里 device_map 以下内容参考 Huggingface Accelerate文档:超大模型推理方法 在 HuggingFace 中有个重要的关键字是 device_ ...

  4. 2019 南昌区域赛 CEGLM 题解 & lagrange 插值

    B. A Funny Bipartite Graph 状压 dp ,利用了原题中选完左边点集,那么右边在 左边编号最大的那个数 之前的所有点都要选的性质,可以优化到 \(O(n \cdot 2^n)\ ...

  5. 4.4K Star!推荐一款新一代的极简监控系统!轻量高性能!超500个监控指标,颜值高、功能强大!

    在信息化快速发展的今天,企业运维面临的挑战日益增多.传统的运维监控系统往往存在功能冗余.性能低下.操作复杂等问题,难以满足现代企业对高效.稳定.智能的运维管理需求. 今天给大家推荐一款新一代极简运维监 ...

  6. 常见 i2c设备地址

    背景 朋友分享的一份i2c器件地址清单,我觉得还不错. reference:https://learn.adafruit.com/i2c-addresses/the-list Special case ...

  7. 【资料分享】全志科技T507-H工业核心板规格书

    1 核心板简介 创龙科技SOM-TLT507是一款基于全志科技T507-H处理器设计的4核ARM Cortex-A53全国产工业核心板,主频高达1.416GHz.核心板CPU.ROM.RAM.电源.晶 ...

  8. Codeforces Round 941 (Div. 2) cf 941 div2 A~D

    每题都有AC代码在伸缩代码框请留意!! A. Card Exchange -------------------------------------------题解------------------ ...

  9. Java核心字符串String进阶

    字符串对象 字符串是对象,不是简单数据类型 封装在java.lang包,自动导入 创建字符串对象 常见创建一个字符串对象有下面2个方法 String str=new String("chen ...

  10. python爬取网站图片保存到本地文件夹

    爬取的网站 https://wallpaperscraft.com/catalog/anime 爬取代码 # 导包 import os import requests import parsel fr ...