springboot多數(shù)據(jù)源配合docker部署mysql主從實(shí)現(xiàn)讀寫(xiě)分離效果
一、使用docker部署mysql主從 實(shí)現(xiàn)主從復(fù)制
此次使用的是windows版本docker,mysql版本是5.7
1、使用docker獲取mysql鏡像
docker pull mysql:5.7.23 #拉取鏡像文件 docker images #查看鏡像文件

2、使用docker運(yùn)行mysql master
docker run --name mysql-master --privileged=true -v F:\dockerV\mysql:/var/lib/mysql -p 3307:3306 -e MYSQL_ROOT_PASSWORD=654321 -d mysql:5.7.23
- --name 容器名稱(chēng)mysql-master
- --privileged 指定了當(dāng)前容器是否真正的具有root權(quán)限,所謂的root權(quán)限是指具有宿主機(jī)的root權(quán)限,而不僅僅只是在容器內(nèi)部有root權(quán)限
- -v 將系統(tǒng)的F:\dockerV\mysql掛載到容器的/var/lib/mysql,注意是將宿主機(jī) 掛載到 容器內(nèi)部,而不是將容器內(nèi)部掛載到宿主機(jī)
- -p 表示宿主機(jī)上的某個(gè)端口映射到docker容器內(nèi)的某個(gè)端口,這里也就是將宿主機(jī)的3307端口映射到容器內(nèi)部的3306端口
- -e 表示指定當(dāng)前容器運(yùn)行的環(huán)境變量,該變量一般在容器內(nèi)部程序的配置文件中使用,而在外部運(yùn)行容器指定該參數(shù)。這里的MYSQL_ROOT_PASSWORD表示容器內(nèi)部的MySQL的啟動(dòng)密碼
- -d 后臺(tái)運(yùn)行,鏡像文件為mysql:5.7.23
接下來(lái)進(jìn)入容器內(nèi)部,修改配置,使其作為mysql master運(yùn)行
docker exec -it mysql-master bash #進(jìn)入容器內(nèi)部

配置mysql master,修改mysql.cnf

使用vim修改mysql.cnf,沒(méi)有安裝vim會(huì)提示bash: vi: command not found 則需要安裝vim
apt-get install vim apt-get update apt-get install vim vim mysqld.cnf #修改cnf文件,添加 server-id 表示master服務(wù)標(biāo)識(shí),同一局域網(wǎng)內(nèi)注意要唯一 和 log-bin=mysql-bin 開(kāi)啟二進(jìn)制日志功能,可以隨便取,用來(lái)完成主從復(fù)制

修改完成mysql的配置后,需要重啟服務(wù)生效
service MySQL restart # 重啟mysql服務(wù)時(shí)會(huì)使得docker容器停止,我們還需要docker start mysql-master啟動(dòng)容器 docker start mysql-master #啟動(dòng)容器
連接mysql客戶(hù)端,創(chuàng)建用來(lái)完成主從復(fù)制的賬號(hào)
mysql -uroot -p654321 #連接mysql

為從服務(wù)器創(chuàng)建一個(gè)可以用來(lái)操作master服務(wù)器的賬戶(hù),也就是創(chuàng)建一個(gè)專(zhuān)門(mén)用來(lái)復(fù)制binlog的賬號(hào),并且賦予該賬號(hào)復(fù)制權(quán)限,其命令如下
grant replication slave on *.* to 'slaveaccount'@'%' identified by '654321'; #賬號(hào)slaveaccount 密碼654321 flush privileges; #刷新用戶(hù)權(quán)限表 show master status #查看mysql master的狀態(tài)

記錄下上的position和file 在創(chuàng)建MySQL slave配置時(shí)會(huì)用到
到這里mysql master 已經(jīng)配置完成了
3、使用docker運(yùn)行mysql slave
docker run --name mysql-slave --privileged=true -v F:\dockerV\mysql-slave:/var/lib/mysql -p 3308:3306 --link mysql-master:master -e MYSQL_ROOT_PASSWORD=654321 -d mysql:5.7.23
mysql slave 的參數(shù)主要和master有兩點(diǎn)不同
- 所映射的宿主機(jī)的端口號(hào)不能與master容器相同,因?yàn)槠湟呀?jīng)被master容器占用;
- 必須加上--link參數(shù),其后指定了當(dāng)前容器所要連接的容器,mysql-master表示所要連接的容器的名稱(chēng),master表示為該容器起的一個(gè)別名,通俗來(lái)講,就是slave容器通過(guò)這兩個(gè)名稱(chēng)都可以訪問(wèn)到master容器。這么做的原因在于,如果master與slave不在同一個(gè)docker network中,那么這兩個(gè)容器相互之間是沒(méi)法訪問(wèn)的。
docker exec -it mysql-slave /bin/bash #進(jìn)入mysql salve容器 vim mysqld.cnf #修改cnf文件,添加 server-id 表示slave服務(wù)標(biāo)識(shí),如果此salve需要作為其他mysql的主,那么就需要配置log-bin=mysql-bin service MySQL restart # 重啟mysql服務(wù)時(shí)會(huì)使得docker容器停止,我們還需要docker start mysql-slave啟動(dòng)容器 docker start mysql-master #啟動(dòng)容器 mysql -uroot -proot #連接mysql服務(wù)
配置mysql slave 使其以slave模式運(yùn)行
change master to master_host='172.17.0.2', master_user='slaveaccount', master_password='654321', master_port=3306, master_log_file='mysql-bin.000001', master_log_pos=2272, master_connect_retry=30;
注意:這一步主要在slave是配置master的信息,包括 master的 地址、端口、賬號(hào)、密碼、log文件、log文件偏移量、重試。下面介紹這些參數(shù)值從何獲取
獲取master_host,使用docker inspect mysql-master查看master容器元數(shù)據(jù)。其中 IPAdress是master_host

- 獲取master_port,是運(yùn)行master映射容器內(nèi)部的端口,這里就是3306
- 獲取master_user和master_password,是第一步中在master中創(chuàng)建的slave賬號(hào)
- 獲取master_log_file和master_log_pos,是配置master中最后一步使用show master status獲取的文件和偏移量
- 獲取master_connect_retry,是slave重試連接master動(dòng)作前的休眠時(shí)間,單位s,默認(rèn)60s
start slave; #以slave模式運(yùn)行 show salve status \G; #查看slave的狀態(tài)

可以看到,Slave_IO_Running:YES和Slave_SQL_Running:YES 證明此時(shí)主從復(fù)制已經(jīng)就緒,slave配置到此完成
4、驗(yàn)證主從復(fù)制效果
- 這里我們?cè)谶B接master,并創(chuàng)建一張表,添加數(shù)據(jù),在從庫(kù)查看數(shù)據(jù)是否同步。
- 在主庫(kù)創(chuàng)建數(shù)據(jù),并在從庫(kù)查看數(shù)據(jù)是否同步成功。
mysql> create database test; Query OK, 1 row affected (0.01 sec) mysql> use test; Database changed mysql> create table t_user(id bigint, name varchar(255)); Query OK, 0 rows affected (0.02 sec) mysql> insert into t_user(id, name) value (1, 'cgg'); Query OK, 1 row affected (0.01 sec) mysql> select * from test.t_user; +------+------+ | id | name | +------+------+ | 1 | cgg | +------+------+ 1 row in set (0.00 sec)
5、mysql主從復(fù)制原理

- 主庫(kù)db的更新事件(update、insert、delete)被寫(xiě)到binlog
- 主庫(kù)創(chuàng)建一個(gè)binlog dump thread,把binlog的內(nèi)容發(fā)送到從庫(kù)
- 從庫(kù)啟動(dòng)并發(fā)起連接,連接到主庫(kù)
- 從庫(kù)啟動(dòng)之后,創(chuàng)建一個(gè)I/O線程,讀取主庫(kù)傳過(guò)來(lái)的binlog內(nèi)容并寫(xiě)入到relay log
- 從庫(kù)啟動(dòng)之后,創(chuàng)建一個(gè)SQL線程,從relay log里面讀取內(nèi)容,從Exec_Master_Log_Pos位置開(kāi)始執(zhí)行讀取到的更新事件,將更新內(nèi)容寫(xiě)入到slave的db
二、springboot項(xiàng)目多數(shù)據(jù)源配置,實(shí)現(xiàn)讀寫(xiě)分離
1、主從多數(shù)據(jù)源配置
yml配置
server:
port: 8888
servlet:
encoding:
charset: UTF-8
force: true
enabled: true
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://127.0.0.1:3307/test?serverTimezone=GMT%2B8&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false&useSSL=false
username: root
password: 654321
slave:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://127.0.0.1:3308/test?serverTimezone=GMT%2B8&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false&useSSL=false
username: root
password: 654321
數(shù)據(jù)源配置
/**
* @author cgg
**/
@Configuration
@EnableTransactionManagement
public class DynamicDataSourceConfig {
@Bean(name = "slaveDatasource")
@ConfigurationProperties(prefix = "slave.datasource")
public DataSource dbSlave() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "dataSource")
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dbMaster() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@Primary
public DataSource multipleDataSource(@Qualifier("dataSource") DataSource db,
@Qualifier("slaveDatasource") DataSource slaveDatasource) {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> targetDataSources = new HashMap<>(16);
targetDataSources.put("dataSource", db);
targetDataSources.put("slaveDatasource", slaveDatasource);
dynamicDataSource.setTargetDataSources(targetDataSources);
//設(shè)置默認(rèn)數(shù)據(jù)源為從庫(kù),如果寫(xiě)操作業(yè)務(wù)多,可以默認(rèn)設(shè)置為主庫(kù)
dynamicDataSource.setDefaultTargetDataSource(slaveDatasource);
return dynamicDataSource;
}
@Bean("sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory() throws Exception {
MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
sqlSessionFactory.setDataSource(multipleDataSource(dbSlave(), dbMaster()));
MybatisConfiguration configuration = new MybatisConfiguration();
configuration.setJdbcTypeForNull(JdbcType.NULL);
configuration.setMapUnderscoreToCamelCase(true);
configuration.setCacheEnabled(false);
sqlSessionFactory.setConfiguration(configuration);
sqlSessionFactory.setMapperLocations((new PathMatchingResourcePatternResolver()).getResources(DEFAULT_MAPPER_LOCATION));
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
paginationInterceptor.setOverflow(false);
paginationInterceptor.setLimit(-1);
paginationInterceptor.setCountSqlParser(tenantSqlParserCountOptimize());
Interceptor[] plugins = new Interceptor[]{new ShardTableInterceptor(), paginationInterceptor};
sqlSessionFactory.setPlugins(plugins);
return sqlSessionFactory.getObject();
}
}
/**
* 動(dòng)態(tài)數(shù)據(jù)源
*
* @author cgg
**/
@Slf4j
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DbContextHolder.getDbType();
}
}
2、配置切面控制主從數(shù)據(jù)源切換、讀從庫(kù)寫(xiě)主庫(kù),實(shí)現(xiàn)讀寫(xiě)分離
aop切面
/**
* @author cgg
**/
@Component
@Order(value = -100)
@Slf4j
@Aspect
public class DataSourceSwitchAspect {
//master 包下的操作都是操作主庫(kù)業(yè)務(wù)
@Pointcut("execution(* com.master..*.*(..))")
private void db1Aspect() {
}
//slave 包下的操作都是操作從庫(kù)業(yè)務(wù)
@Pointcut("execution(* com.slave.*.*(..))")
private void db2Aspect() {
}
@Before("db1Aspect()")
public void dbMaster() {
log.debug("切換到Master 數(shù)據(jù)源...");
DbContextHolder.setDbType("dataSource");
}
@Before("db2Aspect()")
public void dbSlave() {
log.debug("切換到Slave 數(shù)據(jù)源...");
DbContextHolder.setDbType("slaveDatasource");
}
}
/**
* @author cgg
**/
public class DbContextHolder {
private static final ThreadLocal contextHolder = new ThreadLocal<>();
/**
* 設(shè)置數(shù)據(jù)源
* @param dbType
*/
public static void setDbType(String dbType) {
contextHolder.set(dbType);
}
/**
* 取得當(dāng)前數(shù)據(jù)源
* @return
*/
public static String getDbType() {
return (String) contextHolder.get();
}
/**
* 清除上下文數(shù)據(jù)
*/
public static void clearDbType() {
contextHolder.remove();
}
}
3、驗(yàn)證讀寫(xiě)分離效果
使用接口調(diào)用不同接口,切點(diǎn)會(huì)自動(dòng)切換數(shù)據(jù)源,這里寫(xiě)庫(kù)接口只會(huì)操作主庫(kù),讀庫(kù)接口會(huì)操作從庫(kù)。實(shí)現(xiàn)讀寫(xiě)分離
到此這篇關(guān)于springboot多數(shù)據(jù)源配合docker部署mysql主從實(shí)現(xiàn)讀寫(xiě)分離的文章就介紹到這了,更多相關(guān)springboot讀寫(xiě)分離內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
簡(jiǎn)介Java的Spring框架的體系結(jié)構(gòu)以及安裝配置
這篇文章主要介紹了Java的Spring框架的體系結(jié)構(gòu)以及安裝配置,Spring框架是Java的SSH三大web開(kāi)發(fā)框架之一,需要的朋友可以參考下2015-12-12
Java實(shí)現(xiàn)文件復(fù)制及文件夾復(fù)制幾種常用的方式
這篇文章主要給大家介紹了關(guān)于Java實(shí)現(xiàn)文件復(fù)制及文件夾復(fù)制幾種常用的方式,java復(fù)制文件的方式其實(shí)有不少種,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-09-09
java遠(yuǎn)程連接調(diào)用Rabbitmq的實(shí)例代碼
本篇文章主要介紹了java遠(yuǎn)程連接調(diào)用Rabbitmq的實(shí)例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-07-07
java 利用HttpClient PostMethod提交json數(shù)據(jù)操作
這篇文章主要介紹了java 利用HttpClient PostMethod提交json數(shù)據(jù)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01
activiti實(shí)現(xiàn)員工請(qǐng)假流程解析
這篇文章主要介紹了activiti實(shí)現(xiàn)員工請(qǐng)假流程解析,本文通過(guò)實(shí)例代碼圖文相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07
Java元組類(lèi)型javatuples使用實(shí)例
這篇文章主要介紹了Java元組類(lèi)型javatuples使用實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11

