1. 程式人生 > >MySQL5.7安裝+基於GTID主從複製+並行複製+增強半同步複製+讀寫分離+M-S-S架構(聯級複製)

MySQL5.7安裝+基於GTID主從複製+並行複製+增強半同步複製+讀寫分離+M-S-S架構(聯級複製)

實驗環境:

Centos7.2

角色

主機IP

server_id

資料狀態

Proxysql

192.168.148.62

null

Master

192.168.148.62

1

剛安裝的全新MySQL例項

Slave1

192.168.148.61

2

剛安裝的全新MySQL例項

Slave2

192.168.148.64

3

剛安裝的全新MySQL例項

一、安裝最新版本Mysql5.7

  • 下載並安裝MySQL官方的 Yum Repository,並啟動mysql

[[email protected] ~]# yum -y install mysql57-community-release-el7-10.noarch.rpm

[[email protected] ~]# yum -y install mysql-community-server

[[email protected] ~]# systemctl start mysqld.service

[[email protected] ~]# systemctl status mysqld.service

  • 檢視yum安裝後的初始root密碼

[[email protected] ~]# grep "password" /var/log/mysqld.log

2018-07-09T06:57:12.533269Z 1 [Note] A temporary password is generated for [email protected]: H9VtyDlad!T;

  • 如下命令首次進入資料庫並修改密碼:

[[email protected] ~]# mysql -uroot -pH9VtyDlad!T

mysql> ALTER USER 'root'@'%' IDENTIFIED BY '123456';

說明:MySQL預設必須修改密碼之後才能操作資料庫,並滿足密碼複雜度要求

%'                                所有情況都能訪問

‘localhost’                   本機才能訪問

’111.222.33.44‘         指定 ip 才能訪問

 至此mysql已安裝完成。

二、基於GTID主從同步

  • gtid基本概念

傳統的基於binlog position複製的方式有個嚴重的缺點:如果slave連線master時指定的binlog檔案錯誤或者position錯誤,會造成遺漏或者重複,很多時候前後資料是有依賴性的,這樣就會出錯而導致資料不一致。

從MYSQL5.6開始,mysql開始支援GTID複製。GTID的全稱是global transaction id,表示的是全域性事務ID。GTID的分配方式為uuid:trans_id,其中:

  • uuid是每個mysql伺服器都唯一的,記錄在$datadir/auto.cnf中。如果複製結構中,任意兩臺伺服器uuid重複的話(比如直接冷備份時,auto.conf中的內容是一致的),在啟動複製功能的時候會報錯。這時可以刪除auto.conf檔案再重啟mysqld。

基於gtid複製的好處

從上面可以看出,gtid複製的優點大致有:

保證同一個事務在某slave上絕對只執行一次,沒有執行過的gtid事務總是會被執行。

不用像傳統複製那樣保證binlog的座標準確,因為根本不需要binlog以及座標。

故障轉移到新的master的時候很方便,簡化了很多工。

很容易判斷master和slave的資料是否一致。只要master上提交的事務在slave上也提交了,那麼一定是一致的。

當然,MySQL提供了選項可以控制跳過某些gtid事務,防止slave第一次啟動複製時執行master上的所有事務而導致耗時過久。

雖然對於row-based和statement-based的格式都能進行gtid複製,但建議採用row-based格式。 

  • master配置

[[email protected] ~]# vim /etc/my.cnf

[mysqld]

datadir=/var/lib/mysql

socket=/var/lib/mysql/mysql.sock

symbolic-links=0

log-error=/var/log/mysqld.log

pid-file=/var/run/mysqld/mysqld.pid

server-id=1                                                    ##伺服器標識

binlog_format=row                                    

log-bin=mysql-bin                                       ##開啟二進位制日誌          

gtid_mode=ON                                           ## 開啟gtid模式

enforce-gtid-consistency=true                   ## 強制gtid複製

  • 儲存退出後重啟mysql。

[[email protected] ~]# systemctl restart mysqld

  • 進入資料庫並檢視Master狀態

[[email protected] ~]# mysql -uroot -p

Enter password:

mysql>

mysql> show master status ;                                  ##檢視master狀態

+------------------+----------+--------------+------------------+-------------------+

|             File                      |  Position    |    Binlog_Do_DB    |      Binlog_Ignore_DB    |      Executed_Gtid_Set      |

+------------------+----------+--------------+------------------+-------------------+

|   mysql-bin.000003     |      154       |                                   |                                          |                                              |

+------------------+----------+--------------+------------------+-------------------+

  • 授權slave使用者,並重新整理許可權

mysql> grant REPLICATION SLAVE ON *.* to [email protected]'192.168.148.%' identified by  ' yourpasswd '  ;

Query OK, 0 rows affected, 1 warning (0.01 sec)

mysql>flush privileges;

  • 再次檢視master狀態

mysql> show master status; 

+------------------+----------+--------------+------------------+----------------------------------------+

|    File                         |     Position     |     Binlog_Do_DB     |     Binlog_Ignore_DB     |     Executed_Gtid_Set     |

+------------------+----------+--------------+------------------+----------------------------------------+

|   mysql-bin.000003   |       442       |                             |                      | 4e552c02-8345-11e8-b571-000c294897b5:1 |

+------------------+----------+--------------+------------------+----------------------------------------+

1 row in set (0.00 sec)

  • slave1配置

[[email protected] ~]# vim /etc/my.cnf

[mysqld]

datadir=/var/lib/mysql

socket=/var/lib/mysql/mysql.sock

symbolic-links=0

log-error=/var/log/mysqld.log

pid-file=/var/run/mysqld/mysqld.pid

server-id=2                                                           ##伺服器標識

binlog_format=row

gtid_mode=ON                                                   ## 開啟gtid模式

enforce-gtid-consistency=true                     ## 強制gtid複製

  • 儲存退出並重啟mysql

[[email protected] mysql]# systemctl restart mysqld

  • 進入資料庫

[[email protected] ~]# mysql -uroot -p

mysql>change master to master_host='192.168.148.62',
master_user='otter',
master_password='yourpasswd',
master_auto_position=1;
                                             
mysql> start slave;           ##開啟slave mysql> show slave status\G; ##檢視狀態

*************************** 1. row ***************************

Slave_IO_State: Waiting for master to send event

Master_Host: 192.168.148.62

Master_User: otter

Master_Port: 3306

Connect_Retry: 60

Master_Log_File: mysql-bin.000003

Read_Master_Log_Pos: 1373

Relay_Log_File: localhost-relay-bin.000004

Relay_Log_Pos: 360

Relay_Master_Log_File: mysql-bin.000003

Slave_IO_Running: Yes

Slave_SQL_Running: Yes

三、測試:

  • master新建個數據庫test, slave會自動同步

mysql> create database test;

Query OK, 1 row affected (0.00 sec)

mysql> use test;

Database changed

mysql> create table usertable (

-> username varchar(10) not null,

-> password varchar(16) not null);

Query OK, 0 rows affected (0.01 sec)

mysql> insert into usertable values('mark','westos');

Query OK, 1 row affected (0.06 sec)

mysql> insert into usertable values('harry','redhat');

Query OK, 1 row affected (0.00 sec)

mysql> select * from usertable;

+----------+----------+

| username | password |

+----------+----------+

| mark | westos |

| harry | redhat |

+----------+----------+

2 rows in set (0.00 sec)

  • slave1自動同步

mysql> show databases;

+--------------------+

| Database |

+--------------------+

| information_schema |

| mysql |

| performance_schema |

| sys |

| test |

| xuehui |

+--------------------+

6 rows in set (0.00 sec)

mysql> use test;

Database changed

mysql> show tables;

+----------------+

| Tables_in_test |

+----------------+

| usertable |

+----------------+

1 row in set (0.00 sec)

mysql> select * from usertable;

+----------+----------+

| username | password |

+----------+----------+

| mark | westos |

| harry | redhat |

+----------+----------+

2 rows in set (0.00 sec)

四、slave1並行複製

編輯slave1配置檔案

[[email protected] mysql]# vim /etc/my.cnf

[mysqld]

datadir=/var/lib/mysql

socket=/var/lib/mysql/mysql.sock

symbolic-links=0

log-error=/var/log/mysqld.log

pid-file=/var/run/mysqld/mysqld.pid

server-id=2                                           ##伺服器標識

binlog_format=row

gtid_mode=ON                                   ## 開啟gtid模式

enforce-gtid-consistency=true           ## 強制gtid複製

slave-parallel-type=LOGICAL_CLOCK                        ##開啟邏輯時鐘的複製

slave-parallel-workers=4                                                ##最大執行緒16

master_info_repository=TABLE

relay_log_info_repository=TABLE

relay_log_recovery=ON

  • 儲存退出並重啟mysql

[[email protected] mysql]# systemctl restart mysqld

  • 主庫master配置binlog_group_commit

[[email protected] ~]# vim /etc/my.cnf

[mysqld]

datadir=/var/lib/mysql

socket=/var/lib/mysql/mysql.sock

symbolic-links=0

log-error=/var/log/mysqld.log

pid-file=/var/run/mysqld/mysqld.pid

server-id=1                                      ##伺服器標識

binlog_format=row

log-bin=mysql-bin                         ##開啟二進位制日誌

gtid_mode=ON                              ## 開啟gtid模式

enforce-gtid-consistency=true      ## 強制gtid複製

rpl_semi_sync_master_enabled = 1

plugin-load=rpl_semi_sync_master=semisync_master.so

rpl_semi_sync_master_timeout = 1000                                      # 1s

##不配置binlog_group_commit從庫無法做到基於事物的並行複製。

binlog_group_commit_sync_delay = 100                  

binlog_group_commit_sync_no_delay_count = 10

##為了資料安全再配置

sync_binlog=1

innodb_flush_log_at_trx_commit=1

  • 儲存退出並重啟mysql

[[email protected] mysql]# systemctl restart mysqld

  • 進入slave1資料庫,檢視優化項

mysql> use mysql;

Database changed

mysql> show tables;

|....................................|

| slave_worker_info |

  • 檢視4個執行緒

mysql> select * from slave_worker_info;

+----+----------------+---------------+-----------------+----------------+---------------------------+--------------------------+----------------------------+---------------------------+------------------+-----------------------+------------------------------------------------------------------+--------------+

| Id | Relay_log_name | Relay_log_pos | Master_log_name | Master_log_pos | Checkpoint_relay_log_name | Checkpoint_relay_log_pos | Checkpoint_master_log_name | Checkpoint_master_log_pos | Checkpoint_seqno | Checkpoint_group_size | Checkpoint_group_bitmap | Channel_name |

+----+----------------+---------------+-----------------+----------------+---------------------------+--------------------------+----------------------------+---------------------------+------------------+-----------------------+------------------------------------------------------------------+--------------+

| 1 | | 0 | | 0 | | 0 | | 0 | 0 | 64 | | |

| 2 | | 0 | | 0 | | 0 | | 0 | 0 | 64 | | |

| 3 | | 0 | | 0 | | 0 | | 0 | 0 | 64 | | |

| 4 | | 0 | | 0 | | 0 | | 0 | 0 | 64 | | |

+----+----------------+---------------+-----------------+----------------+---------------------------+--------------------------+----------------------------+---------------------------+------------------+-----------------------+------------------------------------------------------------------+--------------+

4 rows in set (0.00 sec)

五、增強半同步

半同步複製

預設情況下,MySQL的複製是非同步的,master將新生成的binlog傳送給各slave後,無需等待slave的ack回覆(slave將接收到的binlog寫進relay log後才會回覆ack),直接就認為這次DDL/DML成功了。半同步複製(semi-synchronous replication)是指master在將新生成的binlog傳送給各slave時,只需等待一個(預設)slave返回的ack資訊就返回成功。

MySQL 5.7對半同步複製作了大改進,新增了一個master執行緒。在MySQL 5.7以前,master上的binlog dump執行緒負責兩件事:dump日誌給slave的io_thread;接收來自slave的ack訊息。它們是序列方式工作的。在MySQL 5.7中,新增了一個專門負責接受ack訊息的執行緒ack collector thread。這樣master上有兩個執行緒獨立工作,可以同時傳送binlog到slave和接收slave的ack。還新增了幾個變數,其中最重要的是 rpl_semi_sync_master_wait_point ,它使得MySQL半同步複製有兩種工作模型。解釋如下。

半同步複製的兩種型別

從MySQL 5.7.2開始,MySQL支援兩種型別的半同步複製。這兩種型別由變數 rpl_semi_sync_master_wait_point (MySQL 5.7.2之前沒有該變數)控制,它有兩種值:AFTER_SYNC和AFTER_COMMIT。在MySQL 5.7.2之後,預設值為AFTER_SYNC,在此版本之前,等價的型別為AFTER_COMMIT。這個變數控制的是master何時提交、何時接收ack以及何時回覆成功資訊給客戶端的時間點。

  1. AFTER_SYNC模式:master將新的事務寫進binlog(buffer),然後傳送給slave,再sync到自己的binlog file(disk)。之後才允許接收slave的ack回覆,接收到ack之後才會提交事務,並返回成功資訊給客戶端。
  2. AFTER_COMMIT模式:master將新的事務寫進binlog(buffer),然後傳送給slave,再sync到自己的binlog file(disk),然後直接提交事務。之後才允許接收slave的ack回覆,然後再返回成功資訊給客戶端。

畫圖理解就很清晰。(前提:已經設定了sync_binlog=1,否則binlog刷盤時間由作業系統決定)

再來分析下這兩種模式的優缺點。

  • AFTER_SYNC:
    • 對於所有客戶端來說,它們看到的資料是一樣的,因為它們看到的資料都是在接收到slave的ack後提交後的資料。
    • 這種模式下,如果master突然故障,不會丟失資料,因為所有成功的事務都已經寫進slave的relay log中了,slave的資料是最新的。
  • AFTER_COMMIT:
    • 不同客戶端看到的資料可能是不一樣的。對於發起事務請求的那個客戶端,它只有在master提交事務且收到slave的ack後才能看到提交的資料。但對於那些非本次事務的請求客戶端,它們在master提交後就能看到提交後的資料,這時候master可能還沒收到slave的ack。
    • 如果master收到ack回覆前,slave和master都故障了,那麼將丟失這個事務中的資料。

在MySQL 5.7.2之前,等價的模式是 AFTER_COMMIT ,在此版本之後,預設的模式為 AFTER_SYNC ,該模式能最大程度地保證資料安全性,且效能上並不比 AFTER_COMMIT 差。

  • master與Slave命令列載入模組

master載入

mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';

Query OK, 0 rows affected (0.05 sec)

mysql> show plugins;

+----------------------------+----------+--------------------+----------------------+---------+

| Name | Status | Type | Library | License |

+----------------------------+----------+--------------------+----------------------+---------+

| binlog | ACTIVE | STORAGE ENGINE | NULL | GPL |

| mysql_native_password | ACTIVE | AUTHENTICATION | NULL | GPL |

|。。。。。。。。。。。。。。。。。。。。。。。。。。。。。|

| validate_password | ACTIVE | VALIDATE PASSWORD | validate_password.so | GPL |

| rpl_semi_sync_master | ACTIVE | REPLICATION | semisync_master.so | GPL |

+----------------------------+----------+--------------------+----------------------+---------+

46 rows in set (0.00 sec)

檢視載入模組資訊

mysql> show variables like '%rpl_semi%';

+-------------------------------------------+------------+

| Variable_name | Value |

+-------------------------------------------+------------+

| rpl_semi_sync_master_enabled | OFF |

| rpl_semi_sync_master_timeout | 10000 |

| rpl_semi_sync_master_trace_level | 32 |

| rpl_semi_sync_master_wait_for_slave_count | 1 |

| rpl_semi_sync_master_wait_no_slave | ON |

| rpl_semi_sync_master_wait_point | AFTER_SYNC |

+-------------------------------------------+------------+

6 rows in set (0.01 sec)

mysql> set global rpl_semi_sync_master_enabled=1; ##global全域性 啟動半同步

Query OK, 0 rows affected (0.00 sec)

mysql> show variables like '%rpl_semi%';

+-------------------------------------------+------------+

| Variable_name | Value |

+-------------------------------------------+------------+

| rpl_semi_sync_master_enabled | ON |

| rpl_semi_sync_master_timeout | 10000 |

| rpl_semi_sync_master_trace_level | 32 |

| rpl_semi_sync_master_wait_for_slave_count | 1 |

| rpl_semi_sync_master_wait_no_slave | ON |

| rpl_semi_sync_master_wait_point | AFTER_SYNC |

+-------------------------------------------+------------+

6 rows in set (0.01 sec)

修改半同步預設超時時間

mysql> set global rpl_semi_sync_master_timeout = 1000;

Query OK, 0 rows affected (0.00 sec)

mysql> show variables like '%rpl_semi%';

+-------------------------------------------+------------+

| Variable_name | Value |

+-------------------------------------------+------------+

| rpl_semi_sync_master_enabled | ON |

| rpl_semi_sync_master_timeout | 1000 |

| rpl_semi_sync_master_trace_level | 32 |

| rpl_semi_sync_master_wait_for_slave_count | 1 |

| rpl_semi_sync_master_wait_no_slave | ON |

| rpl_semi_sync_master_wait_point | AFTER_SYNC |

+-------------------------------------------+------------+

6 rows in set (0.01 sec)

##########################    以上的啟動方式是在命令列操作     ########################################

slave1載入模組

mysql> install plugin rpl_semi_sync_slave SONAME 'semisync_slave.so';                     ##載入slave模組

mysql> set global rpl_semi_sync_slave_enabled=1;                                                        ##開啟slave模組

mysql> show variables like '%rpl_semi%';                                                                          ##檢視資訊

+---------------------------------+-------+

| Variable_name | Value |

+---------------------------------+-------+

| rpl_semi_sync_slave_enabled | ON |

| rpl_semi_sync_slave_trace_level | 32 |

+---------------------------------+-------+

2 rows in set (0.01 sec)

mysql> STOP SLAVE IO_THREAD;                                                                                      ##關閉slaveIO執行緒

ERROR 2006 (HY000): MySQL server has gone away

No connection. Trying to reconnect...

Connection id: 8

Current database: *** NONE ***

Query OK, 0 rows affected (0.01 sec)

mysql> START SLAVE IO_THREAD;                                                                                 ##開啟slaveIO執行緒

Query OK, 0 rows affected (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)

##########################    以上的啟動方式是在命令列操作     ########################################

  • 配置檔案中載入。

主Master:配置檔案里加載semisync_master.so

[[email protected] ~]# vim /etc/my.cnf

[mysqld]

datadir=/var/lib/mysql

socket=/var/lib/mysql/mysql.sock

symbolic-links=0

log-error=/var/log/mysqld.log

pid-file=/var/run/mysqld/mysqld.pid

server-id=1 ##伺服器標識

binlog_format=row

log-bin=mysql-bin ##開啟二進位制日誌

gtid_mode=ON

enforce-gtid-consistency=true

plugin-load=rpl_semi_sync_master=semisync_master.so

rpl_semi_sync_master_enabled = 1

rpl_semi_sync_master_timeout = 1000 # 1s

儲存退出並重啟mysql

[[email protected] mysql]# systemctl restart mysqld

Slave1:  配置檔案里加載semisync_slave.so,並開啟bin-log日誌

[[email protected] mysql]# vim /etc/my.cnf

[mysqld]

datadir=/var/lib/mysql

socket=/var/lib/mysql/mysql.sock

symbolic-links=0

log-error=/var/log/mysqld.log

pid-file=/var/run/mysqld/mysqld.pid

server-id=2                                                                                            ##伺服器標識

binlog_format=row

gtid_mode=ON                                                                              ## 開啟gtid模式

log-bin=mysql-bin-slave1                                                            ##開啟bin-log日誌

log-slave-updates=ON

enforce-gtid-consistency=true                                                     ## 強制gtid複製

slave-parallel-type=LOGICAL_CLOCK                                 ##開啟邏輯時鐘的複製

slave-parallel-workers=4                                                         ##最大執行緒16

master_info_repository=TABLE

relay_log_info_repository=TABLE

relay_log_recovery=ON

plugin-load=rpl_semi_sync_slave=semisync_slave.so

rpl_semi_sync_slave_enabled=1

儲存退出並重啟mysql

[[email protected] mysql]# systemctl restart mysqld

授權:

mysql> grant REPLICATION SLAVE ON *.* to [email protected]'192.168.148.%' identified by '!tp!gNp667aPT';

Query OK, 0 rows affected, 1 warning (0.00 sec)

重新整理許可權

mysql> flush privileges;

Query OK, 0 rows affected (0.00 sec)

再加一臺slave2

slave2:配置server-id,開啟gtid

在slave2上配置server-id,開啟gtid

[[email protected] ~]# vim /etc/my.cnf

[mysqld]

datadir=/var/lib/mysql

socket=/var/lib/mysql/mysql.sock

symbolic-links=0

log-error=/var/log/mysqld.log

pid-file=/var/run/mysqld/mysqld.pid

server-id=3                                                                                  ##伺服器標識

binlog_format=row

gtid_mode=ON                                                                            ## 開啟gtid模式

enforce-gtid-consistency=true                                                   ## 強制gtid複製

儲存並退出,重啟mysql。

  • slave1使用mysqldump全備份資料庫傳給slave2

  • 在slave2上指定master為slave1

mysql>change master to master_host='192.168.148.61',

master_user='otter',

master_password='!tp!gNp667aPT',

master_auto_position=1;

mysql> start slave;

Query OK, 0 rows affected (0.01 sec)

mysql> show slave status\G;

*************************** 1. row ***************************

Slave_IO_State:

Master_Host: 192.168.148.61

Master_User: otter

Master_Port: 3306

Connect_Retry: 60

Master_Log_File:

Read_Master_Log_Pos: 4

Relay_Log_File: localhost-relay-bin.000001

Relay_Log_Pos: 4

Relay_Master_Log_File:

Slave_IO_Running: No

Slave_SQL_Running: Yes

##檢視狀態發現有報錯:Got fatal error 1236 from master when reading data from binary log: 'The slave is connecting using CHANGE MASTER TO MASTER_AUTO_POSITION = 1, but the master has purged binary logs containing GTIDs that the slave requires.'

  • 解決方法:

  • 在slave1上進行mysqldump整庫全備份,mysqldump -uroot -p密碼 -A >all.sql
  • 然後開啟all.sql我們可以看到如下語句:

[[email protected] ~]# more all.sql

-- MySQL dump 10.13 Distrib 5.7.22, for Linux (x86_64)

--

-- Host: localhost Database:

-- ------------------------------------------------------

-- Server version 5.7.22-log

/*!40101 SET @[email protected]@CHARACTER_SET_CLIENT */;

/*!40101 SET @[email protected]@CHARACTER_SET_RESULTS */;

/*!40101 SET @[email protected]@COLLATION_CONNECTION */;

/*!40101 SET NAMES utf8 */;

/*!40103 SET @[email protected]@TIME_ZONE */;

/*!40103 SET TIME_ZONE='+00:00' */;

/*!40014 SET @[email protected]@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;

/*!40014 SET @[email protected]@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;

/*!40101 SET @[email protected]@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;

/*!40111 SET @[email protected]@SQL_NOTES, SQL_NOTES=0 */;

SET @MYSQLDUMP_TEMP_LOG_BIN = @@SESSION.SQL_LOG_BIN;

SET @@SESSION.SQL_LOG_BIN= 0;

--

-- GTID state at the beginning of the backup

--

SET @@GLOBAL.GTID_PURGED='4cfd948e-88c8-11e8-a94a-000c29da1af6:1-2,

4e552c02-8345-11e8-b571-000c294897b5:1-487';

--

-- Current Database: `mysql`

--

CREATE DATABASE /*!32312 IF NOT EXISTS*/ `mysql` /*!40100 DEFAULT CHARACTER SET latin1 */;

USE `mysql`;

--

-- Table structure for table `columns_priv`

--

DROP TABLE IF EXISTS `columns_priv`;

/*!40101 SET @saved_cs_client = @@character_set_client */;

/*!40101 SET character_set_client = utf8 */;

SET @@GLOBAL.GTID_PURGED='4cfd948e-88c8-11e8-a94a-000c29da1af6:1-2,

4e552c02-8345-11e8-b571-000c294897b5:1-487';

此值即為slave1上gtid_executed的值。

  • 在slave2上恢復slave1的備份。

mysql> source all.sql

mysql> reset master;            ##在slave上做一下reset master來清除gtid的一些資訊。

Query OK, 0 rows affected (0.01 sec)

mysql> set global gtid_purged='4cfd948e-88c8-11e8-a94a-000c29da1af6:1-2,4e552c02-8345-11e8-b571-000c294897b5:1-487'; 

Query OK, 0 rows affected (0.00 sec)

mysql> show master status\G;

*************************** 1. row ***************************

File: mysql-bini-slave2.000001

Position: 154

Binlog_Do_DB:

Binlog_Ignore_DB:

Executed_Gtid_Set: 4cfd948e-88c8-11e8-a94a-000c29da1af6:1-2,

4e552c02-8345-11e8-b571-000c294897b5:1-487

1 row in set (0.00 sec)

ERROR:

No query specified

mysql> start slave;                                                                      ##啟動slave。

Query OK, 0 rows affected (0.00 sec)

mysql> show slave status\G; ##檢視狀態恢復正常。雙YES表示恢復正常。

*************************** 1. row ***************************

Slave_IO_State: Waiting for master to send event

Master_Host: 192.168.148.61

Master_User: otter

Master_Port: 3306

Connect_Retry: 60

Master_Log_File: mysql-bin-slave1.000001

Read_Master_Log_Pos: 1071

Relay_Log_File: localhost-relay-bin.000002

Relay_Log_Pos: 435

Relay_Master_Log_File: mysql-bin-slave1.000001

Slave_IO_Running: Yes

Slave_SQL_Running: Yes

master進行一條事務,slave1和slave2同步

master建立個數據庫 aaa

mysql> show databases;

+--------------------+

| Database |

+--------------------+

| information_schema |

| kkk |

| mysql |

| performance_schema |

| sys |

+--------------------+

5 rows in set (0.00 sec)

mysql> create database aaa;

Query OK, 1 row affected (0.01 sec)

mysql> show databases;

+--------------------+

| Database |

+--------------------+

| information_schema |

| aaa |

| kkk |

| mysql |

| performance_schema |

| sys |

+--------------------+

6 rows in set (0.00 sec)

slave1檢視是否同步。

mysql> show databases;

+--------------------+

| Database |

+--------------------+

| information_schema |

| aaa |

| kkk |

| mysql |

| performance_schema |

| sys |

+--------------------+

6 rows in set (0.01 sec)

slave2檢視是否同步。

mysql> show databases;

+--------------------+

| Database |

+--------------------+

| information_schema |

| aaa |

| kkk |

| mysql |

| performance_schema |

| sys |

+--------------------+

6 rows in set (0.00 sec)

七、讀寫分離

安裝MySQL中介軟體ProxySQL。

ProxySQL是用C++語言開發的,雖然也是一個輕量級產品,但效能很好(據測試,能處理千億級的資料),功能也足夠,能滿足中介軟體所需的絕大多數功能,包括:

  • 最基本的讀/寫分離,且方式有多種。
  • 可定製基於使用者、基於schema、基於語句的規則對SQL語句進行路由。換句話說,規則很靈活。基於schema和與語句級的規則,可以實現簡單的sharding。
  • 可快取查詢結果。雖然ProxySQL的快取策略比較簡陋,但實現了基本的快取功能,絕大多數時候也夠用了。此外,作者已經打算實現更豐富的快取策略。
  • 監控後端節點。ProxySQL可以監控後端節點的多個指標,包括:ProxySQL和後端的心跳資訊,後端節點的read-only/read-write,slave和master的資料同步延遲性(replication lag)。
  • 注意點:slave節點需要設定read_only=1。

# 以下是slave1的配置檔案

server-id=2                                                               ##伺服器標識

binlog_format=row

log-bin=mysql-bin-slave1

log-slave-updates=ON

gtid_mode=ON                                                   ## 開啟gtid模式

enforce-gtid-consistency=true                          ## 強制gtid複製

slave-parallel-type=LOGICAL_CLOCK                ##開啟邏輯時鐘的複製

slave-parallel-workers=4                                       ##最大執行緒16

master_info_repository=TABLE

relay_log_info_repository=TABLE

relay_log_recovery=ON

plugin-load=rpl_semi_sync_slave=semisync_slave.so

rpl_semi_sync_slave_enabled=1

read_only=1

# 以下是slave2的配置檔案

server-id=3                                                              ##伺服器標識

log-bin=mysql-bini-slave2

binlog_format=row

gtid_mode=ON                                                       ## 開啟gtid模式

enforce-gtid-consistency=true                              ## 強制gtid複製

read_only=1

  • 安裝ProxySQL

  • 以 CentOS 7 的 rpm 包為例。

cat <<EOF | tee /etc/yum.repos.d/proxysql.repo

[proxysql_repo] name= ProxySQL

baseurl=http://repo.proxysql.com/ProxySQL/proxysql-1.4.x/centos/\$releasever

gpgcheck=1 gpgkey=http://repo.proxysql.com/ProxySQL/repo_pub_key

EOF

  • 然後直接安裝即可。

yum -y install proxysql

[[email protected] ~]# systemctl start proxysql ##啟動 proxysql

[[email protected] ~]# systemctl status proxysql ##檢視 proxysql狀態

Active: active (running) since 四 2018-07-19 18:04:48 CST; 40min ago

  • 啟動後會監聽兩個埠,

預設為6032和6033。6032埠是ProxySQL的管理埠,6033是ProxySQL對外提供服務的埠。

[[email protected] ~]# netstat -tnlp

tcp 0 0 0.0.0.0:6032 0.0.0.0:* LISTEN 977/proxysql

tcp 0 0 0.0.0.0:6033 0.0.0.0:* LISTEN 977/proxysql

  • 向ProxySQL中新增MySQL節點

然後使用mysql客戶端連線到ProxySQL的管理介面(admin interface),該介面的預設管理員使用者和密碼都是admin。

[[email protected] ~]# mysql -uadmin -padmin -P6032 -h127.0.0.1 --prompt 'admin> '

mysql: [Warning] Using a password on the command line interface can be insecure.

Welcome to the MySQL monitor. Commands end with ; or \g.

Your MySQL connection id is 16

Server version: 5.5.30 (ProxySQL Admin Module)

admin> show databases;

+-----+---------------+-------------------------------------+

| seq | name | file |

+-----+---------------+-------------------------------------+

| 0 | main | |

| 2 | disk | /var/lib/proxysql/proxysql.db |

| 3 | stats | |

| 4 | monitor | |

| 5 | stats_history | /var/lib/proxysql/proxysql_stats.db |

+-----+---------------+-------------------------------------+

5 rows in set (0.00 sec)

ProxySQL提供了幾個庫,每個庫都有各自的意義。主要修改main和monitor資料庫中的表。

admin> show tables from main;

+--------------------------------------------+

| tables |

+--------------------------------------------+

| global_variables |

| mysql_collations |

| mysql_group_replication_hostgroups |

| mysql_query_rules |

| mysql_query_rules_fast_routing |

| mysql_replication_hostgroups |

| mysql_servers |

| mysql_users |

| proxysql_servers |

| runtime_checksums_values |

| runtime_global_variables |

| runtime_mysql_group_replication_hostgroups |

| runtime_mysql_query_rules |

| runtime_mysql_query_rules_fast_routing |

| runtime_mysql_replication_hostgroups |

| runtime_mysql_servers |

| runtime_mysql_users |

| runtime_proxysql_servers |

| runtime_scheduler |

| scheduler |

+--------------------------------------------+

20 rows in set (0.00 sec)

admin> show tables from monitor;

+------------------------------------+

| tables |

+------------------------------------+

| mysql_server_connect_log |

| mysql_server_group_replication_log |

| mysql_server_ping_log |

| mysql_server_read_only_log |

| mysql_server_replication_lag_log |

+------------------------------------+

5 rows in set (0.00 sec)

runtime_開頭的是執行時的配置,這些是不能修改的。要修改ProxySQL的配置,需要修改了非runtime_表,修改後必須執行LOAD ... TO RUNTIME才能載入到RUNTIME生效,執行save ... to disk才能將配置持久化儲存到磁碟。

admin> insert into mysql_servers(hostgroup_id,hostname,port) values(10,'192.168.148.62',3306);

admin> insert into mysql_servers(hostgroup_id,hostname,port) values(10,'192.168.148.61',3306);

admin> insert into mysql_servers(hostgroup_id,hostname,port) values(10,'192.168.148.64',3306);

#使用insert語句新增主機到mysql_servers表中,其中:hostgroup_id 10 表示寫組,20表示讀組。

檢視這3個節點是否插入成功,以及它們的狀態。

admin> select * from mysql_servers\G

*************************** 1. row ***************************

hostgroup_id: 10

hostname: 192.168.148.62

port: 3306

status: ONLINE

weight: 1

compression: 0

max_connections: 1000

max_replication_lag: 0

use_ssl: 0

max_latency_ms: 0

comment:

*************************** 2. row ***************************

hostgroup_id: 20

hostname: 192.168.148.61

port: 3306

status: ONLINE

weight: 1

compression: 0

max_connections: 1000

max_replication_lag: 0

use_ssl: 0

max_latency_ms: 0

comment:

*************************** 3. row ***************************

hostgroup_id: 20

hostname: 192.168.148.64

port: 3306

status: ONLINE

weight: 1

compression: 0

max_connections: 1000

max_replication_lag: 0

use_ssl: 0

max_latency_ms: 0

comment:

3 rows in set (0.00 sec)

修改後,載入到RUNTIME,並儲存到disk。

admin> load mysql servers to runtime; admin> save mysql servers to disk;

  • 監控後端MySQL節點

新增節點之後,還需要監控後端節點。對於後端是主從複製的環境來說,這是必須的,因為ProxySQL需要通過每個節點的read_only值來自動調整它們是屬於讀組還是寫組。

首先在後端master節點上建立一個用於監控的使用者名稱(只需在master上建立即可,因為會複製到slave上),這個使用者名稱只需具有USAGE許可權即可。如果還需要監控複製結構中slave是否嚴重延遲於master(這個俗語叫做"拖後腿",術語叫做"replication lag"),則還需具備replication client許可權。這裡直接賦予這個許可權。

# 在master上執行:

mysql> create user [email protected]'192.168.148.%' identified by '[email protected]!'; mysql> grant replication client on *.* to [email protected]'192.168.148.%';

#然後回到ProxySQL上配置監控。

admin> set mysql-monitor_username='monitor'; admin> set mysql-monitor_password='[email protected]!';

#修改後,載入到RUNTIME,並儲存到disk。

admin> load mysql variables to runtime; admin> save mysql variables to disk;

#驗證監控結果:ProxySQL監控模組的指標都儲存在monitor庫的log表中。

以下是連線是否正常的監控(對connect指標的監控):(在前面可能會有很多connect_error,這是因為沒有配置監控資訊時的錯誤,配置後如果connect_error的結果為NULL則表示正常)

admin> select * from mysql_server_connect_log;

+----------------+------+------------------+-------------------------+---------------+

| hostname | port | time_start_us | connect_success_time_us | connect_error |

+----------------+------+------------------+-------------------------+---------------+

| 192.168.148.61 | 3306 | 1532003209576812 | 1113 | NULL |

| 192.168.148.62 | 3306 | 1532003209587641 | 428 | NULL |

| 192.168.148.64 | 3306 | 1532003209598596 | 6290 | NULL |

| 192.168.148.62 | 3306 | 1532003749593625 | 676 | NULL |

| 192.168.148.64 | 3306 | 1532003749604600 | 1459 | NULL |

+----------------+------+------------------+-------------------------+---------------+

30 rows in set (0.00 sec)

#以下是對心跳資訊的監控(對ping指標的監控):

admin> select * from mysql_server_ping_log;

+----------------+------+------------------+----------------------+------------+

| hostname | port | time_start_us | ping_success_time_us | ping_error |

+----------------+------+------------------+----------------------+------------+

| 192.168.148.61 | 3306 | 1532003210786402 | 717 | NULL |

| 192.168.148.64 | 3306 | 1532003610811775 | 382 | NULL |

| 192.168.148.61 | 3306 | 1532003620807891 | 408 | NULL |

| 192.168.148.62 | 3306 | 1532003620809610 | 140 | NULL |

| 192.168.148.64 | 3306 | 1532003800821971 | 420 | NULL |

+----------------+------+------------------+----------------------+------------+

180 rows in set (0.00 sec)

#但是,read_only和replication_lag的監控日誌都為空。

admin> select * from mysql_server_read_only_log;

Empty set (0.00 sec)

admin> select * from mysql_server_replication_lag_log;

Empty set (0.00 sec)

#例如,指定寫組的id為10,讀組的id為20。

admin> insert into mysql_replication_hostgroups values(10,20,1);

在該配置載入到RUNTIME生效之前,先檢視下各mysql server所在的組。

admin> select hostgroup_id,hostname,port,status,weight from mysql_servers;

+--------------+----------------+------+--------+--------+

| hostgroup_id | hostname | port | status | weight |

+--------------+----------------+------+--------+--------+

| 10 | 192.168.148.62 | 3306 | ONLINE | 1 |

| 10 | 192.168.148.61 | 3306 | ONLINE | 1 |

| 10 | 192.168.148.64 | 3306 | ONLINE | 1 |

+--------------+----------------+------+--------+--------+

3 rows in set (0.00 sec)

#3個節點都在hostgroup_id=10的組中。

現在,將剛才mysql_replication_hostgroups表的修改載入到RUNTIME生效。

admin> load mysql servers to runtime;

admin> save mysql servers to disk;

一載入,Monitor模組就會開始監控後端的read_only值,當監控到read_only值後,就會按照read_only的值將某些節點自動移動到讀/寫組。

例如,此處所有節點都在id=10的寫組,slave1和slave2都是slave,它們的read_only=1,這兩個節點將會移動到id=20的組。如果一開始這3節點都在id=20的讀組,那麼移動的將是Master節點,會移動到id=10的寫組。

#看結果:

admin> select hostgroup_id,hostname,port,status,weight from mysql_servers;

+--------------+----------------+------+--------+--------+

| hostgroup_id | hostname | port | status | weight |

+--------------+----------------+------+--------+--------+

| 10 | 192.168.148.62 | 3306 | ONLINE | 1 |

| 20 | 192.168.148.61 | 3306 | ONLINE | 1 |

| 20 | 192.168.148.64 | 3306 | ONLINE | 1 |

+--------------+----------------+------+--------+--------+

3 rows in set (0.00 sec)

admin> select * from mysql_server_read_only_log;

+----------------+------+------------------+-----------------+-----------+--------+

| hostname | port | time_start_us | success_time_us | read_only | error |

+----------------+------+------------------+-----------------+-----------+--------+ |

192.168.148.64 | 3306 | 1532003708480193 | 1218 | 1 | NULL |

| 192.168.148.62 | 3306 | 1532003709979095 | 2369 | 0 | NULL |

| 192.168.148.61 | 3306 | 1532003709978587 | 3464 | 1 | NULL |

| 192.168.148.64 | 3306 | 1532003709981780 | 2562 | 1 | NULL |

| 192.168.148.61 | 3306 | 1532003711479179 | 2449 | 1 | NULL |

| 192.168.148.62 | 3306 | 1532003711479965 | 1918 | 0 | NULL |

+----------------+------+------------------+-----------------+-----------+--------+ |

  • 配置mysql_users

上面的所有配置都是關於後端MySQL節點的,現在可以配置關於SQL語句的,包括:傳送SQL語句的使用者、SQL語句的路由規則、SQL查詢的快取、SQL語句的重寫等等。本小節是SQL請求所使用的使用者配置,例如root使用者。這要求我們需要先在後端MySQL節點新增好相關使用者。這裡以root和sqlsender兩個使用者名稱為例。

#首先,在master節點上執行:(只需master執行即可,會複製給兩個slave)

mysql> grant all on *.* to [email protected]'192.168.148.%' identified by 'passwd';

mysql> grant all on *.* to [email protected]'192.168.148.%' identified by '[email protected]!';

#然後回到ProxySQL,配置mysql_users表,將剛才的兩個使用者新增到該表中。

admin> insert into mysql_users(username,password,default_hostgroup) values('root','passwd',10);

admin> insert into mysql_users(username,password,default_hostgroup) values('sqlsender','[email protected]!',10);

admin> load mysql users to runtime; admin> save mysql users to disk;

mysql_users表有不少欄位,最主要的三個欄位為username、password和default_hostgroup:

  • username:前端連線ProxySQL,以及ProxySQL將SQL語句路由給MySQL所使用的使用者名稱。
  • password:使用者名稱對應的密碼。可以是明文密碼,也可以是hash密碼。如果想使用hash密碼,可以先在某個MySQL節點上執行select password(PASSWORD),然後將加密結果複製到該欄位。
  • default_hostgroup:該使用者名稱預設的路由目標。例如,指定root使用者的該欄位值為10時,則使用root使用者傳送的SQL語句預設情況下將路由到hostgroup_id=10組中的某個節點。

admin> select * from mysql_users\G

*************************** 1. row ***************************

username: root

password: passwd

active: 1

use_ssl: 0

default_hostgroup: 10

default_schema: NULL

schema_locked: 0

transaction_persistent: 1

fast_forward: 0

backend: 1

frontend: 1

max_connections: 10000

*************************** 2. row ***************************

username: sqlsender

password: [email protected]!

active: 1

use_ssl: 0