安裝並登陸管理界面
1 wgethttps://github.com/sysown/proxysql/releases/download/v1.3.6/proxysql-1.3.6-1-centos67.x86_64.rpm
2 yum install perl-DBD-MySQL
3 rpm -ivh proxysql-1.3.6-1-centos67.x86_64.rpm
4 service proxysql start
5 proxysql --version
ProxySQL version 1.3.6-0-g434b376, codename Truls
6 登陸管理界面(配置信息從啓動進程的配置文件查看)
mysql -u admin -padmin -h127.0.0.1 -P6032--prompt='proxysql>'
mysql -u admin -padmin -S /tmp/proxysql_admin.sock--prompt='proxysql>'
配置讀寫分離和路由規則
環境(三個後臺服務器,一主兩從的架構)
Master 10.9.160.248:3306
Slave1 10.9.117.107:3306 設置read_only
Slave2 10.9.150.86:3306 設置read_only
在db的主節點上創建proxy的監控賬戶和業務賬戶
GRANT ALLPRIVILEGES ON *.* TO 'monitor'@'%' IDENTIFIED BY ‘monitor’;
GRANT ALLPRIVILEGES ON *.* TO 'msandbox'@'%' IDENTIFIED BY ‘msandbox’;
在proxy管理命令行添加後端服務器列表
proxysql>INSERTINTO mysql_servers(hostgroup_id,hostname,port) VALUES (1,'10.9.160.248',3306);
proxysql>INSERTINTO mysql_servers(hostgroup_id,hostname,port) VALUES (1,'10.9.150.86',3306);
proxysql>INSERTINTO mysql_servers(hostgroup_id,hostname,port) VALUES (1,'10.9.117.107',3306);
注意到這時的hostgroup都爲1,這沒什麼問題
即使生效
LOAD MYSQL SERVERS TO RUNTIME;
配置和註冊監控賬戶
UPDATE global_variablesSET variable_value='monitor'WHERE variable_name='mysql-monitor_username';
UPDATE global_variables SET variable_value='monitor'WHERE variable_name='mysql-monitor_password';
這裏填的賬戶和密碼均爲monitor的信息就是步驟1中在後端服務器創建的監控賬戶
即使生效,並持久化到磁盤
LOAD MYSQL VARIABLES TO RUNTIME;
SAVE MYSQL VARIABLES TO DISK;
查看監控是否生效
proxysql>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 |
+--------------+------+------------------+-------------------------+---------------+
| 10.9.160.248 | 3306 | 1497001266520778 | 688 | NULL |
| 10.9.150.86 | 3306 | 1497001266510692 | 541 | NULL |
| 10.9.117.107 | 3306 | 1497001266500632 | 718 | NULL |
| 10.9.160.248 | 3306 | 1497001206520719 | 652 | NULL |
| 10.9.150.86 | 3306 | 1497001206510580 | 618 | NULL |
| 10.9.117.107 | 3306 | 1497001206500519 | 744 | NULL |
| 10.9.160.248 | 3306 | 1497001146520616 | 699 | NULL |
| 10.9.150.86 | 3306 | 1497001146510561 | 557 | NULL |
| 10.9.117.107 | 3306 | 1497001146500487 | 759 | NULL |
| 10.9.160.248 | 3306 | 1497001086520458 | 595 | NULL |
+--------------+------+------------------+-------------------------+---------------+
10 rows in set (0.00 sec)
proxysql>SELECT * FROM monitor.mysql_server_ping_log ORDER BY time_start_us DESC LIMIT 10;
+--------------+------+------------------+----------------------+------------+
| hostname | port | time_start_us | ping_success_time_us | ping_error |
+--------------+------+------------------+----------------------+------------+
| 10.9.160.248 | 3306 | 1497001317166006 | 181 | NULL |
| 10.9.150.86 | 3306 | 1497001317164253 | 153 | NULL |
| 10.9.117.107 | 3306 | 1497001317162511 | 262 | NULL |
| 10.9.160.248 | 3306 | 1497001307165766 | 218 | NULL |
| 10.9.150.86 | 3306 | 1497001307163997 | 137 | NULL |
| 10.9.117.107 | 3306 | 1497001307162247 | 289 | NULL |
| 10.9.160.248 | 3306 | 1497001297165761 | 213 | NULL |
| 10.9.150.86 | 3306 | 1497001297163999 | 175 | NULL |
| 10.9.117.107 | 3306 | 1497001297162241 | 280 | NULL |
| 10.9.160.248 | 3306 | 1497001287165645 | 183 | NULL |
+--------------+------+------------------+----------------------+------------+
10 rows in set (0.00 sec)
配置主從信息
表示該複製組,1爲寫入組的編號,2爲讀取組的編號
INSERT INTO mysql_replication_hostgroups VALUES (1,2,’ one-master-2-slave’);
即時生效
LOAD MYSQL SERVERS TO RUNTIME;
當生效後,proxysql會去檢測後端的數據節點
假設該節點read_only爲1,則會將該server移入group 2;
假設該節點read_only爲0,則會將該server移入group 1;
這時再查mysql_servers表,就會發現節點的hostgroup_id根據read_only的情況自動變化了
proxysql>select hostgroup_id,hostname from mysql_servers;
+--------------+--------------+
| hostgroup_id | hostname |
+--------------+--------------+
| 1 | 10.9.160.248 |
| 2 | 10.9.117.107 |
| 2 | 10.9.150.86 |
+--------------+--------------+
3 rows in set (0.00 sec)
確認無誤後 持久化到磁盤
SAVE MYSQL SERVERS TO DISK;
配置和註冊業務賬戶
插入步驟1中生成的業務賬戶
INSERT INTO mysql_users(username,password,default_hostgroup) VALUES ('msandbox','msandbox',1);
proxysql>select * from mysql_users\G
*************************** 1. row ***************************
username: msandbox
password: msandbox
active: 1
use_ssl: 0
default_hostgroup: 1
default_schema: NULL
schema_locked: 0
transaction_persistent: 0
fast_forward: 0
backend: 1
frontend: 1
max_connections: 10000
1 row in set (0.00 sec)
proxysql>update mysql_users set transaction_persistent=1 where username='msandbox';
Query OK, 1 row affected (0.00 sec)
即時生效,持久化
LOAD MYSQL USERS TO RUNTIME;
SAVE MYSQL USERS TO DISK;
說明幾個比較重要的列名含義
Active:表示該用戶是否生效
default_hostgroup
:表示如果沒有配置其他路由規則,那麼默認走的就是該值指定的hostgroup
max_connections:該用戶能創建的最大連接數
transaction_persistent
:如果爲1,則一個完整的SQL只可能路由到一個節點;這點非常重要,主要解決這種情況:一個事務有混合的讀操作和寫操作組成,事務未提交前,如果事務中的讀操作和寫操作路由到不同節點,那麼讀取到的結果必然是髒數據。所以一般情況下,該值應該設置爲1,尤其是業務中使用到事務機制的情況(默認爲0)
PS:該值設爲1後,sysbench不好測試讀寫分離的情況,因爲sysbench中的oltp腳本中的SQL都是包含在一個個事務中的,所以測試階段可以將其值改爲0
frontend
和backend列目前的版本必須爲1,因爲目前的proxy和底層的節點共用一套賬戶認證體系,後續可能會單獨分離出來,所以增加了這兩個字段。
這時驗證下賬戶登陸,確定登陸上去的就是預想中的主庫
[root@10-9-192-94 ~]# mysql -u msandbox -pmsandbox -h 127.0.0.1 -P6033 -e "show slave hosts"
+-----------+------+------+-----------+--------------------------------------+
| Server_id | Host | Port | Master_id | Slave_UUID |
+-----------+------+------+-----------+--------------------------------------+
| 168392043 | | 3306 | 168403192 | baaf5e8b-4cbe-11e7-8ca2-e8611f12fd90 |
| 168400470 | | 3306 | 168403192 | aed780a0-4cbe-11e7-8ca2-6c92bf15bed0 |
配置讀寫分離策略
1 除了select ..for update以爲的select都路由到從庫
2 其他所有操作都路由到主庫
proxysql>showcreate table mysql_query_rules\G
***************************1. row ***************************
table: mysql_query_rules
Create Table:CREATE TABLE mysql_query_rules (
rule_id INTEGER PRIMARY KEY AUTOINCREMENTNOT NULL,
active INT CHECK (active IN (0,1)) NOT NULLDEFAULT 0,
username VARCHAR,
schemaname VARCHAR,
flagIN INT NOT NULL DEFAULT 0,
client_addr VARCHAR,
proxy_addr VARCHAR,
proxy_port INT,
digest VARCHAR,
match_digest VARCHAR,
match_pattern VARCHAR,
negate_match_pattern INT CHECK(negate_match_pattern IN (0,1)) NOT NULL DEFAULT 0,
flagOUT INT,
replace_pattern VARCHAR,
destination_hostgroup INT DEFAULT NULL,
cache_ttl INT CHECK(cache_ttl > 0),
reconnect INT CHECK (reconnect IN (0,1))DEFAULT NULL,
timeout INT UNSIGNED,
retries INT CHECK (retries>=0 ANDretries <=1000),
delay INT UNSIGNED,
mirror_flagOUT INT UNSIGNED,
mirror_hostgroup INT UNSIGNED,
error_msg VARCHAR,
log INT CHECK (log IN (0,1)),
apply INT CHECK(apply IN (0,1)) NOT NULLDEFAULT 0,
comment VARCHAR)
1 row in set(0.00 sec)
Active:是否啓用這個路由規則
Username:如果不爲空,該規則匹配該用戶
schemaname
:如果不爲空,該規則只匹配該庫名稱
destination_hostgroup:該路由規則發往哪個組
apply:爲1表示該正則匹配後,將不再接受其他匹配,直接轉發
match_digest:描述規則的正則表達式,其中1.4版本以後,正則表達式支持PCRE和RE2兩種,默認使用pcre,即perl語言支持的正則表達式
cache_ttl:用戶查詢緩存的時間閾值,單位爲毫秒
更多字段說明,參考官方wiki文檔
https://github.com/sysown/proxysql/wiki/MySQL-Query-Rules
通過查看該表的表結構,不難看出所有的路由規則是用戶完全自定義的,也就是說不僅僅可以實現讀寫分離,任何SQL類型都可以自定義發送到指定的數據節點執行,比如在實現大部分讀寫分離的同時,一些對實時性要求很高的SQL,可以定義成發送到master節點運行;
Insert into mysql_query_rules(rule_id,active,username,match_digest, destination_hostgroup,apply) values(10,1,’ msandbox’,’ ^SELECT.*FORUPDATE$’,1,1);
Insert into mysql_query_rules(rule_id,active,username,match_digest, destination_hostgroup,apply) values(11,1,’ msandbox’,’ ^SELECT’,2,1);
剛纔通過mysql_users表的配置和解釋可知,路由規則以外的SQL都發往mysql_users表中的default_hostgroup字段,即數值1,該組爲master節點;
即時生效並持久化
load mysql query rules to runtime;
save mysql query rules todisk;
讀寫分離測試
清空歷史查詢記錄
proxysql>select* from stats.stats_mysql_query_digest_reset limit 1;
通過查詢stats_mysql_query_digest_reset的效果是這樣的:返回stats_mysql_query_digest表的查詢結果,並且執行truncate table stats_mysql_query_digest清空stats_mysql_query_digest表
proxysql>select* from stats.stats_mysql_query_digest;
Empty set (0.00sec)
修改transaction_persistent爲0,使得sysbench適用該場景
update mysql_users set transaction_persistent=0 where username='msandbox';
LOAD MYSQL USERS TO RUNTIME;
sysbench壓測proxy
sysbench--report-interval=1 --num-threads=4 --max-time=2000 --test=sysbench/tests/db/oltp.lua--mysql-user='msandbox' --mysql-password='msandbox' --oltp-table-size=10000--mysql-host=127.0.0.1 --mysql-port=6033 --mysql-db=proxytest --max-requests=100000000 prepare
sysbench--report-interval=1 --num-threads=4 --max-time=2000 --test=sysbench/tests/db/oltp.lua--mysql-user='msandbox' --mysql-password='msandbox' --oltp-table-size=10000--mysql-host=127.0.0.1 --mysql-port=6033 --mysql-db=proxytest --max-requests=100000000 run
壓測過程中觀察讀寫分離情況
可以看到查詢都是發往group 2,寫操作都是發往group 1,即實現了讀寫分離
另外可以分別登陸三個數據節點show processlist確定proxysql真實實現了讀寫分離;
關於select ..for update和事務的路由測試,可以手工寫幾條特定的SQL測試,這裏忽略;
測試proxysql的查詢緩存
Proxysql的查詢緩存和mysql的查詢緩存有點類似,但不是一回事;proxysql的查詢緩存指的是:如果在指定時間大小範圍內發送的SQL一摸一樣,那麼直接返回結果集,而返回的結果集可能並不是準確的查詢結果,所以需要設置合適的時間範圍,既能提升性能,又得滿足業務需求,即查詢結果足夠的“新”。這個特性我想可以用於這個方便:針對一些查詢頻率很高但結果並不需要太精確的業務,可以單獨給這些SQL配置查詢緩存
proxysql>update mysql_query_rules setcache_ttl=2000 where rule_id=11\G
Query OK, 1 row affected (0.00 sec)
proxysql>LOAD MYSQL QUERY RULES TO RUNTIME;
Query OK, 0 rows affected (0.00 sec)
sysbench --report-interval=1--num-threads=4 --max-time=2000--test=sysbench/tests/db/oltp.lua --mysql-user='msandbox'--mysql-password='msandbox' --oltp-table-size=10000 --mysql-host=127.0.0.1--mysql-port=6033 --mysql-db=proxytest --max-requests=100000000 run
可以看到host部分爲-1,表示這些查詢是使用了proxy查詢緩存的,耗時爲0,這裏的0不可能是真的沒有耗時,只是不好統計,顯示上直接顯示成0
測試查詢重寫
範例 將帶distinct的SQL去掉排序
9.1 寫入下面這條改寫規則,注意規則中大小寫是敏感的
proxysql>select * frommysql_query_rules\G
*************************** 1. row***************************
rule_id: 31
active: 1
username: msandbox
schemaname: NULL
flagIN: 0
client_addr: NULL
proxy_addr: NULL
proxy_port: NULL
digest: NULL
match_digest: NULL
match_pattern: DISTINCT(.*)ORDER BY c
negate_match_pattern: 0
flagOUT: NULL
replace_pattern: DISTINCT\1
destination_hostgroup: NULL
cache_ttl: NULL
reconnect: NULL
timeout: NULL
retries: NULL
delay: NULL
mirror_flagOUT: NULL
mirror_hostgroup: NULL
error_msg: NULL
log: NULL
apply: 1
comment: NULL
1 row in set (0.00 sec)
9.2 執行下面這個SQL
可以看到解析計劃中沒有using filesort,SQL已經被改寫了
9.3 執行一把SQL內容,查看規則命中情況和執行列表確認
proxysql>select * fromstats_mysql_query_rules ;
+---------+------+
| rule_id | hits |
+---------+------+
| 31 | 1 |
+---------+------+
1 row in set (0.00 sec)
proxysql>select hostgroup,digest_textfrom stats.stats_mysql_query_digest;
+-----------+--------------------------------------------------------------------+
| hostgroup | digest_text |
+-----------+--------------------------------------------------------------------+
| 1 | SELECT DISTINCT c FROM sbtest1 WHERE id BETWEEN ? and ? order by c |
+-----------+--------------------------------------------------------------------+
1 row in set (0.00 sec)