記一次mysql配置雙主全過程
[danger] 強烈建議在執行本節所有操作前將所有操作命令操作copy到Sublime Text編輯器替換port爲要配置主從的mysql實例對應的端口,避免誤操作!!!
[danger] 強烈建議在執行本節所有操作前將所有操作命令操作copy到Sublime Text編輯器替換port爲要配置主從的mysql實例對應的端口,避免誤操作!!!
[danger] 強烈建議在執行本節所有操作前將所有操作命令操作copy到Sublime Text編輯器替換port爲要配置主從的mysql實例對應的端口,避免誤操作!!!
備份主庫數據
使用innobackupex工具進行備份(因本次涉及到的數據庫實例較多,所以編寫shell腳本,減少出錯,單實例同樣適用):
#!/bin/bash
#ports='3312 3313 3315'
port=3314 # 單實例時手動指定數據庫port
# for循環和if判斷只在多實例時使用
#for port in $ports;do
# if [ $port = "3313" ];then
# passwd='aaaaaaa'
# else
# passwd='bbbbbbb'
# fi
passwd='xxxxxxx'
mkdir /disk1/backup/$port
innobackupex --socket=/home/mysql/$port/mysql.sock --user=root --password=$passwd --parallel=1 /disk1/backup/$port/ > /tmp/"$port"_backup.log
# 將數據rsync由 **monitor1/mysql5** 機器同步到從庫主機(myslq6):
rsync -avP --bwlimit 50000 /disk1/backup/$port adai@mysql6:/data2/backup/ > /tmp/"$port"_rsync.log
echo "Finish $port"
#done
Note: 執行腳本前保證主庫、從庫磁盤空間充足,並且從庫服務器數據文件存放目錄 (“/data2/backup/”) 存在,還有注意文件權限,否則可能會導致rsync失敗!!!
如果有近期備份好的數據可以直接同步到從庫服務器使用,但是 僅當 備份的binlog文件序號小於主庫保留的最小binlog文件序號時即備份文件的binlog創建日期在主庫保留的binlog文件後面,如mysql-bin.00008(備份文件binlog)、mysql-bin.00005(主庫保留的binlog),備份纔可用,即必須確保從庫搭建好之後,開啓同步時,主庫還保留有同步位置之前的日誌(保證數據連續性、一致性)。
搭建mysql雙主結構
配置文件
數據庫配置示例文件(Attention: 因爲數據庫要配置雙主模式,所以在部署時主庫和從庫的server-id一定不能一樣):
$ cat 3314.cnf
[mysqld3314]
lower_case_table_names=1
# 爲PXC定製
binlog_format=ROW
default-storage-engine = InnoDB
innodb_large_prefix=on
innodb_autoinc_lock_mode=2
# 綁定地址之後只能通過內部網絡訪問
# 不能使用: 10.*.*.*
bind-address=10.215.33.23
auto-increment-increment = 2
auto-increment-offset = 2
# 使用 xtrabackup-v2 必須制定datadir, 因爲操作可能直接對 datadir進行
datadir=/home/mysql/3314/data
# http://www.percona.com/doc/percona-xtrabackup/2.2/innobackupex/privileges.html#permissions-and-privileges-needed
# 權限的配置
# xtrapbackup在Donor上執行,因此只需localhost的權限
# 約定:
server-id = 72
# wsrep模式不依賴於GTID
# 開啓GTID
# enforce_gtid_consistency=1
# gtid_mode=on
# 即便臨時作爲Slave,也記錄自己的binlog
log-slave-updates=1
# master_info_repository=TABLE
# relay_log_info_repository=TABLE
# GENERAL #
user = mysql
port = 3314
socket = /home/mysql/3314/mysql.sock
pid-file = /home/mysql/3314/mysql.pid
# MyISAM #
key-buffer-size = 32M
myisam-recover = FORCE,BACKUP
ft-min-word-len = 4
event-scheduler = 0
# SAFETY #
max-allowed-packet = 16M
skip-name-resolve
max_connections = 2000
max_connect_errors = 30
back-log = 500
character-set-client-handshake = 1
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
#character-set-client-handshake=1
#character-set-client=utf8
#character-set-server=utf8
#collation-server=utf8_general_ci
#key-buffer-size = 256M
table-open-cache = 2048
max-allowed-packet = 2048M
slave-skip-errors = all #Skip duplicated key
sort-buffer-size = 4M
join-buffer-size = 8M
thread-cache-size = 50
concurrent-insert = 2
thread-stack = 192K
net-buffer-length = 8K
read-buffer-size = 256K
read-rnd-buffer-size = 16M
bulk-insert-buffer-size = 64M
# 採用thread pool來處理連接
thread-handling=pool-of-threads
# 新線程創建等待時間,, 單位爲10ms,50即爲500ms
thread-pool-stall-limit = 50
sql-mode = STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_AUTO_VALUE_ON_ZERO,NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY
sysdate-is-now = 1
innodb = FORCE
innodb-strict-mode = 1
# BINARY LOGGING #
log-bin = /home/mysql/3314/data/mysql-bin
expire-logs-days = 5
# LOGGING #
# log-output=file 默認就是FILE
log-error = /home/mysql/3314/data/mysql-error.log
long-query-time = 0.3
# log-queries-not-using-indexes = 1
slow-query-log = 1
slow-query-log-file = /home/mysql/3314/data/mysql-slow.log
# 默認爲0(MySQL不控制binlog的輸出)
# sync-binlog = 1
# CACHES AND LIMITS #
tmp-table-size = 32M
max-heap-table-size = 32M
# 頻繁修改的表不適合做query-cache, 否則反而影響效率
query-cache-type = 0
query-cache-size = 0
# query-cache-limit = 2M
# query-cache-min-res-unit = 512
thread-cache-size = 100
open-files-limit = 65535
table-definition-cache = 1024
table-open-cache = 4096
# INNODB #
innodb-flush-method = O_DIRECT
innodb-log-files-in-group = 2
# innodb-file-per-table = 1設置之後, 下面的配置基本失效
innodb_data_file_path = ibdata1:10M:autoextend
innodb-thread-concurrency = 32
innodb-log-file-size = 256M
innodb-flush-log-at-trx-commit = 2
innodb-file-per-table = 1
innodb-large-prefix = on
# 內存: 全部內存*0.7
innodb-buffer-pool-size = 2G
performance-schema = 0
net-read-timeout = 60
# innodb-open-files 在MySQL5.6 auto-sized
# 來自May2
innodb-rollback-on-timeout
innodb-status-file = 1
# http://dev.mysql.com/doc/refman/5.6/en/innodb-performance-multiple_io_threads.html
# http://zhan.renren.com/formysql?tagId=3942&checked=true
# 從MySQL 5.5
# innodb_file_io_threads = 4
innodb-read-io-threads = 16
innodb-write-io-threads = 8
innodb-io-capacity = 2000
# innodb-stats-update-need-lock = 0 # MySQL 5.6中無效
# innodb-stats-auto-update = 0
innodb-old-blocks-pct = 75
# innodb-adaptive-flushing-method = "estimate"
# innodb-adaptive-flushing = 1
# https://www.facebook.com/notes/mysql-at-facebook/repeatable-read-versus-read-committed-for-innodb/244956410932
# READ-COMMITTED 每次QUERY都要求調用: read_view_open_now, 而REPEATABLE-READ每次Transaction中只要求一次
# REPEATABLE-READ 會導致讀寫不同步
transaction-isolation = READ-COMMITTED
innodb-sync-spin-loops = 100
innodb-spin-wait-delay = 30
innodb-file-format = "Barracuda"
innodb-file-format-max = "Barracuda"
恢復數據到從庫
如上mysql配置,在使用innobackupex恢復數據,會有報錯:
Error: datadir must be specified.
解決上述報錯(在配置文件中添加如下配置):
$ vim /etc/mysql/3314.cnf
[mysqld]
# 使用 xtrabackup-v2 必須制定datadir, 因爲操作可能直接對 datadir進行
datadir=/home/mysql/3314/data
恢復數據到從庫:
$ cd /data2/backup/3314/
# 準備備份文件”--apply-log“
$ innobackupex --use-memory=1G --apply-log /data2/backup/3314/xxxx-xx-xx-xx
## --apply-log:根據Innodb日誌文件中的更改來讀取備份文件,並將更改應用到表空間,保證備份數據的一致性,應該在恢復備份之前執行。
## 參考:http://www.cnblogs.com/zhoujinyi/p/5893333.html
$ rm -rf /home/mysql/3314/data/* # 清除從庫中的髒數據
# 恢復數據到從庫
$ innobackupex --defaults-file=/etc/mysql/3314.cnf --copy-back /data2/backup/3314/xxxx-xx-xx-xx
$ chown -R mysql:mysql /home/mysql/3314
# 啓動mysql
$ systemctl start mysqld@3314
# 數據庫登錄腳本(使用ansible部署時會創建):
# cat /home/mysql/3314/mysql_client.sh
#!/bin/sh
# Do NOT modify this file by hand!
# Generate by ansible
mysql -u root -pxxxxxx -S /home/mysql/3314/mysql.sock $@
配置主從
主從複製原理
- master服務器將數據的改變記錄二進制binlog日誌,當master上的數據發生改變時,則將其改變寫入二進制日誌中;
- slave將master的binary log拷貝到它自己的中繼日誌。
- 首先,slave開始一個工作線程——I/O線程。I/O線程在master上打開一個普通的連接,然後開始binlog dump process。Binlog dump process從master的二進制日誌中讀取事件,如果已經跟上master,它會睡眠並等待master產生新的事件。I/O線程將這些事件寫入中繼日誌。
- SQL slave thread(SQL從線程)處理該過程的最後一步。SQL線程從中繼日誌讀取事件,並重放其中的事件而更新slave的數據,使其與master中的數據一致。只要該線程與I/O線程保持一致,中繼日誌通常會位於OS的緩存中,所以中繼日誌的開銷很小。
注意幾點:
- --master將操作語句記錄到binlog日誌中,然後授予slave遠程連接的權限(master一定要開啓binlog二進制日誌功能;通常爲了數據安全考慮,slave也開啓binlog功能)。
- --slave開啓兩個線程:IO線程和SQL線程。其中:IO線程負責讀取master的binlog內容到中繼日誌relay log裏;SQL線程負責從relay log日誌裏讀出binlog內容,並更新到slave的數據庫裏,這樣就能保證slave數據和 master數據保持一致了。
- --Mysql複製至少需要兩個Mysql的服務,當然Mysql服務可以分佈在不同的服務器上,也可以在一臺服務器上啓動多個服務。
- --Mysql複製最好確保master和slave服務器上的Mysql版本相同(如果不能滿足版本一致,那麼要保證master主節點的版本低於slave從節點的版本)
- --master和slave兩節點間時間需同步
-
mysql複製的模式:
- 主從複製: 主庫授權從庫遠程連接,讀取binlog日誌並更新到本地數據庫的過程;主庫寫數據後,從庫會自動同步過來(從庫跟着主庫變);
- 主主複製: 主從相互授權連接,讀取對方binlog日誌並更新到本地數據庫的過程;只要對方數據改變,自己就跟着改變;
-
mysql主從複製模式
- 基於SQL語句的複製(statement-based replication, SBR)
- 基於行的複製(row-based replication, RBR)
- 混合模式複製(mixed-based replication, MBR)
binlog
binlog日誌作用是用來記錄mysql內部增刪改查等對mysql數據庫有更新的內容的記錄(對數據庫的改動),對數據庫的查詢select或show等不會被binlog日誌記錄;主要用於數據庫的主從複製(實時備份)以及增量恢復。
-
開啓mysql的binlog:
log-bin = /home/mysql/3314/mysql-bin
-
查看是否啓用了日誌:
mysql> show variables like 'log_bin';
-
binlog的三種模式:
- Row level :日誌中會記錄每一行數據被修改的情況,然後在slave端對相同的數據進行修改。
- 優點:能清楚的記錄每一行數據修改的細節
- 缺點:數據量太大
- Statement level(默認):每一條被修改數據的sql都會記錄到master的bin-log中,slave在複製的時候sql進程會解析成和原來master端執行過的相同的sql再次執行
- 優點:解決了 Row level下的缺點,不需要記錄每一行的數據變化,減少bin-log日誌量,節約磁盤IO,提高新能
- 缺點:容易出現主從複製不一致
- Mixed(混合模式):結合了Row level和Statement level的優點
- Row level :日誌中會記錄每一行數據被修改的情況,然後在slave端對相同的數據進行修改。
-
參考:
開始配置
-
主庫授權:
# 在主庫創建主從同步要用到的用戶,並授權(嚴格針對ip授權): mysql> GRANT REPLICATION SLAVE ON *.* TO 'sstuser'@'10.xxx.xx.xx' IDENTIFIED BY 'sstuser_xxxxxxx'; mysql> flush privileges;
-
配置從庫:
$ systemctl start mysqld@3314 $ systemctl status mysqld@3314 # 獲取備份文件binlog文件名、偏移量(搭建主從時用) $ cat /home/mysql/3314/data/xtrabackup_binlog_pos_innodb ## eg:mysql-bin.000028 145809329 # 登錄並配置從庫 $ sh mysql_client.sh
-
授權:
## 指定主庫 mysql> CHANGE MASTER TO MASTER_HOST='10.xxx.xx.xx', MASTER_PORT=3314,MASTER_USER='sstuser', MASTER_PASSWORD='sstuser_xxxxxx', MASTER_LOG_FILE='mysql-bin.000028', MASTER_LOG_POS=145809329; ## 開啓主從同步 mysql> start slave; ## 檢查主從同步狀態 mysql> show slave status\G # 驗證: ## 在主庫插入數據,在從庫查看(簡單操作:create database test_slave;) ## 如果配置了haproxy,也可以檢測haproxy狀態:http://vip:7901/status
-
主從重要狀態信息:
- Slave_IO_Running: I/O線程是否被啓動併成功地連接到主服務器上,Yes表示連接成功!
- **Slave_SQL_Running: ** SQL線程是否被啓動,Yes表示啓動成功!
- **Exec_Master_Log_Pos: ** 來自主服務器的二進制日誌的由SQL線程執行的上一個時間的位置(Relay_Master_Log_File)。
- **Relay_Log_Space: ** 所有原有的中繼日誌結合起來的總大小
- Slave_SQL_Running_State: 從庫SQL線程運行狀態,正常應該處於等待接收主庫數據同步狀態( “ Slave has read all relay log; waiting for the slave I/O thread to update it“)
配置雙主
Note:如果需要新從庫和主庫構建雙主集羣,從庫必須不能接受任何寫請求!!!
-
在從庫服務器對主庫服務器授權:
mysql> GRANT REPLICATION SLAVE ON *.* TO 'sstuser'@'10.xxx.xx.xx2' IDENTIFIED BY 'sstuser_xxxxxx'; # 查看數據庫binlog文件名、偏移量 mysql> show master status; +------------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+-------------------+ | mysql-bin.000001 | 2954215 | | | | +------------------+----------+--------------+------------------+-------------------+ 1 row in set (0.01 sec)
-
配置主庫:
# 停止原有的slave mysql> stop slave; mysql> CHANGE MASTER TO MASTER_HOST='10.xxx.xx.xx2', MASTER_PORT=3314,MASTER_USER='sstuser', MASTER_PASSWORD='sstuser_xxxxxx', MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=2954215; mysql> start slave; mysql> show slave status\G ## 搭建完成後用同樣的方式(插入數據)檢測雙主狀態!!!
問題處理
-
如果搭建過程中發現slave狀態錯誤,應立刻停止slave(stop slave),檢測操作步驟,排查錯誤,重建主從!
-
如果是恢復雙主中的一個節點,必須先停止2個節點主從同步。不能讓健康的節點一直同步錯誤的節點。
-
因mysql版本問題導致的問題:
ERROR 1558 (HY000): Column count of mysql.user is wrong. Expected 43, found 42. Created with MySQL 50552, now running 50637. Please use mysql_upgrade to fix this error
-
原因: 新部署的數據庫和原有數據庫版本不一致(檢測發現小版本有升級),錯誤是由於你曾經升級過數據庫,升級完後沒有使用 mysql_upgrade升級數據結構造成的。
-
解決辦法: 升級數據結構
$ mysql_upgrade -u root -pxxxxxx -S /home/mysql/3314/mysql.sock
-