MaxScale 简单配置读写分离 故障转移

MaxScale 简单配置读写分离 故障转移

快速搭建高可用MySQL环境,实用指南而非理论探讨,数据库使用MariaDb,整体使用docker实现,记录一下方便以后查看。

整体结构拓扑MaxScale作为代理中间件,对应用程序无感知实现读写分离、故障转移

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

| application |

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

|

+-----v-----+

| MaxScale |

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

|

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

| | |

+----v----- + +--- --v-----+ +-------v-----+

| Master | | Slave1 | | Slave2 |

| (Write) | | (Read) | | (Read) |

+-----------+ +------------+ +-------------+节点信息roleIPportnameMaxScale127.0.0.133060maxscaleMaster127.0.0.13306mariadb1Slave127.0.0.13307mariadb2Slave127.0.0.13308mariadb3搭建节点和盖房子一样,按照拓补图从下往上,先搭建一个主从。和上一篇主从复制步骤差不多https://evlan.cc/archives/mariadb-replication.html,所以细节不再赘述

持久化目录创建节点持久化目录保存数据、日志、配置

mkdir -p /opt/mariadb1/{data,logs,conf.d,docker-entrypoint-initdb.d}

mkdir -p /opt/mariadb2/{data,logs,conf.d}

mkdir -p /opt/mariadb3/{data,logs,conf.d}创建数据库初始化的env,设置root密码、创建一个数据库和对他有权限的用户

cat > /opt/mariadb1/mariadb.env <

MARIADB_ROOT_PASSWORD=some-password-string

MARIADB_DATABASE=my_db

MARIADB_USER=my_db_user

MARIADB_PASSWORD=my_db_user_password

EOF配置文件cat > /opt/mariadb1/conf.d/my.cnf <

[mariadb]

log_bin

log_basename=mariadb

binlog_format=row

server_id=1

gtid_strict_mode=1

port=3306

log_slave_updates=1

rpl_semi_sync_master_enabled=1

rpl_semi_sync_master_wait_point=AFTER_SYNC

rpl_semi_sync_master_timeout=3000

rpl_semi_sync_slave_enabled=1

character_set_server=utf8mb4

collation_server=utf8mb4_unicode_ci

[client]

default_character_set=utf8mb4

EOF创建从库配置文件,两个从库只有ID和端口不一样。

cat > /opt/mariadb2/conf.d/my.cnf <

[mariadb]

log_bin

log_basename=mariadb

binlog_format=row

server_id=2

gtid_strict_mode=1

port=3307

log_slave_updates=1

read_only=ON

rpl_semi_sync_master_enabled=1

rpl_semi_sync_master_wait_point=AFTER_SYNC

rpl_semi_sync_master_timeout=3000

rpl_semi_sync_slave_enabled=1

character_set_server=utf8mb4

collation_server=utf8mb4_unicode_ci

[client]

default_character_set=utf8mb4

EOFcat > /opt/mariadb3/conf.d/my.cnf <

[mariadb]

log_bin

log_basename=mariadb

binlog_format=row

server_id=3

gtid_strict_mode=1

port=3308

log_slave_updates=1

read_only=ON

rpl_semi_sync_master_enabled=1

rpl_semi_sync_master_wait_point=AFTER_SYNC

rpl_semi_sync_master_timeout=3000

rpl_semi_sync_slave_enabled=1

character_set_server=utf8mb4

collation_server=utf8mb4_unicode_ci

[client]

default_character_set=utf8mb4

EOF启动数据库直接docker run启动主库

docker run -d \

--name mariadb1 \

-v /opt/mariadb1/data:/var/lib/mysql \

-v /opt/mariadb1/conf.d:/etc/mysql/conf.d \

-v /opt/mariadb1/logs:/var/log/mysql \

-v /opt/mariadb1/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d \

--env-file /opt/mariadb1/mariadb.env \

--network host \

--restart=unless-stopped \

mariadb:11.7.2配置主从创建账号执行docker exec -it mariadb1 mariadb -uroot -p输入env里面设置的密码进入容器创建用来主从复制的账号

CREATE USER 'replication_user'@'%' IDENTIFIED BY 'replication_user_password';

GRANT REPLICATION REPLICA ON *.* TO 'replication_user'@'%';理论上由于开启了半同步复制,执行sql会卡。之前文章有说明。

部署从库同步主库数据文件备份主库到full_backup目录docker exec -it mariadb1 mariadb-backup --backup --target-dir=full_backup --user=root --password

从容器里面复制出来并删除容器里面的full_backup

docker cp mariadb1:full_backup .

docker exec -it mariadb1 rm -rf full_backup 复制到从库各自的目录内,并处理、还原到数据目录

mv full_backup /opt/mariadb2/

cp -r /opt/mariadb2/full_backup /opt/mariadb3/

docker run --rm -v /opt/mariadb2/full_backup:/mariadb_backup_files mariadb:11.7.2 mariadb-backup --prepare

docker run --rm -v /opt/mariadb3/full_backup:/mariadb_backup_files mariadb:11.7.2 mariadb-backup --prepare

docker run --rm -v /opt/mariadb2/full_backup:/mariadb_backup_files -v /opt/mariadb2/data:/var/lib/mysql mariadb:11.7.2 mariadb-backup --copy-back --target-dir=/mariadb_backup_files

docker run --rm -v /opt/mariadb3/full_backup:/mariadb_backup_files -v /opt/mariadb3/data:/var/lib/mysql mariadb:11.7.2 mariadb-backup --copy-back --target-dir=/mariadb_backup_files

docker run --rm -v /opt/mariadb2/data:/var/lib/mysql mariadb:11.7.2 chown -R mysql:mysql /var/lib/mysql/

docker run --rm -v /opt/mariadb3/data:/var/lib/mysql mariadb:11.7.2 chown -R mysql:mysql /var/lib/mysql/查看binlog文件和position或gtid,后面主从复制要用cat /opt/mariadb2/full_backup/mariadb_backup_binlog_info会展示类似于mariadb-bin.000002 704 0-1-5

启动从库docker run -d \

--name mariadb2 \

-v /opt/mariadb2/data:/var/lib/mysql \

-v /opt/mariadb2/conf.d:/etc/mysql/conf.d \

-v /opt/mariadb2/logs:/var/log/mysql \

--network host \

--restart=unless-stopped \

mariadb:11.7.2

docker run -d \

--name mariadb3 \

-v /opt/mariadb3/data:/var/lib/mysql \

-v /opt/mariadb3/conf.d:/etc/mysql/conf.d \

-v /opt/mariadb3/logs:/var/log/mysql \

--network host \

--restart=unless-stopped \

mariadb:11.7.2设置主从复制分别进入两个从节点设置主从复制信息docker exec -it mariadb2 mariadb -uroot -p

docker exec -it mariadb3 mariadb -uroot -p

此处使用gtid

SET GLOBAL gtid_slave_pos="0-1-5";

CHANGE MASTER TO

MASTER_HOST='主节点IP',

MASTER_PORT=3306,

MASTER_USER='replication_user',

MASTER_PASSWORD='replication_user_password',

MASTER_USE_GTID=slave_pos;

START REPLICA;使用SHOW REPLICA STATUS \G;命令看到这两行就OK了

Slave_IO_Running: Yes

Slave_SQL_Running: Yes配置maxscale部署maxscale持久化目录创建持久化目录保存数据、日志并授权

mkdir -p /opt/maxscale/{data,logs}

docker run --rm -v /opt/maxscale/:/opt/maxscale/ mariadb/maxscale:24.02 chown -R maxscale:maxscale /opt/maxscale/创建数据库账号MaxScale需要两个数据库用户供自己使用,在主库创建账号docker exec -it mariadb1 mariadb -uroot -p

maxscale_monitor账号用于监视MariaDB服务器的健康状况并对其执行操作

CREATE USER 'maxscale_monitor'@'%' IDENTIFIED BY 'strong_monitor_password';

GRANT RELOAD, PROCESS, SHOW DATABASES, BINLOG MONITOR, EVENT, SET USER, CONNECTION ADMIN, READ_ONLY ADMIN, REPLICATION SLAVE ADMIN, BINLOG ADMIN, SLAVE MONITOR ON *.* TO `maxscale_monitor`@`%`;

GRANT SELECT ON `mysql`.`user` TO `maxscale_monitor`@`%`;

GRANT SELECT ON `mysql`.`global_priv` TO `maxscale_monitor`@`%`;maxscale_service账号用于获取用户帐户信息

CREATE USER 'maxscale_service'@'%' IDENTIFIED BY 'strong_service_password';

GRANT SHOW DATABASES ON *.* TO `maxscale_service`@`%`;

GRANT SELECT ON `mysql`.* TO `maxscale_service`@`%`;创建配置文件创建配置文件/opt/maxscale/maxscale.cnf配置文件的注释不能跟在配置末尾,要在新行以#开头

根据cpu自动线程数

[maxscale]

threads=auto三个节点信息

[server1]

type=server

address=节点IP

port=3306

[server2]

type=server

address=节点IP

port=3307

[server3]

type=server

address=节点IP

port=3308监控信息

[MariaDB-Monitor]

type=monitor

module=mariadbmon

servers=server1,server2,server3

user=maxscale_monitor

password=strong_monitor_password

monitor_interval=2s

#开启故障转移

auto_failover=true

#开启恢复自动加入

auto_rejoin=true

#简单拓扑结构 1主多从

enforce_simple_topology=true

replication_user=replication_user

replication_password=replication_user_password读写分离信息

[Read-Write-Service]

type=service

router=readwritesplit

servers=server1,server2,server3

user=maxscale_service

password=strong_service_password

transaction_replay=true

transaction_replay_timeout=30s

#让root用户也能登录

enable_root_user=true读写分离服务的端口

[Read-Write-Listener]

type=listener

service=Read-Write-Service

port=33060启动docker run --name maxscale -d -v /opt/maxscale/data/:/var/lib/maxscale/ -v /opt/maxscale/logs/:/var/log/maxscale/ -v /opt/maxscale/maxscale.cnf:/etc/maxscale.cnf --network host mariadb/maxscale:24.02查看日志tail -f -n 40 /opt/maxscale/logs/maxscale.log输出大概是这样

2025-06-25 05:53:28 notice : server3 sent version string '11.7.2-MariaDB-ubu2404-log'. Detected type: MariaDB, version: 11.7.2.

2025-06-25 05:53:28 notice : server1 sent version string '11.7.2-MariaDB-ubu2404-log'. Detected type: MariaDB, version: 11.7.2.

2025-06-25 05:53:28 notice : server2 sent version string '11.7.2-MariaDB-ubu2404-log'. Detected type: MariaDB, version: 11.7.2.

2025-06-25 05:53:28 notice : Server 'server1' charset: utf8mb4_unicode_ci

2025-06-25 05:53:28 notice : Server 'server2' charset: utf8mb4_unicode_ci

2025-06-25 05:53:28 notice : Server 'server3' charset: utf8mb4_unicode_ci

2025-06-25 05:53:28 notice : [mariadbmon] Selecting new primary server.

2025-06-25 05:53:28 notice : [mariadbmon] Setting 'server1' as primary.

2025-06-25 05:53:28 notice : Server changed state: server1[xxx.xxx.xxx.xxx:3306]: master_up. [Down] -> [Master, Running]

2025-06-25 05:53:28 notice : Server changed state: server2[xxx.xxx.xxx.xxx:3307]: slave_up. [Down] -> [Slave, Running]

2025-06-25 05:53:28 notice : Server changed state: server3[xxx.xxx.xxx.xxx:3308]: slave_up. [Down] -> [Slave, Running]

2025-06-25 05:53:28 notice : Starting a total of 1 services...

2025-06-25 05:53:28 notice : (Read-Write-Listener); Listening for connections at [::]:33060

2025-06-25 05:53:28 notice : Service 'Read-Write-Service' started (1/1)

2025-06-25 05:53:28 notice : MaxScale started with 2 worker threads.

2025-06-25 05:53:29 notice : Read 10 user@host entries from 'server1' for service 'Read-Write-Service'.执行maxctrl 查看服务列表 docker exec -it maxscale maxctrl list servers

┌─────────┬────────────────┬──────┬─────────────┬─────────────────┬────────┬─────────────────┐

│ Server │ Address │ Port │ Connections │ State │ GTID │ Monitor │

├─────────┼────────────────┼──────┼─────────────┼─────────────────┼────────┼─────────────────┤

│ server1 │ xxx.xx.xxx.xxx │ 3306 │ 0 │ Master, Running │ 0-1-12 │ MariaDB-Monitor │

├─────────┼────────────────┼──────┼─────────────┼─────────────────┼────────┼─────────────────┤

│ server2 │ xxx.xx.xxx.xxx │ 3307 │ 0 │ Slave, Running │ 0-1-12 │ MariaDB-Monitor │

├─────────┼────────────────┼──────┼─────────────┼─────────────────┼────────┼─────────────────┤

│ server3 │ xxx.xx.xxx.xxx │ 3308 │ 0 │ Slave, Running │ 0-1-12 │ MariaDB-Monitor │

└─────────┴────────────────┴──────┴─────────────┴─────────────────┴────────┴─────────────────┘此时就能通过33060端口正常操作数据库了。

测试故障转移模拟主库意外停止或失联,docker stop mariadb1,然后查看日志和状态

tail -f -n 40 /opt/maxscale/logs/maxscale.log表示主库连不上,从'server2 server3'选中了server2替代server1,然后设置server3的主库为server2

2025-06-25 06:08:27 notice : Server changed state: server1[xxx.xx.xxx.xxx:3306]: master_down. [Master, Running] -> [Down]

2025-06-25 06:08:27 warning: [mariadbmon] Primary has failed. If primary does not return in 4 monitor tick(s), failover begins.

2025-06-25 06:08:34 notice : [mariadbmon] Selecting a server to promote and replace 'server1'. Candidates are: 'server2', 'server3'.

2025-06-25 06:08:34 notice : [mariadbmon] Selected 'server2'.

2025-06-25 06:08:34 notice : [mariadbmon] Performing automatic failover to replace failed primary 'server1'.

2025-06-25 06:08:35 notice : [mariadbmon] Redirecting 'server3' to replicate from 'server2' instead of 'server1'.

2025-06-25 06:08:35 notice : [mariadbmon] All redirects successful.

2025-06-25 06:08:35 notice : [mariadbmon] All redirected slaves successfully started replication from 'server2'.

2025-06-25 06:08:35 notice : [mariadbmon] Failover 'server1' -> 'server2' performed.

2025-06-25 06:08:35 notice : Server changed state: server2[xxx.xx.xxx.xxx:3307]: new_master. [Slave, Running] -> [Master, Running]查看服务列表 docker exec -it maxscale maxctrl list servers

┌─────────┬────────────────┬──────┬─────────────┬─────────────────┬────────┬─────────────────┐

│ Server │ Address │ Port │ Connections │ State │ GTID │ Monitor │

├─────────┼────────────────┼──────┼─────────────┼─────────────────┼────────┼─────────────────┤

│ server1 │ xxx.xx.xxx.xxx │ 3306 │ 0 │ Down │ 0-1-12 │ MariaDB-Monitor │

├─────────┼────────────────┼──────┼─────────────┼─────────────────┼────────┼─────────────────┤

│ server2 │ xxx.xx.xxx.xxx │ 3307 │ 0 │ Master, Running │ 0-1-12 │ MariaDB-Monitor │

├─────────┼────────────────┼──────┼─────────────┼─────────────────┼────────┼─────────────────┤

│ server3 │ xxx.xx.xxx.xxx │ 3308 │ 0 │ Slave, Running │ 0-1-12 │ MariaDB-Monitor │

└─────────┴────────────────┴──────┴─────────────┴─────────────────┴────────┴─────────────────┘自动加入启动mariadb1模拟服务重新上线docker start mariadb1,然后查看日志和状态

tail -f -n 40 /opt/maxscale/logs/maxscale.logserver1上线之后就变成从库,从server2进行复制了

2025-06-25 06:14:25 notice : Server changed state: server1[xxx.xx.xxx.xxx:3306]: server_up. [Down] -> [Running]

2025-06-25 06:14:25 notice : [mariadbmon] Directing standalone server 'server1' to replicate from 'server2'.

2025-06-25 06:14:25 notice : [mariadbmon] Replica connection from server1 to [xxx.xx.xxx.xxx]:3307 created and started.

2025-06-25 06:14:25 notice : [mariadbmon] 1 server(s) redirected or rejoined the cluster.

2025-06-25 06:14:25 notice : Server changed state: server1[xxx.xx.xxx.xxx:3306]: new_slave. [Running] -> [Slave, Running]查看服务列表 docker exec -it maxscale maxctrl list servers

┌─────────┬────────────────┬──────┬─────────────┬─────────────────┬────────┬─────────────────┐

│ Server │ Address │ Port │ Connections │ State │ GTID │ Monitor │

├─────────┼────────────────┼──────┼─────────────┼─────────────────┼────────┼─────────────────┤

│ server1 │ xxx.xx.xxx.xxx │ 3306 │ 0 │ Slave, Running │ 0-1-12 │ MariaDB-Monitor │

├─────────┼────────────────┼──────┼─────────────┼─────────────────┼────────┼─────────────────┤

│ server2 │ xxx.xx.xxx.xxx │ 3307 │ 0 │ Master, Running │ 0-1-12 │ MariaDB-Monitor │

├─────────┼────────────────┼──────┼─────────────┼─────────────────┼────────┼─────────────────┤

│ server3 │ xxx.xx.xxx.xxx │ 3308 │ 0 │ Slave, Running │ 0-1-12 │ MariaDB-Monitor │

└─────────┴────────────────┴──────┴─────────────┴─────────────────┴────────┴─────────────────┘读写分离部署的时候创建了一个初始的用户,用这个账号进行测试,按照上一步的测试,此时server1和server3是从库,server2是主库。

MARIADB_DATABASE=my_dbMARIADB_USER=my_db_userMARIADB_PASSWORD=my_db_user_password使用Dbeaver填好端口和密码然后一直不停的执行语句select @@server_id;进行读操作,会看到返回结果在1和3之间来回切换,说明请求被均匀的分布在server1和server3这两个从库上面了。

测试写,创建一个表CREATE TABLE my_db.test_table (id INT AUTO_INCREMENT PRIMARY KEY, data VARCHAR(255));,插入数据INSERT INTO my_db.test_table (`data`) VALUES ( @@server_id);,然后查询表数据select * FROM my_db.test_table;,可以看到data这一列都是2,说明写操作是server2执行

读写分离完成

相关推荐