1. docker
docker的安裝有很多方式,可以參考:
https://www.runoob.com/docker/centos-docker-install.html
還需要配置鏡像加速
2. docker下安裝mysql
打開docker hub,然後搜索mysql ,我們安裝5.7 版本的mysql
docker pull mysql:5.7
3.做一些啓動前的準備
我這裏就直接在一臺機器上啓動3個mysql容器了,就不分3臺機器了。
主要是將容器中的mysql 存放數據的文件夾映射到主機上,否則,容器一刪除,數據就全沒了。
在主機上添加爲3個數據庫分別添加3個文件夾:
mkdir /usr/local/docker-mysql-data
mkdir /usr/local/docker-mysql-data/master
mkdir /usr/local/docker-mysql-data/slaver1
mkdir /usr/local/docker-mysql-data/slaver2
4. 啓動mysql容器
啓動master:
docker run -p 3339:3306 --name master -v /usr/local/docker-mysql-data/master:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
啓動兩個slaver:
docker run -p 3340:3306 --name slaver1 -v /usr/local/docker-mysql-data/slaver1:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
docker run -p 3341:3306 --name slaver2 -v /usr/local/docker-mysql-data/slaver2:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
參數說明:
都拿master進行舉例說明
- -p 映射端口:將容器中的3306端口映射到主機的3339端口,供外部訪問
- --name : 給容器取的名字,可以自定義,越容易記越好,之後對容器進行操作時可直接使用名字,而不用使用容器id了
- -v: 將容器中的文件夾掛載到主機的文件夾上,這樣的話,當容器被刪除,數據也能夠保存下來
- -e: 外部參數,MYSQL_ROOT_PASSWORD=123456的意思是給root用戶設置密碼爲123456,且通過命令啓動容器後會自動爲root用戶賦予所有的權限,不需要手動賦予權限
- -d :表示讓容器後臺運行
- mysql:5.7 指定運行的mysql的版本,我們只下載了5.7版本的鏡像,所以只能指定5.7版本
5. 使用nacicat進行連接3臺數據庫
在連接前需要開放端口:
- 如果使用的雲服務器,那麼去開放安全組的端口
- 開放linux端口,iptables -A INPUT -ptcp --dport 端口號 -j ACCEPT
然後開始連接,拿master舉例:
端口號使用映射的那個主機端口號即可,賬號爲root,密碼是剛剛設置的12456
6. 配置Master(主)
通過命令進入到Master容器內部:
docker exec -it master /bin/bash
進入容器後,切換到/etc/mysql目錄下
cd /etc/mysql
然後使用命令對my.cnf進行編輯
vi my.cnf
此時會報出bash: vi: command not found
,需要我們在docker容器內部自行安裝vim。一次執行以下命令即可安裝成功
apt-get update
apt-get install vim
然後再次使用vim編輯my.cnf,在my.cnf中添加如下配置:
[mysqld]
## 同一局域網內注意要唯一,所以設置爲主機的mysql 端口地址就可以了
server-id=3339
## 開啓二進制日誌功能,可以隨便取(關鍵)
log-bin=mysql-bin
配置完成之後,需要重啓master容器
#先退出容器內部
exit
#重啓master容器
docker restart master
在Master數據庫創建數據同步用戶,授予用戶 slave REPLICATION SLAVE權限和REPLICATION CLIENT權限,用於在主從庫之間同步數據。做法:使用navicat連接master數據庫,然後新建一個查詢,輸入語句:
CREATE USER 'slave'@'%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'%';
至此,master數據庫配置完成
7. 配置slaver(從)
和配置Master(主)一樣,在Slave配置文件my.cnf中添加如下配置:
[mysqld]
## 設置server_id,注意要唯一
server-id=3340
## 開啓二進制日誌功能,以備Slave作爲其它Slave的Master時使用
log-bin=mysql-slave-bin
## relay_log配置中繼日誌
relay_log=edu-mysql-relay-bin
配置完成後也需要重啓slaver1,slaver2容器。
8. 鏈接Master(主)和Slave(從)
8.1 查詢master的一些狀態
使用navicat連接master數據庫,並輸入查詢語句:
show master status;
結果:
File和Position字段的值後面將會用到,在後面的操作完成之前,需要保證Master庫不能做任何操作,否則將會引起狀態變化,File和Position字段的值變化。
8.2 操作從數據庫
使用navicat連接兩個slaver數據庫,並執行以下sql語句:
change master to master_host='172.17.0.2', master_user='slave', master_password='123456', master_port=3306, master_log_file='mysql-bin.000001', master_log_pos= 2830, master_connect_retry=30;
命令說明:
master_host :Master的地址,指的是容器的獨立ip,可以通過docker inspect --format='{{.NetworkSettings.IPAddress}}' 容器名稱|容器id
查詢容器的ip
master_port:Master的端口號,指的是容器的端口號
master_user:用於數據同步的用戶,即我們上面在master數據庫中創建的slaver用戶,該用戶可以同步主從數據庫數據
master_password:用於同步的用戶的密碼
master_log_file:指定 Slave 從哪個日誌文件開始複製數據,即上文中提到的 File 字段的值
master_log_pos:從哪個 Position 開始讀,即上文中提到的 Position 字段的值
master_connect_retry:如果連接失敗,重試的時間間隔,單位是秒,默認是60秒
8.3 查看slaver狀態
在slaver終端上輸入sql語句
show slave status
結果:slave-io-running 和slave-sql-running 都爲NO
正常情況下,SlaveIORunning 和 SlaveSQLRunning 都是No,因爲我們還沒有開啓主從複製過程。
8.4 開啓主從複製
在slaver數據庫上運行
start slave
然後再次查詢主從同步狀態
會發現SlaveIORunning 和 SlaveSQLRunning 變爲了YES,即表示主從複製鏈接成功
9. 測試主從複製
只需要在master上添加一個數據庫,然後查看兩個從數據庫是否也存在,如果存在,那麼說明覆製成功
10. 其他
10.1 停止主從複製
如果你不想從數據庫去監聽主數據庫的變化了,那麼就需要停止主從複製
在slaver數據庫上執行sql
stop slave;
如果你想重置slaver數據庫讀取主數據變化數據的位置(slaver都是通過讀取master的二進制文件來進行數據庫的數據變更)到主數據庫最原始的狀態,那麼使用以下sql命令:
reset slave;
但是使用了這個指令後,一般都會導致 slaver重新連接master進行主從複製 失敗,只能重新關聯以下master。
先停止以下slaver的主從複製並且重置
stop slave;
reset slave;
再去master數據庫中查看下狀態,即file和position:
show master status;
然後再次再slaver中進行關聯:
change master to master_host='172.17.0.2', master_user='slave', master_password='123456', master_port=3306, master_log_file='mysql-bin.000002', master_log_pos= 344, master_connect_retry=30;
最後重新啓動slaver的主從複製
start slave
查看slaver狀態
show slave status
發現SlaveIORunning 和 SlaveSQLRunning 又變爲了YES,說明成功了。
但是 你在停止主從複製這個時間段的數據已經同步不過來了。
如果你還是想同步過來,那你只能去master中的二進制文件中查看上次停止執行的指針位置,然後重新關聯主從時指定一下
11 spring boot+mybatis實現動態數據源
現在我們已經有一主雙從的數據庫了,那麼接下來就需要實現讀寫分離了。怎麼實現呢?我們先用簡單的動態數據源切換的方式來做
源碼位置:https://gitee.com/tfp-study/dynamic-data-source
我只做簡單的一些說明:
結構大致是這樣的,說明一下config包中的類:
- MapperConfig:這個類中需要定義3個數據源(一主雙從),sqlSession等相關bean
- DynamicDataSource: 這個類主要是繼承AbstractRoutingDataSource類,重寫determineCurrentLookupKey方法,通過這個方法來指定某次的數據庫操作需要使用哪個數據源(調用ThreadLocalDataSource中的方法獲取)
- ThreadLocalDataSource: 通過ThreadLocal來存儲某次數據庫操作需要使用的數據源
當我們需要切換數據源時:
通過向ThreadLocal中設置值來決定使用哪個數據源。
集成時遇到的坑:
問題:
一開始我在application.yml中的配置:
mybatis:
mapper-locations: mybatis/mappers/UserMapper.xml
type-aliases-package: cn.tanfp.dynamicdatasource.pojo
configuration:
map-underscore-to-camel-case: true
call-setters-on-nulls: true
並且我的mapper.xml是放在resource文件夾中的。
然後我每次訪問接口都會報錯:
Invalid bound statement (not found): cn.tanfp.dynamicdatasource.mapper.UserMapper.xxxxxx。
分析:
我一開始以爲可能是xml或掃描包什麼的路徑有問題,結果核對了一遍,發現並沒有錯,並且target中class文件中,也存在xml文件。然後我就很疑惑,通過查看源碼,終於發現了原因:
首先看下源碼:
通過源碼就能發現,由於我們在MapperConfig類中,創建了3個DataSource Bean,導致Mybatis的自動配置類沒有加載成功,從而使得application.yml中有關mybatis的配置屬性全部失效了。
解決辦法:
在我們自己寫的MapperConfig中,通過設置sqlSessionFactoryBean對象的屬性來指定
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean() throws IOException {
SqlSessionFactoryBean sqlSessionFactoryBean=new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicDataSource());
// 設置mapper.xml掃描
// (一定不能在application.yml中配置,因爲我們現在是用的自己的mybatis配置,application.yml中的配置是失效狀態,要配置屬性,只能在這個配置類中配置)
sqlSessionFactoryBean.setMapperLocations(
new PathMatchingResourcePatternResolver().
getResources("classpath:mybatis/mappers/*.xml"));
// 設置返回值pojo的別名包
sqlSessionFactoryBean.setTypeAliasesPackage("cn.tanfp.dynamicdatasource.pojo");
return sqlSessionFactoryBean;
}