본문 바로가기

Database/MySQL

ProxySQL 을 이용한 read / write split 및 failover 구성

ProxySQL 를 이용한 failover 예상 아키텍쳐

전체 아키텍쳐

HAProxy 를 이용한 read / write split 및 failover

proxysql 아키텍쳐

3개의 layer 는 모두 동일한 설정을 가진다. 상호 데이터를 이동할 수 있으며 쿼리 형태로 제공된다.

  • RUNTIME
    • 운영중에 사용되는 설정들 proxysql이 기동중일때 사용되는 layer
  • MEMORY
    • 기동시 disk 로 부터 초기화되는 layer 로 운영 중 변경 내용이 저장되는 layer 다
    • runtime 으로 옮겨야 운영에 적용되며 disk 에 저장해야 영구적용된다.
  • DISK
    • persistent 를 위해 저장용도로만 사용된다.
  • CONFIG
    • config 파일에 설정을 기록하여 proxysql 기동시에 적용할 수 있다.

proxysql threads

main thread

core 및 기타 modure 을 시작하는 역할

admin thread

  • admin interface
  • config 파일 및 데이터베이스 파일 초기 로딩
  • http server handle
  • cluster module
  • start admin listener

mysql workers

mysql client 로 부터의 traffic 에 대한 worker thread

mysql auxiliray threads

  • idle thread
    • client 가 매우 많지만 idle 상태인 경우가 많다면 해당 client 의 connection 을 관리하는 thread

http server

  • bug 로 firefox 에서만 접속 가능
  • status
  • statistics

cluster threads

  • decentralized proxy
    • 수백대의 proxysql 서버를 간단하게 재설정할 수 있다.
    • 기능보다는 관리적 측면
    • application 에 근접하게 설치할 것
  • 아직 미완성
  • 아래 4가지 component 에 대해서 monitoring, re-configuration 기능을 제공
    • mysql_query_rules
    • mysql_servers
    • mysql_users
    • proxysql_servers
  • 앞으로 지속적인 업데이트 예정

query cache purge thread

query cache garbage collector

clickhouse server thread

ClickHouse → Open source OLAP 데이터베이스

sqlite3 server thread

sqlite3 support

monitor threads

  • a main thread responsible to start a thread for each monitor category (5 in total)
  • a thread scheduling connection checks
  • a thread scheduling ping checks
  • a thread scheduling read-only checks
  • a thread scheduling replication lag checks
  • a thread scheduling group replication monitoring
  • a threads pool (initially twice the size of mysql-threads)
Admin> select * from global_variables where variable_name like '%monitor_threads%';
+-------------------------------------+----------------+
| variable_name                       | variable_value |
+-------------------------------------+----------------+
| mysql-monitor_threads_min           | 8              |
| mysql-monitor_threads_max           | 128            |
| mysql-monitor_threads_queue_maxsize | 128            |
+-------------------------------------+----------------+
3 rows in set (0.01 sec)

Admin>

ProxySQL 특징(주요기능)

Query Cache

querey cache 를 위해서는 DB 의 자체 cache 혹은 별도의 application 이 필요하지만, 변경없이 proxysql 을 통해 쿼리 캐싱을 할 수 있다.

다음은 sysbench 로 테스트 한 내용

$ sysbench --num-threads=16 --max-requests=0 --max-time=60 --test=oltp
--mysql-user=msandbox --mysql-password=msandbox --mysql-db=sbtest --mysql-host=127.0.0.1 --mysql-port=6033
--oltp-table-size=10000 --oltp-read-only=on --db-ps-mode=disable --oltp-skip-trx=on
--oltp-point-selects=100 --oltp-simple-ranges=1 --oltp-sum-ranges=1 --oltp-order-ranges=1
--oltp-distinct-ranges=1 run

# 결과
  read/write requests:                 380952 (6341.71 per sec.)


# 캐싱을 등록
Admin> INSERT INTO mysql_query_rules (rule_id,active,digest,cache_ttl,apply)
VALUES (5,1,'0xE8930CB2CC9E68D7',2000,1);
Query OK, 1 row affected (0.00 sec)


# 다시 실행 했을때의 결과
  read/write requests:                 1613248 (26873.58 per sec.)

https://proxysql.com/documentation/Query-Cache/

Query Logging

  • proxysql 을 통해 실행되는 쿼리를 로깅할 수 있다.
    • 사용자별
    • 쿼리 패턴별
    • start, end, duration time, rows_affected, rows_sent
    • json format

Mirroring

  • pattern 기반의 쿼리를 mirroring 해서 쿼리는 두번 실행한다.
    • hostgroup 단위로 두번 실행(original group, mirror group)
    • 같은 쿼리를 실행할 수 있다.
    • pattern 을 기반으로 쿼리를 rewrite 할 수 있다.
    • pattern 을 기반으로 쿼리를 discard 할 수 있다.

Scheduler

  • cron 과 유사한 형태
  • milisecond 단위
  • 관련 테이블
    • scheduler ( in MEMORY )
    • runtime_scheduler ( in RUNTIME )
  • 테이블 구조
Admin> SHOW CREATE TABLE scheduler\G
*************************** 1. row ***************************
       table: scheduler
Create Table: CREATE TABLE scheduler (
    id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
    active INT CHECK (active IN (0,1)) NOT NULL DEFAULT 1,
    interval_ms INTEGER CHECK (interval_ms>=100 AND interval_ms<=100000000) NOT NULL,
    filename VARCHAR NOT NULL,
    arg1 VARCHAR,
    arg2 VARCHAR,
    arg3 VARCHAR,
    arg4 VARCHAR,
    arg5 VARCHAR,
    comment VARCHAR NOT NULL DEFAULT '')
1 row in set (0.00 sec)

ProxySQL Cluster

상기 proxysql threads → cluster threads 설명과 동일

HTTP Web UI Statistic Graphs

상기 proxysql threads → http server 설명과 동일

REST API

restapi endpoint 기능을 제공한다.

-- 설정
Admin> select * from runtime_global_variables where variable_name like '%rest%';
+-----------------------+----------------+
| variable_name         | variable_value |
+-----------------------+----------------+
| admin-restapi_enabled | true           |
| admin-restapi_port    | 6070           |
+-----------------------+----------------+
2 rows in set (0.01 sec)

Admin>

python 기반의 스크립트가 있다고 가정

-- rest api 를 등록한다.
-- ./sciprts/export_user.py 가 python 스크립트
insert into restapi_routes (active, interval_ms, method, uri, script, comment)
 values (1,1000,'POST','export_users','./scripts/export_users.py','comm');

호출했을때의 결과

-- rest api 를 등록한다.
-- ./sciprts/export_user.py 가 python 스크립트
insert into restapi_routes (active, interval_ms, method, uri, script, comment)
 values (1,1000,'POST','export_users','./scripts/export_users.py','comm');

ProxySQL 를 이용한 Failover

Failover 과정

  • master 노드 장애 발생
    • 장애 발생시 수초~수분까지 시간이 소요될 수 있다.
  • 새로운 master 선정
    • mha 에 의해 master 가 lag 이 가장 적은 slave 를 master 로 선정한다.
  • 새로운 master 를 기준으로 데이터 sync
  • 구 master 노드는 복구 후 slave 로 편입
    • dba 에 의해 read_only 가 선행되고 새로운 master 를 통해 데이터를 sync 한다.
    • 단 proxysql 에서는 sync 가 되지 않아도 접속이 가능하면 read hostgroup 에 등록 되므로 일관성이 위배된다.
    • (slave 편입 이전에 mysql_servers 에서 해당 서버를 삭제하고 sync 이후에 편입해야 한다. )

ProxySQL 에서 제공하는 옵션

  • mysql_servers
    • proxysql 에 mysql 서버를 등록할 수 있다.
    • hostgroup 개념이 있으며 read/write 로 호스트그룹을 분류하여 등록
-- 모든 mysql 서버는 read / write hostgroup 에 등록된다.
INSERT INTO mysql_servers (hostname,hostgroup_id,port,weight,max_replication_lag) VALUES ('10.1.5.11',600,3306,1000,0);
INSERT INTO mysql_servers (hostname,hostgroup_id,port,weight,max_replication_lag) VALUES ('10.1.5.11',601,3306,1000,10);
INSERT INTO mysql_servers (hostname,hostgroup_id,port,weight,max_replication_lag) VALUES ('10.1.5.12',600,3306,1000,0);
INSERT INTO mysql_servers (hostname,hostgroup_id,port,weight,max_replication_lag) VALUES ('10.1.5.12',601,3306,1000,10);
  • mysql_replication_hostgroups
    • read hostgrroup, write hostgroup 을 지정
    • read / write 구분 방법을 지전한다. ( read_only, super read_only, innodb read_only )
-- 600 : write hostgroup
-- 601 : read hostgroup
INSERT INTO mysql_replication_hostgroups VALUES (600,601,'read_only','test comment');
  • mysql_users
    • 사용자 관리
-- user : test
-- pass : test
-- default_schema : test
-- default_hostgroup : 600
-- transaction_persistent : 1 (yes)
INSERT INTO mysql_users (username,password,active,default_hostgroup,default_schema,transaction_persistent) VALUES ('test','test',1,600,'test',1);
  • mysql_query_rules
    • 쿼리의 패턴을 이용해 read / write 그룹에 분배할 규칙을 정한다.
-- dml, ddl 등은 기본적으로 write hostgroup 으로 전송
-- "^SELECT.*FOR UPDATE" : select for update clause 는 write hostgroup 에 전달한다.
-- "^SELECT" : select caluse 는 read hostgroup 에 전달 한다.
insert into mysql_query_rules (username,destination_hostgroup,active,retries,match_digest) values('test',600,1,3,'^SELECT.*FOR UPDATE');
insert into mysql_query_rules (username,destination_hostgroup,active,retries,match_digest) values('test',601,1,3,'^SELECT');

Master 와 slave 정상노드 조건

조건

masterglobal variable read_only = off
slave

global variable read_ony = on

  • mysql_servers
    • max_replication_lag 값

slave 의 thread 상태 확인 안함

slave 의 정상노드 조건에서 thread 상태가 빠져 있는 부분에 대해 max_replication_lag 로 갈음 할 수 있다.

  • io thread
    • master 로그로 부터 relay 로그를 쌓는 thread
  • sql trehad
    • relay 로그에서 테이블로 반영하는 thread

두 thread 가 정상 동작하지 않으면 max_replication_lag 값이 증가 하므로 max_replication_lag 만으로도

정상상태를 판단 할 수 있다.

환경

Client

  • 호스트 정보
    • 10.1.5.13
  • 테스트 스크립트
    • 1초에 주기로 haproxy 를 통해 mysql server_id 를 조회하여 master 의 상태를 조회한다.
#!/bin/bash

while true
do
    mysql --port=6033 -e "start transaction;insert into t1(i2) values( 1 ); select now(6), max(i1) , @@server_id from t1;commit;"
    sleep 1
done

PerconaServer

mysql> show create table t1;
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                 |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| t1    | CREATE TABLE `t1` (
  `i1` int NOT NULL AUTO_INCREMENT,
  `i2` int DEFAULT NULL,
  PRIMARY KEY (`i1`)
) ENGINE=InnoDB AUTO_INCREMENT=5230 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

ProxySQL

  • 호스트 정보
    • 10.1.5.13
  • version
    • ProxySQL version 2.0.10-27-g5b31997, codename Truls
  • conf
    • 상기 "ProxySQL 에서 제공하는 옵션" 참고

수행 및 검증

MASTER DB 강제종료

실행

[root@tdb01 ~]# systemctl stop mysqld

확인

master db

[root@tdb01 ~]# ps -ef | grep mysqld
root     10489 10054  0 14:00 pts/0    00:00:00 grep --color=auto mysqld

prosysql log

# 서버 다운 감지
2020-06-01 13:59:36 MySQL_HostGroups_Manager.cpp:2887:replication_lag_action(): [WARNING] Shunning server 10.1.5.12:3306 from HG 601 with replication lag of 60 second

# 재연결 실패
2020-06-01 13:59:37 MySQL_Monitor.cpp:982:monitor_read_only_thread(): [ERROR] Timeout on read_only check for 10.1.5.11:3306 after 0ms. Unable to create a connection. If the server is overload, increase mysql-monitor_connect_timeout. Error: timeout on creating new connection: Can't connect to MySQL server on '10.1.5.11' (115).

# read_only 체크 실패
2020-06-01 13:59:37 MySQL_Monitor.cpp:1138:monitor_read_only_thread(): [ERROR] Server 10.1.5.11:3306 missed 3 read_only checks. Assuming read_only=1

# 재연결 실패
2020-06-01 13:59:38 MySQL_Monitor.cpp:982:monitor_read_only_thread(): [ERROR] Timeout on read_only check for 10.1.5.11:3306 after 0ms. Unable to create a connection. If the server is overload, increase mysql-monitor_connect_timeout. Error: timeout on creating new connection: Can't connect to MySQL server on '10.1.5.11' (115).

# slave 로 간주
2020-06-01 13:59:38 MySQL_Monitor.cpp:1138:monitor_read_only_thread(): [ERROR] Server 10.1.5.11:3306 missed 3 read_only checks. Assuming read_only=1

prosysql table

-- master 셧다운 전
-- write 그룹(600)에 11번호스트가 등록되어 있다.
Admin> select * from mysql_servers;
+--------------+-----------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
| hostgroup_id | hostname  | port | gtid_port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment |
+--------------+-----------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
| 601          | 10.1.5.12 | 3306 | 0         | ONLINE | 1000   | 0           | 1000            | 10                  | 0       | 0              |         |
| 600          | 10.1.5.11 | 3306 | 0         | ONLINE | 1000   | 0           | 1000            | 10                  | 0       | 0              |         |
| 601          | 10.1.5.11 | 3306 | 0         | ONLINE | 1000   | 0           | 1000            | 10                  | 0       | 0              |         |
+--------------+-----------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
3 rows in set (0.00 sec)

-- master 셧다운 후
-- write 그룹(600)에 11번호스트가 제거되어 있다.
Admin> select * from mysql_servers;
+--------------+-----------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
| hostgroup_id | hostname  | port | gtid_port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment |
+--------------+-----------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
| 601          | 10.1.5.12 | 3306 | 0         | ONLINE | 1000   | 0           | 1000            | 10                  | 0       | 0              |         |
| 601          | 10.1.5.11 | 3306 | 0         | ONLINE | 1000   | 0           | 1000            | 10                  | 0       | 0              |         |
+--------------+-----------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
2 rows in set (0.00 sec)

test.sh

+----------------------------+---------+-------------+
| now(6)                     | max(i1) | @@server_id |
+----------------------------+---------+-------------+
| 2020-06-01 14:10:45.031846 |     128 |           1 |
+----------------------------+---------+-------------+
+----------------------------+---------+-------------+
| now(6)                     | max(i1) | @@server_id |
+----------------------------+---------+-------------+
| 2020-06-01 14:10:46.045739 |     129 |           1 |
+----------------------------+---------+-------------+
ERROR 9001 (HY000) at line 1: Max connect timeout reached while reaching hostgroup 600 after 10000ms
ERROR 9001 (HY000) at line 1: Max connect timeout reached while reaching hostgroup 600 after 10000ms
ERROR 9001 (HY000) at line 1: Max connect timeout reached while reaching hostgroup 600 after 10000ms

slave db

mysql> show slave status \G;
*************************** 1. row ***************************
               Slave_IO_State: Reconnecting after a failed master event read
                  Master_Host: 10.1.5.11
                  Master_User: test
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000012
          Read_Master_Log_Pos: 4683
               Relay_Log_File: tdb02-relay-bin.000007
                Relay_Log_Pos: 4857
        Relay_Master_Log_File: mysql-bin.000012
             Slave_IO_Running: Connecting
            Slave_SQL_Running: Yes
... ...
        Seconds_Behind_Master: NULL
... ...

새로운 MASTER 선정

실행

slave db

-- 새로운 master 로 승격
ysql> set global read_only =off;
Query OK, 0 rows affected (0.00 sec)

-- slave 기능 중지
mysql> stop slave;
Query OK, 0 rows affected (0.01 sec)

-- slave 역할 삭제
mysql> stop slave;reset slave all;
Query OK, 0 rows affected, 1 warning (0.00 sec)

Query OK, 0 rows affected (0.02 sec)

확인

slave db


mysql> show variables like 'read_only';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| read_only     | OFF   |
+---------------+-------+
1 row in set (0.01 sec)

mysql> show slave status \G;
Empty set (0.00 sec)

ERROR:
No query specified

mysql>

proxysql log

2020-06-01 14:13:05 [INFO] Changing status for server 601:10.1.5.12:3306 (10.1.5.12:3306) from 4 (4) to 0
2020-06-01 14:13:05 [INFO] Changing status for server 601:10.1.5.11:3306 (10.1.5.11:3306) from 1 (1) to 0
2020-06-01 14:13:05 [INFO] Creating new server in HG 600 : 10.1.5.12:3306 , gtid_port=0, weight=1000, status=0
2020-06-01 14:13:05 [INFO] New mysql_replication_hostgroups table
writer_hostgroup: 600 , reader_hostgroup: 601, check_type read_only, comment: test comment
2020-06-01 14:13:05 [INFO] New mysql_group_replication_hostgroups table
2020-06-01 14:13:05 [INFO] New mysql_galera_hostgroups table
2020-06-01 14:13:05 [INFO] New mysql_aws_aurora_hostgroups table
2020-06-01 14:13:05 [INFO] Dumping current MySQL Servers structures for hostgroup ALL
HID: 601 , address: 10.1.5.12 , port: 3306 , gtid_port: 0 , weight: 1000 , status: ONLINE , max_connections: 1000 , max_replication_lag: 10 , use_ssl: 0 , max_latency_ms: 0 , comment:
HID: 601 , address: 10.1.5.11 , port: 3306 , gtid_port: 0 , weight: 1000 , status: ONLINE , max_connections: 1000 , max_replication_lag: 10 , use_ssl: 0 , max_latency_ms: 0 , comment:
HID: 600 , address: 10.1.5.12 , port: 3306 , gtid_port: 0 , weight: 1000 , status: ONLINE , max_connections: 1000 , max_replication_lag: 10 , use_ssl: 0 , max_latency_ms: 0 , comment:
2020-06-01 14:13:05 [INFO] Dumping mysql_servers: ALL
+-----+-----------+------+------+--------+--------+-----+-----------+---------+-----+---------+---------+-----------------+
| hid | hostname  | port | gtid | weight | status | cmp | max_conns | max_lag | ssl | max_lat | comment | mem_pointer     |
+-----+-----------+------+------+--------+--------+-----+-----------+---------+-----+---------+---------+-----------------+
| 601 | 10.1.5.12 | 3306 | 0    | 1000   | 0      | 0   | 1000      | 10      | 0   | 0       |         | 140141630268576 |
| 600 | 10.1.5.12 | 3306 | 0    | 1000   | 0      | 0   | 1000      | 10      | 0   | 0       |         | 140141576410208 |
| 601 | 10.1.5.11 | 3306 | 0    | 1000   | 0      | 0   | 1000      | 10      | 0   | 0       |         | 140141630268256 |
+-----+-----------+------+------+--------+--------+-----+-----------+---------+-----+---------+---------+-----------------+

proxysql table

-- write 그룹(600) 에 12번서버가 등록됨
Admin> select * from mysql_servers;
+--------------+-----------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
| hostgroup_id | hostname  | port | gtid_port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment |
+--------------+-----------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
| 600          | 10.1.5.12 | 3306 | 0         | ONLINE | 1000   | 0           | 1000            | 10                  | 0       | 0              |         |
| 601          | 10.1.5.11 | 3306 | 0         | ONLINE | 1000   | 0           | 1000            | 10                  | 0       | 0              |         |
| 601          | 10.1.5.12 | 3306 | 0         | ONLINE | 1000   | 0           | 1000            | 10                  | 0       | 0              |         |
+--------------+-----------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
3 rows in set (0.00 sec)

test.sh

+----------------------------+---------+-------------+
| now(6)                     | max(i1) | @@server_id |
+----------------------------+---------+-------------+
| 2020-06-01 14:13:05.801875 |     130 |           2 |
+----------------------------+---------+-------------+
+----------------------------+---------+-------------+
| now(6)                     | max(i1) | @@server_id |
+----------------------------+---------+-------------+
| 2020-06-01 14:13:16.212596 |     131 |           2 |
+----------------------------+---------+-------------+

구 MASTER DB SLAVE 로 편입

실행

구 master db

mysql> CHANGE MASTER TO
    ->      MASTER_HOST = '10.1.5.12',
    ->      MASTER_PORT = 3306,
    ->      MASTER_USER = 'test',
    ->      MASTER_PASSWORD = 'test',
    ->      MASTER_AUTO_POSITION = 1;
Query OK, 0 rows affected, 1 warning (0.02 sec)

mysql> start slave;
Query OK, 0 rows affected (0.00 sec)

확인

구 master db

mysql> show slave status \G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 10.1.5.12
                  Master_User: test
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000002
          Read_Master_Log_Pos: 148602
               Relay_Log_File: tdb01-relay-bin.000002
                Relay_Log_Pos: 111338
        Relay_Master_Log_File: mysql-bin.000002
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
... ...
        Seconds_Behind_Master: 0
... ...

proxysql log

2020-06-01 14:22:56 MySQL_HostGroups_Manager.cpp:2898:replication_lag_action(): [WARNING] Re-enabling server 10.1.5.11:3306 from HG 601 with replication lag of 0 second

proxysql table

Admin> select * from mysql_servers;
+--------------+-----------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
| hostgroup_id | hostname  | port | gtid_port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment |
+--------------+-----------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
| 600          | 10.1.5.12 | 3306 | 0         | ONLINE | 1000   | 0           | 1000            | 10                  | 0       | 0              |         |
| 601          | 10.1.5.11 | 3306 | 0         | ONLINE | 1000   | 0           | 1000            | 10                  | 0       | 0              |         |
| 601          | 10.1.5.12 | 3306 | 0         | ONLINE | 1000   | 0           | 1000            | 10                  | 0       | 0              |         |
+--------------+-----------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
3 rows in set (0.00 sec)

Reference