本文很是詳細地介紹MySQL複製相關的內容,包括基本概念、複製原理、如何配置不一樣類型的複製(傳統複製)等等。在此文章以後,還有幾篇文章分別介紹GTID複製、半同步複製、實現MySQL的動靜分離,以及MySQL 5.7.17引入的革命性功能:組複製(MGR)。html
本文是MySQL Replication的基礎,但卻很是重要。對於MySQL複製,如何搭建它不是重點(由於簡單,網上資源很是多),如何維護它纔是重點(網上資源不集中)。如下幾個知識點是掌握MySQL複製所必備的:mysql
show slave status
中的一些狀態信息本文對以上內容都作了很是詳細的說明。但願對各位初學、深刻MySQL複製有所幫助。算法
mysql replication官方手冊:https://dev.mysql.com/doc/refman/5.7/en/replication.html。sql
mysql複製是指從一個mysql服務器(MASTER)將數據經過日誌的方式通過網絡傳送到另外一臺或多臺mysql服務器(SLAVE),而後在slave上重放(replay或redo)傳送過來的日誌,以達到和master數據同步的目的。shell
它的工做原理很簡單。首先確保master數據庫上開啓了二進制日誌,這是複製的前提。數據庫
change master to
語句設置鏈接到master服務器的鏈接參數,在執行該語句的時候要提供一些信息,包括如何鏈接和要從哪複製binlog,這些信息在鏈接的時候會記錄到slave的datadir下的master.info文件中,之後再鏈接master的時候將不用再提供這新信息而是直接讀取該文件進行鏈接。站在slave的角度上看,過程以下:緩存
站在master的角度上看,過程以下(默認的異步複製模式,前提是設置了sync_binlog=1
,不然binlog刷盤時間由操做系統決定):安全
因此,能夠認爲複製大體有三個步驟:服務器
從複製的機制上能夠知道,在複製進行前,slave上必須具備master上部分完整內容做爲複製基準數據。例如,master上有數據庫A,二進制日誌已經寫到了pos1位置,那麼在複製進行前,slave上必需要有數據庫A,且若是要從pos1位置開始複製的話,還必須有和master上pos1以前徹底一致的數據。若是不知足這樣的一致性條件,那麼在replay中繼日誌的時候將不知道如何進行應用而致使數據混亂。也就是說,複製是基於binlog的position進行的,複製以前必須保證position一致。(注:這是傳統的複製方式所要求的)網絡
能夠選擇對哪些數據庫甚至數據庫中的哪些表進行復制。默認狀況下,MySQL的複製是異步的。slave能夠不用一直連着master,即便中間斷開了也能從斷開的position處繼續進行復制。
MySQL 5.6對比MySQL 5.5在複製上進行了很大的改進,主要包括支持GTID(Global Transaction ID,全局事務ID)複製和多SQL線程並行重放。GTID的複製方式和傳統的複製方式不同,經過全局事務ID,它不要求複製前slave有基準數據,也不要求binlog的position一致。
MySQL 5.7.17則提出了組複製(MySQL Group Replication,MGR)的概念。像數據庫這樣的產品,必需要儘量完美地設計一致性問題,特別是在集羣、分佈式環境下。Galera就是一個MySQL集羣產品,它支持多主模型(多個master),可是當MySQL 5.7.17引入了MGR功能後,Galera的優點再也不明顯,甚至MGR能夠取而代之。MGR爲MySQL集羣中多主複製的不少問題提供了很好的方案,可謂是一項革命性的功能。
複製和二進制日誌息息相關,因此學習本章必須先有二進制日誌的相關知識。
圍繞下面的拓撲圖來分析:
主要有如下幾點好處:
1.提供了讀寫分離的能力。
replication讓全部的slave都和master保持數據一致,所以外界客戶端能夠從各個slave中讀取數據,而寫數據則從master上操做。也就是實現了讀寫分離。
須要注意的是,爲了保證數據一致性,寫操做必須在master上進行。
一般說到讀寫分離這個詞,馬上就能意識到它會分散壓力、提升性能。
2.爲MySQL服務器提供了良好的伸縮(scale-out)能力。
因爲各個slave服務器上只提供數據檢索而沒有寫操做,所以"隨意地"增長slave服務器數量來提高整個MySQL羣的性能,而不會對當前業務產生任何影響。
之因此"隨意地"要加上雙引號,是由於每一個slave都要和master創建鏈接,傳輸數據。若是slave數量巨多,master的壓力就會增大,網絡帶寬的壓力也會增大。
3.數據庫備份時,對業務影響降到最低。
因爲MySQL服務器羣中全部數據都是一致的(至少幾乎是一致的),因此在須要備份數據庫的時候能夠任意中止某一臺slave的複製功能(甚至中止整個mysql服務),而後從這臺主機上進行備份,這樣幾乎不會影響整個業務(除非只有一臺slave,但既然只有一臺slave,說明業務壓力並不大,短時間內將這個壓力分配給master也不會有什麼影響)。
4.能提高數據的安全性。
這是顯然的,任意一臺mysql服務器斷開,都不會丟失數據。即便是master宕機,也只是丟失了那部分尚未傳送的數據(異步複製時纔會丟失這部分數據)。
5.數據分析再也不影響業務。
須要進行數據分析的時候,直接劃分一臺或多臺slave出來專門用於數據分析。這樣OLTP和OLAP能夠共存,且幾乎不會影響業務處理性能。
MySQL支持兩種不一樣的複製方法:傳統的複製方式和GTID複製。MySQL 5.7.17以後還支持組複製(MGR)。
從數據同步方式的角度考慮,MySQL支持4種不一樣的同步方式:同步(synchronous)、半同步(semisynchronous)、異步(asynchronous)、延遲(delayed)。因此對於複製來講,就分爲同步複製、半同步複製、異步複製和延遲複製。
客戶端發送DDL/DML語句給master,master執行完畢後還須要等待全部的slave都寫完了relay log才認爲這次DDL/DML成功,而後纔會返回成功信息給客戶端。同步複製的問題是master必須等待,因此延遲較大,在MySQL中不使用這種複製方式。
例如上圖中描述的,只有3個slave全都寫完relay log並返回ACK給master後,master纔會判斷這次DDL/DML成功。
客戶端發送DDL/DML語句給master,master執行完畢後還要等待一個slave寫完relay log並返回確認信息給master,master才認爲這次DDL/DML語句是成功的,而後纔會發送成功信息給客戶端。半同步複製只需等待一個slave的迴應,且等待的超時時間能夠設置,超時後會自動降級爲異步複製,因此在局域網內(網絡延遲很小)使用半同步複製是可行的。
例如上圖中,只有第一個slave返回成功,master就判斷這次DDL/DML成功,其餘的slave不管複製進行到哪個階段都可有可無。
客戶端發送DDL/DML語句給master,master執行完畢當即返回成功信息給客戶端,而無論slave是否已經開始複製。這樣的複製方式致使的問題是,當master寫完了binlog,而slave尚未開始複製或者複製還沒完成時,slave上和master上的數據暫時不一致,且此時master忽然宕機,slave將會丟失一部分數據。若是此時把slave提高爲新的master,那麼整個數據庫就永久丟失這部分數據。
顧名思義,延遲複製就是故意讓slave延遲一段時間再從master上進行復制。
此處先配置默認的異步複製模式。因爲複製和binlog息息相關,若是對binlog還不熟悉,請先了解binlog,見:詳細分析二進制日誌。
mysql支持一主一從和一主多從。可是每一個slave必須只能是一個master的從,不然從多個master接受二進制日誌後重放將會致使數據混亂的問題。
如下是一主一從的結構圖:
在開始傳統的複製(非GTID複製)前,須要完成如下幾個關鍵點,這幾個關鍵點指導後續複製的全部步驟。
server-id
,這是主從複製結構中很是關鍵的標識號。到了MySQL 5.7,彷佛不設置server id就沒法開啓binlog。設置server id須要重啓MySQL實例。sync_binlog=1
(MySQL 5.7.7以後默認爲1,以前的版本默認爲0),這樣每寫一次二進制日誌都將其刷新到磁盤,讓slave服務器能夠儘快地複製。防止萬一master的二進制日誌還在緩存中就宕機時,slave沒法複製這部分丟失的數據。innodb_flush_log_at_trx_commit=1
(默認值爲1),這樣每次提交事務都會當即將事務刷盤保證持久性和一致性。replication slave
用來讀取binlog。read_only
選項。這種狀況下,除了具備super權限(mysql 5.7.16還提供了super_read_only
禁止super的寫操做)和SQL線程能寫數據庫,其餘用戶都不能進行寫操做。這種禁寫對於slave來講,絕大多數場景都很是適合。一主一從是最簡單的主從複製結構。本節實驗環境以下:
[mysqld] # master datadir=/data socket=/data/mysql.sock log-bin=master-bin sync-binlog=1 server-id=100
[mysqld] # slave datadir=/data socket=/data/mysql.sock relay-log=slave-bin server-id=111
service mysqld restart
create user 'repl'@'192.168.100.%' identified by 'P@ssword1!'; grant REPLICATION SLAVE on *.* to 'repl'@'192.168.100.%';
對於複製而言,有幾種狀況:
第一種狀況此處不贅述。第二種狀況有幾種方法,例如使用mysqldump、冷備份、xtrabackup等工具,這其中又須要考慮是MyISAM表仍是InnoDB表。
在實驗開始以前,首先在master上新增一些測試數據,以innodb和myisam的數值輔助表爲例。
DROP DATABASE IF EXISTS backuptest; CREATE DATABASE backuptest; USE backuptest; # 建立myisam類型的數值輔助表和插入數據的存儲過程 CREATE TABLE num_isam (n INT NOT NULL PRIMARY KEY) ENGINE = MYISAM ; DROP PROCEDURE IF EXISTS proc_num1; DELIMITER $$ CREATE PROCEDURE proc_num1 (num INT) BEGIN DECLARE rn INT DEFAULT 1 ; TRUNCATE TABLE backuptest.num_isam ; INSERT INTO backuptest.num_isam VALUES(1) ; dd: WHILE rn * 2 < num DO BEGIN INSERT INTO backuptest.num_isam SELECT rn + n FROM backuptest.num_isam; SET rn = rn * 2 ; END ; END WHILE dd; INSERT INTO backuptest.num_isam SELECT n + rn FROM backuptest.num_isam WHERE n + rn <= num; END ; $$ DELIMITER ; # 建立innodb類型的數值輔助表和插入數據的存儲過程 CREATE TABLE num_innodb (n INT NOT NULL PRIMARY KEY) ENGINE = INNODB ; DROP PROCEDURE IF EXISTS proc_num2; DELIMITER $$ CREATE PROCEDURE proc_num2 (num INT) BEGIN DECLARE rn INT DEFAULT 1 ; TRUNCATE TABLE backuptest.num_innodb ; INSERT INTO backuptest.num_innodb VALUES(1) ; dd: WHILE rn * 2 < num DO BEGIN INSERT INTO backuptest.num_innodb SELECT rn + n FROM backuptest.num_innodb; SET rn = rn * 2 ; END ; END WHILE dd; INSERT INTO backuptest.num_innodb SELECT n + rn FROM backuptest.num_innodb WHERE n + rn <= num ; END ; $$ DELIMITER ; # 分別向兩個數值輔助表中插入100W條數據 CALL proc_num1 (1000000) ; CALL proc_num2 (1000000) ;
所謂數值輔助表是隻有一列的表,且這個字段的值全是數值,從1開始增加。例如上面的是從1到100W的數值輔助表。
mysql> select * from backuptest.num_isam limit 10; +----+ | n | +----+ | 1 | | 2 | | 3 | | 4 | | 5 | | 6 | | 7 | | 8 | | 9 | | 10 | +----+
若是master是全新的數據庫實例,或者在此以前沒有開啓過binlog,那麼它的座標位置是position=4。之因此是4而非0,是由於binlog的前4個記錄單元是每一個binlog文件的頭部信息。
若是master已有數據,或者說master之前就開啓了binlog並寫過數據庫,那麼須要手動獲取position。爲了安全以及沒有後續寫操做,必須先鎖表。
mysql> flush tables with read lock;
注意,此次的鎖表會致使寫阻塞以及innodb的commit操做。
而後查看binlog的座標。
mysql> show master status; # 爲了排版,簡化了輸出結果 +-------------------+----------+--------------+--------+--------+ | File | Position | Binlog_Do_DB | ...... | ...... | +-------------------+----------+--------------+--------+--------+ | master-bin.000001 | 623 | | | | +-------------------+----------+--------------+--------+--------+
記住master-bin.000001和623。
下面給出3種備份方式以及對應slave的恢復方法。建議備份全部庫到slave上,若是要篩選一部分數據庫或表進行復制,應該在slave上篩選(篩選方式見後文篩選要複製的庫和表),而不該該在master的備份過程當中指定。
innodb_file_per_table=ON
的InnoDB表。若是沒有開啓該變量,innodb表使用公共表空間,沒法直接冷備份。因此,若是沒有涉及到innodb表,那麼在鎖表以後,能夠直接冷拷貝。最後釋放鎖。
mysql> flush tables with read lock; mysql> show master status; # 爲了排版,簡化了輸出結果 +-------------------+----------+--------------+--------+--------+ | File | Position | Binlog_Do_DB | ...... | ...... | +-------------------+----------+--------------+--------+--------+ | master-bin.000001 | 623 | | | | +-------------------+----------+--------------+--------+--------+
shell> rsync -avz /data 192.168.100.150:/ mysql> unlock tables;
此處實驗,假設要備份的是整個實例,由於涉及到了innodb表,因此建議關閉MySQL。由於是冷備份,因此slave上也應該關閉MySQL。
# master和slave上都執行 shell> mysqladmin -uroot -p shutdown
而後將整個datadir拷貝到slave上(固然,有些文件是不用拷貝的,好比master上的binlog、mysql庫等)。
# 將master的datadir(/data)拷貝到slave的datadir(/data) shell> rsync -avz /data 192.168.100.150:/
須要注意,在冷備份的時候,須要將備份到目標主機上的DATADIR/auto.conf刪除,這個文件中記錄的是mysql server的UUID,而master和slave的UUID必須不能一致。
而後重啓master和slave。由於重啓了master,因此binlog已經滾動了,不過此次不用再查看binlog座標,由於重啓形成的binlog日誌移動不會影響slave。
這種方式簡單的多,並且對於innodb表很適用,可是slave上恢復時速度慢,由於恢復時數據全是經過insert插入的。由於mysqldump能夠進行定時點恢復甚至記住binlog的座標,因此無需再手動獲取binlog的座標。
shell> mysqldump -uroot -p --all-databases --master-data=2 >dump.db
注意,--master-data
選項將再dump.db中加入change master to
相關的語句,值爲2時,change master to
語句是註釋掉的,值爲1或者沒有提供值時,這些語句是直接激活的。同時,--master-data
會鎖定全部表(若是同時使用了--single-transaction
,則不是鎖全部表,詳細內容請參見mysqldump)。
所以,能夠直接從dump.db中獲取到binlog的座標。記住這個座標。
[root@xuexi ~]# grep -i -m 1 'change master to' dump.db -- CHANGE MASTER TO MASTER_LOG_FILE='master-bin.000002', MASTER_LOG_POS=154;
而後將dump.db拷貝到slave上,使用mysql執行dump.db腳本便可。也能夠直接在master上遠程鏈接到slave上執行。例如:
shell> mysql -uroot -p -h 192.168.100.150 -e 'source dump.db'
這是三種方式中最佳的方式,安全性高、速度快。由於xtrabackup備份的時候會記錄master的binlog的座標,所以也無需手動獲取binlog座標。
xtrabackup詳細的備份方法見:xtrabackup
注意:master和slave上都安裝percona-xtrabackup。
以全備份爲例:
innobackupex -u root -p /backup
備份完成後,在/backup下生成一個以時間爲名稱的目錄。其內文件以下:
[root@xuexi ~]# ll /backup/2018-05-29_04-12-15 total 77872 -rw-r----- 1 root root 489 May 29 04:12 backup-my.cnf drwxr-x--- 2 root root 4096 May 29 04:12 backuptest -rw-r----- 1 root root 1560 May 29 04:12 ib_buffer_pool -rw-r----- 1 root root 79691776 May 29 04:12 ibdata1 drwxr-x--- 2 root root 4096 May 29 04:12 mysql drwxr-x--- 2 root root 4096 May 29 04:12 performance_schema drwxr-x--- 2 root root 12288 May 29 04:12 sys -rw-r----- 1 root root 22 May 29 04:12 xtrabackup_binlog_info -rw-r----- 1 root root 115 May 29 04:12 xtrabackup_checkpoints -rw-r----- 1 root root 461 May 29 04:12 xtrabackup_info -rw-r----- 1 root root 2560 May 29 04:12 xtrabackup_logfile
其中xtrabackup_binlog_info中記錄了binlog的座標。記住這個座標。
[root@xuexi ~]# cat /backup/2018-05-29_04-12-15/xtrabackup_binlog_info master-bin.000002 154
而後將備份的數據執行"準備"階段。這個階段不要求鏈接mysql,所以不用給鏈接選項。
innobackupex --apply-log /backup/2018-05-29_04-12-15
最後,將/backup目錄拷貝到slave上進行恢復。恢復的階段就是向MySQL的datadir拷貝。但注意,xtrabackup恢復階段要求datadir必須爲空目錄。不然報錯:
[root@xuexi ~]# innobackupex --copy-back /backup/2018-05-29_04-12-15/ 180529 23:54:27 innobackupex: Starting the copy-back operation IMPORTANT: Please check that the copy-back run completes successfully. At the end of a successful copy-back run innobackupex prints "completed OK!". innobackupex version 2.4.11 based on MySQL server 5.7.19 Linux (x86_64) (revision id: b4e0db5) Original data directory /data is not empty!
因此,中止slave的mysql並清空datadir。
service mysqld stop rm -rf /data/*
恢復時使用的模式是"--copy-back",選項後指定要恢復的源備份目錄。恢復時由於不須要鏈接數據庫,因此不用指定鏈接選項。
[root@xuexi ~]# innobackupex --copy-back /backup/2018-05-29_04-12-15/ 180529 23:55:53 completed OK!
恢復完成後,MySQL的datadir的文件的全部者和屬組是innobackupex的調用者,因此須要改回mysql.mysql。
shell> chown -R mysql.mysql /data
啓動slave,並查看恢復是否成功。
shell> service mysqld start shell> mysql -uroot -p -e 'select * from backuptest.num_isam limit 10;' +----+ | n | +----+ | 1 | | 2 | | 3 | | 4 | | 5 | | 6 | | 7 | | 8 | | 9 | | 10 | +----+
通過前面的一番折騰,總算是把該準備的數據都準備到slave上,也獲取到master上binlog的座標(154)。如今還欠東風:鏈接master。
鏈接master時,須要使用change master to
提供鏈接到master的鏈接選項,包括user、port、password、binlog、position等。
mysql> change master to master_host='192.168.100.20', master_port=3306, master_user='repl', master_password='P@ssword1!', master_log_file='master-bin.000002', master_log_pos=154;
完整的change master to
語法以下:
CHANGE MASTER TO option [, option] ... option: | MASTER_HOST = 'host_name' | MASTER_USER = 'user_name' | MASTER_PASSWORD = 'password' | MASTER_PORT = port_num | MASTER_LOG_FILE = 'master_log_name' | MASTER_LOG_POS = master_log_pos | MASTER_AUTO_POSITION = {0|1} | RELAY_LOG_FILE = 'relay_log_name' | RELAY_LOG_POS = relay_log_pos | MASTER_SSL = {0|1} | MASTER_SSL_CA = 'ca_file_name' | MASTER_SSL_CAPATH = 'ca_directory_name' | MASTER_SSL_CERT = 'cert_file_name' | MASTER_SSL_CRL = 'crl_file_name' | MASTER_SSL_CRLPATH = 'crl_directory_name' | MASTER_SSL_KEY = 'key_file_name' | MASTER_SSL_CIPHER = 'cipher_list' | MASTER_SSL_VERIFY_SERVER_CERT = {0|1}
而後,啓動IO線程和SQL線程。能夠一次性啓動兩個,也能夠分開啓動。
# 一次性啓動、關閉 start slave; stop slave; # 單獨啓動 start slave io_thread; start slave sql_thread;
至此,複製就已經能夠開始工做了。當master寫入數據,slave就會從master處進行復制。
例如,在master上新建一個表,而後去slave上查看是否有該表。由於是DDL語句,它會寫二進制日誌,因此它也會複製到slave上。
change master to
後,在slave的datadir下就會生成master.info文件和relay-log.info文件,這兩個文件隨着複製的進行,其內數據會隨之更新。
master.info文件記錄的是IO線程相關的信息,也就是鏈接master以及讀取master binlog的信息。經過這個文件,下次鏈接master時就不須要再提供鏈接選項。
如下是master.info的內容,每一行的意義見官方手冊
[root@xuexi ~]# cat /data/master.info 25 # 本文件的行數 master-bin.000002 # IO線程正從哪一個master binlog讀取日誌 154 # IO線程讀取到master binlog的位置 192.168.100.20 # master_host repl # master_user P@ssword1! # master_password 3306 # master_port 60 # master_retry,slave重連master的超時時間(單位秒) 0 0 30.000 0 86400 0
relay-log.info文件中記錄的是SQL線程相關的信息。如下是relay-log.info文件的內容,每一行的意義見官方手冊
[root@xuexi ~]# cat /data/relay-log.info 7 # 本文件的行數 ./slave-bin.000001 # 當前SQL線程正在讀取的relay-log文件 4 # SQL線程已執行到的relay log位置 master-bin.000002 # SQL線程最近執行的操做對應的是哪一個master binlog 154 # SQL線程最近執行的操做對應的是master binlog的哪一個位置 0 # slave上必須落後於master多長時間 0 # 正在運行的SQL線程數 1 # 一種用於內部信息交流的ID,目前值老是1
在slave上執行show slave status
能夠查看slave的狀態信息。信息很是多,每一個字段的詳細意義可參見官方手冊
mysql> show slave status\G *************************** 1. row *************************** Slave_IO_State: # slave上IO線程的狀態,來源於show processlist Master_Host: 192.168.100.20 Master_User: repl Master_Port: 3306 Connect_Retry: 60 Master_Log_File: master-bin.000002 Read_Master_Log_Pos: 154 Relay_Log_File: slave-bin.000001 Relay_Log_Pos: 4 Relay_Master_Log_File: master-bin.000002 Slave_IO_Running: No # IO線程的狀態,此處爲未運行且未鏈接狀態 Slave_SQL_Running: No # SQL線程的狀態,此處爲未運行狀態 Replicate_Do_DB: # 顯式指定要複製的數據庫 Replicate_Ignore_DB: # 顯式指定要忽略的數據庫 Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: # 以通配符方式指定要複製的表 Replicate_Wild_Ignore_Table: Last_Errno: 0 Last_Error: Skip_Counter: 0 Exec_Master_Log_Pos: 154 Relay_Log_Space: 154 Until_Condition: None # start slave語句中指定的until條件, # 例如,讀取到哪一個binlog位置就中止 Until_Log_File: Until_Log_Pos: 0 Master_SSL_Allowed: No Master_SSL_CA_File: Master_SSL_CA_Path: Master_SSL_Cert: Master_SSL_Cipher: Master_SSL_Key: Seconds_Behind_Master: NULL # SQL線程執行過的位置比IO線程慢多少 Master_SSL_Verify_Server_Cert: No Last_IO_Errno: 0 Last_IO_Error: Last_SQL_Errno: 0 Last_SQL_Error: Replicate_Ignore_Server_Ids: Master_Server_Id: 0 # master的server id Master_UUID: Master_Info_File: /data/master.info SQL_Delay: 0 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: # slave SQL線程的狀態 Master_Retry_Count: 86400 Master_Bind: Last_IO_Error_Timestamp: Last_SQL_Error_Timestamp: Master_SSL_Crl: Master_SSL_Crlpath: Retrieved_Gtid_Set: Executed_Gtid_Set: Auto_Position: 0 Replicate_Rewrite_DB: Channel_Name: Master_TLS_Version: 1 row in set (0.01 sec)
由於太長,後面再列出show slave status
時,將裁剪一些意義不大的行。
再次回到上面show slave status
的信息。除了那些描述IO線程、SQL線程狀態的行,還有幾個log_file和pos相關的行,以下所列。
Master_Log_File: master-bin.000002 Read_Master_Log_Pos: 154 Relay_Log_File: slave-bin.000001 Relay_Log_Pos: 4 Relay_Master_Log_File: master-bin.000002 Exec_Master_Log_Pos: 154
理解這幾行的意義相當重要,前面由於排版限制,描述看上去有些重複。因此這裏完整地描述它們:
Master_Log_File
:IO線程正在讀取的master binlog;Read_Master_Log_Pos
:IO線程已經讀取到master binlog的哪一個位置;Relay_Log_File
:SQL線程正在讀取和執行的relay log;Relay_Log_Pos
:SQL線程已經讀取和執行到relay log的哪一個位置;Relay_Master_Log_File
:SQL線程最近執行的操做對應的是哪一個master binlog;Exec_Master_Log_Pos
:SQL線程最近執行的操做對應的是master binlog的哪一個位置。因此,(Relay_Master_Log_File, Exec_Master_log_Pos)構成一個座標,這個座標表示slave上已經將master上的哪些數據重放到本身的實例中,它能夠用於下一次change master to
時指定的binlog座標。
與這個座標相對應的是slave上SQL線程的relay log座標(Relay_Log_File, Relay_Log_Pos)。這兩個座標位置不一樣,但它們對應的數據是一致的。
最後還有一個延遲參數Seconds_Behind_Master
須要說明一下,它的本質意義是SQL線程比IO線程慢多少。若是master和slave之間的網絡情況優良,那麼slave的IO線程讀速度和master寫binlog的速度基本一致,因此這個參數也用來描述"SQL線程比master慢多少",也就是說slave比master少多少數據,只不過衡量的單位是秒。但須要注意,這個參數的描述並不標準,只有在網速很好的時候作個大概估計,不少種狀況下它的值都是0,即便SQL線程比IO線程慢了不少也是如此。
上面的master.info、relay-log.info和show slave status
的狀態都是剛鏈接上master還未啓動IO thread、SQL thread時的狀態。下面將顯示已經進行一段正在執行復制的slave狀態。
首先查看啓動io thread和sql thread後的狀態。使用show processlist
查看便可。
mysql> start slave; mysql> show processlist; # slave上的信息,爲了排版,簡化了輸出 +----+-------------+---------+--------------------------------------------------------+ | Id | User | Command | State | +----+-------------+---------+--------------------------------------------------------+ | 4 | root | Sleep | | | 7 | root | Query | starting | | 8 | system user | Connect | Waiting for master to send event | | 9 | system user | Connect | Slave has read all relay log; waiting for more updates | +----+-------------+---------+--------------------------------------------------------+
能夠看到:
Id=8
的線程負責鏈接master並讀取binlog,它是IO 線程,它的狀態指示"等待master發送更多的事件";Id=9
的線程負責讀取relay log,它是SQL線程,它的狀態指示"已經讀取了全部的relay log"。再看看此時master上的信息。
mysql> show processlist; # master上的信息,爲了排版,通過了修改 +----+------+-----------------------+-------------+--------------------------------------+ | Id | User | Host | Command | State | +----+------+-----------------------+-------------+--------------------------------------+ | 4 | root | localhost | Query | starting | |----|------|-----------------------|-------------|--------------------------------------| | 16 | repl | 192.168.100.150:39556 | Binlog Dump | Master has sent all binlog to slave; | | | | | | waiting for more updates | +----+------+-----------------------+-------------+--------------------------------------+
master上有一個Id=16
的binlog dump線程,該線程的用戶是repl。它的狀態指示"已經將全部的binlog發送給slave了"。
如今,在master上執行一個長事件,以便查看slave上的狀態信息。
仍然使用前面插入數值輔助表的存儲過程,此次分別向兩張表中插入一億條數據(儘管去抽菸、喝茶,夠等幾分鐘的。若是機器性能很差,請大幅減小插入的行數)。
call proc_num1(100000000); call proc_num2(100000000);
而後去slave上查看信息,以下。由於太長,已經裁剪了一部分沒什麼用的行。
mysql> show slave status\G mysql: [Warning] Using a password on the command line interface can be insecure. *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.100.20 Master_User: repl Master_Port: 3306 Connect_Retry: 60 Master_Log_File: master-bin.000003 Read_Master_Log_Pos: 512685413 Relay_Log_File: slave-bin.000003 Relay_Log_Pos: 336989434 Relay_Master_Log_File: master-bin.000003 Slave_IO_Running: Yes Slave_SQL_Running: Yes Exec_Master_Log_Pos: 336989219 Slave_SQL_Running_State: Reading event from the relay log
從中獲取到的信息有:
能夠看出,IO線程比SQL線程超前了不少不少,因此SQL線程比IO線程的延遲較大。
不少人覺得change master to
語句是用來鏈接master的,實際上這種說法是錯的。鏈接master是IO線程的事情,change master to
只是爲IO線程鏈接master時提供鏈接參數。
若是slave歷來沒鏈接過master,那麼必須使用change master to
語句來生成IO線程所須要的信息,這些信息記錄在master.info中。這個文件是change master to
成功以後當即生成的,之後啓動IO線程時,IO線程都會自動讀取這個文件來鏈接master,不須要先執行change master to
語句。
例如,能夠隨時stop slave
來中止複製線程,而後再隨時start slave
,只要master.info存在,且沒有人爲修改過它,IO線程就必定不會出錯。這是由於master.info會隨着IO線程的執行而更新,不管讀取到master binlog的哪一個位置,都會記錄下這個位置,如此一來,IO線程下次啓動的時候就知道從哪裏開始監控master binlog。
前面還提到一個文件:relay-log.info
文件。這個文件中記錄的是SQL線程的相關信息,包括讀取、執行到relay log的哪一個位置,剛重放的數據對應master binlog的哪一個位置。隨着複製的進行,這個文件的信息會即時改變。因此,經過relay-log.info,下次SQL線程啓動的時候就能知道從relay log的哪一個地方繼續讀取、執行。
若是不當心把relay log文件刪除了,SQL線程可能會丟失了一部分相比IO線程延遲的數據。這時候,只需將relay-log.info中第四、5行記錄的"SQL線程剛重放的數據對應master binlog的座標"手動修改到master.info中便可,這樣IO線程下次鏈接master就會從master binlog的這個地方開始監控。固然,也能夠將這個座標做爲change master to
的座標來修改master.info。
此外,當mysql實例啓動時,默認會自動start slave
,也就是MySQL一啓動就自動開啓複製線程。若是想要禁止這種行爲,在配置文件中加上:
[mysqld] skip-slave-start
默認狀況下,slave鏈接到master後會在slave的datadir下生成master.info和relay-log.info文件,可是這是能夠經過設置變量來改變的。
master-info-repository={TABLE|FILE}
:master的信息是記錄到文件master.info中仍是記錄到表mysql.slave_master_info中。默認爲file。relay-log-info-repository={TABLE|FILE}
:slave的信息是記錄到文件relay-log.info中仍是記錄到表mysql.slave_relay_log_info中。默認爲file。IO線程每次從master複製日誌要寫入到relay log中,可是它是先放在內存的,等到必定時機後纔會將其刷到磁盤上的relay log文件中。刷到磁盤的時機能夠由變量控制。
另外,IO線程每次從master複製日誌後都會更新master.info的信息,也是先更新內存中信息,在特定的時候纔會刷到磁盤的master.info文件;同理SQL線程更新realy-log.info也是同樣的。它們是能夠經過變量來設置更新時機的。
sync-relay-log=N
:設置爲大於0的數表示每從master複製N個事件就刷一次盤。設置爲0表示依賴於操做系統的sync機制。sync-master-info=N
:依賴於master-info-repository
的設置,若是爲file,則設置爲大於0的值時表示每更新多少次master.info將其寫入到磁盤的master.info中,設置爲0則表示由操做系統來決定什麼時候調用fdatasync()
函數刷到磁盤。若是設置爲table,則設置爲大於0的值表示每更新多少次master.info就更新mysql.slave_master_info表一次,若是設置爲0則表示永不更新該表。sync-relay-log-info=N
:同上。一主多從有兩種狀況,結構圖以下。
如下是一主多從的結構圖(和一主一從的配置方法徹底一致):
如下是一主多從,但某slave是另外一羣MySQL實例的master:
配置一主多從時,須要考慮一件事:slave上是否要開啓binlog? 若是不開啓slave的binlog,性能確定要稍微好一點。可是開啓了binlog後,能夠經過slave來備份數據,也能夠在master宕機時直接將slave切換爲新的master。此外,若是是上面第二種主從結構,這臺slave必須開啓binlog。能夠將某臺或某幾臺slave開啓binlog,並在mysql動靜分離的路由算法上稍微減小一點到這些slave上的訪問權重。
上面第一種一主多從的結構沒什麼可解釋的,它和一主一從的配置方式徹底同樣,可是能夠考慮另外一種狀況:向現有主從結構中添加新的slave。因此,稍後先介紹這種添加slave,再介紹第二種一主多從的結構。
官方手冊:https://dev.mysql.com/doc/refman/5.7/en/replication-howto-additionalslaves.html
例如在前文一主一從的實驗環境下添加一臺新的slave。
由於新的slave在開始複製前,要有master上的基準數據,還要有master binlog的座標。按照前文一主一從的配置方式,固然很容易獲取這些信息,但這樣會將master鎖住一段時間(由於要備份基準數據)。
深刻思考一下,其實slave上也有數據,還有relay log以及一些倉庫文件標記着數據複製到哪一個地方。因此,徹底能夠從slave上獲取基準數據和座標,也建議這樣作。
仍然有三種方法從slave上獲取基準數據:冷備份、mysqldump和xtrabackup。方法見前文將slave恢復到master指定的座標。
其實臨時關閉一個slave對業務影響很小,因此我我的建議,新添加slave時採用冷備份slave的方式,不只備份恢復的速度最快,配置成slave也最方便,這一點和前面配置"一主一從"不同。但冷備份slave的時候須要注意幾點:
change master to
語句設置鏈接參數。此處實現的一主多從是下面這種結構:
這種結構對MySQL複製來講,是一個很好的提高性能的方式。對於只有一個master的主從複製結構,每多一個slave,意味着master多發一部分binlog,業務稍微繁忙一點時,這種壓力會加重。而這種一個主master、一個或多個輔助master的主從結構,很是有助於MySQL集羣的伸縮性,對壓力的適應性也很強。
除上面一主多從、從中有從的方式可提高複製性能,還有幾種提高MySQL複製性能的方式:
- 將不一樣數據庫複製到不一樣slave上。
- 能夠將master上的事務表(如InnoDB)複製爲slave上的非事務表(如MyISAM),這樣slave上回放的速度加快,查詢數據的速度在必定程度上也會提高。
回到這種主從結構,它有些不一樣,master只負責傳送日誌給slave一、slave2和slave3,slave 2_1和slave 2_2的日誌由slave2負責傳送,因此slave2上也必需要開啓binlog選項。此外,還必須開啓一個選項--log-slave-updates
讓slave2可以在重放relay log時也寫本身的binlog,不然slave2的binlog僅接受人爲的寫操做。
問:slave可否進行寫操做?重放relay log的操做是否會記錄到slave的binlog中?
read-only
選項(只讀變量)時,任何有寫權限的用戶均可以進行寫操做,這些操做都會記錄到binlog中。注意,read-only選項對具備super權限的用戶以及SQL線程執行的重放寫操做無效。默認這個選項是關閉的。mysql> show variables like "read_only"; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | read_only | OFF | +---------------+-------+
log-slave-updates
和binlog選項時,重放relay log不會記錄binlog。因此若是slave2要做爲某些slave的master,那麼在slave2上必需要開啓log-slave-updates
和binlog選項。爲了安全和數據一致性,在slave2上還應該啓用read-only選項。
環境以下:
如下是master、slave1和slave2上配置文件內容。
# master上的配置 [mysqld] datadir=/data socket=/data/mysql.sock server_id=100 sync-binlog=1 log_bin=master-bin log-error=/data/err.log pid-file=/data/mysqld.pid # slave1上的配置 [mysqld] datadir=/data socket=/data/mysql.sock server_id=111 relay-log=slave-bin log-error=/data/err.log pid-file=/data/mysqld.pid log-slave-updates # 新增配置 log-bin=master-slave-bin # 新增配置 read-only=ON # 新增配置 # slave2上的配置 [mysqld] datadir=/data socket=/data/mysql.sock server_id=123 relay-log=slave-bin log-error=/data/err.log pid-file=/data/mysqld.pid read-only=ON
由於slave2目前是全新的實例,因此先將slave1的基準數據備份到slave2。因爲slave1自身就是slave,臨時關閉一個slave對業務影響很小,因此直接採用冷備份slave的方式。
# 在slave2上執行 shell> mysqladmin -uroot -p shutdown # 在slave1上執行: shell> mysqladmin -uroot -p shutdown shell> rsync -az --delete /data 192.168.100.19:/ shell> service mysqld start
冷備份時,如下幾點千萬注意:
skip-slave-start
,不然slave2將再次鏈接到master並做爲master的slave。repl
是否容許slave2鏈接。若是不容許,應該去master上修改這個用戶。因此,在slave2上繼續操做:
shell> ls /data auto.cnf ib_buffer_pool ib_logfile1 performance_schema slave-bin.000005 backuptest ibdata1 master.info relay-log.info slave-bin.index err.log ib_logfile0 mysql slave-bin.000004 sys shell> rm -f /data/{master.info,relay-log.info,auto.conf,slave-bin*} shell> service mysqld start
最後連上slave2,啓動複製線程。
shell> mysql -uroot -p mysql> change master to master_host='192.168.100.150', master_port=3306, master_user='repl', master_password='P@ssword1!', master_log_file='master-slave-bin.000001', master_log_pos=4; mysql> start slave; mysql> show slave status\G
默認狀況下,slave會複製master上全部庫。能夠指定如下變量顯式指定要複製的庫、表和要忽略的庫、表,也能夠將其寫入配置文件。
Replicate_Do_DB: 要複製的數據庫 Replicate_Ignore_DB: 不復制的數據庫 Replicate_Do_Table: 要複製的表 Replicate_Ignore_Table: 不復制的表 Replicate_Wild_Do_Table: 通配符方式指定要複製的表 Replicate_Wild_Ignore_Table: 通配符方式指定不復制的表
若是要指定列表,則屢次使用這些變量進行設置。
須要注意的是,儘管顯式指定了要複製和忽略的庫或者表,可是master仍是會將全部的binlog傳給slave並寫入到slave的relay log中,真正負責篩選的slave上的SQL線程。
另外,若是slave上開啓了binlog,SQL線程讀取relay log後會將全部的事件都寫入到本身的binlog中,只不過對於那些被忽略的事件只記錄相關的事務號等信息,不記錄事務的具體內容。因此,若是以前設置了被忽略的庫或表,後來取消忽略後,它們在取消忽略之前的變化是不會再重放的,特別是基於gtid的複製會嚴格比較binlog中的gtid。
總之使用篩選的時候應該多多考慮是否真的要篩選,是不是永久篩選。
reset slave
會刪除master.info/relay-log.info和relay log,而後新生成一個relay log。可是change master to
設置的鏈接參數還在內存中保留着,因此此時能夠直接start slave,並根據內存中的change master to
鏈接參數複製日誌。
reset slave all
除了刪除reset slave
刪除的東西,還刪除內存中的change master to
設置的鏈接信息。
reset master
會刪除master上全部的二進制日誌,並新建一個日誌。在正常運行的主從複製環境中,執行reset master
極可能致使異常情況。因此建議使用purge來刪除某個時間點以前的日誌(應該保證只刪除那些已經複製完成的日誌)。
若是想查看master有幾個slave的信息,可使用show slave hosts
。如下爲某個master上的結果:
mysql> show slave hosts; +-----------+------+------+-----------+--------------------------------------+ | Server_id | Host | Port | Master_id | Slave_UUID | +-----------+------+------+-----------+--------------------------------------+ | 111 | | 3306 | 11 | ff7bb057-2466-11e7-8591-000c29479b32 | | 1111 | | 3306 | 11 | 9b119463-24d2-11e7-884e-000c29867ec2 | +-----------+------+------+-----------+--------------------------------------+
能夠看到,該show中會顯示server-id、slave的主機地址和端口號、它們的master_id以及這些slave獨一無二的uuid號。
其中show結果中的host顯示結果是由slave上的變量report_host控制的,端口是由report_port控制的。
例如,在slave2上修改其配置文件,添加report-host項後重啓MySQL服務。
[mysqld] report_host=192.168.100.19
在slave1(前文的實驗環境,slave1是slave2的master)上查看,host已經顯示爲新配置的項。
mysql> show slave hosts; +-----------+----------------+------+-----------+--------------------------------------+ | Server_id | Host | Port | Master_id | Slave_UUID | +-----------+----------------+------+-----------+--------------------------------------+ | 111 | 192.168.100.19 | 3306 | 11 | ff7bb057-2466-11e7-8591-000c29479b32 | | 1111 | | 3306 | 11 | 9b119463-24d2-11e7-884e-000c29867ec2 | +-----------+----------------+------+-----------+--------------------------------------+
在老版本中,只有一個SQL線程讀取relay log並重放。重放的速度確定比IO線程寫relay log的速度慢很是多,致使SQL線程很是繁忙,且實現到從庫上延遲較大。沒錯,多線程複製能夠解決主從延遲問題,且使用得當的話效果很是的好(關於主從複製延遲,是生產環境下最多見的問題之一,且沒有很好的辦法來避免。後文稍微介紹了一點方法)。
在MySQL 5.6中引入了多線程複製(multi-thread slave,簡稱MTS),這個多線程指的是多個SQL線程,IO線程仍是隻有一個。當IO線程將master binlog寫入relay log中後,一個稱爲"多線程協調器(multithreaded slave coordinator)"會對多個SQL線程進行調度,讓它們按照必定的規則去執行relay log中的事件。
須要謹記於心的是,若是對多線程複製沒有了解的很透徹,千萬不要在生產環境中使用多線程複製。它的確帶來了一些複製性能的提高,而且能解決主從超高延遲的問題,但隨之而來的是不少的"疑難雜症",這些"疑難雜症"並不是是bug,只是須要多多瞭解以後才知道爲什麼會出現這些問題以及如何解決這些問題。稍後會簡單介紹一種多線程複製問題:gaps。
經過全局變量slave-parallel-workers
控制SQL線程個數,設置爲非0正整數N,表示多加N個SQL線程,加上原有的共N+1個SQL線程。默認爲0,表示不加任何SQL線程,即關閉多線程功能。
mysql> show variables like "%parallel%"; +------------------------+-------+ | Variable_name | Value | +------------------------+-------+ | slave_parallel_workers | 0 | +------------------------+-------+
顯然,多線程只有在slave上開啓纔有效,由於只有slave上纔有SQL線程。另外,設置了該全局變量,須要重啓SQL線程才生效,不然內存中仍是隻有一個SQL線程。
例如,初始時slave上的processlist以下:
設置slave_parallel_workers=2
。
mysql> set @@global.slave_parallel_workers=2; mysql> stop slave sql_thread; msyql> start slave sql_thread; mysql> show full processlist;
可見多出了兩個線程,其狀態信息是"Waiting for an event from Coordinator"。
雖然是多個SQL線程,可是複製時每一個庫只能使用一個線程(默認狀況下,能夠經過--slave-parallel-type
修改並行策略),由於若是一個庫可使用多個線程,多個線程並行重放relay log,可能致使數據錯亂。因此應該設置線程數等於或小於要複製的庫的數量,設置多了無效且浪費資源。
雖然多線程複製帶來了必定的複製性能提高,但它也帶來了不少問題,最嚴重的是一致性問題。完整的內容見官方手冊。此處介紹其中一個最重要的問題。
關於多線程複製,最多見也是開啓多線程複製前最須要深刻了解的問題是:因爲多個SQL線程同時執行relay log中的事務,這使得slave上提交事務的順序極可能和master binlog中記錄的順序不一致(除非指定變量slave_preserve_commit_order=1
)。(注意:這裏說的是事務而不是事件。由於MyISAM的binlog順序無所謂,只要執行完了就正確,並且多線程協調器可以協調好這些任務。因此只需考慮innodb基於事務的binlog)
舉個簡單的例子,master上事務A先於事務B提交,到了slave上由於多SQL線程的緣由,可能事務B提交了事務A卻還沒提交。
是否還記得show slave status
中的Exec_master_log_pos
表明的意義?它表示SQL線程最近執行的事件對應的是master binlog中的哪一個位置。問題由此而來。經過show slave status
,咱們看到已經執行事件對應的座標,它前面可能還有事務沒有執行。而在relay log中,事務B記錄的位置是在事務A以後的(和master同樣),因而事務A和事務B之間可能就存在一個孔洞(gap),這個孔洞是事務A剩餘要執行的操做。
正常狀況下,多線程協調器記錄了一切和多線程複製相關的內容,它能識別這種孔洞(經過打低水位標記low-watermark),也能正確填充孔洞。即便是在存在孔洞的狀況下執行stop slave
也不會有任何問題,由於在中止SQL線程以前,它會等待先把孔洞填充完。但危險因素太多,好比忽然宕機、忽然殺掉mysqld進程等等,這些都會致使孔洞持續下去,甚至可能由於操做不當而永久丟失這部分孔洞。
那麼如何避免這種問題,出現這種問題如何解決?
1.如何避免gap。
前面說了,多個SQL線程是經過協調器來調度的。默認狀況下,可能會出現gap的狀況,這是由於變量slave_preserve_commit_order
的默認值爲0。該變量指示協調器是否讓每一個SQL線程執行的事務按master binlog中的順序提交。所以,將其設置爲1,而後重啓SQL線程便可保證SQL線程按序提交,也就不可能會有gap的出現。
當事務B準備先於事務A提交的時候,它將一直等待。此時slave的狀態將顯示:
Waiting for preceding transaction to commit # MySQL 5.7.8以後顯示該狀態 Waiting for its turn to commit # MySQL 5.7.8以前顯示該狀態
儘管不會出現gap,但show slave status
的Exec_master_log_pos
仍可能顯示在事務A的座標以後。
因爲開啓slave_preserve_commit_order
涉及到很多操做,它還要求開啓slave的binlog--log-bin
(所以須要重啓mysqld),且開啓重放relay log也記錄binlog的行爲--log-slave-updates
,此外,還必須設置多線程的並行策略--slave-parallel-type=LOGICAL_CLOCK
。
shell> mysqladmin -uroot -p shutdown shell> cat /etc/my.cnf log_bin=slave-bin log-slave-updates slave_parallel_workers=1 slave_parallel_type=LOGICAL_CLOCK shell>service mysqld start
2.如何處理已經存在的gap。
方法之一,是從master上從新備份恢復到slave上,這種方法是處理不當的最後解決辦法。
正常的處理方法是,使用START SLAVE [SQL_THREAD] UNTIL SQL_AFTER_MTS_GAPS;
,它表示SQL線程只有先填充gaps後才能啓動。實際上,它涉及了兩個操做:
通常來講,在填充完gaps以後,應該先reset slave
移除已經執行完的relay log,而後再去啓動sql_thread。
多線程的帶來的問題不止gaps一種,因此沒有深刻了解多線程的狀況下,千萬不能在生產環境中啓用它。若是想將多線程切換回單線程,能夠執行以下操做:
START SLAVE UNTIL SQL_AFTER_MTS_GAPS; SET @@GLOBAL.slave_parallel_workers = 0; START SLAVE SQL_THREAD;
當master忽然宕機,有時候須要切換到slave,將slave提高爲新的master。但對於master忽然宕機可能形成的數據丟失,主從切換是沒法解決的,它只是儘量地不間斷提供MySQL服務。
假如如今有主服務器M,從服務器S一、S2,S1做爲未來的新的master。
show slave status; show processlist;
mysql> stop slave; mysql> reset master;
change master to
修改master的指向爲S1,而後再啓動io線程和SQL線程。mysql> stop slave; mysql> change master to master_host=S1,... mysql> start slave;
注意:reset master
很重要,若是不是基於GTID複製且開啓了log-slave-updates
選項時,S1在應用relay log的時候會將其寫入到本身的binlog,之後S2會複製這些日誌致使重複執行的問題。
其實上面只是提供一種slave升級爲Master的解決思路,在實際應用中環境可能比較複雜。例如,上面的S1是S2的master,這時S1若是沒有設置爲read-only,當M宕機時,能夠不用中止S1,也不須要reset master
等操做,受影響的操做僅僅只是S1一直沒法鏈接M而已,但這對業務不會有多大的影響。
相信理解了前面的內容,分析主從切換的思路應該也沒有多大問題。
前面說的篩選要複製的庫和表能夠用於指定不復制到slave上的庫和表,但卻沒有篩選不復制到slave的語句。
但有些特殊狀況下,可能須要這種功能。例如,master上建立專門用於複製的用戶repl,這種語句其實沒有必要複製到slave上,甚至出於安全的考慮不該該複製到slave上。
可使用sql_log_bin
變量對此進行設置,默認該變量的值爲1,表示全部語句都寫進binlog,從而被slave複製走。若是設置爲0,則以後的語句不會寫入binlog,從而實現"不復制某些語句到slave"上的功能。
例如:屏蔽建立repl用戶的語句。
mysql> set sql_log_bin=0; mysql> create user repl@'%' identified by 'P@ssword1!'; mysql> grant replication slave on *.* to repl@'%'; mysql> set sql_log_bin=1;
在使用該變量時,默認是會話範圍內的變量,必定不能設置它的全局變量值,不然全部語句都將不寫binlog。
slave經過IO線程獲取master的binlog,並經過SQL線程來應用獲取到的日誌。由於各個方面的緣由,常常會出現slave的延遲(即Seconds_Behind_Master
的值)很是高(動輒幾天的延遲是常見的,幾個小時的延遲已經算短的),使得主從狀態不一致。
一個很容易理解的延遲示例是:假如master串行執行一個大事務須要30分鐘,那麼slave應用這個事務也大約要30分鐘,從master提交的那一刻開始,slave的延遲就是30分鐘,更極端一點,因爲binlog的記錄時間點是在事務提交時,若是這個大事務的日誌量很大,好比要傳輸10多分鐘,那麼極可能延遲要達到40分鐘左右。並且更嚴重的是,這種延遲具備滾雪球的特性,從延遲開始,很容易致使後續加重延遲。
因此,第一個優化方式是不要在mysql中使用大事務,這是mysql主從優化的第一口訣。
在迴歸正題,要解決slave的高延遲問題,先要知道Second_Behind_Master
是如何計算延遲的:SQL線程比IO線程慢多少(其本質是NOW()減去Exec_Master_Log_Pos
處設置的TIMESTAMP)。在主從網絡狀態良好的狀況下,IO線程和master的binlog大多數時候都能保持一致(也便是IO線程沒有多少延遲,除非事務很是大,致使二進制日誌傳輸時間久,但mysql優化的一個最基本口訣就是大事務切成小事務),因此在這種理想狀態下,能夠認爲主從延遲說的是slave上的數據狀態比master要延遲多少。它的計數單位是秒。
1.從產生Binlog的master上考慮,能夠在master上應用group commit的功能,並設置參數binlog_group_commit_sync_delay
和binlog_group_commit_sync_no_delay_count
,前者表示延遲多少秒才提交事務,後者表示要堆積多少個事務以後再提交。這樣一來,事務的產生速度下降,slave的SQL線程相對就獲得緩解。
2.再者從slave上考慮,能夠在slave上開啓多線程複製(MTS)功能,讓多個SQL線程同時從一個IO線程中取事務進行應用,這對於多核CPU來講是很是有效的手段。可是前面介紹多線程複製的時候說過,沒有掌握多線程複製的方方面面以前,千萬不要在生產環境中使用多線程複製,要是出現gap問題,很讓人崩潰。
3.最後從架構上考慮。主從延遲是由於slave跟不上master的速度,那麼能夠考慮對master進行節流控制,讓master的性能降低,從而變相提升slave的能力。這種方法確定是沒人用的,但確實是一種方法,提供了一種思路,好比slave使用性能比master更好的硬件。另外一種比較可取的方式是加多箇中間slave層(也就是master->slaves->slaves),讓多箇中間slave層專一於複製(也可做爲非業務的他用,好比用於備份)。
4.使用組複製或者Galera/PXC的多寫節點,此外還能夠設置相關參數,讓它們對延遲自行調整。但通常都不須要調整,由於有默認設置。
還有比較細緻的方面能夠下降延遲,好比設置爲row格式的Binlog要比statement要好,由於不須要額外執行語句,直接修改數據便可。好比master設置保證數據一致性的日誌刷盤規則(sync_binlog/innodb_flush_log_at_trx_commit設置爲1),而slave關閉binlog或者設置性能優先於數據一致性的binlog刷盤規則。再好比設置slave的隔離級別使得slave的鎖粒度放大,不會輕易鎖表(多線程複製時避免使用此方法)。還有不少方面,選擇好的磁盤,設計好分庫分表的結構等等,這些都是直接全局的,實在沒什麼必要在這裏多作解釋。