MariaDB MaxScale 是一个数据库代理,它扩展了 MariaDB Server 的高可用性,可伸缩性和安全性,同时通过将其与底层数据库基础架构分离来简化应用程序开发,采用可扩展的体系结构设计,支持插件,将其功能扩展到透明负载平衡之外,成为数据库防火墙。 通过内置多个路由器,过滤器和协议的插件,MariaDB MaxScale 可以配置为根据业务和技术要求转发数据库请求和修改数据库响应。例如,屏蔽敏感数据或扩展读取,读写分离等等。
MariaDB Galera Cluster 是 MariaDB 的同步多主集群。 它仅在 Linux 上可用,并且仅支持 XtraDB / InnoDB 存储引擎。简单来说就是一个多主架构的数据库集群。
环境架构
这里使用 MaxScale 做数据库代理,数据库代理后端则使用 Galera Cluster,当然实际生产还需要 MaxScale 配合 Keepalived 做高可用,并使用 vip 地址做数据库对外使用的地址。
MaxScale 数据库代理配置信息
[root@db-proxy ~]# cat /etc/maxscale.cnf
[maxscale]
threads=4
log_debug=0
[db01]
type=server
address=db01.ssgwo.com
port=3306
protocol=MySQLBackend
priority=11
[db02]
type=server
address=db02.ssgwo.com
port=3306
protocol=MySQLBackend
priority=12
[db03]
type=server
address=db03.ssgwo.com
port=3306
protocol=MySQLBackend
priority=13
[MySQL Monitor]
type=monitor
module=galeramon
servers=db01,db02,db03
user=root
passwd=test123
monitor_interval=10000
use_priority=true
[Select_Query_Filter]
type=filter
module=namedserverfilter
match=select
options=ignorecase
server=db02
[Read-Write Service]
type=service
router=readwritesplit
servers=db01,db02,db03
user=root
passwd=test123
enable_root_user=true
max_slave_connections=1
use_sql_variables_in=master
router_options=master_accept_reads=true
filters=Select_Query_Filter
[MaxAdmin Service]
type=service
router=cli
[Read-Write Listener]
type=listener
service=Read-Write Service
protocol=MySQLClient
port=3306
[MaxAdmin Listener]
type=listener
service=MaxAdmin Service
protocol=maxscaled
socket=default
MaxScale 查看后端数据库各节点信息
[root@db-proxy ~]# maxadmin list servers
Servers.
-------------------+-----------------+-------+-------------+--------------------
Server | Address | Port | Connections | Status
-------------------+-----------------+-------+-------------+--------------------
db01 | db01.ssgwo.com | 3306 | 0 | Master, Synced, Running
db02 | db02.ssgwo.com | 3306 | 0 | Slave, Synced, Running
db03 | db03.ssgwo.com | 3306 | 0 | Slave, Synced, Running
-------------------+-----------------+-------+-------------+--------------------
MariaDB Galera 集群三个数据库节点配置信息
[root@db01 ~]# cat /etc/my.cnf.d/server.cnf
[server]
[mysqld]
[galera]
wsrep_provider=/usr/lib64/galera/libgalera_smm.so
wsrep_cluster_address="gcomm://172.16.27.131,172.16.27.132,172.16.27.133"
default_storage_engine=InnoDB
innodb_autoinc_lock_mode=2
bind-address=0.0.0.0
wsrep_cluster_name="ssgwo"
wsrep_node_address="172.16.27.131"
wsrep_sst_method=rsync
max_connections=20480
max_connect_errors=10240
max_allowed_packet=34M
wsrep_causal_reads=ON
skip-character-set-client-handshake
collation-server=utf8_bin
character-set-server=utf8
wsrep_sst_donor="172.16.27.133,172.16.27.132,"
innodb_log_file_size=256M
innodb_buffer_pool_size=1024M
binlog_format=row
general_log
general_log_file=/var/log/mysql/queries.log
slow_query_log
slow_query_log_file=/var/log/mysql/slow.log
#server_id="131"
#log_slave_updates=1
#log_bin=binlog
#expire_logs_days=7
[embedded]
[mariadb]
transaction-isolation = READ-COMMITTED
log_bin_trust_function_creators = 1
net_read_timeout = 180
net_retry_count = 2
net_write_timeout = 180
[mariadb-10.0]
[root@db02 mysql]# cat /etc/my.cnf.d/server.cnf
[server]
[mysqld]
[galera]
wsrep_provider=/usr/lib64/galera/libgalera_smm.so
wsrep_cluster_address="gcomm://172.16.27.131,172.16.27.132,172.16.27.133"
default_storage_engine=InnoDB
innodb_autoinc_lock_mode=2
bind-address=0.0.0.0
wsrep_cluster_name="ssgwo"
wsrep_node_address="172.16.27.132"
wsrep_sst_method=rsync
max_connections=20480
max_connect_errors=10240
max_allowed_packet=34M
wsrep_causal_reads=ON
skip-character-set-client-handshake
collation-server=utf8_bin
character-set-server=utf8
wsrep_sst_donor="172.16.27.133,172.16.27.132,"
innodb_log_file_size=256M
innodb_buffer_pool_size=1024M
binlog_format=row
general_log
general_log_file=/var/log/mysql/queries.log
slow_query_log
slow_query_log_file=/var/log/mysql/slow.log
#server_id="132"
#log_slave_updates=1
#log_bin=binlog
#expire_logs_days=7
[embedded]
[mariadb]
transaction-isolation = READ-COMMITTED
log_bin_trust_function_creators = 1
net_read_timeout = 180
net_retry_count = 2
net_write_timeout = 180
[mariadb-10.0]
[root@db03 mysql]# cat /etc/my.cnf.d/server.cnf
[server]
[mysqld]
[galera]
wsrep_provider=/usr/lib64/galera/libgalera_smm.so
wsrep_cluster_address="gcomm://172.16.27.131,172.16.27.132,172.16.27.133"
default_storage_engine=InnoDB
innodb_autoinc_lock_mode=2
bind-address=0.0.0.0
wsrep_cluster_name="ssgwo"
wsrep_node_address="172.16.27.133"
wsrep_sst_method=rsync
max_connections=20480
max_connect_errors=10240
max_allowed_packet=34M
wsrep_causal_reads=ON
skip-character-set-client-handshake
collation-server=utf8_bin
character-set-server=utf8
wsrep_sst_donor="172.16.27.133,172.16.27.132,"
innodb_log_file_size=256M
innodb_buffer_pool_size=1024M
binlog_format=row
general_log
general_log_file=/var/log/mysql/queries.log
slow_query_log
slow_query_log_file=/var/log/mysql/slow.log
#server_id="133"
#log_slave_updates=1
#log_bin=binlog
#expire_logs_days=7
[embedded]
[mariadb]
transaction-isolation = READ-COMMITTED
log_bin_trust_function_creators = 1
net_read_timeout = 180
net_retry_count = 2
net_write_timeout = 180
[mariadb-10.0]
集群任意一节点查看 Galera 集群状态
MariaDB [(none)]> show status like ‘%wsrep%‘;
+------------------------------+----------------------------------------------------------+
| Variable_name | Value |
+------------------------------+----------------------------------------------------------+
| wsrep_local_state_uuid | 501ee58e-b8a4-11e8-86d2-36e60e7d9c84 |
| wsrep_protocol_version | 7 |
| wsrep_last_committed | 8 |
| wsrep_replicated | 3 |
| wsrep_replicated_bytes | 1373 |
| wsrep_repl_keys | 3 |
| wsrep_repl_keys_bytes | 93 |
| wsrep_repl_data_bytes | 1088 |
| wsrep_repl_other_bytes | 0 |
| wsrep_received | 12 |
| wsrep_received_bytes | 3040 |
| wsrep_local_commits | 0 |
| wsrep_local_cert_failures | 0 |
| wsrep_local_replays | 0 |
| wsrep_local_send_queue | 0 |
| wsrep_local_send_queue_max | 1 |
| wsrep_local_send_queue_min | 0 |
| wsrep_local_send_queue_avg | 0.000000 |
| wsrep_local_recv_queue | 0 |
| wsrep_local_recv_queue_max | 2 |
| wsrep_local_recv_queue_min | 0 |
| wsrep_local_recv_queue_avg | 0.083333 |
| wsrep_local_cached_downto | 1 |
| wsrep_flow_control_paused_ns | 0 |
| wsrep_flow_control_paused | 0.000000 |
| wsrep_flow_control_sent | 0 |
| wsrep_flow_control_recv | 0 |
| wsrep_cert_deps_distance | 1.000000 |
| wsrep_apply_oooe | 0.000000 |
| wsrep_apply_oool | 0.000000 |
| wsrep_apply_window | 1.000000 |
| wsrep_commit_oooe | 0.000000 |
| wsrep_commit_oool | 0.000000 |
| wsrep_commit_window | 1.000000 |
| wsrep_local_state | 4 |
| wsrep_local_state_comment | Synced |
| wsrep_cert_index_size | 2 |
| wsrep_causal_reads | 13 |
| wsrep_cert_interval | 0.000000 |
| wsrep_incoming_addresses | 172.16.27.132:3306,172.16.27.133:3306,172.16.27.131:3306 |
| wsrep_evs_delayed | |
| wsrep_evs_evict_list | |
| wsrep_evs_repl_latency | 0/0/0/0/0 |
| wsrep_evs_state | OPERATIONAL |
| wsrep_gcomm_uuid | a9c25806-b8a5-11e8-a058-e2a4949e7de5 |
| wsrep_cluster_conf_id | 3 |
| wsrep_cluster_size | 3 |
| wsrep_cluster_state_uuid | 501ee58e-b8a4-11e8-86d2-36e60e7d9c84 |
| wsrep_cluster_status | Primary |
| wsrep_connected | ON |
| wsrep_local_bf_aborts | 0 |
| wsrep_local_index | 2 |
| wsrep_provider_name | Galera |
| wsrep_provider_vendor | Codership Oy <info@codership.com> |
| wsrep_provider_version | 3.12(r9921e73) |
| wsrep_ready | ON |
| wsrep_thread_count | 2 |
+------------------------------+----------------------------------------------------------+
参数说明
这里说明几个比较关注的参数:
wsrep_provider:wsrep 库的路径
wsrep_node_name:当前数据库节点的名称,在集群中不能重复
wsrep_sst_method:数据快照进行同步的方式,例如可以选择 rsync、mysqldump 等
wsrep_sst_donor:数据恢复时指定的供体,也就是指定哪个节点作为数据同步的提供者
如果需要从该集群做主从复制,除了开启 binlog 外,一定别忘了添加 log_slave_updates=1 参数
故障处理
如果只是集群的个别节点挂掉,通常重启数据库服务即可。如果数据库集群 down 掉,关于如何选择节点中数据较为最新的节点,可以根据 wsrep_last_committed 的值来找到值最大的,成功启动以后,再逐一启用其他节点:
MySQL [(none)]> show status like ‘%wsrep_last_committed%‘;
+----------------------+-----------+
| Variable_name | Value |
+----------------------+-----------+
| wsrep_last_committed | 509679719 |
+----------------------+-----------+
但是在我们生产中并不是采取这种方式恢复集群的,我们默认是选择一个节点当做恢复集群的主库,也就是最先启动的一个数据库。因为通常我们的业务所有数据库请求基本都是通过 MaxScale 请求到后端的数据库集群,但是个别的一些开发环境由于对数据库写的数据较大,我们是单独指定到数据库集群中的一个指定的数据库节点的,后面集群出现问题,我们也是以这个指定的数据库节点作为恢复集群的起始节点开始恢复集群的。
首先我们在第一个节点进行数据库集群的恢复时,使用以下命令启用数据库:
[root@db01 ~]# /etc/init.d/mysql bootstrap
Bootstrapping the cluster.. Starting MySQL.. SUCCESS!
其他节点则正常启动数据库服务即可,如果直接启动数据库服务不正常,则需要执行以下命令:
[root@db02 ~]# mysqld --user=mysql --wsrep-cluster-address="gcomm://172.16.27.131,172.16.27.132,172.16.27.133"
上述这个命令同步会依照 wsrep_sst_method=rsync 来使用 rsync 然后根据参数 wsrep_sst_donor=”172.16.27.133,172.16.27.132,” 根据顺序选择最优的服务器作为同步源。在数据全量同步的时候,作为同步源的数据库是不能正常处理读写请求的,它此时的状态是供体(Donor)。上述命令因为配置参数 wsrep_sst_method 配置为 rsync,所以使用 rsync 同步,我们生产中 120G 左右的数据同步大概不到 30 分钟。在节点同步恢复的过程中,可以看到后台有 rsync 在进行数据同步,数据同步完成后,需要再启用数据库服务。判断数据同时,可根据日志观察同步情况。
也可使用以下命令来查看当前节点状态,下面的输出是我某个节点是正常情况下的输出结果:
MySQL [(none)]> show status like ‘wsrep_local_state_comment‘;
+---------------------------+--------+
| Variable_name | Value |
+---------------------------+--------+
| wsrep_local_state_comment | Synced |
+---------------------------+--------+
状态说明:
Open:节点启动成功,尝试连接到集群,如果失败则根据配置退出或创建新的集群
Primary:节点已处于集群中,在新节点加入时,选取 donor 进行数据同步时会产生的状态
Joiner:节点处于等待接收/接收同步文件时的状态
Joined:节点完成数据同步,但有部分数据没跟上,在尝试保持和集群进度一致的过程状态。例如某个节点故障后,重新加入集群,在追赶集群进度时的状态
Synced:节点正常提供服务的状态,表示已经同步完成并和集群进度保持一致
Donor:节点处于为新节点提供全量数据数据同步时的状态。此时该节点对客户端不提供服务
这几个参数是参考:http://zjzone.cc/index.php/2017/04/16/galera-ji-qun-hui-fu-di-chang-jian-qi-zhong-chang-jing/
小结
-
上述 MaxScale 和 Galera 的配置都是从生产环境中拷贝经过ip的修改后在虚拟机运行的,所以没有进行过多的测试。
-
生产环境中的 MaxScale 使用 keepalived 做高可用,通过 vip 来提供数据库服务。keepalived 网上的配置实在太多,这里就不在赘述。Galera 集群节点之间的同步使用的是单独一个网卡,前端的请求则是另外一个网卡,也就是数据库集群服务器使用的是双网卡。
- 在使用 Galera 集群的大概 2 年的时间里,出过两三次问题,其中有一次卡死是因为开发部门的同事在改一个很大很大的表,结果整个数据库请求都卡住了大半天。为了避免修改大表造成的集群卡死,应该避免白天在生产环境中修改,特别大的表可以考虑通过 pt-online-schema-change 来更改。但作为一个数据库多主架构,整体来说还算挺稳定的,最后别忘了数据一定要频繁备份!备份!备份!