十三、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模式。