導讀:
在之前,我們搭建了MySQL組複製集羣環境,MySQL組複製集羣環境解決了MySQL集羣內部的自動故障轉移,但是,組複製並沒有解決外部業務的故障轉移。舉個例子,在A、B、C 3臺機器上搭建了組複製環境,且運行在單主模式下,這裏假設A爲主節點,應用程序連接A寫數據,如果A節點發生宕機,主節點切換到B機器上,此時,應用程序是不會自動連接到B服務器上的,需要人工進行切換。
在這篇文章中,我們要介紹的ProxySQL就能夠解決上面的問題,ProxySQL能夠實現業務層面故障轉移、讀寫分離功能,當然ProxySQL不僅僅只有這兩項功能,還有更多的其它功能。其架構如下:
我們不妨來了解一下。
ProxySQL是一款MySQL代理軟件,其核心特點爲讀寫分離、故障轉移,詳細其功能如下:
- 應用層代理。ProxySQL不僅能夠實現負載均衡,還可提供端到端連接處理、實時信息統計和數據庫流量檢查;
- 零停機時間變更。ProxySQL在內存中直接進行配置修改,然後可以持久存儲到磁盤和推送到運行中;
- 數據庫防火牆。可充當應用程序與數據庫之間的防火牆,使DBA可以保護數據庫免受惡意活動或有問題的程序的影響;
- 高級查詢規則。使用ProxySQL豐富的查詢規則定義查詢路由,有效的分發和緩存數據,從而最大的提高數據庫服務緩存效率;
- 數據分片與轉換
- 故障轉移檢測。ProxySQL通過連續監視數據庫後端並在拓撲更改時將流量重新路由到運行正常的節點。
這裏,我們使用ProxySQL來對MySQL組複製環境實現讀寫分離以及故障轉移。我的環境如下:
IP地址 | 主機名 | 用途 |
192.168.10.11 | mgr-node1 | MySQL組複製成員 |
192.168.10.12 | mgr-node2 | MySQL組複製成員 |
192.168.10.13 | mgr-node3 | MySQL組複製成員 |
192.168.10.10 | proxysql | ProxySQL代理服務器 |
MySQL採用多主模式,搭建過程見文檔:《MySQL組複製MGR(二)-- 組複製搭建》,本文把重點放在ProxySQL的搭建與配置上。
(二)安裝ProxySQL
安裝ProxySQL,有2種方法,如果有網絡,可以直接使用yum安裝,如果沒有網絡,可以下載ProxySQL發行包安裝,下載地址爲:https://github.com/sysown/proxysql/releases。這裏爲了方便,直接使用yum在線安裝。
添加yum源,使用Linux root用戶執行如下配置:
cat <<EOF | tee /etc/yum.repos.d/proxysql.repo [proxysql_repo] name= ProxySQL YUM repository baseurl=https://repo.proxysql.com/ProxySQL/proxysql-2.0.x/centos/\$releasever gpgcheck=1 gpgkey=https://repo.proxysql.com/ProxySQL/repo_pub_key EOF
安裝proxysql:
yum install -y proxysql OR yum install proxysql-version
如果要查看安裝的文件在哪,可以使用如下命令:
[root@proxysql yum.repos.d]# rpm -ql proxysql /etc/logrotate.d/proxysql /etc/proxysql.cnf /etc/systemd/system/proxysql-initial.service /etc/systemd/system/proxysql.service /usr/bin/proxysql /usr/share/proxysql/tools/proxysql_galera_checker.sh /usr/share/proxysql/tools/proxysql_galera_writer.pl
查看ProxySQL進程:
[root@proxysql yum.repos.d]# ps -ef|grep proxy avahi 740 1 0 15:52 ? 00:00:00 avahi-daemon: registering [proxysql-65.local] root 761 1 0 15:52 ? 00:00:00 /usr/sbin/gssproxy -D proxysql 2058 1 0 16:09 ? 00:00:00 /usr/bin/proxysql --idle-threads -c /etc/proxysql.cnf proxysql 2059 2058 1 16:09 ? 00:00:00 /usr/bin/proxysql --idle-threads -c /etc/proxysql.cnf root 2087 1589 0 16:09 pts/0 00:00:00 grep --color=auto proxy
查看端口,6032是proxysql的管理端口,6033是對外服務端口
[root@proxysql yum.repos.d]# netstat -anlp | grep proxysql tcp 0 0 0.0.0.0:6032 0.0.0.0:* LISTEN 2059/proxysql tcp 0 0 0.0.0.0:6033 0.0.0.0:* LISTEN 2059/proxysql
(三)啓動關閉ProxySQL
啓動ProxySQL
service proxysql start
關閉ProxySQL
service proxysql stop
重啓ProxySQL
service proxysql restart
查看ProxySQL的狀態
service proxysql status
(四)ProxySQL基礎知識瞭解
ProxySQL的配置,相對而言還是比較複雜的。因此,在配置ProxySQL之前,我們需要對其架構有一些瞭解,這樣在配置的時候,纔不會一臉懵逼。
(4.1)ProxySQL多層配置系統
前面我們說到ProxySQL具有“零停機時間變更”功能,它是通過3層配置來實現的,3層配置包括:Runtime、Memory、Disk & Configuration File。
- Runtime層表示ProxySQL工作線程使用的內存數據結構;
- Memory(也被稱爲main)層經由一個MySQL兼容接口露出的內存數據庫,用戶可以使用MySQL客戶端連接到管理界面,查看、編輯ProxySQL配置表;
- Disk & Configuration File。Disk層是一個存放在磁盤上的SQLite3數據庫,Disk層可將內存中的配置信息保存到磁盤,以便ProxySQL重新啓動後配置還可用。
3個層面的信息有什麼區別呢?我個人的理解是:3個層面保存的都是ProxySQL的配置信息,如果管理員未作修改,那麼3個層面的配置信息是相同的。如果管理員要修改配置信息,首先需要修改Memory層,要讓修改的信息立刻生效,則需要把Memory層的變更信息推到Runtime層;要讓修改的配置信息在ProxySQL重啓後還能保存下來,則需要把Memory層的信息推到Disk層。Runtime層是ProxySQL正在使用的配置信息,Memory層是用戶可以編輯的信息,Disk層可以把配置信息永久保存在磁盤上。
各層之間數據如何同步呢?我們可以看上圖的箭頭部分,通過load/save命令來實現同步。具體命令如下:
[1] LOAD <item> FROM MEMORY/LOAD <item> TO RUNTIME 將配置項從內存數據庫加載到運行時數據結構 [2] SAVE <item> TO MEMORY/SAVE <item> FROM RUNTIME 將配置項從運行時保存到內存數據庫中 [3] LOAD <item> TO MEMORY/LOAD <item> FROM DISK 將持久性配置項目從磁盤數據庫加載到內存數據庫 [4] SAVE <item> FROM MEMORY/SAVE <item> TO DISK 將配置項從內存數據庫保存到磁盤數據庫 [5] LOAD <item> FROM CONFIG 將配置項從配置文件加載到內存數據庫中
常用的配置有:
# 激活用戶配置到RUNTIME LOAD MYSQL USERS TO RUNTIME; # 保存用戶信息到磁盤上 SAVE MYSQL USERS TO DISK; --------------------------------- # 激活MySQL服務器信息到RUNTIME LOAD MYSQL SERVERS TO RUNTIME; # 保存MySQL服務器信息到磁盤 SAVE MYSQL SERVERS TO DISK; --------------------------------- # 激活查詢路由規則到RUNTIME LOAD MYSQL QUERY RULES TO RUNTIME; # 保存查詢路由規則到磁盤 SAVE MYSQL QUERY RULES TO DISK; ---------------------------------- # 激活MySQL變量到RUNTIME LOAD MYSQL VARIABLES TO RUNTIME; # 保存MySQL變量到磁盤 SAVE MYSQL VARIABLES TO DISK; ---------------------------------- # 激活proxySQL admin變量到RUNTIME LOAD ADMIN VARIABLES TO RUNTIME; # 保存proxySQL admin變量到磁盤 SAVE ADMIN VARIABLES TO DISK;
(4.2)ProxySQL的配置管理接口
ProxySQL有2種配置方式:
- 使用ProxySQL的命令行管理接口進行配置
- 使用配置文件進行配置
通常使用第一種方法進行配置,這裏我們只瞭解第1種方法。
ProxySQL管理界面使用的是MySQL協議的界面,通過使用mysql客戶端連接到SQLite3進行配置的查詢、管理。可以使用默認的admin用戶連接到proxySQL數據庫。
[root@proxysql ~]# mysql -uadmin -padmin -h127.0.0.1 -P6032 mysql> 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 | +-----+---------------+-------------------------------------+
這些數據庫作用如下:
- main:內存配置數據庫,使用此數據庫,可以方便的查詢和更新ProxySQL的配置。與上面所曾配置系統的Memory層對應;
- disk:“main”數據庫的磁盤鏡像。重新啓動ProxySQL時,main中的數據就是從該數據庫加載的。與上面所曾配置系統的disk層對應;
- stats:ProxySQL收集的一些指標。如每個查詢規則的匹配次數,當前正在運行的查詢等;
- monitor:包含於ProxySQL連接的後端服務器的指標。如連接帶後端服務器對其進行ping操作的最小、最大時間;
ProxySQL設定了2個用戶來管理配置數據庫:
- 賬號admin 密碼admin : 該用戶具有全部標的讀寫權限;
- 賬號stats 密碼stats : 該用戶具有統計信息表的只讀權限;
(五)一步一步配置ProxySQL--基礎配置
(5.1)檢查配置信息
查看相關配置表是否存在信息,因爲還沒開始配置,所以是不存在信息的,如果已經配置過了,可以先刪除信息。
mysql> select * from mysql_servers; Empty set (0.00 sec) mysql> select * from mysql_users; Empty set (0.01 sec) mysql> select * from mysql_query_rules; Empty set (0.00 sec) mysql> select * from mysql_group_replication_hostgroups; Empty set (0.00 sec)
(5.2)組的配置
所謂組的配置,即定義讀組、寫組等,可以使用如下兩個表來定義讀寫組:
- mysql_replication_hostgroups:該表用於傳統的master/slave的異步複製或者半同步複製的配置。
- mysql_group_replication_hostgroups:該表用於MySQL Group Replication、InnoDB Cluster or Galera/Percona XtraDB Cluster的配置
因爲我們這裏是使用proxySQL來實現MGR集羣業務層面的實現故障轉移以及讀寫分離的,所以配置mysql_group_replication_hostgroups表即可,該表定義如下:
show create table mysql_group_replication_hostgroups; ------------------------------------------------------------ CREATE TABLE mysql_group_replication_hostgroups ( writer_hostgroup INT CHECK (writer_hostgroup>=0) NOT NULL PRIMARY KEY, backup_writer_hostgroup INT CHECK (backup_writer_hostgroup>=0 AND backup_writer_hostgroup<>writer_hostgroup) NOT NULL, reader_hostgroup INT NOT NULL CHECK (reader_hostgroup<>writer_hostgroup AND backup_writer_hostgroup<>reader_hostgroup AND reader_hostgroup>0), offline_hostgroup INT NOT NULL CHECK (offline_hostgroup<>writer_hostgroup AND offline_hostgroup<>reader_hostgroup AND backup_writer_hostgroup<>offline_hostgroup AND offline_hostgroup>=0), active INT CHECK (active IN (0,1)) NOT NULL DEFAULT 1, max_writers INT NOT NULL CHECK (max_writers >= 0) DEFAULT 1, writer_is_also_reader INT CHECK (writer_is_also_reader IN (0,1,2)) NOT NULL DEFAULT 0, max_transactions_behind INT CHECK (max_transactions_behind>=0) NOT NULL DEFAULT 0, comment VARCHAR, UNIQUE (reader_hostgroup), UNIQUE (offline_hostgroup), UNIQUE (backup_writer_hostgroup))
這些字段含義如下:
- write_hostgroup:默認情況下會將所有流量發送到這個組。具有read_only=0的節點也將分配到這個組;
- backup_writer_hostgroup:如果集羣有多個寫節點(read_only=0)且超過了max_writers規定數量,則會把多出來的寫節點放到備用寫組裏面;
- reader_hostgroup:讀取的流量應該發送到該組,只讀節點(read_only=1)會被分配到該組;
- offline_hostgroup:當ProxySQL監視到某個節點不正常時,會被放入該組;
- active:是否啓用主機組,當啓用時,ProxySQL將監視主機在各族之間移動;
- max_writers:最大寫節點的數量,超過該值的節點應該被放入backup_write_hostgroup;
- writer_is_also_reader:一個節點既做寫節點也做讀節點,如果該值爲2,則backup_writer_hostgroup的節點做讀寫點,但是writer_hostgroup不會做讀節點;
我們對該表進行如下配置:
mysql> select * from mysql_group_replication_hostgroups; +------------------+-------------------------+------------------+-------------------+--------+-------------+-----------------------+-------------------------+---------+ | writer_hostgroup | backup_writer_hostgroup | reader_hostgroup | offline_hostgroup | active | max_writers | writer_is_also_reader | max_transactions_behind | comment | +------------------+-------------------------+------------------+-------------------+--------+-------------+-----------------------+-------------------------+---------+ | 1 | 2 | 3 | 4 | 1 | 1 | 0 | 100 | NULL | +------------------+-------------------------+------------------+-------------------+--------+-------------+-----------------------+-------------------------+---------+
(5.3)視圖添加
如果ProxySQL是與組複製MGR一起使用的,那麼還需要在MGR集羣添加如下視圖:
USE sys; DELIMITER $$ CREATE FUNCTION IFZERO(a INT, b INT) RETURNS INT DETERMINISTIC RETURN IF(a = 0, b, a)$$ CREATE FUNCTION LOCATE2(needle TEXT(10000), haystack TEXT(10000), offset INT) RETURNS INT DETERMINISTIC RETURN IFZERO(LOCATE(needle, haystack, offset), LENGTH(haystack) + 1)$$ CREATE FUNCTION GTID_NORMALIZE(g TEXT(10000)) RETURNS TEXT(10000) DETERMINISTIC RETURN GTID_SUBTRACT(g, '')$$ CREATE FUNCTION GTID_COUNT(gtid_set TEXT(10000)) RETURNS INT DETERMINISTIC BEGIN DECLARE result BIGINT DEFAULT 0; DECLARE colon_pos INT; DECLARE next_dash_pos INT; DECLARE next_colon_pos INT; DECLARE next_comma_pos INT; SET gtid_set = GTID_NORMALIZE(gtid_set); SET colon_pos = LOCATE2(':', gtid_set, 1); WHILE colon_pos != LENGTH(gtid_set) + 1 DO SET next_dash_pos = LOCATE2('-', gtid_set, colon_pos + 1); SET next_colon_pos = LOCATE2(':', gtid_set, colon_pos + 1); SET next_comma_pos = LOCATE2(',', gtid_set, colon_pos + 1); IF next_dash_pos < next_colon_pos AND next_dash_pos < next_comma_pos THEN SET result = result + SUBSTR(gtid_set, next_dash_pos + 1, LEAST(next_colon_pos, next_comma_pos) - (next_dash_pos + 1)) - SUBSTR(gtid_set, colon_pos + 1, next_dash_pos - (colon_pos + 1)) + 1; ELSE SET result = result + 1; END IF; SET colon_pos = next_colon_pos; END WHILE; RETURN result; END$$ CREATE FUNCTION gr_applier_queue_length() RETURNS INT DETERMINISTIC BEGIN RETURN (SELECT sys.gtid_count( GTID_SUBTRACT( (SELECT Received_transaction_set FROM performance_schema.replication_connection_status WHERE Channel_name = 'group_replication_applier' ), (SELECT @@global.GTID_EXECUTED) ))); END$$ CREATE FUNCTION gr_member_in_primary_partition() RETURNS VARCHAR(3) DETERMINISTIC BEGIN RETURN (SELECT IF( MEMBER_STATE='ONLINE' AND ((SELECT COUNT(*) FROM performance_schema.replication_group_members WHERE MEMBER_STATE != 'ONLINE') >= ((SELECT COUNT(*) FROM performance_schema.replication_group_members)/2) = 0), 'YES', 'NO' ) FROM performance_schema.replication_group_members JOIN performance_schema.replication_group_member_stats USING(member_id)); END$$ CREATE VIEW gr_member_routing_candidate_status AS SELECT sys.gr_member_in_primary_partition() as viable_candidate, IF( (SELECT (SELECT GROUP_CONCAT(variable_value) FROM performance_schema.global_variables WHERE variable_name IN ('read_only', 'super_read_only')) != 'OFF,OFF'), 'YES', 'NO') as read_only, sys.gr_applier_queue_length() as transactions_behind, Count_Transactions_in_queue as 'transactions_to_cert' from performance_schema.replication_group_member_stats;$$ DELIMITER ;
然後授權給監控用戶,這裏需要特別注意,我的監控用戶在5.5.1步才創建,因此這一步需要放到5.5.1後執行:
grant select on sys.* to monitoring_user;
(5.4)MySQL服務器添加
mysql_server表是用來存儲ProxySQL路由轉換的MySQL節點的信息。
mysql> insert into mysql_servers (hostgroup_id, hostname, port) values(1,'192.168.10.11',3306); mysql> insert into mysql_servers (hostgroup_id, hostname, port) values(1,'192.168.10.12',3306); mysql> insert into mysql_servers (hostgroup_id, hostname, port) values(1,'192.168.10.13',3306); mysql> select * from mysql_servers; +--------------+---------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+ | hostgroup_id | hostname | port | gtid_port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment | +--------------+---------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+ | 1 | 192.168.10.11 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | | | 1 | 192.168.10.12 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | | | 1 | 192.168.10.13 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | | +--------------+---------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
然後執行下面的命令生效:
LOAD MYSQL SERVERS TO RUNTIME; SAVE MYSQL SERVERS TO DISK;
(5.5)監控配置及檢查
這裏配置監控信息,用來監控ProxySQL與後端的MySQL通信是否正常。
(5.5.1)監控用戶配置
在ProxySQL的變量表裏面設定監控用戶密碼,用於ProxySQL監控後端MySQL服務器的用戶信息
mysql> UPDATE global_variables SET variable_value='monitor' WHERE variable_name='mysql-monitor_username'; Query OK, 1 row affected (0.00 sec) mysql> UPDATE global_variables SET variable_value='monitor' WHERE variable_name='mysql-monitor_password'; Query OK, 1 row affected (0.00 sec) mysql> select variable_name,variable_value from global_variables where variable_name in ('mysql-monitor_username','mysql-monitor_password'); +------------------------+----------------+ | variable_name | variable_value | +------------------------+----------------+ | mysql-monitor_password | monitor | | mysql-monitor_username | monitor | +------------------------+----------------+
需要注意,既然使用該用戶監控後臺MySQL數據庫,那麼後臺MySQL數據庫也需要創建該用戶並授權,monitor用戶需要有usage權限去連接、ping和檢查read_only信息,如果要檢測複製延遲,還需要具有replication client權限。特別注意,不能使用mysql_users裏面的用戶來做監控用戶。
-- 在MySQL服務器上創建監控用戶 -- 需要注意,這裏MySQL使用的是MGR,所以只需要在一臺節點創建用戶即可,其它節點會自動同步用戶信息 create user monitor@'%' identified by 'monitor'; grant usage,replication client on *.* to monitor@'%'; flush privileges;
注意:因爲ProxySQL+組複製添加了新的視圖,見”5.3 視圖添加”,因此還需授權:
grant select on sys.* to monitor;
(5.5.2)配置監控間隔
這裏把連接、ping、read_only監控間隔改爲2s,也可以根據需要改成其它,也可以不做修改
mysql> update global_variables set variable_value='2000' -> where variable_name in('mysql-monitor_connect_interval','mysql-monitor_ping_interval','mysql-monitor_read_only_interval'); Query OK, 3 rows affected (0.00 sec) mysql> select * from global_variables where variable_name like 'mysql-monitor%'; +--------------------------------------------------------------+----------------+ | variable_name | variable_value | +--------------------------------------------------------------+----------------+ | mysql-monitor_enabled | true | | mysql-monitor_connect_timeout | 600 | | mysql-monitor_ping_max_failures | 3 | | mysql-monitor_ping_timeout | 1000 | | mysql-monitor_read_only_max_timeout_count | 3 | | mysql-monitor_replication_lag_interval | 10000 | | mysql-monitor_replication_lag_timeout | 1000 | | mysql-monitor_groupreplication_healthcheck_interval | 5000 | | mysql-monitor_groupreplication_healthcheck_timeout | 800 | | mysql-monitor_groupreplication_healthcheck_max_timeout_count | 3 | | mysql-monitor_groupreplication_max_transactions_behind_count | 3 | | mysql-monitor_galera_healthcheck_interval | 5000 | | mysql-monitor_galera_healthcheck_timeout | 800 | | mysql-monitor_galera_healthcheck_max_timeout_count | 3 | | mysql-monitor_replication_lag_use_percona_heartbeat | | | mysql-monitor_query_interval | 60000 | | mysql-monitor_query_timeout | 100 | | mysql-monitor_slave_lag_when_null | 60 | | mysql-monitor_threads_min | 8 | | mysql-monitor_threads_max | 128 | | mysql-monitor_threads_queue_maxsize | 128 | | mysql-monitor_wait_timeout | true | | mysql-monitor_writer_is_also_reader | true | | mysql-monitor_username | monitor | | mysql-monitor_password | monitor | | mysql-monitor_history | 600000 | | mysql-monitor_connect_interval | 2000 | | mysql-monitor_ping_interval | 2000 | | mysql-monitor_read_only_interval | 2000 | | mysql-monitor_read_only_timeout | 500 | +--------------------------------------------------------------+----------------+ 30 rows in set (0.01 sec)
在修改完變量之後,一定要加載到內存中生效以及永久保存到磁盤中:
LOAD MYSQL VARIABLES TO RUNTIME; SAVE MYSQL VARIABLES TO DISK;
(5.5.3)檢查監控信息是否存在異常
監控配置完成後,我們需要檢查ProxySQL與後端MySQL通信是否有異常,monitor數據庫中的表用於存儲監視信息,需要注意的是,這些表並非都已經被使用。
mysql> show tables from monitor; +--------------------------------------+ | tables | +--------------------------------------+ | mysql_server_aws_aurora_check_status | | mysql_server_aws_aurora_failovers | | mysql_server_aws_aurora_log | | mysql_server_connect_log | | mysql_server_galera_log | | mysql_server_group_replication_log | | mysql_server_ping_log | | mysql_server_read_only_log | | mysql_server_replication_lag_log | +--------------------------------------+
查看ProxySQL與後臺服務器連接是否正常:
mysql> select * from monitor.mysql_server_connect_log order by time_start_us desc limit 10; +---------------+------+------------------+-------------------------+---------------+ | hostname | port | time_start_us | connect_success_time_us | connect_error | +---------------+------+------------------+-------------------------+---------------+ | 192.168.10.13 | 3306 | 1596263409501584 | 2191 | NULL | | 192.168.10.11 | 3306 | 1596263409480641 | 1911 | NULL | | 192.168.10.12 | 3306 | 1596263409459524 | 3671 | NULL | | 192.168.10.13 | 3306 | 1596263407504677 | 1451 | NULL | | 192.168.10.11 | 3306 | 1596263407481776 | 1398 | NULL | | 192.168.10.12 | 3306 | 1596263407458859 | 1378 | NULL | | 192.168.10.12 | 3306 | 1596263405490389 | 3480 | NULL | | 192.168.10.13 | 3306 | 1596263405474367 | 2804 | NULL | | 192.168.10.11 | 3306 | 1596263405458569 | 1612 | NULL | | 192.168.10.13 | 3306 | 1596263403497485 | 2132 | NULL | +---------------+------+------------------+-------------------------+---------------+ 10 rows in set (0.00 sec)
查看組複製是否正常,檢查節點是否只讀和交易滯後時間:
mysql> select * from mysql_server_group_replication_log order by time_start_us desc limit 10; +---------------+------+------------------+-----------------+------------------+-----------+---------------------+-------+ | hostname | port | time_start_us | success_time_us | viable_candidate | read_only | transactions_behind | error | +---------------+------+------------------+-----------------+------------------+-----------+---------------------+-------+ | 192.168.10.13 | 3306 | 1596263494597039 | 5671 | YES | NO | 0 | NULL | | 192.168.10.12 | 3306 | 1596263494596052 | 3231 | YES | NO | 0 | NULL | | 192.168.10.11 | 3306 | 1596263494595139 | 3245 | YES | NO | 0 | NULL | | 192.168.10.13 | 3306 | 1596263489596357 | 3027 | YES | NO | 0 | NULL | | 192.168.10.12 | 3306 | 1596263489595491 | 3306 | YES | NO | 0 | NULL | | 192.168.10.11 | 3306 | 1596263489594645 | 3110 | YES | NO | 0 | NULL | | 192.168.10.13 | 3306 | 1596263484595710 | 3680 | YES | NO | 0 | NULL | | 192.168.10.12 | 3306 | 1596263484594839 | 3618 | YES | NO | 0 | NULL | | 192.168.10.11 | 3306 | 1596263484594114 | 3214 | YES | NO | 0 | NULL | | 192.168.10.13 | 3306 | 1596263479595072 | 1887 | YES | NO | 0 | NULL | +---------------+------+------------------+-----------------+------------------+-----------+---------------------+-------+ 10 rows in set (0.01 sec)
查看ProxySQL ping後端MySQL服務器是否正常:
mysql> select * from mysql_server_ping_log order by time_start_us desc limit 10; +---------------+------+------------------+----------------------+------------+ | hostname | port | time_start_us | ping_success_time_us | ping_error | +---------------+------+------------------+----------------------+------------+ | 192.168.10.12 | 3306 | 1596263541810631 | 496 | NULL | | 192.168.10.11 | 3306 | 1596263541786903 | 612 | NULL | | 192.168.10.13 | 3306 | 1596263541762973 | 749 | NULL | | 192.168.10.12 | 3306 | 1596263539796079 | 565 | NULL | | 192.168.10.13 | 3306 | 1596263539779040 | 403 | NULL | | 192.168.10.11 | 3306 | 1596263539762769 | 1141 | NULL | | 192.168.10.12 | 3306 | 1596263537797512 | 848 | NULL | | 192.168.10.11 | 3306 | 1596263537779520 | 845 | NULL | | 192.168.10.13 | 3306 | 1596263537761840 | 742 | NULL | | 192.168.10.12 | 3306 | 1596263535814945 | 843 | NULL | +---------------+------+------------------+----------------------+------------+ 10 rows in set (0.00 sec)
通過監控信息,我們可以得出結論,所有配置都是健康的,繼續下一步。
(5.6)用戶配置
(5.6.1)ProxySQL的雙層用戶認證機制
如果使用了ProxySQL來做中間路由,那麼與我們平時登錄數據庫有一些區別:平時我們直接使用數據庫的用戶密碼,即可訪問到數據庫,如果使用了ProxySQL,則要先使用賬號密碼訪問到ProxySQL的數據庫,然後再由ProxySQL進行用戶請求的轉發,那麼,ProxySQL中的用戶與數據庫層的用戶有什麼關聯呢?很奇怪,這部分ProxySQL居然沒在文檔裏面給出來。
只能自己測試了,經過個人測試,發現:當中間件用戶與數據庫用戶以及密碼一致時,才能正常訪問數據庫。測試結果如下:
MySQL數據庫用戶(mysql.user表) | ProxySQL用戶(main.mysql_users表) | 使用ProxySQL的6033端口訪問數據庫 |
usera | usera | 正常訪問 |
userb | 無法登入proxysql | |
userc | 可以登入proxysql,但是無法讀寫 |
這裏是我的測試過程:
在MySQL數據庫上創建用戶:usera和userb
create user `usera`@`%` identified by '123456'; grant all privileges on *.* to `usera`@`%`; create user `userb`@`%` identified by '123456'; grant all privileges on *.* to `userb`@`%`; flush privileges;
在ProxySQL上創建用戶:usera和userc
insert into mysql_users(username,password,default_hostgroup) values('usera','123456',1); insert into mysql_users(username,password,default_hostgroup) values('userc','123456',1); load mysql users to runtime; save mysql users to disk;
登入測試(分爲2步:先登入,再查詢):
(1)usera用戶登入無問題,查詢無問題
[root@proxysql ~]# mysql -uusera -p123456 -P6033 -h192.168.10.10 mysql> select count(*) from lijiamandb.test03; +----------+ | count(*) | +----------+ | 1 | +----------+ 1 row in set (0.01 sec)
(2)userb無法登入,提示用戶名密碼錯誤
[root@proxysql ~]# mysql -uuserb -p123456 -P6033 -h192.168.10.10 mysql: [Warning] Using a password on the command line interface can be insecure. ERROR 1045 (28000): ProxySQL Error: Access denied for user 'userb'@'192.168.10.10' (using password: YES)
(3)userc可以正常登入,但是查詢的時候提示密碼不對
[root@proxysql ~]# mysql -uuserc -p123456 -P6033 -h192.168.10.10 mysql> mysql> select count(*) from lijiamandb.test03; ERROR 1045 (28000): Access denied for user 'userc'@'192.168.10.10' (using password: YES)
用戶認證小結:只有ProxySQL中的用戶名密碼與MySQL中的用戶名密碼相同時,才能正常訪問底層MySQL數據庫。因此,如果要使用ProxySQL訪問數據庫,需要在MySQL和ProxySQL中都要創建相同的賬號,並且密碼也要保持一致。
(5.6.2)ProxySQL用戶創建
ProxySQL的用戶保存在mysql_users表中,用戶創建直接執行insert插入即可。如創建一個用戶名爲“lijiaman”,密碼爲“123456”,默認用戶組爲1的用戶:
insert into mysql_users(username,password,default_hostgroup) values('lijiaman','123456',1);
load mysql users to runtime; save mysql users to disk;
mysql_users表最重要的字段爲:
- username
- password
- default_hostgroup:默認組。如果此用戶發送的查詢沒有匹配的規則,則它生成的流量將發送到指定的主機組
- transaction_persistent:如果爲與MySQL客戶端連接到ProxySQL的用戶設置了此選項,則在主機組內啓動的事務將保留在該主機組內,而不管其他任何規則。例如,一個事務中存在讀與寫操作,如果不指定該選項,可能會把讀與寫請求分發到不同的主機上,造成數據不一致。
(六)故障轉移(failover)測試
在上一節,我們已經配置了:
- 組:寫組、備用寫組、讀組、離線組。並且讀組最多隻有1臺server;
- MySQL Server:配置了3臺Server,並且將其放入到了寫組中;
- 監控信息:
- 用戶信息
此時,ProxySQL已經具備故障轉移的能力了,我們進行測試一下。
STEP1:現在的配置如下,192.168.10.13主機是寫節點,其它2個節點是備用寫節點:
-- mysql_serve在memory層r的配置信息 mysql> select * from mysql_servers; +--------------+---------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+ | hostgroup_id | hostname | port | gtid_port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment | +--------------+---------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+ | 1 | 192.168.10.11 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | | | 1 | 192.168.10.12 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | | | 1 | 192.168.10.13 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | | +--------------+---------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+ 3 rows in set (0.00 sec) --加載到RUNTIME後,由於組定義中最多隻有1個寫節點,其餘的主節點移動到備用寫組裏面 mysql> select * from runtime_mysql_servers; +--------------+---------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+ | hostgroup_id | hostname | port | gtid_port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment | +--------------+---------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+ | 2 | 192.168.10.11 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | | | 1 | 192.168.10.13 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | | | 2 | 192.168.10.12 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | | +--------------+---------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+ 3 rows in set (0.01 sec)
使用ProxySQL來訪問MyQSL集羣,發現可以支持讀寫
-- 使用ProxySQL 6033端口訪問MySQL數據庫 [root@proxysql ~]# mysql -uusera -p123456 -P6033 -h192.168.10.10 mysql> use testdb -- 通過主機名,額可以看到,我們訪問到的是寫節點 mysql> select @@hostname; +------------+ | @@hostname | +------------+ | mgr-node3 | +------------+ 1 row in set (0.00 sec) -- 可以這次插入、查詢數據 mysql> insert into test01 values(1,'a'); Query OK, 1 row affected (0.00 sec) mysql> select * from test01; +----+------+ | id | name | +----+------+ | 1 | a | +----+------+ 1 row in set (0.00 sec)
STEP2:關閉寫節點
# 直接關閉主機 [root@mgr-node3 ~]# reboot
Connection closed by foreign host. Disconnected from remote host(mgr-node3) at 19:00:31. Type `help' to learn how to use Xshell prompt.
[c:\~]$ -- 需要注意的是,以前連接在主節點上的會話會斷開,不會轉移到新的主節點,很正常,Oracle也不會 mysql> select * from test01; ERROR 2013 (HY000): Lost connection to MySQL server during query
STEP3:查看是否會有備用寫節點轉爲寫節點,可以看到192.168.10.12服務器已經轉爲寫節點,而已經關閉的192.168.10.13服務器已經進入離線組。
mysql> select * from runtime_mysql_servers; +--------------+---------------+------+-----------+---------+--------+-------------+-----------------+---------------------+---------+----------------+---------+ | hostgroup_id | hostname | port | gtid_port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment | +--------------+---------------+------+-----------+---------+--------+-------------+-----------------+---------------------+---------+----------------+---------+ | 2 | 192.168.10.11 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | | | 1 | 192.168.10.12 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | | | 4 | 192.168.10.13 | 3306 | 0 | SHUNNED | 1 | 0 | 1000 | 0 | 0 | 0 | | +--------------+---------------+------+-----------+---------+--------+-------------+-----------------+---------------------+---------+----------------+---------+ 3 rows in set (0.00 sec)
STEP4:再次使用ProxySQL來訪問MyQSL集羣,發現可以支持讀寫,業務不會因主節點的改變而受影響。
-- 使用ProxySQL 6033端口訪問MySQL數據庫 [root@proxysql ~]# mysql -uusera -p123456 -P6033 -h192.168.10.10 mysql> use testdb -- 通過主機名,額可以看到,我們訪問到的是新的寫節點 mysql> select @@hostname; +------------+ | @@hostname | +------------+ | mgr-node2 | +------------+ 1 row in set (0.00 sec) -- 可以這次插入、查詢數據 mysql> insert into test01 values(2,'b'); Query OK, 1 row affected (0.00 sec)
通過上面的測試,可以看到,MGR結合ProxySQL已經可以實現業務的自動故障轉移。
接下來,我們開始研究ProxySQL的讀寫分離功能。