Mysql Replication運維與實施(三)

十三、Replication部署準備

我們在三臺機器上構建Replication架構,使用MySQL 5.7.10:

1)三個mysql實例分別爲“mysql-1”、“mysql-2”、“mysql-3”

2)IP分別爲“192.168.1.100”、“192.168.1.101”、“192.168.1.102”,其中mysql-1爲初始master。

3)mysql安裝目錄爲“/usr/local/mysql”,即“basedir”。

4)配置文件爲my.cnf,放置在安裝目錄下。

5)他們的datadir爲“/data/mysql”,在構建replicaiton架構之前,我們首先準備實例化三個mysql實例:

1、簡要配置文件

Java代碼 收藏代碼

[mysqld]  
server_id=10  
bind_address=0.0.0.0    
#綁定端口    
port=3306    
socket=/tmp/mysql.socket    
#mysql的安裝路徑    
basedir=/usr/local/mysql    
#數據文件保存的目錄    
datadir=/data/mysql  

#binlog  
log_bin=my-binlog    
binlog_checksum=CRC32    
binlog_format=MIXED    
sync_binlog=1    
binlog_row_image=FULL      
max_binlog_size=2G    
binlog_order_commits=1    
##binlog保存的時間,單位“天”    
##自動刪除早期的binlog文件    
expire_logs_days=180    
#開啓GTID,強烈建議開啓    
gtid_mode=ON  
enforce_gtid_consistency=ON  

#replication模式下選項    
relay_log_purge=1    
##當前實例的IP地址    
report_host=192.168.1.100    
##當前實例的端口    
report_port=3306    
##replication的用戶賬號名    
report_user=rpl_sys    
##slave上是否將執行的變更操作寫入binlog    
log_slave_updates=0    
master_verify_checksum=1    
relay_log=relay-bin    
relay_log_info_repository=TABLE    
sync_relay_log=1    
master_info_repository=TABLE    
#slave與master鏈接超時時間    
slave_net_timeout=3600    
##建議爲database個數 + 1,或者簡單爲2    
slave_parallel_workers=2    
slave_sql_verify_checksum=1  

配置文件內容在三個節點上基本保持一致,全部配置信息請參考【MySQL配置】,如果使用“僞分佈式”部署,那麼需要將datadir等參數調整以防止衝突。

2、依次初始化三個mysql:

Java代碼 收藏代碼

#mysql  
>sudo ./mysqld --datadir=/data/mysql --initialize --user=mysql  

3、用戶權限:

因爲“mysql”這個系統默認的數據庫,通常不能(嚴格來說絕對不能)被replication;因此,我們在master上創建的用戶權限,也不會被replication到slaves上,爲了便於管理,我們需要在每個節點上都增加如下用戶:

1)root,最高級權限

2)test,一個模擬的用戶,具有指定數據庫的所有操作權限(需要master與slaves上必須嚴格一致,否則application無法對reads操作進行load balance)

3)rpl_sys,用於master與slaves之間replication的特殊用戶

按照如下指令依次在三個實例上執行:

Java代碼 收藏代碼

##以無權限驗證模式,啓動mysql  
>sudo ./mysqld_safe --defaults-file=../my.cnf --skip-grant-tables &  

Java代碼 收藏代碼

#訪問mysql  
>./mysql --host=127.0.0.1 --port=3306  
#初始化root密碼  
>use mysql;  
>UPDATE user SET authentication_string = PASSWORD('root'),  
password_expired = 'N'  
WHERE User = 'root' AND Host = 'localhost';  
>FLUSH PRIVILEGES;  

Java代碼 收藏代碼

#關閉mysql,並以授權認證的方式重新啓動,並增加兩個用戶  
>./mysqladmin --host=127.0.0.1 --port=3306 shutdown;  
#重新啓動  
>./mysqld_safe --defaults-file=../my.cnf &  
#使用root用戶登錄mysql  
>./mysql --host=127.0.0.1 --port=3306 -u root -p  
>#輸入密碼登錄  
>CREATE DATABASE mydb;##貌似需要首先創建DB,才能授權  
>GRANT ALL ON mydb.* TO 'test'@'%' IDENTIFIED BY 'test';  
#新增一個rpl_sys,用於在replication模式中可以複製binlog數據  
>GRANT REPLICATION SLAVE ON *.* TO 'rpl_sys'@'%' IDENTIFIED BY 'replication';  
>FLUSH PRIVILEGES;  

在初始化root密碼之後,關閉mysql需要指定密碼:

Java代碼 收藏代碼

>./mysqladmin --host=127.0.0.1 --port=3306 -u root -p shutdown;  

最終我們達成,三個mysql實例持有相同的用戶授權信息。

十四、架構過程與要求

1、架構模式

本文使用三個mysql實例構建replicaiton,全部採用“半同步”模式,我們暫且選定mysql-1爲master,其他兩個實例爲slaves。爲了避免一些不必要的錯誤或者bug,我們在任何時候都需要儘可能保證,master與slaves上的配置文件基本一致,且master和slaves的數據庫引擎設置和模式一致,而且不會在slaves上直接變更數據或者修改table的引擎類型、字段類型等。

Java代碼 收藏代碼

#常用架構方式  
|---------|                  |---------|  
| master  |------semisync----| slave   |  
|---------|                  |---------|  
          |      (2~5個)                               (任意多個)  
          |                  |---------|              |---------|  
          -------semisync----|   slave |-----aysnc----|  slave  |  
                             |---------|              |---------|  

2、安裝semi插件:

semi插件有2個,分別爲“semisync_master”、“semisync_slave”,默認情況下“plugin_dir”爲“<basedir>/lib/plugin”,我們可以查看這個目錄下是否有這兩個插件,我們則在三個mysql實例上均安裝此插件: 

Java代碼 收藏代碼

>INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';  
>INSTALL PLUGIN rpl_semi_sync_SLAVE SONAME 'semisync_slave.so';  
>use mysql;  
>select * from plugin;  

需要在三個節點上都執行上述命令,無論master還是slaves;即使你的環境爲“僞分佈式”,仍然需要在每個實例上執行,插件數據將會寫入各自的“mysql”數據庫中。

3、設定semisync全局變量:

在三個mysql實例上均執行如下命令,設置semisync變量:

Java代碼 收藏代碼

#只在master上執行,  
#或者爲了便於Failover,可以在所有節點都執行  
>SET GLOBAL rpl_semi_sync_master_enabled=ON;  
#可以只在slave上執行  
>SET GLOBAL rpl_semi_sync_slave_enabled=ON;  

如果你覺得使用全局變量的方式可讀性不強,也可以在配置文件中增加如下配置: 

Java代碼 收藏代碼

rpl_semi_sync_master_enabled=1  
rpl_semi_sync_master_timeout=10000  
rpl_semi_sync_master_wait_no_slave=0  
rpl_semi_sync_slave_enabled=1  
replicate_ignore_db=mysql  

修改配置文件後,需要重啓mysql實例。

4、備份Master數據(可選)

爲了測試,我們首先在在master上的“mydb”數據庫中插入一些測試數據,來模擬“單點架構擴展成replicaiton架構”的場景。

1)鎖定數據庫

Java代碼 收藏代碼

>./mysql -h 127.0.0.1 -P3306 -u root -p;  
>FLUSH TABLES WITH READ LOCK;  
>SET GLOBAL read_only=1;  

2)dump數據

Java代碼 收藏代碼

>./mysqldump -h 127.0.0.1 -P3306 -u root -p -B mydb>/tmp/mydb.sql  

3)釋放鎖

Java代碼 收藏代碼

>UNLOCK TABLES;  
>SET GLOBAL read_only=0;  

5、將數據導入到slaves(可選)

Java代碼 收藏代碼

#首先把master上的mydb.sql文件同步到各個slaves  
#在slave上逐個執行,不過需要首先啓動slave實例  
>mysql -h 127.0.0.1 -P3306 -u root -p >/tmp/mydb.sql  
#登陸mysql  
>show databases;  
#將會列出mydb數據庫   

6、初始化replication

到目前爲止,我們已經將master和slaves單點準備完畢,且master上先前的數據也已經導入到slaves中,接下來我們將初始化replicaiton集羣:

Java代碼 收藏代碼

#slaves上執行  
>SET GLOBAL read_only=1;  
>CHANGE MASTER TO MASTER_HOST='192.168.1.100',MASTER_PORT=3306, MASTER_USER='rpl_sys', MASTER_PASSWORD='replication', MASTER_AUTO_POSITION=1;  
>START SLAVE;  

千萬別忘了最後執行“START SLAVE”,否則將無法執行replicaiton。在一個slave上執行“START SLAVE”之後,即使此slave 重啓,它仍然保持自己的“slave”角色,且不需要再次執行“CHARGE MASTER TO”,因爲這些信息已經被保存在內部數據庫中。我們可以使用“STOP SLAVE”在停止replicaiton,此後slave可以作爲master或者執行“CHARGE MASTER TO”跟進其他的master。

我們可以在master上執行“SHOW SLAVE HOSTS”指令,查看slaves列表:

Java代碼 收藏代碼

mysql> SHOW SLAVE HOSTS;  
+-----------+---------+------+-----------+--------------------------------------+  
| Server_id | Host    | Port | Master_id | Slave_UUID                           |  
+-----------+---------+------+-----------+--------------------------------------+  
|        11 | mysql-2 | 3306 |        10 | 4694924e-b818-11e5-b9ad-ccddf019aa0c |  
|        12 | mysql-3 | 3306 |        10 | 26baac30-b820-11e5-ad9c-933c718547d4 |  
+-----------+---------+------+-----------+--------------------------------------+  
2 rows in set (0.00 sec)  

7、數據測試

我們可以在master上寫入數據一些數據測試,然後在slaves上查看,發現數據已經正確的被replication,目前架構基本達成了我們的需要。

十五、要點描述

1、配置部分

1)binlog_format:replication時使用的格式,爲了數據一致性和性能,我們選用“MIXED”模式,讓mysql在合適的時機選用“row”、“statement”模式。在後續的版本中,我們已經不建議使用“statement”模式。

2)log_slave_updates:slave是否將變更操作也寫入自己的binlog,如果此slave後端沒有slave跟進,則建議關閉。

3)gtid_mode,enforce_gtid_consistency:這兩個參數需要配合,同時開啓“ON”,在replicaiton模式中GTID特性強烈建議開啓。

4)report_port,report_host,report_user:需要在slave上配置,主要在master上使用“SHOW SLAVE HOSTS”查看slaves列表時展示。

5)slave_parallel_workers:是否開啓併發replication,默認爲“0”表示不開啓,此時slave只有一個SQL線程,用於讀取relay log並執行statements;如果此值大於0,表示開啓worker線程的個數,此時SQL線程只負責從relay log讀取statements,然後轉發給workers線程,有worker線程負責執行。通常workers線程的個數與database的個數一樣,或者簡單設置爲2。

6)binlog_checksum,master_verify_checksum,slave_sql_verify_checksum:建議開啓,對binlog開啓校驗和驗證。

7)server_id:這個配置項必備,每個節點都不同,數字類型。

8)sync_binlog=1、innodb_flush_log_at_trx_commit=1,這兩個參數配合用於控制binlog刷新磁盤的時機,“1”表示每次事務提交都立即刷新磁盤,效率較低但是可以儘可能的保證數據完整性;如果你的數據庫是一些社交內容的數據(而非訂單數據),可以爲了考慮併發能力,而稍微降低binlog刷新頻率。

2、數據與replication:首先“mysql”這個特殊的系統數據庫是不能被replication的,我們需要在配置文件中通過“replicate_ignore_db=mysql”來忽略;然後在master上授權一個具有“REPLICATION SLAVE”權限的用戶,比如本文中的“rpl_sys”用戶,slaves可以使用此用戶與master建立鏈接並進行數據同步;如果你希望Failover的話,需要在所有的節點上都添加此用戶,而且所有的節點上的配置把包括root用戶都應該一樣,因爲任何節點都有可能被提升爲master。

3、GTID是一個新的特性,它保證了master事務在replication的一致性、有序性,可以有效的簡化replication的複雜度和人工干預,我們應該開啓此特性。(無論是修改MyISAM,還是InnoDB,無論是否手動開始事務還是自動提交的事務,均會生成GTID,每個事務一個ID,自動提交模式下每個操作一個GTID)

4、多線程replication,即“slave_parallel_workers”控制,這個是個需要權衡的配置項,如果你的系統中writes操作比較密集、databases個數較多、大事務較多,那麼你應該考慮開啓此參數來提高relay log的執行效率,線程數不建議特別多,通常設置爲“2”或者根據和databases的個數一致。

5、semisync特性,即“半同步”也是新特性,官方推薦使用特性以降低數據丟失的可能性,半同步的核心就是在master與slaves之間有一次同步 + 消息確認的過程,會降低master事務併發提交,但是它保證master與slave之間binlog複製的同步能力。通常,我們將集羣中“大多數”slaves開啓半同步,部分slaves仍然採用異步同步,這是在數據完整性和性能上做權衡。

6、在從master上dump數據時,一定要首先對master實例進行LOCK,我們期望dump的數據是完整的(而非變動、增量的),即使用“FLUSH TABLES WITH READ LOCK;”,此指令將導致binlog、數據文件立即刷新磁盤,且阻止writes操作提交,在dump結束後,我們通過“UNLOCK TABLES”釋放鎖。上文中已經提到“mysql”這個系統數據通常不能參與replicaiton,所以我們也不能將“mysql”數據dump到其他slaves中。

7、關於是否在slave上開啓read_only參數,存在一些權衡,按照規範,我們應該在slave上開啓read_only,以避免slave上的數據被客戶端意外變更,而導致replication集羣中數據無法對齊的問題。即slave上的數據是可以直接修改的。

此外slave原則上,仍然可以創建其他databases,這些databases與replication master沒有關係,application可以直接操作slaves並修改數據。爲了避免這種糾纏不清的問題,我們建議slaves統一開啓“read_only”。read_only並不會影響replication進程對數據的變更,也不會影響SUPER用戶對“系統”狀態的變更(比如SET GLOBAL ...)

8、在普通的replication模式下,通常master失效後,優先恢復master,而不是failover到其他slaves上。

如果master無法恢復:

1)將某個slave提升爲master,這種方式下可能需要調整客戶端Connector的URL配置(因爲jdbc沒有角色感知的功能,需要開發額外的代碼)。

2)將slave提升爲master,且此slave也使用master的IP(VIP方式,或者硬性路由),此後再重新搭建一個slave即可(可以使用原slave的IP),此種方式需要修改客戶端Connector URL配置。

十六、運維操作

1、mysqldump腳本:

Java代碼 收藏代碼

./mysqldump -h 127.0.0.1 -P 3306 -u root -p -B mydb >/home/data/mydb.sql  

這是mysqldump腳本常用的模式,它還有幾個有用的參數選項,全部參數請參考【mysqldump】:

1)--add-drop-database:默認情況下,dump文件中會寫入“CREATE DATABASE”語句,如果slave上已經有了此database,在導入數據時可能會發生錯誤;此選項,就是在“CREATE DATABASE”語句之前首先添加一行“DROP DATABASE”,這樣在導入數據時不會發生衝突。

2)--no-create-db:簡寫爲“-n”,是否添加“CREATE DATABASE”語句。

3)--ignore-table=<db>.<table>:指定忽略的數據庫表,此參數可以重複出現多次。

4)--no-data:簡寫“-d”,只寫入“CREATE TABLE”不寫入表數據,即只dump表結構。

5)--default-character-set=<character-set>:使用的字符集,默認爲utf8。

6)--all-databases:簡寫“-A”,dump所有的數據庫。

7)--databases:簡寫“-B”,指定需要dumpl的數據庫列表,以空格分隔。

2、show processlist:我們可以在master或者slave上執行“SHOW PROCESSLIST”查看與replication有關的進程。

3、show slave hosts:在master執行此命令,用於查看跟進master的所有slaves列表以及相應的report信息。

4、show master status:用於查看master的狀態,主要是查看master上GTID的狀況。

5、show slave status:查看slave的狀態,涉及到的內容很多,【參見詳情】。我們需要關注兩項:Retrieved_Gtid_Set 和 Executed_Gtid_Set,前者表示IO線程已經讀取的GTID的範圍,後者表示SQL線程已經執行的GTID的範圍,我們可以根據這兩個參數來判斷slave與master上數據的距離。

Java代碼 收藏代碼

mysql> show slave status\G;  
*************************** 1. row ***************************  
Slave_IO_State: Waiting for master to send event  
Master_Host: 127.0.0.1  
Master_User: rpl_sys  
Master_Port: 3306  
Connect_Retry: 60  
Master_Log_File: my-binlog.000008  
##IO線程已讀取的master binlog位置  
##如果沒有開啓GTID,在“CHANGE MASTER TO”中  
##可以使用此POS  
Read_Master_Log_Pos: 2403  
Relay_Log_File: relay-bin.000006  
Relay_Log_Pos: 789  
Relay_Master_Log_File: my-binlog.000008  
##slave IO線程狀態  
Slave_IO_Running: Yes  
##slave SQL線程狀態  
Slave_SQL_Running: Yes  
Replicate_Do_DB:  
Replicate_Ignore_DB: mysql  
Replicate_Do_Table:  
Replicate_Ignore_Table:  
...  
##SQL線程已執行的binlog位置  
Exec_Master_Log_Pos: 2403  
Relay_Log_Space: 1030  
...  
##slave與master的步差  
Seconds_Behind_Master: 0  
Master_SSL_Verify_Server_Cert: No  
...  
Replicate_Ignore_Server_Ids:  
Master_Server_Id: 10  
Master_UUID: 8a542af6-b79d-11e5-b2cf-c699ad96e9cc  
Master_Info_File: mysql.slave_master_info  
SQL_Delay: 0  
SQL_Remaining_Delay: NULL  
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates  
...  
##GTID模式下  
##已經從master讀取的GTID範圍  
Retrieved_Gtid_Set: 8a542af6-b79d-11e5-b2cf-c699ad96e9cc:1-7  
##SQL線程已經執行的GTI範圍  
Executed_Gtid_Set: 8a542af6-b79d-11e5-b2cf-c699ad96e9cc:1-7  
Auto_Position: 1  
Replicate_Rewrite_DB:  
Channel_Name:  
Master_TLS_Version:  

6、show status like “rpl_semi_sync%”:用於查看semisync的各項變量的值。

7、slave有2種線程類型:IO線程、SQL線程,可以通過“START(或STOP) SLAVE IO_THREAD”,“START(或STOP) SLAVE SQL_THREAD”對兩種線程單獨控制。IO線程就是與master建立鏈接讀取binlog、寫入relay log的線程,通常一個slave只有一個;SQL線程,負責從relay log中讀取變更操作並執行語句,默認爲單線程,我們可以通過“slave_parallel_workers”來開啓多線程方式。

十七、Failover

如果master失效或者人工干預,可以將slave提升爲master;在實際環境中,如果master失效,我們建議優先將master重新上線,而不是急於Failover,除非master無法短時間內恢復。因爲在普通的replication架構下,failover機制尚不是非常健全,而且還涉及到客戶端Connection相關策略問題。

1、終止Master節點(人工干預):

如果希望將一個活躍的master節點角色遷移成slave,那麼需要首先中斷master的寫服務:

Java代碼 收藏代碼

#在master上執行  
>FLUSH TABLES WITH READ LOCK;  
>SET GLOBAL read_only=1;  

2、將slave提升爲master:

1)如果master已經失效,那麼就需要將逐個查看slaves的狀態,並確定一個數據最完整的slave(Retrieved_Gtid_Set範圍最大的那個),並將其選定爲master候選;且等待此slave數據複製完成之後,再切換成master,即“show slave status”中“Exec_Master_Log_Pos”=“Read_Master_Log_Pos”。

2)如果人工終止Master,如1、,master上終止writes服務之後,可以通過“show master status”查看當前master已經執行的GTIDs;當所有的slaves的“Retrieved_Gtid_Set”值與master相等時,再將此slave切換爲master。

3、在所有的slaves執行(包括候選master候選):“STOP SLAVE”。在候選master上,還需要並關閉read_only。

4、在其他slaves上執行(新master除外):“CHANGE MASTER TO”指令,與新master跟進,然後執行“START SLAVE”。

5、如果是人工終止Master,別忘了釋放鎖。與1、對應。

Java代碼 收藏代碼

>UNLOCK TABLES;  
>SET GLOBAL read_only=0;  

6、如果舊的master需要再次成爲master,它首先需要與當前master跟進並保持數據完整(其實還需要將當前master處於read_only狀態),然後在舊master上執行“RESET MASTER”,此指令會導致舊master上原有的binlog清除,以避免slaves跟進時帶來問題。

十八、Replication模式問題

1、模式一致:在replicaiton架構中,Master與slaves核心配置項應該保持一致,所有的database的引擎保持一致,表結構保持一致等。我們不應該在replication架構中,人爲將數據的模式修改爲不同,比如手動修改slaves的引擎類型,將Innodb改爲MyISAM,有可能你是基於“slaves上使用MyISAM引擎可以提供更好的read效率”考慮,但是這種調整將會導致很多意外的問題。 而且我們通常在slalves上設定全局的“read_only=1”以避免人爲的、或者客戶端意外的在slaves上修改數據。

2、限定binlog的數據庫:我們可以在master端設定“binlog_do_db”、“binlog_ignore_db”配置項,來指定master端將那些數據庫的操作寫入binlog,以及忽略哪些數據庫的binlog。默認情況下,所有的databases中的數據變更都將寫入binlog。如果指定了“binlog_do_db”,那麼binlog中只會寫入相應數據庫的操作日誌;如果指定了“binlog_ignore_db”,那麼此數據庫的操作記錄將不會被寫入binlog。

不過,在SBR和RBR兩種replication不同的格式下,binlog的寫入行爲有些不同,這一點需要非常小心。

binlog_do_db:

1)SBR:根據“默認”數據庫(即USE指令選擇)名來判斷是否寫入binlog,而不是根據updates的目標數據庫判斷,比如我們指定“binlog_do_db=sales”,下面兩個SQL語句的binlog行爲將是不同的:

Java代碼 收藏代碼

1、  
> USE prices;  
> UPDATE sales.january SET amount = amount + 1000;  

2、  
>USE sales;  
>UPDATE prices.discounts SET percentage = percentage + 10;  

通過“USE”選擇的數據庫爲“默認數據庫”,因爲“binlog_do_db”值檢測“默認數據庫”是否匹配,而不關注實際的updates語句中的目標數據庫(即跨數據庫操作的),因此對於1、情況將不會寫入binlog,但是2、會寫入binlog。

2)RBR:基於row-based格式的binlog模式,binlog的寫入似乎更加合乎邏輯,它不在單純的只判斷“默認數據庫”,而是判斷updates實際的目標數據庫是否匹配,如果匹配則寫入binlog,即使此時“默認數據庫”不匹配。比如“binlog_do_db=sales”,上例中1、語句仍然會被寫入binlog,但是2、語句儘管“默認數據庫”爲“sales”但是其updates的目標數據庫不匹配,將不會被寫入binlog。

binlog_ignore_db:

1)SBR:基本思路同上,即根據“默認數據庫”來判斷是否忽略此statements,比如“binlog_ignore_db=sales”,那麼上例中2、語句將不會寫入binlog(被忽略)。

2)RBR:思路同上,根據updates實際的目標數據庫來判斷是否忽略此statements,比如“binlog_ignore_db=sales”,那麼上例中1、語句將不會寫入binlog,而2、語句會被寫入。

3、限定replicate數據庫:binlog_do_db和binlog_ignore_db是在master端生效,那些寫入binlog的內容將會被全權發送給slaves,當slave的SQL線程讀取binlog變更操作後,slave還可以判斷是否繼續執行此變更操作,有兩個配置項來決定“replicate_do_db”、“replicate_ignore_db”。它們基本思路同2、部分,請參看“binlog_do_db”、“binlog_ignore_db”。

4、log_slave_updates:本文一再強調,如果你的slave後端沒有其他的slaves跟進,即二級slaves,那麼建議關閉此選項。如果開啓,那麼slave的SQL線程在執行變更操作時也會寫入binlog日誌,在failover時,如果此slave被選舉爲master,其他slaves與它跟進時,其本地的binlog會再次發給其他slaves,那麼也意味着其他slaves接收了2次相同的binlog內容。(GTID開啓時應該不會接受2次,未測試!如果slave上開啓了此特性,那麼當次slave被提升爲master後,需要執行一次“RESET MASTER”指令)

5、AUTO_INCREMENT:基於SBR的replication格式下,AUTO_INCREMENT、LAST_INSERT_ID()、TIMESTAMP的值都可以正確的被複制,但是需要注意如下幾個問題(RBR不需要考慮):

1)Master與slaves的存儲引擎必須一致,表的schema必須一致。即master和slave上AUTO_INCREMENT的字段必須一一對應。

2)如果slave端有trigger,且在trigger中修改了AUTO_INCREMENT的字段值,這種方式有可能導致replication下數據不一致問題。

3)如果是組合主鍵(composite primary key),那麼AUTO_INCREMENT字段必須是組合主鍵的首個字段。(InnoDB)

4)對於“INSERT ... SELECT ...”語句,可能導致在master與slaves上執行時得到的數據順序不同,連帶導致AUTO_INCREMENT的值不同。所以,我們必須在SELECT語句中使用“ORDER BY”來完全保障順序。

6、字符集:爲了避免亂碼,我們應該在master和slaves上配置相同的字符集類型,而且儘可能在create表時指定字符集,而不是使用默認字符集,這樣可以避免replication過程中產生亂碼。

7、NOW():這個函數,可能是我們用的最多的,表示當前系統的時間;在SBR模式下,大家肯定會疑惑,如果slave與master的時間不同步怎麼辦?會不會導致數據不一致?無論是SBR還是RBR,NOW()總是在解析時計算並替換成“TIMESTAMP”,所以在replication到slave端時已經是計算後的值,而不是NOW()語句。但是最大的問題是“time_zone”,因爲timestamp的轉換成日期時將會受到時區的影響,這也就要求master和slaves上必須配置相同的time_zone,而不是使用默認的系統時區。

Java代碼 收藏代碼

> SHOW VARIABLES LIKE '%time_zone%';  
+------------------+--------+  
| Variable_name    | Value  |  
+------------------+--------+  
| system_time_zone | CST    |  
| time_zone        | SYSTEM |  
+------------------+--------+  
> SELECT @@GLOBAL.time_zone;  

我們可以使用如下幾種方式來設置time_zone:

Java代碼 收藏代碼

##配置文件中  
default_time_zone=+8:00  
##全局變量  
SET GLOBAL time_zone=+8:00  

Java代碼 收藏代碼

#查看binlog內容  
>./mysqlbinlog binlog.000001  
...  
SET TIMESTAMP=1453473604/*!*/;  
INSERT INTO test(created) value(now())  
...  

注意time_zone值採用UTC格式,中國採用“+8:00”,可參考列表【timezone】

8、LIMIT:在SRB模式下,delete、updates、insert...select批量操作且使用了LIMIT時,如果操作不能有效限定特定的行(比如指定主鍵ID),那麼將可能導致語句在slave與master上執行的結果不同,所以,對於上述語句,指定LIMIT的同時還要使用order by,以保證被操作的行是一致性的。(底層存儲,大家都知道“holes”)

9、REPAIR:當MySQL實例異常中斷後,或者希望對錶進行repair,那麼需要首先暫停所有slaves上的IO線程,即中斷binlog複製。然後在master上執行“repair table”執行,這個過程會導致部分“不完整的行”被刪除,但是這種刪除是基於底層文件存儲,而不會記錄在binlog中;所以在repair結束之後,需要將master上相應table的frm、ibd文件數據copy到slaves上,並在master上執行“REST MASTER”。

(表空間優化指令“OPTIMIZE TABLE”則不需要上述過程,只需要相應的節點處於只讀狀態即可。)

10、系統數據庫mysql:我們都知道,MySQL實例在初始化時會創建mysql數據庫,這是一個系統數據庫,mysql數據庫中保存了當前實例的很多狀態信息。在replication模式下,mysql數據庫的數據變更仍然會被寫入binlog,對於有些statements比如“GRANT”、“REVOKE”等將會以SBR格式寫入binlog,進而同步給slaves。直觀來說,如果你在master上新增了系統用戶或者授權操作,也將會被同步給slaves,這種情況存在利弊,或許你希望這麼做,但或許你不希望。所以,我們建議“mysql”數據庫不能被replicate,在slaves端使用“replicate_ignore_db=mysql”來限定。

11、事務:最大的問題就在於一個事務中同時修改了非事務表和事務表,這種情況我們應該避免,將會導致replication模式下數據不一致問題。首先非事務性表的修改是立即生效,而且無法rollback;當事務中,穿插執行“事務性表”和“非事務性”表更新的操作時,任何一個事務性表操作都可能發生異常而rollback,進而那些非事務性表的修改將變得不可確定,那麼最終數據一致性(邏輯上)將是無法保證的。

1) 如果在一個事務中,在修改事務性表數據之前,執行任何修改非事務性表的操作,都將立即被寫入binlog,此後的修改事務性表的操作將首先被cached,並在COMMIT時寫入binlog(COMMIT和ROLLBACK指令也會寫入binlog)。

2)如果配置中開啓了GTID且指定“enfore_gtid_consistency=ON”時,那麼在事務中,執行修改事務性表數據之後,再嘗試修改非事務性表時將會拋出異常。通常我們建議非事務性表則不需要再事務中執行。

Java代碼 收藏代碼

ERROR 1785 (HY000): Statement violates GTID consistency: Updates to non-transactional tables can only be done in either autocommitted statements or single-statement transactions, and never in the same statement as updates to transactional tables.  

爲了避免不必要的問題,我們建議在所有數據庫表均採用InnoDB引擎,對於目前而言,InnoDB引擎在讀寫效率上已經和MyISAM沒有太大性能差別。

12、slave_preserve_commit_order:對於多線程slaves,來保障事務在slave上執行的順序與relay log中的順序嚴格一致,只有當“slave_parallel_workers”開啓時有效;此時“log_bin”、“log_slave_updates”必須開啓,而且“slave_parallel_type”值必須爲“LOGICAL_CLOCK”(默認值爲DATABASE)。即當多線程開啓時,且根據relay log中事務的邏輯順序執行statements,是否需要嚴格保持順序,默認值爲0表示併發執行忽略順序。slave_parallel_type默認爲DATABASE,能夠確保每個DATABASE中的事務順序嚴格一致,但是無法保證所有databases的事務執行順序也是嚴格一致的(gap),比如兩個事務依次操作了2個DB:A和B,儘管事務A、B分別被worker X、Y線程接收,但是因爲線程調度的問題,有可能導致A的執行時機落後於B。如果你的事務經常是“跨DB”操作,那麼可以考慮使用此參數限定順序。當此參數開啓時,這要求任何worker線程執行的事務時,只有當前事務中此之前的所有事務都執行後(被其他worker線程執行),才能執行和提交。(每個事務中,都記錄了當前GTID的privious GTID,只有privious GTID被提交後,當前GTID事務才能提交)

13、TRUNCATE:清理表數據,並重建表的schema。這個指令其實是個DML指令,不過replication模式中,它將作爲DDL語句寫入binlog,無論是SBR、RBR還是MIXED模式。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章