一、概述
1、原理
複製(Replication)是從一臺MySQL數據庫服務器(主服務器master)複製數據到另一個服務器(從服務器slave)的一個進程。
主服務器將更新寫入二進制日誌文件,並維護文件的一個索引以跟蹤日誌循環。這些日誌可以記錄發送到從服務器的更新。當一個從服務器連接主服務器時,它通知主服務器從服務器在日誌中讀取的最後一次成功更新的位置。從服務器接收從那時起發生的任何更新,然後封鎖並等待主服務器通知新的更新。
當進行復制時,所有對複製中的表的更新必須在主服務器上進行。否則,你必須要小心,以避免用戶對主服務器上的表進行的更新與對從服務器上的表所進行的更新之間的衝突。
2、mysql支持的複製類型
(1)基於語句的複製: 在主服務器上執行的SQL語句,在從服務器上執行同樣的語句。MySQL默認採用基於語句的複製,效率比較高。
一旦發現沒法精確複製時, 會自動選着基於行的複製。
(2)基於行的複製:把改變的內容複製過去,而不是把命令在從服務器上執行一遍. 從mysql5.0開始支持
(3)混合類型的複製: 默認採用基於語句的複製,一旦發現基於語句的無法精確的複製時,就會採用基於行的複製。
3、MySQL複製技術的一些特點:
(1)數據分佈 (Data distribution )
(2)負載平衡(load balancing)
(3)備份(Backups)
(4)高可用性和容錯行 High availability and failover
4、工作方式
主要分三個步驟
(1)master將改變記錄到二進制日誌(binary log)中(這些記錄叫做二進制日誌事件,binary log events);
(2)slave將master的binary log events拷貝到它的中繼日誌(relay log);
(3)slave重做中繼日誌中的事件,將改變反映它自己的數據。
該過程的第一部分就是master記錄二進制日誌。在每個事務更新數據完成之前,master在二日誌記錄這些改變。MySQL將事務串行的寫入二進制日誌,即使事務中的語句都是交叉執行的。在事件寫入二進制日誌完成後,master通知存儲引擎提交事務。
下一步就是slave將master的binary log拷貝到它自己的中繼日誌。首先,slave開始一個工作線程——I/O線程。I/O線程在master上打開一個普通的連接,然後開始binlog dump process(可以使用 show processlist 查看 Binlog Dump線程)。從服務器I/O線程讀取主服務器Binlog Dump線程發送的內容,Binlog dump process從master的二進制日誌中讀取事件,如果已經跟上master,它會睡眠並等待master產生新的事件,I/O線程將這些事件寫入中繼日誌。
SQL slave thread(SQL從線程)處理該過程的最後一步。SQL線程從中繼日誌讀取事件,並重放其中的事件而更新slave的數據,使其與master中的數據一致。只要該線程與I/O線程保持一致,中繼日誌通常會位於OS的緩存中,所以中繼日誌的開銷很小。
此外,在master中也有一個工作線程:和其它MySQL的連接一樣,slave在master中打開一個連接也會使得master開始一個線程。複製過程有一個很重要的限制——複製在slave上是串行化的,也就是說master上的並行更新操作不能在slave上並行操作。
二、實踐
以下基於 CentOS x86_64 使用 mariadb-10.1.11.tar.gz 進行配置,首先在主從節點安裝好mariadb
主節點:192.168.1.106 CentOS6.6 x86_64
從節點:192.168.1.113 CentOS6.6 x86_64
[root@localhost ~]# wget https://downloads.mariadb.org/interstitial/mariadb-10.1.11/source/mariadb-10.1.11.tar.gz
[root@localhost ~]# yum -y install gcc gcc-c++ make cmake ncurses-devel ncurses libxml2 libxml2-devel openssl-devel bison bison-devel #安裝編譯環境
[root@localhost ~]# tar xf mariadb-10.1.11.tar.gz -C /usr/local/
[root@localhost ~]# cd /usr/local/
[root@localhost local]# ln -s mariadb-10.1.11/ mysql
[root@localhost local]# cd mysql
[root@localhost mysql]# cmake . -DCMAKE_INSTALL_PREFIX=/usr/local/mysql -DMYSQL_DATADIR=/data/mydata -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_ARCHIVE_STPRAGE_ENGINE=1 -DWITH_BLACKHOLE_STORAGE_ENGINE=1 -DWIYH_READLINE=1 -DWIYH_SSL=system -DVITH_ZLIB=system -DWITH_LOBWRAP=0 -DMYSQL_UNIX_ADDR=/tmp/mysql.sock -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci
[root@localhost mysql]# make && make install
剩餘步驟與mysql編譯安裝方法相同,此處不再詳寫
配置過程:
版本:
1、雙方的MySQL版本要一致;
2、如果不一致:主的要低於從的;
主服務器:
1、改server-id
2、啓用二進制日誌
3、創建有複製權限的帳號
從服務器:
1、改server-id
2、啓用中繼日誌
3、連接主服務器
4、啓動複製線程
複製開始的位置:
1、都從0開始:
2、若主服務器已經運行一段時間,並且存在不小的數據集,此時需要先將主服務器數據庫進行備份,然後在從服務恢復,從主服務器上覆制時從主服務器所處的位置開始複製;
(1)從0開始複製
配置主服務器:
安裝好後配置文件中默認已經啓用二進制日誌,主節點server-id可以先不用修改,但是兩個節點的server-id不能相同,二進制日誌文件不要和數據文件放在一塊,然後開始創建有複製權限的帳號:
MariaDB [(none)]> grant replication slave,replication client on *.* to 'repluser'@'192.168.%.%' identified by 'replpass';
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]> flush privileges;
配置從服務器:
[root@localhost ~]# vim /etc/my.cnf #只需修改以下幾項
#log-bin=mysql-bin
#關閉二進制日誌
server-id = 11
#修改server-id
relay-log = /mydata/relaylogs/relay-bin
#啓用中繼日誌
[root@localhost ~]# mkdir /mydata/relaylogs #創建目錄
[root@localhost ~]# chown -R mysql.mysql /mydata/
MariaDB [(none)]> show global variables like '%relay%';
+-----------------------+-----------------------------------+
| Variable_name | Value |
+-----------------------+-----------------------------------+
| max_relay_log_size | 1073741824 |
| relay_log | /mydata/relaylogs/relay-bin |
| relay_log_basename | /mydata/relaylogs/relay-bin |
| relay_log_index | /mydata/relaylogs/relay-bin.index |
| relay_log_info_file | relay-log.info |
| relay_log_purge | ON |
| relay_log_recovery | OFF |
| relay_log_space_limit | 0 |
| sync_relay_log | 10000 |
| sync_relay_log_info | 10000 |
+-----------------------+-----------------------------------+
10 rows in set (0.05 sec)
然後啓動複製功能:
MariaDB [(none)]> change master to master_host='192.168.1.106',master_user='repluser',master_password='replpass';
Query OK, 0 rows affected (0.05 sec)
MariaDB [(none)]> start slave; #啓動slave
Query OK, 0 rows affected (0.03 sec)
[root@localhost data]# tail -f localhost.localdomain.err #查看日誌文件
2016-01-31 2:01:13 140230526023648 [Note] InnoDB: Waiting for purge to start
2016-01-31 2:01:13 140230526023648 [Note] InnoDB: Percona XtraDB (http://www.percona.com) 5.6.26-76.0 started; log sequence number 1616829
2016-01-31 2:01:13 140230526023648 [Note] Plugin 'FEEDBACK' is disabled.
2016-01-31 2:01:13 140229866223360 [Note] InnoDB: Dumping buffer pool(s) not yet started
2016-01-31 2:01:13 140230526023648 [Note] Server socket created on IP: '::'.
2016-01-31 2:01:13 140230526023648 [Note] /usr/local/mysql/bin/mysqld: ready for connections.
Version: '10.1.11-MariaDB' socket: '/tmp/mysql.sock' port: 3306 Source distribution
2016-01-31 2:10:05 140230215559936 [Note] 'CHANGE MASTER TO executed'. Previous state master_host='', master_port='3306', master_log_file='', master_log_pos='4'. New state master_host='192.168.1.106', master_port='3306', master_log_file='', master_log_pos='4'.
2016-01-31 2:14:53 140230214953728 [Note] Slave SQL thread initialized, starting replication in log 'FIRST' at position 0, relay log '/mydata/relaylogs/relay-bin.000001' position: 4
2016-01-31 2:14:54 140230215256832 [Note] Slave I/O thread: connected to master '[email protected]:3306',replication started in log 'FIRST' at position 4
在這裏主要是看:
Slave_IO_Running=Yes
Slave_SQL_Running=Yes
在主服務器插入數據:
MariaDB [(none)]> show master status;
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000001 | 641 | | |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)
MariaDB [(none)]> create database mydb;
Query OK, 1 row affected (0.00 sec)
MariaDB [(none)]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mydb |
| mysql |
| performance_schema |
| test |
+--------------------+
5 rows in set (0.00 sec)
MariaDB [(none)]> show master status;
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000001 | 762 | | |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)
查看從服務器,已經收到數據了:
MariaDB [(none)]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mydb |
| mysql |
| performance_schema |
| test |
+--------------------+
5 rows in set (0.01 sec)
MariaDB [(none)]> stop slave; #停止從服務器服務
Query OK, 0 rows affected (0.01 sec)
[root@localhost ~]# service mysqld stop #停止服務器重新啓動後,slave會自動啓動
Shutting down MySQL.. SUCCESS!
[root@localhost ~]# service mysqld start
Starting MySQL. SUCCESS!
[root@localhost ~]# mysql
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 5
Server version: 10.1.11-MariaDB Source distribution
Copyright (c) 2000, 2015, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> start slave;
Query OK, 0 rows affected, 1 warning (0.00 sec)
MariaDB [(none)]> show warnings;
+-------+------+--------------------------+
| Level | Code | Message |
+-------+------+--------------------------+
| Note | 1254 | Slave is already running |
+-------+------+--------------------------+
1 row in set (0.00 sec)
(2)從半道複製
主服務器:
MariaDB [(none)]> drop database mydb; #首先在主從節點先刪除數據庫
Query OK, 0 rows affected (0.01 sec)
[root@cacti mydata]# mysql < hellodb.sql #導入自定義的數據庫
[root@cacti mydata]# mysqldump --all-databases --flush-logs --master-data=2 --lock-all-tables > all.sql #先備份
-- Warning: Skipping the data of table mysql.event. Specify the --events option explicitly.
[root@cacti mydata]# scp all.sql [email protected]:~ #將數據複製到從服務器
MariaDB [(none)]> show master status;
+-------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+-------------------+----------+--------------+------------------+
| master-bin.000002 | 366 | | |
+-------------------+----------+--------------+------------------+
1 row in set (0.00 sec)
從服務器:
[root@localhost ~]# mysql < all.sql
MariaDB [(none)]> stop slave; #確保slave功能關閉
MariaDB [(none)]> change master to master_host='192.168.1.106',master_user='repluser',master_password='replpass',master_log_file='mysql-bin.000002',master_log_pos=366;
Query OK, 0 rows affected (0.04 sec)
MariaDB [(none)]> start slave;
Query OK, 0 rows affected (0.03 sec)
在主服務器端插入數據進行MariaDB [(none)]> create database mydb;
Query OK, 1 row affected (0.00 sec)
MariaDB [(none)]> show databases;
+——————–+
| Database |
+——————–+
| hellodb |
| information_schema |
| mydb |
| mysql |
| performance_schema |
| test |
+——————–+
6 rows in set (0.00 sec)器:
MariaDB [(none)]> show databases;
+--------------------+
| Database |
+--------------------+
| hellodb |
| information_schema |
| mydb |
| mysql |
| performance_schema |
| test |
+--------------------+
6 rows in set (0.00 sec)
可以看到兩邊數據一致,說明覆制已經沒有問題了。
從半道複製和從0複製其實差別不大,只是在從服務器中連接主服務器時需要指定二進制日誌文件及其位置即可。
三、更多知識
1、主從服務器時間要同步(ntp):
*/5 * * * * /usr/sbin/ntpdate 192.168.1.1
2、如何限制從服務器只讀?
read-only=ON (在/etc/my.cnf 中定義)
注意:僅能限制那不具有SUPER權限用戶無法執行寫操作;
想限制所有用戶:
mysql> FLUSH TABLES WITH READ LOCK;
3、如何主從複製時的事務安全?
在主服務器上配置:
sync_binlog=1 #每次提交立即將緩衝去內容同步到日誌,需要關閉自動提交功能
4、複製過濾器:爲了節約資源並提高服務器的性能,可以設置過濾器只複製希望備份的數據庫,在配置的文件 my.cnf 中的 [mysqld] 塊中使用以下配置進行過濾:
master上把事件從二進制日誌中過濾:
binlog_do_db= #複製哪些數據庫,白名單
binlog_ignore_db= #相反的,黑名單
slave上事件從中繼日誌中過濾:
replicate_do_db= #數據庫的白名單
replicate_ignore_db= #數據庫的黑名單
replicate_do_table= db_name.table_name #表的白名單
replicate_ignore_table= #表的黑名單
replicate_wild_do_table= #支持通配符的白名單
replicate_wild_ignore_table= #支持通配符的黑名單
MySQL 5.5以上,下面的這些表都建議過濾掉,只複製生產環境數據。
replicate-wild-ignore-table =mysql.%
replicate-wild-ignore-table =test.%
replicate-wild-ignore-table =log.%
replicate-wild-ignore-table =information_schema.%
replicate-wild-ignore-table =performance_schema.%
四、配置過程的問題
1)設定 Replication 完成後, start slave後出現 “Could not initialize master info structure”
1.MariaDB [(none)]> reset slave; # 重點就是這行
2.MariaDB [(none)]> CHANGE MASTER TO MASTER_LOG_FILE=’mysql-bin.000001′, MASTER_LOG_POS=107; # 請依照自行環境設定
3.MariaDB [(none)]> start slave; # 就正常了.
重新設置 slave, MASTER_LOG_FILE 和 MASTER_LOG_POS 會被清空, 所以需要重新設置.
2)mysql 主從同步失敗 Last_IO_Error: Got fatal error 1236 from master
先進入slave中執行:”slave stop;”來停止從庫同步;
再去master中執行:”flush logs;”來清空日誌;
然後在master中執行:”show master status;”查看下主庫的狀態,主要是日誌的文件和position;
然後回到slave中,執行:”CHANGE MASTER TO MASTER_LOG_FILE=’log-bin.000004′,MASTER_LOG_POS=106;”,文件和位置對應master中的;
最後在slave中執行:”slave start;”來啓動同步。