基于Docker搭建MySQL主從復(fù)制架構(gòu)實(shí)踐
下面,直接進(jìn)入主題,如果還沒(méi)接觸過(guò)Docker的朋友,建議先去學(xué)習(xí)一下Docker。Docker對(duì)于個(gè)人開(kāi)發(fā)者來(lái)說(shuō)也是一大神器,會(huì)大大降低我們很多學(xué)習(xí)成本,比如搭建各種組件環(huán)境等。
創(chuàng)建網(wǎng)段
因?yàn)槭谴罱ㄒ粋€(gè)主從復(fù)制架構(gòu),master和slave數(shù)據(jù)庫(kù)都需要進(jìn)行網(wǎng)絡(luò)通信,所以需要保證master和slave的網(wǎng)絡(luò)是通的。
我們可以基于Docker模擬一個(gè)內(nèi)網(wǎng)網(wǎng)段。如下,創(chuàng)建mysql-replication網(wǎng)絡(luò):
docker network create mysql-replication
查看剛才創(chuàng)建的網(wǎng)絡(luò):
docker network inspect mysql-replication
輸出內(nèi)容如下:
[{"Name":"mysql-replication","Id":"8e0e22b80aff26987986959c2814fa90a2b390f2de36020f7c245d79a308bc91","Created":"2021-02-20T10:08:28.364053029+08:00","Scope":"local","Driver":"bridge","EnableIPv6":false,"IPAM":{"Driver":"default","Options":{},"Config":[{"Subnet":"172.29.0.0/16","Gateway":"172.29.0.1"}]},"Internal":false,"Attachable":false,"Ingress":false,"ConfigFrom":{"Network":""},"ConfigOnly":false,"Containers":{},"Options":{},"Labels":{}}]
這樣我們就創(chuàng)建好了網(wǎng)絡(luò),模擬了一個(gè)內(nèi)網(wǎng)網(wǎng)段。
創(chuàng)建mysql容器
1.拉取mysql鏡像
這里我們以拉去5.7版本為例
docker pull mysql:5.7
2.啟動(dòng)兩個(gè)mysql容器
啟動(dòng)mysql容器,命名為mysql-a
docker run -d \--network=mysql-replication \--name=mysql-a \-p 3308:3306 \-v /root/mysql-replication-test/mysql-a/data:/var/lib/mysql \-e MYSQL_ROOT_PASSWORD=root mysql:5.7
啟動(dòng)mysql容器,命名為mysql-b
docker run -d \--network=mysql-replication \--name=mysql-b \-p 3309:3306 \-v /root/mysql-replication-test/mysql-b/data:/var/lib/mysql \-e MYSQL_ROOT_PASSWORD=root mysql:5.7
這樣,我們就啟動(dòng)了兩個(gè)mysql容器了,并且都處于mysql-replication這個(gè)網(wǎng)絡(luò)下。
搭建主從
我們讓mysql-a擔(dān)任Master角色
數(shù)據(jù)準(zhǔn)備
查看mysql-a、mysql-b的ip
docker inspect mysql-a | grep "IPAddress"docker inspect mysql-b | grep "IPAddress"
mysql-a的ip=172.29.0.2,mysql-b的ip=172.29.0.3
修改master數(shù)據(jù)庫(kù)配置
進(jìn)入mysql容器修改配置
docker exec-ti mysql-a bash
然后修改配置:
設(shè)置mysql id
開(kāi)啟binlog日志
vim /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]pid-file =/var/run/mysqld/mysqld.pidsocket =/var/run/mysqld/mysqld.sockdatadir =/var/lib/mysql#log-error = /var/log/mysql/error.log# By default we only accept connections from localhost#bind-address = 127.0.0.1# Disabling symbolic-links is recommended to prevent assorted security riskssymbolic-links=0### 配置mysql id,要確保主從的id不同server-id=100### 開(kāi)啟binlog日志log-bin=mysql-bin
重啟mysql-a容器,讓配置生效
docker restart mysql-a
重新登錄mysql-a的mysql,執(zhí)行show master status查看當(dāng)前狀態(tài),后面配置slave mysql需要用到,這里我們查到:
我們后面會(huì)用到File和Position字段的值
master數(shù)據(jù)庫(kù)創(chuàng)建同步用戶
在master mysql上面,創(chuàng)建一個(gè)用戶,用于同步數(shù)據(jù)。
# 創(chuàng)建用戶slave_01,密碼是salve_01,允許ip為172.29.0.*的機(jī)器同步數(shù)據(jù)create user 'slave_01'@'172.29.0.%' identified by'slave_01';# 授權(quán)grant replication slave on *.* to 'slave_01'@'172.29.0.%';# 生效flush privileges;
備份master數(shù)據(jù)庫(kù)到slave數(shù)據(jù)庫(kù)
如果master數(shù)據(jù)庫(kù)沒(méi)有需要同步備份到從數(shù)據(jù)庫(kù)的數(shù)據(jù),這一步可以不處理。
在mysql-a容器里,執(zhí)行命令:
mysqldump --single-transaction -uroot -p --master-data=2-A>dump.sql
這樣在當(dāng)前目錄就可以看到了dump.sql,把dump.sql文件移動(dòng)到和宿主機(jī)共享的目錄:
mv dump.sql /var/lib/mysql
退出mysql-a容器,到dump.sql目錄拷貝dump.sql到容器mysql-b:
docker cp dump.sql b2a06688fe70:/
其中,b2a06688fe70是mysql-b容器的id。
登錄mysql-b,導(dǎo)入備份數(shù)據(jù):
docker exec-ti mysql-b bash# 登錄mysqlmysql -u root -p# 登錄上mysql后,執(zhí)行備份sqlsource /dump.sql
這樣,我們就把mysql-a的數(shù)據(jù)備份到mysql-b。
修改slave數(shù)據(jù)庫(kù)配置
修改mysql配置文件:
vim /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]pid-file =/var/run/mysqld/mysqld.pidsocket =/var/run/mysqld/mysqld.sockdatadir =/var/lib/mysql#log-error = /var/log/mysql/error.log# By default we only accept connections from localhost#bind-address = 127.0.0.1# Disabling symbolic-links is recommended to prevent assorted security riskssymbolic-links=0# 設(shè)置salve mysql id,不和master重復(fù)server-id=101# 開(kāi)啟并配置slave的binlog文件名稱log-bin=mysql-slave-bin# 配置binlog中繼文件log-bin=mysql-slave-bin
重啟mysql-b,讓配置生效:
docker restart mysql-b
配置從庫(kù)從主庫(kù)復(fù)制數(shù)據(jù):
change master to master_host='172.29.0.2',master_user='slave_01',master_password='slave_01',master_port=3306,master_log_file='mysql-bin.000001',master_log_pos=154,master_connect_retry=30
master_host:master的host
master_user:用于復(fù)制數(shù)據(jù)用的賬號(hào)
master_password:用戶復(fù)制數(shù)據(jù)用的賬號(hào)密碼
master_port:master的端口
masterlogfile:master的binlog的File名稱,在show master status命令可以查出來(lái)
masterlogpost:master的binlog的Position,在show master status命令可以查出來(lái)
masterconnectretry:連接重試時(shí)間,單位:秒
開(kāi)啟主從復(fù)制:
start slave;
查看開(kāi)啟狀態(tài):
show slave status \G;
如果看到:
SlaveSQLRunning: YES
SlaveIORunning: YES
SlaveSQLRunning_State: Slave has read all relay log; waiting for more updates
那么主從復(fù)制就已經(jīng)配置完成了。
驗(yàn)證是否完成主從復(fù)制
我們可以在mysql-a添加一條數(shù)據(jù),然后到mysql-b上面看,是不是有新添加的數(shù)據(jù)。
有就表示主從復(fù)制已經(jīng)完成,沒(méi)有就表示主從復(fù)制沒(méi)有完成。
半同步復(fù)制優(yōu)化
我們到這里是搭建好了MySQL的主從復(fù)制,但是有個(gè)問(wèn)題,它是異步復(fù)制的。在一些極端條件下會(huì)導(dǎo)致數(shù)據(jù)問(wèn)題。比如master插入一條數(shù)據(jù)并成功提交事務(wù)了,這個(gè)時(shí)候binlog還沒(méi)有同步到slave,master就宕機(jī)了。當(dāng)主從發(fā)生切換后,新的master是沒(méi)有舊的master新插入的那條數(shù)據(jù)的。
解決這個(gè)問(wèn)題,業(yè)界有個(gè)方案:半同步。這里不展開(kāi)說(shuō)半同步的原理,只說(shuō)怎么實(shí)現(xiàn)半同步主從復(fù)制。
安裝半同步插件
在master上安裝半同步插件:
# 安裝插件install plugin rpl_semi_sync_master soname 'semisync_master.so';# 開(kāi)啟插件setglobal rpl_semi_sync_master_enabled=1;# 查詢是否開(kāi)啟了半同步,Rpl_semi_sync_master_status=ON表示半同步開(kāi)啟show status like 'Rpl%';
配置mysql重啟后加載半同步插件:
vim /etc/mysql/mysql.conf.d/mysqld.cnf
# 半同步插件加載plugin-load=rpl_semi_sync_master=semisync_master.so# 打開(kāi)主的半同步rpl_semi_sync_master_enabled=1
在slave上安裝半同步插件:
# 安裝插件install plugin rpl_semi_sync_slave soname 'semisync_slave.so'# 開(kāi)啟插件setglobal rpl_semi_sync_slave_enabled=1;# 重啟同步線程stop slave io_thread;start slave_io_thread;# 查詢是否開(kāi)啟了半同步,Rpl_semi_sync_slave_status=ON表示半同步開(kāi)啟show status like 'Rpl%';
配置mysql重啟加載半同步插件:
vim /etc/mysql/mysql.conf.d/mysqld.cnf
# 從庫(kù)半同步插件加載plugin-load=rpl_semi_sync_slave=semisync_slave.so# 打開(kāi)從的半同步rpl_semi_sync_slave_enabled=1
到這里就開(kāi)啟了半同步。
從庫(kù)設(shè)置為只讀
我們配置主從復(fù)制的時(shí)候,一般是不允許從庫(kù)寫(xiě)入的,這樣會(huì)導(dǎo)致數(shù)據(jù)不一致,因?yàn)閺膸?kù)數(shù)據(jù)無(wú)法同步給到主庫(kù)。所以我們一般會(huì)把從庫(kù)設(shè)置為只讀。
# 查看只讀狀態(tài),read_only=ON是開(kāi)啟,OFF是關(guān)閉show global variables like "%read_only%";# 給所有表加上讀鎖flush tables with read lock;# 設(shè)置只讀setglobal read_only=1;# 如果想解除只讀,執(zhí)行下面兩行命令即可unlock tables;setglobal read_only=0;
注意點(diǎn)
我們安裝的mysql容器,是沒(méi)有vim工具的,需要我們自行安裝,安裝步驟:
apt-get updateapt-get install vim
