讀寫分離
我們可以設定兩台 mysql 可以同步資料,分為主及從伺服器。
一般主伺服器是寫入,而從伺服器是用來讀出資料。
如果設定得到,可以用多台伺服器來分擔負載。
master slave 配置
master: master1 (172.19.0.2)
slave: slave1 (172.19.0.3)
docker-compose.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| version: '2.2'
services: master1: image: yobasystems/alpine-mariadb restart: always hostname: master1 container_name: master1 volumes: - /master1.cnf:/etc/my.cnf.d/mariadb-server.cnf ports: - 3301:3306 environment: - MYSQL_ROOT_PASSWORD=my-secret-pw - MYSQL_DATABASE=wordpress - MYSQL_USER=slave - MYSQL_PASSWORD=my-secret-pw slave1: image: yobasystems/alpine-mariadb restart: always hostname: slave1 container_name: slave1 volumes: - /slave1.cnf:/etc/my.cnf.d/mariadb-server.cnf ports: - 3302:3306 environment: - MYSQL_ROOT_PASSWORD=my-secret-pw - MYSQL_DATABASE=wordpress - MYSQL_USER=slave - MYSQL_PASSWORD=my-secret-pw
|
master cnf
master1.cnf
1 2 3 4 5 6 7
| [mysqld] server-id=1 log-bin=mysql-bin binlog-ignore-db=mysql binlog-ignore-db=information_schema binlog-do-db=wordpress binlog_format=STATEMENT
|
slave cnf
slave1.cnf
1 2 3 4
| autocommit=1 log-bin=mysql-bin server-id=2 lower_case_table_names=1
|
啟動
1
| $ docker-compose up -d master1 slave1
|
slave 權限
在 master1 上執行
1
| $ docker exec -ti master1 mysql -uroot -pmy-secret-pw wordpress
|
1
| MariaDB [wordpress]> grant replication slave on *.* to 'slave'@'%' identified by'my-secret-pw'; flush privileges;
|
1 2 3 4 5 6 7 8 9
| MariaDB [wordpress]> show master status\G; *************************** 1. row *************************** File: mysql-bin.000003 Position: 1308 Binlog_Do_DB: wordpress Binlog_Ignore_DB: mysql,information_schema 1 row in set (0.001 sec)
ERROR: No query specified
|
slave 設定
在 slave1 上執行
1
| $ docker exec -ti slave1 mysql -uroot -pmy-secret-pw wordpress
|
設定連上 master1 資訊。
1
| MariaDB [wordpress]> CHANGE MASTER TO MASTER_HOST = 'master1', MASTER_USER = 'slave', MASTER_PASSWORD = 'my-secret-pw';
|
1
| MariaDB [wordpress]> start slave;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| MariaDB [wordpress]> show slave status \G; *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: master1 Master_User: slave Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000003 Read_Master_Log_Pos: 1308 Relay_Log_File: slave1-relay-bin.000004 Relay_Log_Pos: 1607 Relay_Master_Log_File: mysql-bin.000003 Slave_IO_Running: Yes Slave_SQL_Running: Yes 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: 1308 Relay_Log_Space: 2487 Until_Condition: None 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: 0 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: 1 Master_SSL_Crl: Master_SSL_Crlpath: Using_Gtid: No Gtid_IO_Pos: Replicate_Do_Domain_Ids: Replicate_Ignore_Domain_Ids: Parallel_Mode: conservative SQL_Delay: 0 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it Slave_DDL_Groups: 6 Slave_Non_Transactional_Groups: 0 Slave_Transactional_Groups: 0 1 row in set (0.002 sec)
|
最重要的是要看到下面兩行出現,就代表我們的設定正確了。
1 2
| Slave_IO_Running: Yes Slave_SQL_Running: Yes
|
驗證
先在 slave1 上確認狀態
1
| $ docker exec -ti slave1 mysql -uroot -pmy-secret-pw wordpress
|
1
| MariaDB [wordpress]> show tables;
|
現在應該看到沒有任何 table。
在 master1 上執行
1
| $ docker exec -ti master1 mysql -uroot -pmy-secret-pw wordpress
|
我們在 master1 上建立新的 table,然後在 slave1 上確認會同步過去。
1
| MariaDB [wordpress]> create table contacts ( contact_id INT(11) not null auto_increment,last_name varchar(30) not null, first_name varchar(25), birthday date, constraint contacts_pk primary key (contact_id));
|
目前在 master1 上我們新建立了一個 contacts 的 table;
我們如果設定都正確,應該可以在 slave1 上看到同樣的 table 被建立了。
在 slave1 上。
1
| $ docker exec -ti slave1 mysql -uroot -pmy-secret-pw wordpress
|
1 2 3 4 5 6 7 8
| MariaDB [wordpress]> show tables; + | Tables_in_wordpress | + | contacts | + 1 row in set (0.001 sec)
|
2 master 配置
雙主環境
host |
OS |
MariaDB |
masterm1 |
alpine 3.11 |
10.4.15-MariaDB |
masterm2 |
alpine 3.11 |
10.4.15-MariaDB |
1
| 注意:主從複製,主主複製時,主從服務器的時間也要同步,需要有同一個同步源,數據庫的版本也最好一致。
|
docker-compose
基本的安裝和設定 mariadb 不是我們的重點,我們以 docker 來建立 mariadb 的伺服器。
我們是用 docker-compose 來建立實驗環境。有 docker 來協助,我們有很多方便的地方。
網路的問題和兩個伺服器主機的獨立性也得到了保證。
1 2 3
| 為了方便,我們在系統中建立了兩個用戶 root 及 slave。 我們都是用 slave 帳號來同步彼此的資料。 預設我們建立了 wordpress 的 database 用以驗證結果。
|
- root 密碼 my-secret-pw
- 一般用戶 slave 密碼 my-secret-pw
- 設定檔是 masterm1.cnf/masterm2.cnf
- 伺服器對外 port 是 3331/3332
- 伺服器的 hostname/container_name 都是 masterm1/masterm2
- masterm1 及 masterm2 因為 docker 的幫助可以用彼此的名字互相網路相通
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| masterm1: image: yobasystems/alpine-mariadb restart: always hostname: masterm1 container_name: masterm1 volumes: - /db/master/masterm1.cnf:/etc/my.cnf.d/mariadb-server.cnf ports: - 3331:3306 environment: - MYSQL_ROOT_PASSWORD=my-secret-pw - MYSQL_DATABASE=wordpress - MYSQL_USER=slave - MYSQL_PASSWORD=my-secret-pw
|
- masterm2
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| masterm2: image: yobasystems/alpine-mariadb restart: always hostname: masterm2 container_name: masterm2 volumes: - /db/master/masterm2.cnf:/etc/my.cnf.d/mariadb-server.cnf ports: - 3332:3306 environment: - MYSQL_ROOT_PASSWORD=my-secret-pw - MYSQL_DATABASE=wordpress - MYSQL_USER=slave - MYSQL_PASSWORD=my-secret-pw
|
伺服器設定檔
伺服器設定裡有很多項可以調整,在我們的例子中我們一定要設定的是 server-id 及 log-bin。
- server-id : masterm1 是 3331 而 masterm2 是 3332
- log-bin : 都是設為 mysql-bin
1 2 3 4 5 6 7
| [mysqld] server-id=3331 log-bin=mysql-bin binlog-ignore-db=mysql binlog-ignore-db=information_schema #binlog-do-db=wordpress binlog_format=STATEMENT
|
1 2 3 4 5 6 7
| [mysqld] server-id=3332 log-bin=mysql-bin binlog-ignore-db=mysql binlog-ignore-db=information_schema #binlog-do-db=wordpress binlog_format=STATEMENT
|
下面是一些其他的設定,及相對的說明,供參考。
1 2 3 4 5 6 7 8 9 10 11 12
| datadir = /data/madata/mysql/ # 數據庫數據文件存放目錄-->也是同步目錄 log-bin = log-bin = /var/lib/mysql/binlogs/master1-bin #啓用二進制日誌 relay-log = /var/lib/mysql/relaylogs/relay-bin #啓用中繼日誌 auto_increment_increment = 1 #設定為主服務器的數量,防止auto_increment字段重復 auto_increment_offset = 2 #自增長字段的初始值,在多台master環境下,不會出現自增長ID衝突 server-id = 131 # 用於標識該語句最初是從哪個server寫入的,server-id一定要有的 log-error = /data/mdata/logs/mariadb.err #記錄錯誤日誌文件 log-slave-updates = on # 建議開啓,多主多從時要開啓,否則不能同步 expire-logs-days = 14 # 日誌過期時間 sync-binlog = 1 # # 確保主從事務安全 #binlog-ignore-db=mysql # 設置忽略binlog數據庫-->根據需要自行設置
|
啟動兩個 mariadb
1
| $ docker-compose up -d masterm1 masterm2
|
設定 masterm1 及 masterm2 的 slave 帳戶權限
讓 slave 可以在彼此之間有同步的權限。
1 2 3
| $ sql="grant replication slave on *.* to 'slave'@'%' identified by'my-secret-pw'; flush privileges;" $ docker exec masterm1 mysql -uroot -pmy-secret-pw -e "$sql" wordpress $ docker exec masterm2 mysql -uroot -pmy-secret-pw -e "$sql" wordpress
|
打通 masterm1 及 masterm2 的 slave 主從連接帳戶
1 2 3 4
| sql="CHANGE MASTER TO MASTER_HOST = 'masterm2', MASTER_USER = 'slave', MASTER_PASSWORD = 'my-secret-pw';start slave;" docker exec masterm1 mysql -uroot -pmy-secret-pw -e "$sql" wordpress sql="CHANGE MASTER TO MASTER_HOST = 'masterm1', MASTER_USER = 'slave', MASTER_PASSWORD = 'my-secret-pw';start slave;" docker exec masterm2 mysql -uroot -pmy-secret-pw -e "$sql" wordpress
|
參數解釋:
- MASTER_HOST : 設置要連接的主服務器的ip地址
- MASTER_PORT : 設置要連接的主服務器的端口
- MASTER_USER : 設置要連接的主服務器的用戶名
- MASTER_PASSWORD : 設置要連接的主服務器的密碼
- MASTER_LOG_FILE : 設置要連接的主服務器的bin日誌的日誌名稱,即第4步得到的信息
- MASTER_LOG_POS : 設置要連接的主服務器的bin日誌的記錄位置,即第4步得到的信息,(這裡注意,最後一項不需要加引號。否則配置失敗)
檢視 slave 狀態
最主要確認下面兩項都是 yes。
1 2
| Slave_IO_Running: Yes Slave_SQL_Running: Yes
|
- Slave_IO_Running: 這是I/O線程狀態,I/O線程負載從從庫去主庫讀取binlog日誌,並寫入從庫的中繼日誌中,狀態為Yes表示I/O線程工作正常。
- Slave_SQL_Running: 這個是SQL線程狀態,SQL線程負載讀取中繼日誌(relay-log)中的數據並轉換為SQL語句應用到從庫數據庫中,狀態為Yes表示I/O線程工作正常
如果有問題,可以從下面四個項目中找到一些線索
1 2 3 4
| Last_IO_Errno: 0 Last_IO_Error: Last_SQL_Errno: 0 Last_SQL_Error:
|
masterm1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| $ docker exec masterm1 mysql -uroot -pmy-secret-pw wordpress -e 'show slave status \G;' *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: masterm2 Master_User: slave Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000003 Read_Master_Log_Pos: 675 Relay_Log_File: masterm1-relay-bin.000004 Relay_Log_Pos: 974 Relay_Master_Log_File: mysql-bin.000003 Slave_IO_Running: Yes Slave_SQL_Running: Yes 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: 675 Relay_Log_Space: 2009 Until_Condition: None 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: 0 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: 3332 Master_SSL_Crl: Master_SSL_Crlpath: Using_Gtid: No Gtid_IO_Pos: Replicate_Do_Domain_Ids: Replicate_Ignore_Domain_Ids: Parallel_Mode: conservative SQL_Delay: 0 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it Slave_DDL_Groups: 6 Slave_Non_Transactional_Groups: 0 Slave_Transactional_Groups: 0
|
masterm2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| $ docker exec masterm2 mysql -uroot -pmy-secret-pw wordpress -e 'show slave status \G;' *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: masterm1 Master_User: slave Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000003 Read_Master_Log_Pos: 675 Relay_Log_File: masterm2-relay-bin.000004 Relay_Log_Pos: 974 Relay_Master_Log_File: mysql-bin.000003 Slave_IO_Running: Yes Slave_SQL_Running: Yes 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: 675 Relay_Log_Space: 2009 Until_Condition: None 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: 0 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: 3331 Master_SSL_Crl: Master_SSL_Crlpath: Using_Gtid: No Gtid_IO_Pos: Replicate_Do_Domain_Ids: Replicate_Ignore_Domain_Ids: Parallel_Mode: conservative SQL_Delay: 0 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it Slave_DDL_Groups: 6 Slave_Non_Transactional_Groups: 0 Slave_Transactional_Groups: 0
|
從庫提升為主庫
1 2
| sql="stop slave;reset slave all;" docker exec masterm2 mysql -uroot -pmy-secret-pw -e "$sql" wordpress
|
RESET SLAVE ALL
是清除從庫的同步複製信息,包括連接信息和二進制文件名、位置
從庫上執行這個命令後,使用 show slave status
將不會有輸出
雙主雙從
基本架構相同,兩個 master 及兩個 slave。
host |
OS |
MariaDB |
port |
mastera1 |
alpine 3.11 |
10.4.15-MariaDB |
3341 |
slavea1 |
alpine 3.11 |
10.4.15-MariaDB |
3342 |
mastera2 |
alpine 3.11 |
10.4.15-MariaDB |
3343 |
slavea2 |
alpine 3.11 |
10.4.15-MariaDB |
3344 |
- 啟動四個全新的 MariaDB container,建立好同樣的 slave 帳號。
- 設定好 slave 來同步資料的帳號權限。
- 設定好 mastera1 及 slavea1 和 mastera2 及 slavea2 同步帳號及連通,並啟動 slave。
- 設定 mastera1 及 mastera2 之間的同步連通帳號,並啟動 slave 。
- 驗證建立 database 可以在任一 db 伺服器中可以同步到更新。
原理和上面差不多,只是設定上多了兩個slave,我們直接看錄影。
github repo
上面提用到的所有資料都可以從 github 找到
參考 README.md 了解如何使用這些資料
結論
其實整個設定程序上都很簡易,讓我們好好使用 mysql 吧。