这里介绍的是MYSQL的主从复制实现及其原理,数据源分为主从,主数据源用于写操作,从数据源用于读操作,实现了读写分离
MYSQL主从复制实现以及原理
##############配置端口
server.port=8089
###########################################################
#**********************多数据源配置*************************
############################################################
######################主数据库配置(写入)##############
spring.datasource.master.url=jdbc:mysql://192.168.223.129:3306/seckill?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
spring.datasource.master.username=root
spring.datasource.master.password=123456
spring.datasource.master.driver-class-name=com.mysql.jdbc.Driver
######################从库配置(读取)##############
spring.datasource.slave.url=jdbc:mysql://192.168.223.130:3306/seckill?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
spring.datasource.slave.username=root
spring.datasource.slave.password=123456
spring.datasource.slave.driver-class-name=com.mysql.jdbc.Driver
####################################druid连接池配置-开始##########################################
#连接池类型
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.filters=stat
### 配置初始化大小、最小、最大
#数据库连接池初始化大小
spring.datasource.initialSize=100
#最小空闲数
spring.datasource.minIdle=500
#最大活跃数
spring.datasource.maxActive=1000
#配置获取连接等待超时的时间
spring.datasource.maxWait=60000
#配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
#配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000
#测试连接
spring.datasource.validationQuery=select 'x'
#申请连接的时候检测,建议配置为true,不影响性能,并且保证安全性
spring.datasource.testWhileIdle=true
#获取连接时执行检测,建议关闭,影响性能
spring.datasource.testOnBorrow=false
#归还连接时执行检测,建议关闭,影响性能
spring.datasource.testOnReturn=false
#是否开启PSCache,PSCache对支持游标的数据库性能提升巨大,oracle建议开启,mysql下建议关闭
spring.datasource.poolPreparedStatements=false
#开启poolPreparedStatements后生效
spring.datasource.maxOpenPreparedStatements=20
#配置扩展插件,常用的插件有=>stat:监控统计 log4j2:日志 wall:防御sql注入
pring.datasource.filters.commons-log.connection-logger-name=stat,wall,log4j2
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
spring.datasource.druid.connection-properties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=2000
#### Druid WebStatFilter配置,url统计
spring.datasource.druid.web-stat-filter.enabled=true
spring.datasource.druid.web-stat-filter.url-pattern=/*
spring.datasource.druid.web-stat-filter.exclusions='*.gif,*.png,*.jpg,*.html,*.js,*.css,*.ico,/druid/*'
#### Druid StatViewServlet配置,监控界面配置
spring.datasource.druid.stat-view-servlet.enabled=true
spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
spring.datasource.druid.stat-view-servlet.reset-enable=true
spring.datasource.druid.stat-view-servlet.login-username=root
spring.datasource.druid.stat-view-servlet.login-password=123456
########################druid连接池配置-结束##########################################
###########################################################
#**********************配置日志文件名***********************
############################################################
logging.config=classpath:log4j2-prod.xml
###配置日志输出
spring.datasource.druid.filter.slf4j.enabled=true
spring.datasource.druid.filter.slf4j.statement-create-after-log-enabled=false
spring.datasource.druid.filter.slf4j.statement-close-after-log-enabled=false
spring.datasource.druid.filter.slf4j.result-set-open-after-log-enabled=false
spring.datasource.druid.filter.slf4j.result-set-close-after-log-enabled=false
###########################################################
#**********************不显示排除日志依赖信息****************
############################################################
logging.level.org.springframework.boot.autoconfigure=ERROR
###########################################################
#**********************thymeleaf配置***********************
############################################################
spring.thymeleaf.cache=false
spring.thymeleaf.servlet.content-type=text/html
spring.thymeleaf.enabled=true
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
###########################################################
#**********************redis配置***********************
############################################################
redis.host=120.78.192.109
redis.port=6379
redis.timeout=3
redis.password=123456
redis.poolMaxTotal=100
redis.poolMaxIdle=50
redis.poolMaxWait=20
默认是主数据源
/**
* 自定义数据源注解,默认主库(master)
*
* @author Administrator
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String value() default "master";
}
/**
* @author Administrator
* 配置数据库信息
*/
@Configuration
public class DataSourceConfig {
/**
* 主库
*/
public static final String DB_SECKILL_GOOD = "master";
/**
* 用从库
*/
public static final String DB_SECKILL_USER = "slave";
/**
* name中master作为主数据源
* (@Primary)该注解声明是默认数据源
*
* @return 商品数据源
*/
@Bean(name = "master")
@Primary
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource goodDateSource() {
return DruidDataSourceBuilder.create().build();
}
/**
* name中slave作为从数据源
* (@Primary)该注解声明是默认数据源
*
* @return 用户数据源
*/
@Bean(name = "slave")
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource userDateSource() {
return DruidDataSourceBuilder.create().build();
}
/**
* 默认数据源配置和多数据源配置
*
* @return 数据源
*/
@Bean(name = "dynamicDataSource")
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// 默认数据源
dynamicDataSource.setDefaultTargetDataSource(goodDateSource());
// 配置多数据源
Map<Object, Object> dataBaseMap = new HashMap<>(16);
dataBaseMap.put(DB_SECKILL_USER, userDateSource());
dataBaseMap.put(DB_SECKILL_GOOD, goodDateSource());
dynamicDataSource.setTargetDataSources(dataBaseMap);
return dynamicDataSource;
}
@Bean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
// 指定扫描的xml文件所在位置,在配置文件里面配置,会报Invalid bound statement
Resource[] resources = new PathMatchingResourcePatternResolver()
.getResources("classpath:mybatis/*.xml");
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setMapUnderscoreToCamelCase(true);
bean.setMapperLocations(resources);
bean.setConfiguration(configuration);
return bean.getObject();
}
/**
* 事务管理
*
* @param dataSource 数据源
* @return 事务管理
*/
@Bean(name = "sqlTransactionManager")
public PlatformTransactionManager platformTransactionManager(@Qualifier("dynamicDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "sqlSessionTemplate")
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
**
* 获得和设置上下文环境 主要负责改变上下文数据源的名称
* @author Administrator
*/
@Component
@Slf4j
public class DataSourceContextHolder {
/**
* 线程独立
*/
private static ThreadLocal<String> contextHolder = new ThreadLocal<String>();
/**
* 主库
*/
public static final String DB_SECKILL_GOOD = "master";
/**
* 获取数据源名
*
* @return 数据库名
*/
public static String getDataBaseType() {
return contextHolder.get();
}
/**
* 设置数据源名(切换数据源)
*
* @param dataBase 数据库类型
*/
public static void setDataBaseType(String dataBase) {
log.info("设置数据源:" + dataBase);
contextHolder.set(dataBase);
}
/**
* 清除数据源名
*/
public static void clearDataBaseType() {
contextHolder.remove();
}
}
/**
* @author Administrator
* 建立动态数据源
*/
@Configuration
@Slf4j
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* 获取当前数据源并打印日志记录
*
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
log.error("当前数据源:" + DataSourceContextHolder.getDataBaseType());
return DataSourceContextHolder.getDataBaseType();
}
}
/**
* @author Administrator
* 动态切换数据源类
*/
@Aspect
@Component
@Order(-1)
@Slf4j
public class DynamicDataSourceAspect {
@Before("@annotation(DataSource)")
public void beforeSwitchDataSource(JoinPoint point) {
//获得当前访问的class
Class<?> className = point.getTarget().getClass();
//获得访问的方法名
String methodName = point.getSignature().getName();
//得到方法的参数的类型
Class[] argClass = ((MethodSignature) point.getSignature()).getParameterTypes();
String dataSource = DataSourceContextHolder.DB_SECKILL_GOOD;
try {
// 得到访问的方法对象
Method method = className.getMethod(methodName, argClass);
// 判断是否存在@DataSource注解
if (method.isAnnotationPresent(DataSource.class)) {
DataSource annotation = method.getAnnotation(DataSource.class);
// 取出注解中的数据源名
dataSource = annotation.value();
}
} catch (Exception e) {
log.error("动态切换数据源失败");
}
// 切换数据源
DataSourceContextHolder.setDataBaseType(dataSource);
}
@After("@annotation(DataSource)")
public void afterSwitchDataSource(JoinPoint point) {
DataSourceContextHolder.clearDataBaseType();
}
}
1.我们前面在配置文件配置了druid的监控界面,登录进去查看数据源
查看数据源,可以看到里面有主从两个数据源
查看数据源配置,发现连接池配置没有生效,那么只需要按主从那样配置即可生效
代码测试如下
5,完整的实现请参考
springboot整合多数据源