Mysql主从复制(docker例子)
一、Binlog
Binlog 日志主要作用是數(shù)據(jù)恢復(fù)和主從復(fù)制。本身就是二進制格式的日志文件,網(wǎng)絡(luò)傳輸無需進行協(xié)議轉(zhuǎn)換。MySQL 集群的高可用,負載均衡,讀寫分離等功能都是基于Binlog 來實現(xiàn)的。關(guān)于Binlog的文章
二、MySQL 主從復(fù)制主流架構(gòu)模型
我們基于 Binlog 可以復(fù)制出一臺 MySQL 服務(wù)器,也可以復(fù)制出多臺,取決于我們想實現(xiàn)什么功能。主流的系統(tǒng)架構(gòu)有如下幾種方式:
一主一從 / 一主多從
一主一從和一主多從是最常見的主從架構(gòu)方式,一般實現(xiàn)主從配置或者讀寫分離都可以采用這種架構(gòu)。如果是一主多從的模式,當 Slave 增加到一定數(shù)量時,Slave 對 Master 的負載以及網(wǎng)絡(luò)帶寬都會成為一個嚴重的問題。
多主一從
MySQL 5.7 開始支持多主一從的模式,將多個庫的數(shù)據(jù)備份到一個庫中存儲。
雙主復(fù)制
理論上跟主從一樣,但是兩個MySQL服務(wù)器互做對方的從,任何一方有變更,都會復(fù)制對方的數(shù)據(jù)到自己的數(shù)據(jù)庫。雙主適用于寫壓力比較大的業(yè)務(wù)場景,或者 DBA 做維護需要主從切換的場景,通過雙主架構(gòu)避免了重復(fù)搭建從庫的麻煩。(主從相互授權(quán)連接,讀取對方binlog日志并更新到本地數(shù)據(jù)庫的過程;只要對方數(shù)據(jù)改變,自己就跟著改變)
級聯(lián)復(fù)制
級聯(lián)模式下因為涉及到的 slave 節(jié)點很多,所以如果都連在 master 上對主服務(wù)器的壓力肯定是不小的。所以部分 slave 節(jié)點連接到它上一級的從節(jié)點上。這樣就緩解了主服務(wù)器的壓力。級聯(lián)復(fù)制解決了一主多從場景下多個從庫復(fù)制對主庫的壓力,帶來的弊端就是數(shù)據(jù)同步延遲比較大。
企業(yè)級架構(gòu)
海量用戶通過數(shù)據(jù)庫代理(mycat,shardingjdbc)訪問數(shù)據(jù)庫,然后數(shù)據(jù)庫讀寫分離,寫由master承擔,讀由slave承擔,同時,不同的業(yè)務(wù)場景可以指定特定的slave,降低數(shù)據(jù)庫并發(fā)量。同時,有兩個master(雙主復(fù)制,進行熱備份,需要keepalived等技術(shù)的支持),兩個互相同步,只要有一個master宕機,就提升另一個master為主master,而不是從slave中選舉。但是,這樣一個但master也無法突破數(shù)據(jù)量,那就得進行分庫分表了(進而將數(shù)據(jù)存在硬盤上),分完每一個分庫,又做成改圖得邏輯架構(gòu),從而提升mysql集群的可用性
三、MySQL 主從復(fù)制原理
MySQL 主從復(fù)制涉及到三個線程:
- 一個在主節(jié)點的線程:log dump thread
- 從庫會生成兩個線程:一個 I/O 線程,一個 SQL 線程
如下圖所示:
-
主節(jié)點 log dump 線程
當從節(jié)點連接主節(jié)點時,主節(jié)點會為其創(chuàng)建一個 log dump 線程,用于發(fā)送和讀取 Binlog 的內(nèi)容。在讀取 Binlog 中的操作時,log dump 線程會對主節(jié)點上的 Binlog 加鎖;當讀取完成發(fā)送給從節(jié)點之前,鎖會被釋放。主節(jié)點會為自己的每一個從節(jié)點創(chuàng)建一個 log dump 線程。 -
從節(jié)點I/O線程
當從節(jié)點上執(zhí)行start slave命令之后,從節(jié)點會創(chuàng)建一個 I/O 線程用來連接主節(jié)點,請求主庫中更新的Binlog。I/O 線程接收到主節(jié)點的 log dump 進程發(fā)來的更新之后,保存在本地 relay-log(中繼日志)中。 -
relay log
這里又引申出一個新的日志概念。MySQL 進行主主復(fù)制或主從復(fù)制的時候會在要復(fù)制的服務(wù)器下面產(chǎn)生相應(yīng)的 relay log。relay log 是怎么產(chǎn)生的呢?從服務(wù)器 I/O 線程將主服務(wù)器的 Binlog 日志讀取過來,解析到各類 Events 之后記錄到從服務(wù)器本地文件,這個文件就被稱為 relay log。然后 SQL 線程會讀取 relay log 日志的內(nèi)容并應(yīng)用到從服務(wù)器,從而使從服務(wù)器和主服務(wù)器的數(shù)據(jù)保持一致。中繼日志充當緩沖區(qū),這樣 master 就不必等待 slave 執(zhí)行完成才發(fā)送下一個事件。
四、relay log 相關(guān)參數(shù)查詢
show variables like '%relay%';標記 relay log 允許的最大值,如果該值為 0,則默認值為 max_binlog_size(1G);如果不為 0,則max_relay_log_size 則為最大的 relay_log 文件大小。
是否自動清空不再需要中繼日志時。默認值為1(啟用)。
當 slave 從庫宕機后,假如 relay log 損壞了,導(dǎo)致一部分中繼日志沒有處理,則自動放棄所有未執(zhí)行的 relay log,并且重新從 master 上獲取日志,這樣就保證了 relay log 的完整性。默認情況下該功能是關(guān)閉的,將 relay_log_recovery 的值設(shè)置為 1 時,可在 slave 從庫上開啟該功能,建議開啟。
防止中繼日志寫滿磁盤,這里設(shè)置中繼日志最大限額。但此設(shè)置存在主庫崩潰,從庫中繼日志不全的情況,不到萬不得已,不推薦使用。
這個參數(shù)和 Binlog 中的 sync_binlog作用相同。當設(shè)置為 1 時,slave 的 I/O 線程每次接收到 master 發(fā)送過來的 Binlog 日志都要寫入系統(tǒng)緩沖區(qū),然后刷入 relay log 中繼日志里,這樣是最安全的,因為在崩潰的時候,你最多會丟失一個事務(wù),但會造成磁盤的大量 I/O。當設(shè)置為 0 時,并不是馬上就刷入中繼日志里,而是由操作系統(tǒng)決定何時來寫入,雖然安全性降低了,但減少了大量的磁盤 I/O 操作。這個值默認是 0,可動態(tài)修改,建議采用默認值。
當設(shè)置為 1 時,slave 的 I/O 線程每次接收到 master 發(fā)送過來的 Binlog 日志都要寫入系統(tǒng)緩沖區(qū),然后刷入 relay-log.info 里,這樣是最安全的,因為在崩潰的時候,你最多會丟失一個事務(wù),但會造成磁盤的大量 I/O。當設(shè)置為 0 時,并不是馬上就刷入 relay-log.info 里,而是由操作系統(tǒng)決定何時來寫入,雖然安全性降低了,但減少了大量的磁盤 I/O 操作。這個值默認是0,可動態(tài)修改,建議采用默認值。
五、從節(jié)點 SQL 線程
SQL 線程負責讀取 relay log 中的內(nèi)容,解析成具體的操作并執(zhí)行,最終保證主從數(shù)據(jù)的一致性。對于每一個主從連接,都需要這三個進程來完成。當主節(jié)點有多個從節(jié)點時,主節(jié)點會為每一個當前連接的從節(jié)點建一個 log dump 進程,而每個從節(jié)點都有自己的 I/O 進程,SQL 進程。從節(jié)點用兩個線程將從主庫拉取更新和執(zhí)行分成獨立的任務(wù),這樣在執(zhí)行同步數(shù)據(jù)任務(wù)的時候,不會降低讀操作的性能。比如,如果從節(jié)點沒有運行,此時 I/O 進程可以很快從主節(jié)點獲取更新,盡管 SQL 進程還沒有執(zhí)行。如果在 SQL 進程執(zhí)行之前從節(jié)點服務(wù)停止,至少 I/O 進程已經(jīng)從主節(jié)點拉取到了最新的變更并且保存在本地 relay log 中,當服務(wù)再次起來之后就可以完成數(shù)據(jù)的同步。要實施復(fù)制,首先必須打開 Master 端的 Binlog 功能,否則無法實現(xiàn)。因為整個復(fù)制過程實際上就是 Slave 從 Master 端獲取該日志然后再在自己身上完全順序的執(zhí)行日志中所記錄的各種操作。如下圖所示:
六、數(shù)據(jù)復(fù)制的基本過程
七、MySQL 基于 Binlog 主從復(fù)制的模式介紹
MySQL 主從復(fù)制默認是異步的模式。MySQL增刪改操作會全部記錄在 Binlog 中,當 slave 節(jié)點連接 master 時,會主動從 master 處獲取最新的 Binlog 文件。并把 Binlog 存儲到本地的 relay log 中,然后去執(zhí)行 relay log 的更新內(nèi)容。
- 異步模式 (async-mode)
異步模式如下圖所示:
這種模式下,主節(jié)點不會主動推送數(shù)據(jù)到從節(jié)點,主庫在執(zhí)行完客戶端提交的事務(wù)后會立即將結(jié)果返給給客戶端,并不關(guān)心從庫是否已經(jīng)接收并處理,這樣就會有一個問題,主節(jié)點如果崩潰掉了,此時主節(jié)點上已經(jīng)提交的事務(wù)可能并沒有傳到從節(jié)點上,如果此時,強行將從提升為主,可能導(dǎo)致新主節(jié)點上的數(shù)據(jù)不完整。
- 半同步模式(semi-sync)
介于異步復(fù)制和全同步復(fù)制之間,主庫在執(zhí)行完客戶端提交的事務(wù)后不是立刻返回給客戶端,而是等待至少一個從庫接收到并寫到 relay log 中才返回成功信息給客戶端(只能保證主庫的 Binlog 至少傳輸?shù)搅艘粋€從節(jié)點上),否則需要等待直到超時時間然后切換成異步模式再提交。
相對于異步復(fù)制,半同步復(fù)制提高了數(shù)據(jù)的安全性,一定程度的保證了數(shù)據(jù)能成功備份到從庫,同時它也造成了一定程度的延遲,但是比全同步模式延遲要低,這個延遲最少是一個 TCP/IP 往返的時間。所以,半同步復(fù)制最好在低延時的網(wǎng)絡(luò)中使用。半同步模式不是 MySQL 內(nèi)置的,從 MySQL 5.5 開始集成,需要 master 和 slave 安裝插件開啟半同步模式。
- 全同步模式
指當主庫執(zhí)行完一個事務(wù),然后所有的從庫都復(fù)制了該事務(wù)并成功執(zhí)行完才返回成功信息給客戶端。因為需要等待所有從庫執(zhí)行完該事務(wù)才能返回成功信息,所以全同步復(fù)制的性能必然會收到嚴重的影響。
八、基于docker搭建Mysql主從復(fù)制例子
- mydata/mysql/master
master下編寫my.conf - mydata/mysql/slave-1
slave-1下編寫my-slave-1.cnf - mydata/mysql/slave-2
slave-2下編寫my-slave-2.cnf
my.conf內(nèi)容如下
[mysqld] user=mysql character-set-server=utf8 default_authentication_plugin=mysql_native_password secure_file_priv=/var/lib/mysql expire_logs_days=7 sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION max_connections=1000[client] default-character-set=utf8[mysql] default-character-set=utf8參考文章
在my.cnf中添加以下配置,注意添加到[mysqld]下面
#服務(wù)器id 留心各自的server-id一定要彼此獨立,不能重復(fù),否則,會出現(xiàn)如下錯誤: #Slave: received end packet FROM server, apparent master shutdown server_id=1#bing二進制日志文件 log-bin=mysql-bin#是否只讀 read-only=0#要同步的數(shù)據(jù)庫 binlog-do-db=test#不同步的數(shù)據(jù)庫 binlog-ignore-db=ignore#以下是每個數(shù)據(jù)庫都會自帶的數(shù)據(jù)庫,不需要復(fù)制 replicate-ignore-db=mysql replicate-ignore-db=sys replicate-ignore-db=information_schema replicate-ignore-db=performance_schema也分別在my-slave-1.cnf、my-slave-2.cnf配置以上信息(注意修改server_id),并重啟docker
注意這里,8.0版本的mysql已經(jīng)將用戶的創(chuàng)建和授權(quán)分開了。
- CREATE USER 'backup'@'%' IDENTIFIED WITH mysql_native_password BY '123456'; - GRANT REPLICATION SLAVE ON *.* TO 'backup'@'%';這里一定要注意:由于我3個mysql都是基于docker的虛擬網(wǎng)絡(luò)的,所以這里的MASTER_PORT不是暴露給外網(wǎng)的13306,而是基于docker局域網(wǎng)內(nèi)的3306,如果遇到Slave_IO_Running:Connecting ; Slave_SQL_Running:Yes的問題,參考這兩篇文章一定能解決,文章1,文章2。
注意這里的host不是簡單的localhost,而是docker分配的
- 啟動同步:
- 關(guān)閉同步
至此,mysql主從同步搭建完成
可以看到master中的test同步到了slave中,而ignore則沒有
九、主從復(fù)制可能會出現(xiàn)的問題
因為 Slave 端是通過 I/O thread 單線程來實現(xiàn)數(shù)據(jù)解析入庫;而 Master 端寫 Binlog 由于是順序?qū)懶屎芨?#xff0c;當主庫的 TPS 很高的時候,必然 Master 端的寫效率要高過 Slave 端的讀效率,這時候就有同步延遲的問題。I/O Thread 的同步是基于庫的,即同步幾個庫就會開啟幾個 I/O Thread??梢酝ㄟ^ show slave status 命令查看 Seconds_Behind_Master 的值來看是否出現(xiàn)同步延遲,這個值代表主從同步延遲的時間,值越大說明延遲越嚴重。值為 0 為正常情況,正值表示已經(jīng)出現(xiàn)延遲,數(shù)字越大從庫落后主庫越多?;?Binlog 的復(fù)制方式肯定有這種問題,MySQL 官方也意識到,單線程不如多線程強,所以在 MySQL 5.7 版本引入了基于組提交的并行復(fù)制(官方稱為Enhanced Multi-threaded Slaves,即MTS),設(shè)置參數(shù):slave_parallel_workers>0 即可,并且 global.slave_parallel_type=‘LOGICAL_CLOCK’,即可支持一個 schema(庫) 下,slave_parallel_workers個 worker 線程并發(fā)執(zhí)行 relay log 中主庫提交的事務(wù)。
一個組提交的事務(wù)都是可以并行回放(配合binary log group commit);slave 機器的 relay log 中 last_committed 相同的事務(wù)(sequence_num不同)可以并發(fā)執(zhí)行。其中,變量 slave-parallel-type 可以有兩個值:
- DATABASE 默認值,基于庫的并行復(fù)制方式
- LOGICAL_CLOCK,基于組提交的并行復(fù)制方式
MySQL 5.7 開啟 MTS 很簡單,只需要在 Slave 從數(shù)據(jù)庫的 my.cnf 文件中如下配置即可:
# slaveslave-parallel-type=LOGICAL_CLOCKslave-parallel-workers=8 #一般建議設(shè)置4-8,太多的線程會增加線程之間的同步開銷master_info_repository=TABLErelay_log_info_repository=TABLErelay_log_recovery=ON當然多線程帶來的并行復(fù)制方案也有很多實現(xiàn)難點,比如事務(wù)都是有序執(zhí)行的,如果并行回放會不會存在執(zhí)行數(shù)據(jù)錯亂的問題。這些問題就不在本節(jié)解釋,大家有興趣可以繼續(xù)深究。
參考文章
總結(jié)
以上是生活随笔為你收集整理的Mysql主从复制(docker例子)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 看看老司机是如何提升B端产品架构能力的
- 下一篇: MySQL数据同步,出现Slave_SQ