MySQL主從復制


MySQL主從復制

將主數據庫中的DDL和DML操作通過二進制日志傳輸到從數據庫上,然后將這些日志重新執行(重做)一遍;從而使得從數據庫的數據與主數據庫保持一致。

1、MySQL 主從復制的基本介紹

MySQL支持單向、異步復制,復制過程中一個服務器充當主服務器,而一個或多個其它服務器充當從服務器。

MySQL復制是基於主服務器在二進制日志中跟蹤所有對數據庫的更改。因此,要進行復制,必須在主服務器上啟用二進制日志。每個從服務器從主服務器接收主服務器已經記錄到日志的數據。

當一個從服務器連接主服務器時,它通知主服務器從服務器在日志中讀取的最后一次成功更新的位置。從服務器接收從那時起發生的任何更新,並在本機上執行相同的更新。然后封鎖並等待主服務器通知新的更新。從服務器執行備份不會干擾主服務器,在備份過程中主服務器可以繼續處理更新。

2、什么是主從復制

簡單來說,是使用兩個或兩個以上相同的數據庫,將一個數據庫當做主數據庫,而另一個數據庫當做從數據庫。在主數據庫中進行相應操作時,從數據庫記錄下所有主數據庫的操作,使其二者一模一樣。

MySQL數據庫主從復制主要分為三步:

  1. master將改變記錄到二進制日志(binary log)中(這些記錄叫做二進制日志事件,binary log events)。

  2. slave的io線程將master的binary log events拷貝到它的中繼日志(relay log)。

  3. slave的sql線程解析中繼日志中的事件並在從庫執行,保持與主庫一致。

Binary log:主數據庫的二進制日志。

Relay log:從服務器的中繼日志。

注意:復制過程有一個很重要的限制——復制在slave上是串行化的,也就是說master上的並行更新操作不能在slave上並行操作。

1、從庫准備

  1. 從庫change master to 時,ip port user password binlog position寫入到master.info進行記錄。
  2. 從庫 start slave 時,會啟動IO線程和SQL線程。

2、同步的過程

  1. 從庫的IO線程,讀取master.info信息,獲取主庫信息並連接主庫。

  2. 主庫接收從庫的鏈接請求后,會生成一個准備binlog DUMP的線程,來響應從庫。

  3. 主庫一旦有新的日志生成,會發送“信號”給主庫的binlog dump線程,然后binlog dump線程會讀取binlog日志的更新。

  4. 通過binlog dump線程j將數據傳送給從庫的IO線程。

  5. IO線程將收到的日志存儲到了TCP/IP 緩存。

  6. 寫入TCP/IP緩存后,立即返回ACK消息給主庫 ,此時主庫工作完成。

  7. IO線程更新master.info文件、binlog 文件名和postion定位。

  8. IO線程將緩存中的數據,存儲到relay-log日志文件,此時io線程工作完成。

  9. 從庫SQL線程讀取relay-log.info文件,獲取到上次執行到的relay-log的位置,作為起點。

  10. 從庫SQL線程基於從步驟9中獲取到的起點,去中繼日志relay-log.000001獲取后續操作,在從庫回放relay-log中繼日志之中內從主庫復制過來的數據。

  11. SQL線程回放完成之后,會更新relay-log.info文件,把當前操作的位置記入,作為下一次操作的起點。

  12. relay-log會有自動清理的功能。

注:在tcp協議中通訊之前都要經過三次握手,請求方發出一個syn信號請求連接,對方收到並接受的時候就會發出ack消息,ack就是回應的意思。

3、主從復制的方式

MySQL的主從復制有兩種復制方式,分別是異步復制和半同步復制。

1、異步復制

我們之前介紹的就是異步復制,即客戶端線程提交一個寫操作,寫入主庫的binlog日志后就立即返回,並不需要等待從庫完成同步操作,而主庫的dump線程會監測binlog日志的變量然后主動將更新推送給從庫。

MySQL 主從復制默認是異步的模式。

1.1、主從復制實現

要實現主從復制,需要如下幾步:

1、在主庫上創建一個用於復制的賬號。

2、修改主庫配置文件,開啟主庫的Binlog,並設置server-id和重啟。

3、導出主庫中所有的數據,先導給從庫

4、修改從庫配置文件並重啟

5、配置主從復制

6、開啟主從復制

  1. 在主庫上創建一個用於復制的賬號
mysql> grant replication slave on *.* to 'shanhe'@'%' identified by '123456';
mysql> flush privileges;
  1. 修改主庫配置文件
[root@mysql-1 ~]# cat /etc/my.cnf 
[mysqld]
basedir=/usr/local/mysql
datadir=/data
port=3306
socket=/usr/local/mysql/mysql.sock
character-set-server=utf8
log-error=/var/log/mysqld.log
pid-file=/tmp/mysqld.pid

server-id=1        
log-bin=/data/log-bin/binlog
sync_binlog=1
binlog_format=row
expire_logs_days=7
max_binlog_size=100m
binlog_cache_size=4m
max_binlog_cache_size=512m
binlog-ignore-db=mysql
auto-increment-offset=1
auto-increment-increment=1
slave-skip-errors=all

[mysql]
socket=/usr/local/mysql/mysql.sock

[client]
socket=/usr/local/mysql/mysql.sock

# 重啟
[root@mysql-1 ~]# systemctl restart mysqld
  1. 導出主庫中所有的數據,先導給從庫
[root@mysql-1 ~]# mysqldump -uroot -p123456 -A -E -R --triggers --master-data=2 --single-transaction > /tmp/all.sql
  1. 將數據導入從庫
[root@mysql-1 ~]# scp /tmp/all.sql root@192.168.15.62:/tmp/
root@192.168.15.62''s password: 
all.sql                            100%  853KB   3.5MB/s   00:00 

[root@mysql-2 mysql]# mysql -uroot -p123456 < /tmp/all.sql 
  1. 修改從庫配置文件並重啟
[root@mysql-2 mysql]# vim /etc/my.cnf
[mysqld]
basedir=/usr/local/mysql
datadir=/data
port=3306
socket=/usr/local/mysql/mysql.sock
character-set-server=utf8
log-error=/var/log/mysqld.log
pid-file=/tmp/mysqld.pid

server-id=2
# 中繼日志
relay-log=mysql-relay-bin
replicate-wild-ignore-table=mysql.%
replicate-wild-ignore-table=test.%
replicate-wild-ignore-table=information_schema.%

[mysql]
socket=/usr/local/mysql/mysql.sock
[client]
socket=/usr/local/mysql/mysql.sock

[root@mysql-2 mysql]# systemctl restart mysqld
  1. 配置主從復制

配置主從復制,首先得在MySQL Master節點查出binlog日志狀態,然后配置主從復制

  • 在MySQL Master節點查出binlog日志狀態
mysql> show master status ;
+---------------+----------+--------------+------------------+-------------------+
| File          | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+---------------+----------+--------------+------------------+-------------------+
| binlog.000001 |      154 |              | mysql            |                   |
+---------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
  • 在從庫配置主從復制
# 首先測試主庫是否連接正常
[root@mysql-2 mysql]# mysql -uroot -p123456 -h192.168.15.61

[root@slave1 ~]# mysql -uroot -p123456 # 登錄然后執行
change master to
master_host='192.168.15.61',  			-- 庫服務器的IP
master_port=3306,              			-- 主庫端口
master_user='shanhe', 							-- 主庫用於復制的用戶
master_password='123456', 					-- 密碼
master_log_file='binlog.000001', 		-- 主庫日志名
master_log_pos=154;			 						-- 主庫日志偏移量,即從何處開始復制

  • 查看主從復制結果

2、半同步復制

介於異步復制和全同步復制之間,主庫在執行完客戶端提交的事務后不是立刻返回給客戶端,而是等待至少一個從庫接收到並寫到relay log中才返回給客戶端。相對於異步復制,半同步復制提高了數據的安全性,同時它也造成了一定程度的延遲,這個延遲最少是一個TCP/IP往返的時間。所以,半同步復制最好在低延時的網絡中使用。

半同步復制超時則會切換回異步復制,正常后則切回半同步復制

在半同步復制時,如果主庫的一個事務提交成功了,在推送到從庫的過程當中,從庫宕機了或網絡故障,導致從庫並沒有接收到這個事務的Binlog,此時主庫會等待一段時間(這個時間由rpl_semi_sync_master_timeout的毫秒數決定),如果這個時間過后還無法推送到從庫,那MySQL會自動從半同步復制切換為異步復制,當從庫恢復正常連接到主庫后,主庫又會自動切換回半同步復制。

2.1、部署半同步復制

半同步模式是作為MySQL5.5的一個插件來實現的,主從庫使用的插件不一樣。

  1. 先確認主從的MySQL服務器是否支持動態增加插件
mysql> select @@have_dynamic_loading;
+------------------------+
| @@have_dynamic_loading |
+------------------------+
| YES                    |
+------------------------+
1 row in set (0.08 sec)
  1. 分別在主從庫上安裝對用插件
-- 主庫安裝插件
mysql> install plugin rpl_semi_sync_master soname 'semisync_master.so';
Query OK, 0 rows affected (0.28 sec)

-- 從庫安裝插件
mysql> install plugin rpl_semi_sync_slave soname 'semisync_slave.so';
Query OK, 0 rows affected (0.20 sec)
  1. 在主庫開啟半同步復制
mysql> set global rpl_semi_sync_master_enabled=1;
Query OK, 0 rows affected (0.09 sec)

mysql> set global rpl_semi_sync_master_timeout=30000;
Query OK, 0 rows affected (0.00 sec)


# 添加到配置文件
[root@mysql-1 ~]# vim /etc/my.cnf
rpl_semi_sync_master_enabled=1
rpl_semi_sync_master_timeout=1000
  1. 在從庫開啟半同步復制
mysql> set global rpl_semi_sync_slave_enabled=1;
Query OK, 0 rows affected (0.07 sec)

mysql> stop slave io_thread;
Query OK, 0 rows affected (0.09 sec)

mysql> start slave io_thread;
Query OK, 0 rows affected (0.06 sec)


# 添加到配置文件之中
[root@mysql-2 ~]# vim /etc/my.cnf
rpl_semi_sync_slave_enabled =1
  1. 在主庫上查看半同步復制的狀態
-- 主庫查看
mysql> show status like 'Rpl_semi_sync_master_status';
+-----------------------------+-------+
| Variable_name               | Value |
+-----------------------------+-------+
| Rpl_semi_sync_master_status | ON    |
+-----------------------------+-------+
1 row in set (0.00 sec)

-- 從庫查看
mysql> show status like 'Rpl_semi_sync_slave_status';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | ON    |
+----------------------------+-------+
1 row in set (0.00 sec)

這兩個變量常用來監控主從是否運行在半同步復制模式下。

3、多主多從

在企業中,數據庫高可用一直是企業的重中之重,中小企業很多都是使用mysql主從方案,一主多從,讀寫分離等,但是單主存在單點故障,從庫切換成主庫需要作改動。因此,如果是雙主或者多主,就會增加mysql入口,增加高可用。不過多主需要考慮自增長ID問題,這個需要特別設置配置文件,比如雙主,可以使用奇偶,總之,主之間設置自增長ID相互不沖突就能完美解決自增長ID沖突問題。

3.1、MySQL雙主(主主)架構方案思路

  1. 兩台mysql都可讀寫,互為主備,默認只使用一台(masterA)負責數據的寫入,另一台(masterB)備用。

  2. masterA是masterB的主庫,masterB又是masterA的主庫,它們互為主從。

  3. 兩台主庫之間做高可用,可以采用keepalived等方案(使用VIP對外提供服務)。

  4. 所有提供服務的從服務器與masterB進行主從同步(雙主多從)。

  5. 建議采用高可用策略的時候,masterA或masterB均不因宕機恢復后而搶占VIP(非搶占模式)。

這樣做可以在一定程度上保證主庫的高可用,在一台主庫down掉之后,可以在極短的時間內切換到另一台主庫上(盡可能減少主庫宕機對業務造成的影響),減少了主從同步給線上主庫帶來的壓力;但是也有幾個不足的地方:

  1. masterB可能會一直處於空閑狀態。
  2. 主庫后面提供服務的從庫要等masterB先同步完了數據后才能去masterB上去同步數據,這樣可能會造成一定程度的同步延時。

3.2、修改主節點的配置文件

[mysqld]
basedir=/usr/local/mysql
datadir=/data
port=3306
socket=/usr/local/mysql/mysql.sock
character-set-server=utf8
log-error=/var/log/mysqld.log
pid-file=/tmp/mysqld.pid

# 節點ID,確保唯一
server-id = 1        
 
#開啟mysql的binlog日志功能
log-bin=binlog
#控制數據庫的binlog刷到磁盤上去 , 0 不控制,性能最好,1每次事物提交都會刷到日志文件中,性能最差,最安全
sync_binlog=1
#binlog日志格式
binlog_format=row
#binlog過期清理時間
expire_logs_days=7
#binlog每個日志文件大小
max_binlog_size=100m
#binlog緩存大小
binlog_cache_size=4m   
#最大binlog緩存大小
max_binlog_cache_size=512m         
 
#不生成日志文件的數據庫,多個忽略數據庫可以用逗號拼接,或者 復制黏貼下述配置項,寫多行
binlog-ignore-db=mysql 
# 表中自增字段每次的偏移量
auto-increment-offset=1
# 表中自增字段每次的自增量
auto-increment-increment=2
#跳過從庫錯誤
slave-skip-errors=all

#將復制事件寫入binlog,一台服務器既做主庫又做從庫此選項必須要開啟
log-slave-updates=true
gtid-mode=on

enforce-gtid-consistency=true
master-info-repository=file
relay-log-info-repository=file
sync-master-info=1
slave-parallel-workers=0
sync_binlog=0
binlog-checksum=CRC32
master-verify-checksum=1
slave-sql-verify-checksum=1
binlog-rows-query-log_events=1
max_binlog_size=1024M

# 忽略同步的數據庫
replicate-ignore-db=mysql
replicate-ignore-db=information_schema
replicate-ignore-db=performance_schema
replicate-ignore-db=sys

max_connections=3000
max_connect_errors=30

#忽略應用程序想要設置的其他字符集
skip-character-set-client-handshake
#連接時執行的SQL                                
init-connect='SET NAMES utf8'
#服務端默認字符集
character-set-server=utf8
#請求的最大連接時間
wait_timeout=1800
#和上一參數同時修改才會生效
interactive_timeout=1800
#sql模式
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

max_allowed_packet=10M
bulk_insert_buffer_size=8M
query_cache_type=1
query_cache_size=128M
query_cache_limit=4M
key_buffer_size=256M
read_buffer_size=16K
# 禁用反向解析
skip-name-resolve
slow_query_log=1
long_query_time=6
slow_query_log_file=slow-query.log
innodb_flush_log_at_trx_commit=2
innodb_log_buffer_size=16M



[mysql]
socket=/usr/local/mysql/mysql.sock

[client]
socket=/usr/local/mysql/mysql.sock

3.3、修改備節點的配置文件

[mysqld]
basedir=/usr/local/mysql
datadir=/data
port=3306
socket=/usr/local/mysql/mysql.sock
character-set-server=utf8
log-error=/var/log/mysqld.log
pid-file=/tmp/mysqld.pid

# 節點ID,確保唯一
server-id=2      
 
#開啟mysql的binlog日志功能
log-bin=binlog
#控制數據庫的binlog刷到磁盤上去 , 0 不控制,性能最好,1每次事物提交都會刷到日志文件中,性能最差,最安全
sync_binlog=1
#binlog日志格式
binlog_format=row
#binlog過期清理時間
expire_logs_days=7
#binlog每個日志文件大小
max_binlog_size=100m
#binlog緩存大小
binlog_cache_size=4m   
#最大binlog緩存大小
max_binlog_cache_size=512m  
 
#不生成日志文件的數據庫,多個忽略數據庫可以用逗號拼接,或者 復制黏貼下述配置項,寫多行
binlog-ignore-db=mysql 
 
# 表中自增字段每次的偏移量
auto-increment-offset=2
# 表中自增字段每次的自增量
auto-increment-increment=2  
#跳過從庫錯誤
slave-skip-errors=all

#將復制事件寫入binlog,一台服務器既做主庫又做從庫此選項必須要開啟
log-slave-updates=true
gtid-mode=on

enforce-gtid-consistency=true
master-info-repository=file
relay-log-info-repository=file
sync-master-info=1
slave-parallel-workers=0
sync_binlog=0
binlog-checksum=CRC32
master-verify-checksum=1
slave-sql-verify-checksum=1
binlog-rows-query-log_events=1
max_binlog_size=1024M

# 忽略同步的數據庫
replicate-ignore-db=information_schema
replicate-ignore-db=performance_schema
replicate-ignore-db=sys

max_connections=3000
max_connect_errors=30

#忽略應用程序想要設置的其他字符集
skip-character-set-client-handshake
#連接時執行的SQL
init-connect='SET NAMES utf8'
#服務端默認字符集
character-set-server=utf8
#請求的最大連接時間
wait_timeout=1800
#和上一參數同時修改才會生效
interactive_timeout=1800
#sql模式
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

max_allowed_packet=10M
bulk_insert_buffer_size=8M
query_cache_type=1
query_cache_size=128M
query_cache_limit=4M
key_buffer_size=256M
read_buffer_size=16K

# 禁用反向解析
skip-name-resolve
slow_query_log=1
long_query_time=6
slow_query_log_file=slow-query.log
innodb_flush_log_at_trx_commit=2
innodb_log_buffer_size=16M

[mysql]
socket=/usr/local/mysql/mysql.sock

[client]
socket=/usr/local/mysql/mysql.sock

3.4、兩個master階段都必須重新初始化數據庫

[root@mysql-1 ~]# mysqld --initialize --user=mysql --basedir=/usr/local/mysql --datadir=/data
[root@mysql-2 ~]# mysqld --initialize --user=mysql --basedir=/usr/local/mysql --datadir=/data

3.5、分別登陸數據庫並創建復制賬號(每個機器上都必須執行)

-- 查看生成的密碼
[root@mysql-2 ~]# grep password /var/log/mysqld.log 

-- 使用臨時密碼登陸數據庫
[root@mysql-2 ~]# mysql -uroot -p'_<r,zjMpG6-.'

-- 修改數據庫臨時密碼
mysql>  alter user   root@localhost   identified  by  'Test123!';
Query OK, 0 rows affected (0.00 sec)

-- 利用數據庫臨時密碼創建登陸
[root@mysql-2 ~]# mysql -uroot -p'Test123!'

-- 創建遠程連接賬號
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456' WITH GRANT OPTION;
Query OK, 0 rows affected, 1 warning (0.08 sec)

-- 刪除其他默認密碼
mysql> delete from mysql.user where host = 'localhost';
Query OK, 3 rows affected (0.29 sec)

-- 刷新權限
mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.09 sec)

-- 重新登陸並創建遠程復制賬號
[root@mysql-2 ~]# mysql -uroot -p123456
mysql> grant replication slave on *.* to 'shanhe'@'%' identified by '123456';
Query OK, 0 rows affected, 1 warning (0.01 sec)

mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)

# 'masterB節點操作與上述操作一致~~,當然除了初始密碼不一樣。。。'

3.6、開始配置主從復制

-- 1. 先查看master1上的信息
'如果masterB上需要連接,則需要此處的file以及position信息'
mysql> show master status\G
*************************** 1. row ***************************
             File: binlog.000002
         Position: 1616
     Binlog_Do_DB: 
 Binlog_Ignore_DB: mysql
Executed_Gtid_Set: f6be1c0e-30d0-11ec-afbb-000c29e0e250:1-7
1 row in set (0.00 sec)

-- 2. 接着查看master2上的信息
mysql> show master status\G
*************************** 1. row ***************************
             File: binlog.000002
         Position: 1459
     Binlog_Do_DB: 
 Binlog_Ignore_DB: mysql
Executed_Gtid_Set: fe9529ac-30d0-11ec-8a3c-000c297a2694:1-6
1 row in set (0.00 sec)

-- 在master2上執行
mysql> change master to 
    -> master_host='192.168.15.61',
    -> master_port=3306,
    -> master_user='shanhe',
    -> master_password='123456',
    -> master_log_file='binlog.000002',
    -> master_log_pos=1459;
Query OK, 0 rows affected, 2 warnings (0.00 sec)

-- 在master1上執行
mysql> change master to 
    -> master_host='192.168.15.62',
    -> master_port=3306,
    -> master_user='shanhe',
    -> master_password='123456',
    -> master_log_file='binlog.000002',
    -> master_log_pos=1459;
Query OK, 0 rows affected, 2 warnings (0.00 sec)

3.7、查看連接結果

-- 查看masterB的信息  
mysql> show slave status\G
'基本上IO線程和SQL線程都出現yes即為ok'

3.8、測試雙主

-- 1. masterA執行如下操作
mysql> create database db01;
Query OK, 1 row affected (0.00 sec)

mysql> use db01;
Database changed
mysql> create table t1(id int primary key);
Query OK, 0 rows affected (0.02 sec)

mysql> insert into t1 values(100);
Query OK, 1 row affected (0.07 sec)

mysql> select * from t1;
+-----+
| id  |
+-----+
| 100 |
+-----+
1 row in set (0.00 sec)

-- 2. masterB查看t1表是否有內容
mysql> select * from db01.t1;
+-----+
| id  |
+-----+
| 100 |
+-----+
1 row in set (0.00 sec)


-- 3. masterB插入一條新數據
mysql> insert into db01.t1 values(200);
Query OK, 1 row affected (0.01 sec)


-- 4. masterA查看數據是否同步完成
mysql> select * from t1;
+-----+
| id  |
+-----+
| 100 |
| 200 |
+-----+
2 rows in set (0.00 sec)
'END'

題解決
#在第八步查看連接結果時,最初顯示狀態,兩個線程的狀態都為NO不可用

1. 錯誤日志並沒有error信息
2. 經過網絡上看,發現大多數是由於數據庫的uuid相同,而導致觸發此異常
3. 而我一檢查,發現我兩台數據庫uuid並不一樣,但是 !!! 本機由於前面的原因,有兩個auto.cnf文件(這兩個文件的uuid一致...)
	'test02機器也是一樣的~'
    [root@test01 ~]# find / -name auto.cnf
    /usr/local/mysql-5.7.35/data/auto.cnf
    /data/auto.cnf
4. 對比一下/etc/my.cnf,確定新的auto.cnf文件是在data目錄下
5. 刪除了舊的auto.cnf文件,重啟數據庫再次查看即為yes

3.9、雙主高可用

高可用是使用keepalived實現VIP。從而實現一個IP無感知操作兩個主節點。

1、安裝keepalived高可用軟件(#兩個節點上全都安裝)
[root@mysql-1 ~]# yum install keepalived -y
[root@mysql-2 ~]# yum install keepalived -y


2、修改keepalived的配置文件
[root@mysql-1 ~]# vim /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
    router_id LVS_DEVEL
}
vrrp_script chk_kubernetes {
    script "/etc/keepalived/check_kubernetes.sh"
    interval 2
    weight -5
    fall 3
    rise 2
}
vrrp_instance VI_1 {
    state MASTER
    interface eth0
    mcast_src_ip 192.168.15.61			# 所在節點的IP
    virtual_router_id 51
    priority 100
    advert_int 2
    authentication {
        auth_type PASS
        auth_pass K8SHA_KA_AUTH
    }
    virtual_ipaddress {
        192.168.15.60
    }
}

# 兩台機器開啟keepalived
 systemctl start keepalived

# navicat連接設置的vip進行測試


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM