springboot多數(shù)據(jù)源配合docker部署mysql主從實現(xiàn)讀寫分離效果
一、使用docker部署mysql主從 實現(xiàn)主從復(fù)制
此次使用的是windows版本docker,mysql版本是5.7
1、使用docker獲取mysql鏡像
docker pull mysql:5.7.23 #拉取鏡像文件 docker images #查看鏡像文件
2、使用docker運行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 容器名稱mysql-master
- --privileged 指定了當前容器是否真正的具有root權(quán)限,所謂的root權(quán)限是指具有宿主機的root權(quán)限,而不僅僅只是在容器內(nèi)部有root權(quán)限
- -v 將系統(tǒng)的F:\dockerV\mysql掛載到容器的/var/lib/mysql,注意是將宿主機 掛載到 容器內(nèi)部,而不是將容器內(nèi)部掛載到宿主機
- -p 表示宿主機上的某個端口映射到docker容器內(nèi)的某個端口,這里也就是將宿主機的3307端口映射到容器內(nèi)部的3306端口
- -e 表示指定當前容器運行的環(huán)境變量,該變量一般在容器內(nèi)部程序的配置文件中使用,而在外部運行容器指定該參數(shù)。這里的MYSQL_ROOT_PASSWORD表示容器內(nèi)部的MySQL的啟動密碼
- -d 后臺運行,鏡像文件為mysql:5.7.23
接下來進入容器內(nèi)部,修改配置,使其作為mysql master運行
docker exec -it mysql-master bash #進入容器內(nèi)部
配置mysql master,修改mysql.cnf
使用vim修改mysql.cnf,沒有安裝vim會提示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ù)標識,同一局域網(wǎng)內(nèi)注意要唯一 和 log-bin=mysql-bin 開啟二進制日志功能,可以隨便取,用來完成主從復(fù)制
修改完成mysql的配置后,需要重啟服務(wù)生效
service MySQL restart # 重啟mysql服務(wù)時會使得docker容器停止,我們還需要docker start mysql-master啟動容器 docker start mysql-master #啟動容器
連接mysql客戶端,創(chuàng)建用來完成主從復(fù)制的賬號
mysql -uroot -p654321 #連接mysql
為從服務(wù)器創(chuàng)建一個可以用來操作master服務(wù)器的賬戶,也就是創(chuàng)建一個專門用來復(fù)制binlog的賬號,并且賦予該賬號復(fù)制權(quán)限,其命令如下
grant replication slave on *.* to 'slaveaccount'@'%' identified by '654321'; #賬號slaveaccount 密碼654321 flush privileges; #刷新用戶權(quán)限表 show master status #查看mysql master的狀態(tài)
記錄下上的position和file 在創(chuàng)建MySQL slave配置時會用到
到這里mysql master 已經(jīng)配置完成了
3、使用docker運行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有兩點不同
- 所映射的宿主機的端口號不能與master容器相同,因為其已經(jīng)被master容器占用;
- 必須加上--link參數(shù),其后指定了當前容器所要連接的容器,mysql-master表示所要連接的容器的名稱,master表示為該容器起的一個別名,通俗來講,就是slave容器通過這兩個名稱都可以訪問到master容器。這么做的原因在于,如果master與slave不在同一個docker network中,那么這兩個容器相互之間是沒法訪問的。
docker exec -it mysql-slave /bin/bash #進入mysql salve容器 vim mysqld.cnf #修改cnf文件,添加 server-id 表示slave服務(wù)標識,如果此salve需要作為其他mysql的主,那么就需要配置log-bin=mysql-bin service MySQL restart # 重啟mysql服務(wù)時會使得docker容器停止,我們還需要docker start mysql-slave啟動容器 docker start mysql-master #啟動容器 mysql -uroot -proot #連接mysql服務(wù)
配置mysql slave 使其以slave模式運行
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的 地址、端口、賬號、密碼、log文件、log文件偏移量、重試。下面介紹這些參數(shù)值從何獲取
獲取master_host,使用docker inspect mysql-master查看master容器元數(shù)據(jù)。其中 IPAdress是master_host
- 獲取master_port,是運行master映射容器內(nèi)部的端口,這里就是3306
- 獲取master_user和master_password,是第一步中在master中創(chuàng)建的slave賬號
- 獲取master_log_file和master_log_pos,是配置master中最后一步使用show master status獲取的文件和偏移量
- 獲取master_connect_retry,是slave重試連接master動作前的休眠時間,單位s,默認60s
start slave; #以slave模式運行 show salve status \G; #查看slave的狀態(tài)
可以看到,Slave_IO_Running:YES和Slave_SQL_Running:YES 證明此時主從復(fù)制已經(jīng)就緒,slave配置到此完成
4、驗證主從復(fù)制效果
- 這里我們在連接master,并創(chuàng)建一張表,添加數(shù)據(jù),在從庫查看數(shù)據(jù)是否同步。
- 在主庫創(chuàng)建數(shù)據(jù),并在從庫查看數(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ù)制原理
- 主庫db的更新事件(update、insert、delete)被寫到binlog
- 主庫創(chuàng)建一個binlog dump thread,把binlog的內(nèi)容發(fā)送到從庫
- 從庫啟動并發(fā)起連接,連接到主庫
- 從庫啟動之后,創(chuàng)建一個I/O線程,讀取主庫傳過來的binlog內(nèi)容并寫入到relay log
- 從庫啟動之后,創(chuàng)建一個SQL線程,從relay log里面讀取內(nèi)容,從Exec_Master_Log_Pos位置開始執(zhí)行讀取到的更新事件,將更新內(nèi)容寫入到slave的db
二、springboot項目多數(shù)據(jù)源配置,實現(xiàn)讀寫分離
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è)置默認數(shù)據(jù)源為從庫,如果寫操作業(yè)務(wù)多,可以默認設(shè)置為主庫 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(); } } /** * 動態(tài)數(shù)據(jù)源 * * @author cgg **/ @Slf4j public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DbContextHolder.getDbType(); } }
2、配置切面控制主從數(shù)據(jù)源切換、讀從庫寫主庫,實現(xiàn)讀寫分離
aop切面
/** * @author cgg **/ @Component @Order(value = -100) @Slf4j @Aspect public class DataSourceSwitchAspect { //master 包下的操作都是操作主庫業(yè)務(wù) @Pointcut("execution(* com.master..*.*(..))") private void db1Aspect() { } //slave 包下的操作都是操作從庫業(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); } /** * 取得當前數(shù)據(jù)源 * @return */ public static String getDbType() { return (String) contextHolder.get(); } /** * 清除上下文數(shù)據(jù) */ public static void clearDbType() { contextHolder.remove(); } }
3、驗證讀寫分離效果
使用接口調(diào)用不同接口,切點會自動切換數(shù)據(jù)源,這里寫庫接口只會操作主庫,讀庫接口會操作從庫。實現(xiàn)讀寫分離
到此這篇關(guān)于springboot多數(shù)據(jù)源配合docker部署mysql主從實現(xiàn)讀寫分離的文章就介紹到這了,更多相關(guān)springboot讀寫分離內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
簡介Java的Spring框架的體系結(jié)構(gòu)以及安裝配置
這篇文章主要介紹了Java的Spring框架的體系結(jié)構(gòu)以及安裝配置,Spring框架是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下2015-12-12Java實現(xiàn)文件復(fù)制及文件夾復(fù)制幾種常用的方式
這篇文章主要給大家介紹了關(guān)于Java實現(xiàn)文件復(fù)制及文件夾復(fù)制幾種常用的方式,java復(fù)制文件的方式其實有不少種,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2023-09-09java 利用HttpClient PostMethod提交json數(shù)據(jù)操作
這篇文章主要介紹了java 利用HttpClient PostMethod提交json數(shù)據(jù)操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-01-01