ProxySQL(2):初試讀寫分離
文章轉載自:https://www.cnblogs.com/f-ck-need-u/p/9278839.html
實現一個簡單的讀寫分離
這裡通過一個簡單的示例實現ProxySQL的讀寫分離功能,算是ProxySQL的快速入門。即使是快速入門,需要配置的內容也很多,包括:後端MySQL配置、監控配置、傳送SQL語句的使用者、SQL語句的路由規則。所以,想要實現一個ProxySQL+MySQL,即使只實現最基本的功能,步驟也是挺多的,不過配置的邏輯都很簡單。
實驗環境:
為了演示完整的過程,這裡把後端MySQL主從複製的基本配置步驟也列出來了。如瞭解配置過程,可跳過主從配置的部分。
注意點:slave節點需要設定read_only=1。如果後端是PXC/MGR/MariaDB Galera,則無需手動設定,因為會自動設定。
配置後端的主從複製
提供3個MySQL節點的配置檔案。
# 以下是Master的配置檔案 [mysqld] datadir=/data socket=/data/mysql.sock server-id=110 log-bin=/data/master-bin sync-binlog=1 log-error=/data/error.log pid-file=/data/mysqld.pid # 以下是slave1的配置檔案 [mysqld] datadir=/data socket=/data/mysql.sock server-id=120 relay_log=/data/relay-log log-error=/data/error.log pid-file=/data/mysqld.pid read_only=1 # 以下是slave2的配置檔案 [mysqld] datadir=/data socket=/data/mysql.sock server-id=130 relay_log=/data/relay-log log-error=/data/error.log pid-file=/data/mysqld.pid read_only=1
# 為3個MySQL節點提供資料目錄/data mkdir /data chown -R mysql.mysql /data # 初始化三個MySQL節點 mysqld --initialize-insecure --user=mysql --datadir=/data # 啟動3個MySQL節點的mysqld服務 systemctl start mysqld # 連上master,修改root密碼,建立用於複製的使用者repl # 以下在master上執行 mysql> alter user root@localhost identified by 'P@ssword1!'; mysql> create user repl@'192.168.100.%' identified by 'P@ssword1!'; mysql> grant replication slave on *.* to repl@'192.168.100.%'; mysql> flush privileges;; mysql> show master status; # 連上兩個slave,開啟複製執行緒 # 以下在兩個slave節點上都執行 change master to master_host='192.168.100.22', master_user='repl', master_password='P@ssword1!', master_port=3306, master_log_file='master-bin.000001', master_log_pos=4; start slave; show slave status\G
如此配置之後,3個MySQL節點就保持了同步。
向ProxySQL中新增MySQL節點
首先啟動ProxySQL。
service proxysql start
啟動後會監聽兩個埠,預設為6032和6033。6032埠是ProxySQL的管理埠,6033是ProxySQL對外提供服務的埠。
# netstat -tnlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:6032 0.0.0.0:* LISTEN 1231/proxysql
tcp 0 0 0.0.0.0:6033 0.0.0.0:* LISTEN 1231/proxysql
然後使用mysql客戶端(需要單獨安裝)連線到ProxySQL的管理介面(admin interface),該介面的預設管理員使用者和密碼都是admin。
# mysql -uadmin -padmin -P6032 -h127.0.0.1 --prompt 'admin> '
# --prompt為MySQL命令列提示符
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 |
+--------------------------------------------+
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 |
+------------------------------------+
runtime_開頭的是執行時的配置,這些是不能修改的。要修改ProxySQL的配置,需要修改了非runtime_表,修改後必須執行LOAD ... TO RUNTIME才能載入到RUNTIME生效,執行save ... to disk才能將配置持久化儲存到磁碟。具體操作見後文。
insert into mysql_servers(hostgroup_id,hostname,port) values(10,'192.168.100.22',3306);
insert into mysql_servers(hostgroup_id,hostname,port) values(10,'192.168.100.23',3306);
insert into mysql_servers(hostgroup_id,hostname,port) values(10,'192.168.100.24',3306);
注:上面語句中沒有先切換到main庫也執行成功了,因為ProxySQL內部使用的SQLite3資料庫引擎,和MySQL的解析方式是不一樣的。即使執行了USE main語句也是無任何效果的,但不會報錯。
檢視這3個節點是否插入成功,以及它們的狀態。請認真讀一讀每個欄位的名稱,混個眼熟。
admin> select * from mysql_servers\G
*************************** 1. row ***************************
hostgroup_id: 10
hostname: 192.168.100.22
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: 10
hostname: 192.168.100.23
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: 10
hostname: 192.168.100.24
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。
load mysql servers to runtime;
save mysql servers to disk;
監控後端MySQL節點
新增節點之後,還需要監控後端節點。對於後端是主從複製的環境來說,這是必須的,因為ProxySQL需要通過每個節點的read_only值來自動調整它們是屬於讀組還是寫組。
首先在後端master節點上建立一個用於監控的使用者名稱(只需在master上建立即可,因為會複製到slave上),這個使用者名稱只需具有USAGE許可權即可。如果還需要監控複製結構中slave是否嚴重延遲於master(先混個眼熟:這個俗語叫做"拖後腿",術語叫做"replication lag"),則還需具備replication client許可權。這裡直接賦予這個許可權。
# 在master上執行:
mysql> create user monitor@'192.168.100.%' identified by 'P@ssword1!';
mysql> grant replication client on *.* to monitor@'192.168.100.%';
然後回到ProxySQL上配置監控。
set mysql-monitor_username='monitor';
set mysql-monitor_password='P@ssword1!';
以上設定實際上是在修改global_variables表,它和下面兩個語句是等價的:
UPDATE global_variables SET variable_value='monitor'
WHERE variable_name='mysql-monitor_username';
UPDATE global_variables SET variable_value='P@ssword1!'
WHERE variable_name='mysql-monitor_password';
修改後,載入到RUNTIME,並儲存到disk。
load mysql variables to runtime;
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.100.22 | 3306 | 1530968712977867 | 4174 | NULL |
| 192.168.100.23 | 3306 | 1530968712988986 | 4908 | NULL |
| 192.168.100.24 | 3306 | 1530968713000074 | 3044 | NULL |
| 192.168.100.22 | 3306 | 1530968772978982 | 3407 | NULL |
| 192.168.100.23 | 3306 | 1530968772989627 | 3404 | NULL |
| 192.168.100.24 | 3306 | 1530968773000778 | 3444 | NULL |
+----------------+------+------------------+-------------------------+---------------+
以下是對心跳資訊的監控(對ping指標的監控):
admin> select * from mysql_server_ping_log;
+----------------+------+------------------+----------------------+-------------+
| hostname | port | time_start_us | ping_success_time_us | ping_error |
+----------------+------+------------------+----------------------+-------------+
| 192.168.100.22 | 3306 | 1530968712666540 | 452 | NULL |
| 192.168.100.23 | 3306 | 1530968712668779 | 458 | NULL |
| 192.168.100.24 | 3306 | 1530968712671541 | 324 | NULL |
| 192.168.100.22 | 3306 | 1530968722667071 | 1190 | NULL |
| 192.168.100.23 | 3306 | 1530968722669574 | 1162 | NULL |
| 192.168.100.24 | 3306 | 1530968722673162 | 1380 | NULL |
| 192.168.100.22 | 3306 | 1530968732668840 | 1065 | NULL |
| 192.168.100.23 | 3306 | 1530968732670709 | 1054 | NULL |
| 192.168.100.24 | 3306 | 1530968732672703 | 1040 | NULL |
+----------------+------+------------------+----------------------+-------------+
但是,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)
這是因為還沒有對ProxySQL中的節點分組:writer_hostgroup、reader_hostgroup。設定分組資訊,需要修改的是main庫中的mysql_replication_hostgroups表,該表只有3個欄位:第一個欄位名為writer_hostgroup,第二個欄位為reader_hostgroup,第三個欄位為註釋欄位,可隨意寫。
例如,指定寫組的id為10,讀組的id為20。
insert into mysql_replication_hostgroups values(10,20);
在該配置載入到RUNTIME生效之前,先檢視下各mysql server所在的組。
admin> select hostgroup_id,hostname,port,status,weight from mysql_servers;
+--------------+----------------+------+--------+--------+
| hostgroup_id | hostname | port | status | weight |
+--------------+----------------+------+--------+--------+
| 10 | 192.168.100.22 | 3306 | ONLINE | 1 |
| 10 | 192.168.100.23 | 3306 | ONLINE | 1 |
| 10 | 192.168.100.24 | 3306 | ONLINE | 1 |
+--------------+----------------+------+--------+--------+
3個節點都在hostgroup_id=10的組中。
現在,將剛才mysql_replication_hostgroups表的修改載入到RUNTIME生效。
load mysql servers to runtime;
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.100.22 | 3306 | ONLINE | 1 |
| 20 | 192.168.100.23 | 3306 | ONLINE | 1 |
| 20 | 192.168.100.24 | 3306 | ONLINE | 1 |
+--------------+----------------+------+--------+--------+
admin> select * from mysql_server_read_only_log;
+----------------+------+------------------+-----------------+-----------+--------+
| hostname | port | time_start_us | success_time_us | read_only | error |
+----------------+------+------------------+-----------------+-----------+--------+
| 192.168.100.22 | 3306 | 1530970372197917 | 8487 | 0 | NULL |
| 192.168.100.23 | 3306 | 1530970372198992 | 7907 | 1 | NULL |
| 192.168.100.24 | 3306 | 1530970372199835 | 8064 | 1 | NULL |
| 192.168.100.22 | 3306 | 1530970373698824 | 10078 | 0 | NULL |
| 192.168.100.23 | 3306 | 1530970373699825 | 9845 | 1 | NULL |
| 192.168.100.24 | 3306 | 1530970373700786 | 10745 | 1 | NULL |
+----------------+------+------------------+-----------------+-----------+--------+
配置mysql_users
上面的所有配置都是關於後端MySQL節點的,現在可以配置關於SQL語句的,包括:傳送SQL語句的使用者、SQL語句的路由規則、SQL查詢的快取、SQL語句的重寫等等。
本小節是SQL請求所使用的使用者配置,例如root使用者。這要求我們需要先在後端MySQL節點新增好相關使用者。這裡以root和sqlsender兩個使用者名稱為例。
首先,在master節點上執行:(只需master執行即可,會複製給兩個slave)
grant all on *.* to root@'192.168.100.%' identified by 'P@ssword1!';
grant all on *.* to sqlsender@'192.168.100.%' identified by 'P@ssword1!';
然後回到ProxySQL,配置mysql_users表,將剛才的兩個使用者新增到該表中。
insert into mysql_users(username,password,default_hostgroup) values('root','P@ssword1!',10);
insert into mysql_users(username,password,default_hostgroup) values('sqlsender','P@ssword1!',10);
load mysql users to runtime;
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: P@ssword1!
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: P@ssword1!
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
雖然本文不詳細介紹mysql_users表,但上面標註了"注意本行"的兩個欄位必須要引起注意。
只有active=1的使用者才是有效的使用者。
至於transaction_persistent欄位,當它的值為1時,表示事務持久化:當某連線使用該使用者開啟了一個事務後,那麼在事務提交/回滾之前,所有的語句都路由到同一個組中,避免語句分散到不同組(更進一步的,它會自動禁用multiplexing,讓同一個事務的語句從同一個連線路由出去,保證路由到同一個組的同一個節點)。在以前的版本中,預設值為0,不知道從哪個版本開始,它的預設值為1。我們期望的值為1,所以在繼續下面的步驟之前,先檢視下這個值,如果為0,則執行下面的語句修改為1。
update mysql_users set transaction_persistent=1 where username='root';
update mysql_users set transaction_persistent=1 where username='sqlsender';
load mysql users to runtime;
save mysql users to disk;
然後,另開一個終端,分別使用root使用者和sqlsender使用者測試下它們是否能路由到預設的hostgroup_id=10(它是一個寫組)讀、寫資料。
[root@s1 ~]# mysql -uroot -pP@ssword1! -P6033 -h127.0.0.1 -e "select @@server_id"
+-------------+
| @@server_id |
+-------------+
| 110 |
+-------------+
[root@s1 ~]# mysql -uroot -pP@ssword1! -P6033 -h127.0.0.1 -e "create database proxy_test"
[root@s1 ~]# mysql -uroot -pP@ssword1! -P6033 -h127.0.0.1 -e "show databases;"
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| proxy_test |
| sys |
+--------------------+
[root@s1 ~]# mysql -usqlsender -pP@ssword1! -P6033 -h127.0.0.1 -e '\
use proxy_test;\
create table t(id int);'
[root@s1 ~]# mysql -usqlsender -pP@ssword1! -P6033 -h127.0.0.1 -e 'show tables from proxy_test;'
+-------------------------+
| Tables_in_proxy_test |
+-------------------------+
| t |
+-------------------------+
讀寫分離:配置路由規則
ProxySQL的路由規則非常靈活,可以基於使用者、基於schema以及基於每個語句實現路由規則的定製。
本文作為入門文章,實現一個最簡單的語句級路由規則,從而實現讀寫分離。必須注意,這只是實驗,實際的路由規則絕不應該僅根據所謂的讀、寫操作進行分離,而是從各項指標中找出壓力大、執行頻繁的語句單獨寫規則、做快取等等。
和查詢規則有關的表有兩個:mysql_query_rules和mysql_query_rules_fast_routing,後者是前者的擴充套件表,1.4.7之後才支援該快速路由表。本文只介紹第一個表。
插入兩個規則,目的是將select語句分離到hostgroup_id=20的讀組,但由於select語句中有一個特殊語句SELECT...FOR UPDATE它會申請寫鎖,所以應該路由到hostgroup_id=10的寫組。
insert into mysql_query_rules(rule_id,active,match_digest,destination_hostgroup,apply)
VALUES (1,1,'^SELECT.*FOR UPDATE$',10,1),
(2,1,'^SELECT',20,1);
load mysql query rules to runtime;
save mysql query rules to disk;
select ... for update規則的rule_id必須要小於普通的select規則的rule_id,因為ProxySQL是根據rule_id的順序進行規則匹配的。
再來測試下,讀操作是否路由給了hostgroup_id=20的讀組。
[root@s1 ~]# mysql -uroot -pP@ssword1! -P6033 -h127.0.0.1 -e 'select @@server_id'
mysql: [Warning] Using a password on the command line interface can be insecure.
+-------------+
| @@server_id |
+-------------+
| 120 |
+-------------+
[root@s1 ~]# mysql -uroot -pP@ssword1! -P6033 -h127.0.0.1 -e 'select @@server_id'
mysql: [Warning] Using a password on the command line interface can be insecure.
+-------------+
| @@server_id |
+-------------+
| 130 |
+-------------+
讀操作已經路由給讀組,再看看寫操作。這裡以事務持久化進行測試。
[root@s1 ~]# mysql -uroot -pP@ssword1! -P6033 -h127.0.0.1 -e '\
start transaction;\
select @@server_id;\
commit;\
select @@server_id;'
+-------------+
| @@server_id |
+-------------+
| 110 |
+-------------+
+-------------+
| @@server_id |
+-------------+
| 120 |
+-------------+
顯然,一切都按照預期進行。
最後,如果想檢視路由的資訊,可查詢stats庫中的stats_mysql_query_digest表。以下是該表的一個輸出格式示例(和本文無關)。
admin> SELECT hostgroup hg,
sum_time,
count_star,
digest_text
FROM stats_mysql_query_digest
ORDER BY sum_time DESC;
+----+----------+------------+-------------------------------------------------------------+
| hg | sum_time | count_star | digest_text |
+----+----------+------------+-------------------------------------------------------------+
| 2 | 14520738 | 50041 | SELECT c FROM sbtest1 WHERE id=? |
| 1 | 3142041 | 5001 | COMMIT |
| 1 | 2270931 | 5001 | SELECT c FROM sbtest1 WHERE id BETWEEN ? AND ?+? ORDER BY c |
| 1 | 2021320 | 5003 | SELECT c FROM sbtest1 WHERE id BETWEEN ? AND ?+? |
| 1 | 1768748 | 5001 | UPDATE sbtest1 SET k=k+? WHERE id=? |
| 1 | 1697175 | 5003 | SELECT SUM(K) FROM sbtest1 WHERE id BETWEEN ? AND ?+? |
| 1 | 1346791 | 5001 | UPDATE sbtest1 SET c=? WHERE id=? |
| 1 | 1263259 | 5001 | DELETE FROM sbtest1 WHERE id=? |
| 1 | 1191760 | 5001 | INSERT INTO sbtest1 (id, k, c, pad) VALUES (?, ?, ?, ?) |
| 1 | 875343 | 5005 | BEGIN |
+----+----------+------------+-------------------------------------------------------------+
至此,MySQL的讀寫分離嚐鮮結束。