MySQL Group Replication(简称MGR)是MySQL官方于2016年12月推出的一个全新的高可用与高扩展的解决方案。MGR是MySQL官方在5.7.17版本引进的一个数据库高可用与高扩展的解决方案,以插件形式提供,实现了分布式下数据的最终一致性, 它是MySQL5.7版本出现的新特性,它提供了高可用、高扩展、高可靠的MySQL集群服务。MySQL组复制分单主模式和多主模式,mysql 的复制技术仅解决了数据同步的问题,如果 master 宕机,意味着数据库管理员需要介入,应用系统可能需要修改数据库连接地址或者重启才能实现。(这里也可以使用数据库中间件产品来避免应用系统数据库连接的问题,例如 mycat 和 atlas 等产品)。组复制在数据库层面上做到了,只要集群中大多数主机可用,则服务可用,也就是说3台服务器的集群,允许其中1台宕机。
1. MGR组复制的特点
- 高一致性:基于分布式paxos协议实现组复制,保证数据一致性;
- 高容错性:自动检测机制,只要不是大多数节点都宕机就可以继续工作,内置防脑裂保护机制;
- 高扩展性:节点的增加与移除会自动更新组成员信息,新节点加入后,自动从其他节点同步增量数据,直到与其他节点数据一致;
- 高灵活性:提供单主模式和多主模式,单主模式在主库宕机后能够自动选主,所有写入都在主节点进行,多主模式支持多节点写入。
2. MGR与传统复制的区别和大幅改进
2.1 传统复制 (即异步复制)
主-从复制:有一个主和不等数量的从。主节点执行的事务会异步发送给从节点,在从节点重新执行。即是在主节点执行和提交事务,然后把他们异步的发送到从节点,行复制的重新执行主节点的SQL语句,这是一个 shared-nothing 的系统,默认情况下所有 server 成员都有一个完整的数据副本。
2.2 半同步复制
它在协议中添加了一个同步步骤。 这意味着主节点在提交时需要等待从节点确认它已经接收到事务。只有这样,主节点才能继续提交操作。 半同步相对异步来说, Master会确认Slave是否接到数据,更加安全。
2.3 并行复制
并行复制:复制->广播->正式复制.

一般Mysql主从复制有三个线程参与,都是单线程:Binlog Dump(主) -> IO Thread (从) -> SQL Thread(从)。复制出现延迟一般出在两个地方: - SQL线程忙不过来 (可能需要应用数据量较大,可能和从库本身的一些操作有锁和资源的冲突;主库可以并发写,SQL线程不可以;主要原因) - 网络抖动导致IO线程复制延迟(次要原因)。 MySQL主从复制延迟的解决办法:MySQL从5.6开始有了SQL Thread多个的概念,可以并发还原数据,即并行复制技术。并行复制的机制,是MySQL的一个非常重要的特性,可以很好的解决MySQL主从延迟问题! 在MySQL 5.6中,设置参数slave_parallel_workers = 4(>1),即可有4个SQL Thread(coordinator线程)来进行并行复制,其状态为:Waiting for an evant from Coordinator。但是其并行只是基于Schema的,也就是基于库的。如果数据库实例中存在多个Schema,这样设置对于Slave复制的速度可以有比较大的提升。通常情况下单库多表是更常见的一种情形,那基于库的并发就没有卵用。其核心思想是:不同schema下的表并发提交时的数据不会相互影响,即slave节点可以用对relay log中不同的schema各分配一个类似SQL功能的线程,来重放relay log中主库已经提交的事务,保持数据与主库一致。 MySQL 5.6版本支持所谓的并行复制,但是其并行只是基于schema的,也就是基于库的。如果用户的MySQL数据库实例中存在多个schema,对于从机复制的速度的确可以有比较大的帮助。但是基于schema的并行复制存在两个问题: 1) crash safe功能不好做,因为可能之后执行的事务由于并行复制的关系先完成执行,那么当发生crash的时候,这部分的处理逻辑是比较复杂的。 2) 最为关键的问题是这样设计的并行复制效果并不高,如果用户实例仅有一个库,那么就无法实现并行回放,甚至性能会比原来的单线程更差。而 单库多表是比多库多表更为常见的一种情形 。 注意:mysql 5.6的MTS是基于库级别的并行,当有多个数据库时,可以将slave_parallel_workers设置为数据库的数量,为了避免新建库后来回修改,也可以将该参数设置的大一些。设置为库级别的事务时,不允许这样做,会报错。 在MySQL 5.7中,引入了基于组提交的并行复制(官方称为Enhanced Multi-threaded Slaves,即MTS),设置参数slave_parallel_workers>0并且global.slave_parallel_type=‘LOGICAL_CLOCK’,即可支持一个schema下,slave_parallel_workers个的worker线程并发执行relay log中主库提交的事务。其核心思想:一个组提交的事务都是可以并行回放(配合binary log group commit);slave机器的relay log中 last_committed相同的事务(sequence_num不同)可以并发执行。其中,变量slave-parallel-type可以有两个值:1)DATABASE 默认值,基于库的并行复制方式;2)LOGICAL_CLOCK,基于组提交的并行复制方式; MySQL 5.7开启Enhanced Multi-Threaded Slave很简单,只需要在Slave从数据库的my.cnf文件中如下配置即可: # slave slave-parallel-type=LOGICAL_CLOCK slave-parallel-workers=8 #一般建议设置4-8,太多的线程会增加线程之间的同步开销 master_info_repository=TABLE relay_log_info_repository=TABLE relay_log_recovery=ON MySQL 5.7是基于组提交的并行复制,并且是支持"真正"的并行复制功能,这其中最为主要的原因:就是slave服务器的回放与主机是一致的, 即master服务器上是怎么并行执行的slave上就怎样进行并行回放。不再有库的并行复制限制,对于二进制日志格式也无特殊的要求(基于库的并行复制也没有要求)。 MySQL5.7的并行复制,期望最大化还原主库的并行度,实现方式是在binlog event中增加必要的信息,以便slave节点根据这些信息实现并行复制。MySQL5.7的并行复制建立在group commit的基础上,所有在主库上能够完成prepared的语句表示没有数据冲突,就可以在slave节点并行复制。 所以在并行复制环境中,除了在Slace从数据库中配置之外,还需要在Master主数据库上的my.cnf文件中添加binlog_group_commit配置,否则从库无法做到基于事物的并行复制: binlog_group_commit_sync_delay = 100 binlog_group_commit_sync_no_delay_count = 10 binlog_group_commit_sync_delay,这个参数控制着日志在刷盘前日志提交要等待的时间,默认是0也就是说提交后立即刷盘,但是并不代表是关闭了组提交,当设置为0以上的时候,就允许多个事物的日志同时间一起提交刷盘,也就是我们说的组提交。组提交是并行复制的基础,我们设置这个值的大于0就代表打开了组提交的延迟功能,而组提交是默认开启的。最大值只能设置为1000000微妙。 binlog_group_commit_sync_no_delay_count ,这个参数表示我们在binlog_group_commit_sync_delay等待时间内,如果事物数达到这个参数的设定值,就会触动一次组提交,如果这个值设为0的话就不会有任何的影响。如果到达时间但是事物数并没有达到的话,也是会进行一次组提交操作的。 MySQL 5.7并行复制的思想简单易懂,一言以蔽之: 一个组提交的事务都是可以并行回放 ,因为这些事务都已进入到事务的prepare阶段,则说明事务之间没有任何冲突(否则就不可能提交)。为了兼容MySQL 5.6基于库的并行复制,5.7引入了新的变量slave-parallel-type,其可以配置的值有: - DATABASE:默认值,基于库的并行复制方式 - LOGICAL_CLOCK:基于组提交的并行复制方式 支持并行复制的GTID 如何知道事务是否在一组中,又是一个问题,因为原版的MySQL并没有提供这样的信息。在MySQL 5.7版本中,其设计方式是将组提交的信息存放在GTID中。那么如果用户没有开启GTID功能,即将参数gtid_mode设置为OFF呢?故MySQL 5.7又引入了称之为Anonymous_Gtid的二进制日志event类型,如: mysql> SHOW BINLOG EVENTS in 'mysql-bin.000003';
2.4 组复制 (MGR)
MGR组复制原理
组复制是一种可用于实现容错系统的技术。 复制组是一个通过消息传递相互交互的 server 集群。通信层提供了原子消息(atomic message)和完全有序信息交互等保障机制实现了基于复制协议的多主更新 复制组由多个 server成员构成,并且组中的每个 server 成员可以独立地执行事务。但所有读写(RW)事务只有在冲突检测成功后才会提交。只读(RO)事务不需要在冲突检测,可以立即提交。句话说,对于任何 RW 事务,提交操作并不是由始发 server 单向决定的,而是由组来决定是否提交。准确地说,在始发 server 上,当事务准备好提交时,该 server 会广播写入值(已改变的行)和对应的写入集(已更新的行的唯一标识符)。然后会为该事务建立一个全局的顺序。最终,这意味着所有 server 成员以相同的顺序接收同一组事务。因此,所有 server 成员以相同的顺序应用相同的更改,以确保组内一致。
MySQL组复制协议工作流程:
需要注意:MGR组复制是一种 share-nothing 复制方案,其中每个 server 成员都有自己的完整数据副本。
MGR实现了基于复制协议的多主更新
-> 复制组由多个 server成员构成,并且组中的每个 server 成员可以独立地执行事务。但所有读写(RW)事务只有在冲突检测成功后才会提交。只读(RO)事务不需要在冲突检测,可以立即提交。
-> 换句话说,对于任何 RW 事务,提交操作并不是由始发 server 单向决定的,而是由组来决定是否提交。准确地说,在始发 server 上,当事务准备好提交时,该 server 会广播写入值(已改变的行)和对应的写入集(已更新的行的唯一标识符)。然后会为该事务建立一个全局的顺序。最终,这意味着所有 server 成员以相同的顺序接收同一组事务。因此,所有 server 成员以相同的顺序应用相同的更改,以确保组内一致。
-> 组复制使您能够根据在一组 server 中复制系统的状态来创建具有冗余的容错系统。因此,只要它不是全部或多数 server 发生故障,即使有一些 server 故障,系统仍然可用,最多只是性能和可伸缩性降低,但它仍然可用。server 故障是孤立并且独立的。它们由组成员服务来监控,组成员服务依赖于分布式故障检测系统,其能够在任何 server 自愿地或由于意外停止而离开组时发出信号。
-> 他们是由一个分布式恢复程序来确保当有 server 加入组时,它们会自动更新组信息到最新。并且多主更新确保了即使在单个服务器故障的情况下也不会阻止更新,不必进行 server故障转移。因此,MySQL 组复制保证数据库服务持续可用。
-> 值得注意的一点是,尽管数据库服务可用,但当有一个 server 崩溃时,连接到它的客户端必须定向或故障转移到不同的 server。这不是组复制要解决的问题。连接器,负载均衡器,路由器或其他形式的中间件更适合处理这个问题。
总之,MGR组复制提供了高可用性,高弹性,可靠的 MySQL 服务。
MGR故障检测
故障检测是提供关于哪些 server 可能已死的信息(猜测)的分布式服务。 某个 server 无响应时触发猜测,组中其余成员进行协调决定以排除给定成员。如果某个 server 与组的其余成员隔离,则它会怀疑所有其他 server 都失败了。由于无法与组达成协议(因为它无法确保仲裁成员数),其怀疑不会产生后果。当服务器以此方式与组隔离时,它无法执行任何本地事务。 在线 server 列表通常称为视图,新成员server的加入离开,无论是自愿还是被迫的离开,该组都会动态地重新规划其配置,并触发视图更新
MGR的限制
- 存储引擎必须为Innodb,即仅支持InnoDB表,并且每张表一定要有一个主键,用于做write set的冲突检测;
- 每个表必须提供主键;
- 只支持ipv4,网络需求较高;
- 必须打开GTID特性,二进制日志格式必须设置为ROW,用于选主与write set;
- COMMIT可能会导致失败,类似于快照事务隔离级别的失败场景;
- 目前一个MGR集群组最多支持9个节点;
- 不支持外键于save point特性,无法做全局间的约束检测与部分部分回滚;
- 二进制日志binlog不支持Replication event checksums;
- 多主模式(也就是多写模式) 不支持SERIALIZABLE事务隔离级别;
- 多主模式不能完全支持级联外键约束;
- 多主模式不支持在不同节点上对同一个数据库对象并发执行DDL(在不同节点上对同一行并发进行RW事务,后发起的事务会失败);
MGR组复制优势
- 弹性复制(高扩展性):server动态添加移除
- 高可用分片(高扩展性):分片实现写扩展,每个分片是一个复制组。
- 替代主从复制(高扩展性):整组写入,避免单点争用。
- 自动化系统:自动化部署Mysql复制到已有复制协议的自动化系统。
- 故障检测与容错:自动检测,若服务faild,组内成员大多数达成认为该服务已不正常,则自动隔离。
- 组内成员会构成一个视图,组内成员主动加入或离开(主动或被动),都会更新组配置,更新视图。成员自愿离开,先更新组配置,然后采用大多数成员(不包含主动脱离的成员)意见是否确认该成员离开更新视图。如果是故障要排除,则需大多数服务确认(包括故障成员意见),然后才会更新组配置和视图。
- 最大允许即时故障数:f=(n-1)/2,多数正常则正常
3. 组复制两种运行模式
-> 在单主模式下, 组复制具有自动选主功能,每次只有一个 server成员接受更新。单写模式group内只有一台节点可写可读,其他节点只可以读。对于group的部署,需要先跑起primary节点(即那个可写可读的节点,read_only = 0)然后再跑起其他的节点,并把这些节点一一加进group。其他的节点就会自动同步primary节点上面的变化,然后将自己设置为只读模式(read_only = 1)。当primary节点意外宕机或者下线,在满足大多数节点存活的情况下,group内部发起选举,选出下一个可用的读节点,提升为primary节点。primary选举根据group内剩下存活节点的UUID按字典序升序来选择,即剩余存活的节点按UUID字典序排列,然后选择排在最前的节点作为新的primary节点。
-> 在多主模式下, 所有的 server 成员都可以同时接受更新。group内的所有机器都是primary节点,同时可以进行读写操作,并且数据是最终一致的。
按照我的理解来说:
单主模式:比多主模式多一个选举程序,第一次引导开启集群的为主,后加入的为追随者(也可以叫从机Slave),只有住的有读写权限,别的追随者在加入组的时候自动把权限禁了。如果主的挂了,其他服务器会根据UUID和一个值(类似权重)进行重新选主。每次选主都会重新把权限禁一遍。
多主模式:所有服务器加入组时,读写权限全部放开,大家都可以读写,但是只能更改不同行的数据,如果后加入集群的服务器改了一行数据,那前面的服务器就不能再对这行数据进行改动了,如果改动则报事务回滚取消改动,而后加入的可以改前面加入集群改过的数据。
环境: centos 7.6.1810 三台ip分别: 10.10.100.186 10.10.100.194 10.10.100.195
一、mysql 8.0.18 MGR搭建
1、下载mysql免安装包, 准备好系统环境
mysql-8.0.18-linux-glibc2.12-x86_64.tar #不选择安装系统平台下载即可,此版本解压即可使用
systemctl stop firewalld.service #停止防火墙
systemctl disable firewalld.service #禁止开机启动
sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/sysconfig/selinux
cat /etc/sysconfig/selinux
vim /etc/hosts #增加三台机器的ip域名映射
dmesg | grep -i scheduler #确定centos7的io调度算法 deadline
echo "vm.swappiness = 0" >>/etc/sysctl.conf #0表示尽力使用物理内存,100尽力使用swap分区
sysctl -p
#通过sysctl -a 查看
cat /etc/security/limits.conf #更改系统限制
...
* soft nproc 65536
* hard nproc 65536
* soft nofile 65536
* hard nofile 65536
2、安装mysql,初始化配置,开启group_replication
-----创建mysql用户和组
# useradd -u 1001 -d /home/mysql -m mysql
# id mysql
# rpm -e `rpm -qa|grep mysql` --nodeps
# yum install -y autoconf automake imake libxml2-devel expat-devel cmake gcc gcc-c++ libaio libaio-devel bzr bison libtool ncurses5-devel ncurses-devle perl-DBD-mysql
-----解压安装包并设置权限,并创建相关目录
tar xf mysql-8.0.18-linux-glibc2.12-x86_64.tar -C /usr/local/
cd /usr/local
mv mysql-8.0.18-linux-glibc2.12-x86_64 mysql
chown mysql:mysql /usr/local/mysql -R
cp -f mysql/support-files/mysql.server /etc/init.d/mysqld
vim /etc/my.cnf #建立配置文件

#根据实际情况配置 [client] socket=/service/data/sock/mysql.sock #sock文件所在路径 [mysqld] user=mysql server-id=186 basedir=/usr/local/mysql/ socket=/service/data/sock/mysql.sock pid-file=/service/data/sock/mysql.pid datadir=/service/data/data tmpdir=/service/data/tmpdir log-error=/service/data/log/error.log log_timestamps = SYSTEM slow_query_log=1 log_queries_not_using_indexes=1 slow_query_log_file=/service/data/slowlog/slow-query.log log-bin=/service/data/binlog/mysql-bin relay-log=/service/data/relaylog/mysql-relay-bin innodb_data_home_dir=/service/data/ib innodb_log_group_home_dir=/service/data/redo innodb_undo_directory=/service/data/undo/ innodb_undo_tablespaces=3 character_set_server=utf8mb4 #MGR使用乐观锁,所以官网建议隔离级别是RC,减少锁粒度 transaction_isolation=READ-COMMITTED gtid_mode=on enforce_gtid_consistency=1 binlog_format=row secure_file_priv=/tmp performance_schema=ON innodb_redo_log_archive_dirs=/service/data/redo_archive #因为集群会在故障恢复时互相检查binlog的数据,所以需要记录下集群内其他服务器发过来已经执行过的binlog,按GTID来区分是否执行过. log-slave-updates=1 #binlog校验规则,5.6之后的高版本是CRC32,低版本都是NONE,但是MGR要求使用NONE binlog_checksum=NONE #基于安全的考虑,MGR集群要求复制模式要改成slave记录记录到表中,不然就报错 master_info_repository=TABLE relay_log_info_repository=TABLE #记录事务的算法,官网建议设置该参数使用 XXHASH64 算法 transaction_write_set_extraction = XXHASH64 #相当于此GROUP的名字,是UUID值,不能和集群内其他GTID值的UUID混用,可用uuidgen来生成 loose-group_replication_group_name = 'de720075-8b42-4833-a4d9-a60e8efb8f5a' #是否随服务器启动而自动启动组复制,不建议直接启动,怕故障恢复时有扰乱数据准确性的特殊情况 loose-group_replication_start_on_boot = OFF #本地MGR的IP地址和端口,host:port,是MGR的端口,不是数据库的端口 loose-group_replication_local_address = '10.10.100.186:33061' #需要接受本MGR实例控制的服务器IP地址和端口,是MGR的端口,不是数据库的端口 loose-group_replication_group_seeds = '10.10.100.186:33061,10.10.100.194:33061,10.10.100.195:33061' #开启引导模式,添加组成员,用于第一次搭建MGR或重建MGR的时候使用,只需要在集群内的其中一台开启 loose-group_replication_bootstrap_group = OFF #IP地址白名单,不会允许来自外部主机的连接,按需安全设置 #loose-group_replication_ip_whitelist = report_host=10.10.100.186 report_port=3306

[mysqld] server_id = 19 user = mysql expire_logs_days = 10 log_bin_trust_function_creators = ON explicit_defaults_for_timestamp = ON symbolic_links = 0 show_compatibility_56 = ON lower_case_table_names = 1 plugin_load = validate_password.so plugin_load = group_replication.so secure_file_priv=/tmp tmp_table_size = 48M max_heap_table_size = 48M max_connections = 2000 max_connect_errors = 20000 port = 3306 datadir = /service/data/ socket = /service/data/mysqld.sock log_bin = /service/data/mysql-bin skip_name_resolve log_error = /service/data/mysqld.log pid-file = /service/data/mysqld.pid innodb_flush_method = O_DIRECT innodb_flush_log_at_trx_commit=2 gtid_mode = ON enforce-gtid-consistency = true binlog_format = ROW master_info_repository = TABLE relay_log_info_repository = TABLE binlog_checksum = NONE log_slave_updates = ON transaction_write_set_extraction = XXHASH64 loose_group_replication_start_on_boot = OFF loose_group_replication_bootstrap_group = OFF loose_group_replication_group_name = aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaddddd loose_group_replication_local_address = '10.10.100.19:6606' loose_group_replication_group_seeds = '10.10.100.19:6606,10.10.100.28:6606,10.10.100.39:6606' loose_group_replication_member_weight = 80 group_replication_single_primary_mode = TRUE auto_increment_increment = 1 loose_group_replication_auto_increment_increment = 1 sql_mode = NO_ENGINE_SUBSTITUTION query_cache_type = 0 event_scheduler = ON innodb_log_group_home_dir = /service/data/ innodb_buffer_pool_size = 12G innodb_log_file_size = 512M innodb_file_per_table = ON innodb_log_files_in_group = 2 innodb_buffer_pool_instances = 8 innodb_page_cleaners = 8 innodb_read_io_threads = 8 character_set_server = utf8mb4 collation_server = utf8mb4_unicode_ci innodb_undo_directory = /service/data/ log_timestamps = SYSTEM innodb_page_cleaners = 32 innodb_doublewrite=0 innodb_flush_log_at_trx_commit=2 innodb_flush_method=O_DIRECT_NO_FSYNC innodb_thread_concurrency=64 sync_binlog=0 [mysql] default_character_set = utf8mb4 socket = /service/data/mysqld.sock [client] default_character_set = utf8mb4 socket = /service/data/mysqld.sock
mkdir -p /service/data/{data,sock,tmpdir,log,ib,redo,undo,slowlog,binlog,relaylog}
chown -R mysql.mysql /service/
-----更改/home/mysql/.bash_profie和/etc/profile,增加以下内容
PATH=$PATH:$HOME/.local/bin:$HOME/bin:/usr/local/mysql/bin
LD_LIBRARY_PATH=/usr/local/mysql/lib/
export PATH
export LD_LIBRARY_PATH
-------------------------------------------
./usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf --initialize-insecure &
./bin/mysqld_safe --defaults-file=/etc/my.cnf & /etc/my.cnf>/etc/mysql/my.cnf>$mysql_home/my.cnf>~/.my.cnf
ps -ef |grep mysql
netstat -anultp |grep 3306
yum -y install net-tools
mysql -S /service/data/sock/mysql.sock #登录数据库
mysql>install plugin group_replication soname 'group_replication.so'; #安装插件
mysql>set sql_log_bin=0; #关闭bin log
mysql>alter user root@'%' identified with mysql_native_password by 'password';
mysql>grant all on *.* to root@'%' with grant option;
mysql>create user repl@'%';
mysql>alter user 'repl'@'%' identified with mysql_native_password by 'repl';
mysql>grant replication slave on *.* to repl@'%'with mysql_native identified by 'repl';
mysql>select user,host,plugin from mysql.user; #查看用户 登录主机 使用加密模式
mysql>flush privileges;
mysql>set sql_log_bin=1;
mysql>change master to master_user='repl',master_password='repl' for channel 'group_replication_recovery'; #创建复制
mysql>SET GLOBAL group_replication_bootstrap_group=ON;
mysql>START GROUP_REPLICATION;
mysql>SET GLOBAL group_replication_bootstrap_group=OFF;
mysql>SELECT * FROM performance_schema.replication_group_members; #查看MGR组信息
---------------报错无法加入或者组成员是recovering状态--------
mysql>set global group_replication_allow_local_disjoint_gtids_join=ON; #开启前关闭
-----recovering状态
mysql>show global variables like 'gtid_purged'; #查看主库的gtid
mysql>stop group_replication; #从库操作
mysql>reset master; #从库操作
mysql>set @@global.gtid_purged='主库查到的gtid';
mysql>start group_replication;
mysql>select * from performance_schema.replication_group_members; #再次查看mgr组信息
二、proxysql 搭建
2.1、下载proxysql并安装在 194 195两台机器上
rpm -ivh proxysql-2.0.8-1-centos7.x86_64.rpm
service proxysql start
2.2、在任意一台mysql上查看当前的 primary master
SELECT member_host as "primary master" FROM performance_schema.global_status JOIN performance_schema.replication_group_members WHERE variable_name = 'group_replication_primary_member' AND member_id=variable_value;
+----------------+
| primary master |
+----------------+
| 10.10.100.186 |
+----------------+
1 row in set (0.00 sec)
2.3、在primary master 10.10.100.186止执行以下:
mysql> create USER monitor@'%';
mysql> GRANT SELECT on sys.* to 'monitor'@'%' identified by 'monitor';
mysql> create USER proxysql@'%';
mysql> grant all privileges on *.* to 'proxysql'@'%' identified by 'proxysql';
mysql> flush privileges;
2.4、在primary master的mysql上创建proxysql所需的表和函数
mysql > USE sys;
DELIMITER $$
CREATE FUNCTION IFZERO(a INT, b INT)
RETURNS INT
DETERMINISTIC
RETURN IF(a = 0, b, a)$$
CREATE FUNCTION LOCATE2(needle TEXT(10000), haystack TEXT(10000), offset INT)
RETURNS INT
DETERMINISTIC
RETURN IFZERO(LOCATE(needle, haystack, offset), LENGTH(haystack) + 1)$$
CREATE FUNCTION GTID_NORMALIZE(g TEXT(10000))
RETURNS TEXT(10000)
DETERMINISTIC
RETURN GTID_SUBTRACT(g, '')$$
CREATE FUNCTION GTID_COUNT(gtid_set TEXT(10000))
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE result BIGINT DEFAULT 0;
DECLARE colon_pos INT;
DECLARE next_dash_pos INT;
DECLARE next_colon_pos INT;
DECLARE next_comma_pos INT;
SET gtid_set = GTID_NORMALIZE(gtid_set);
SET colon_pos = LOCATE2(':', gtid_set, 1);
WHILE colon_pos != LENGTH(gtid_set) + 1 DO
SET next_dash_pos = LOCATE2('-', gtid_set, colon_pos + 1);
SET next_colon_pos = LOCATE2(':', gtid_set, colon_pos + 1);
SET next_comma_pos = LOCATE2(',', gtid_set, colon_pos + 1);
IF next_dash_pos < next_colon_pos AND next_dash_pos < next_comma_pos THEN
SET result = result +
SUBSTR(gtid_set, next_dash_pos + 1,
LEAST(next_colon_pos, next_comma_pos) - (next_dash_pos + 1)) -
SUBSTR(gtid_set, colon_pos + 1, next_dash_pos - (colon_pos + 1)) + 1;
ELSE
SET result = result + 1;
END IF;
SET colon_pos = next_colon_pos;
END WHILE;
RETURN result;
END$$
CREATE FUNCTION gr_applier_queue_length()
RETURNS INT
DETERMINISTIC
BEGIN
RETURN (SELECT sys.gtid_count( GTID_SUBTRACT( (SELECT
Received_transaction_set FROM performance_schema.replication_connection_status
WHERE Channel_name = 'group_replication_applier' ), (SELECT
@@global.GTID_EXECUTED) )));
END$$
CREATE FUNCTION gr_member_in_primary_partition()
RETURNS VARCHAR(3)
DETERMINISTIC
BEGIN
RETURN (SELECT IF( MEMBER_STATE='ONLINE' AND ((SELECT COUNT(*) FROM
performance_schema.replication_group_members WHERE MEMBER_STATE != 'ONLINE') >=
((SELECT COUNT(*) FROM performance_schema.replication_group_members)/2) = 0),
'YES', 'NO' ) FROM performance_schema.replication_group_members JOIN
performance_schema.replication_group_member_stats USING(member_id));
END$$
CREATE VIEW gr_member_routing_candidate_status AS SELECT
sys.gr_member_in_primary_partition() as viable_candidate,
IF( (SELECT (SELECT GROUP_CONCAT(variable_value) FROM
performance_schema.global_variables WHERE variable_name IN ('read_only',
'super_read_only')) != 'OFF,OFF'), 'YES', 'NO') as read_only,
sys.gr_applier_queue_length() as transactions_behind, Count_Transactions_in_queue as 'transactions_to_cert' from performance_schema.replication_group_member_stats;$$
DELIMITER ;
2.5、在 194和195上各执行一遍以下命令:
# su - mysql
$mysql -u admin -padmin -h 127.0.0.1 -P6032 --prompt='Admin>' --default-auth=mysql_native_password
Admin> INSERT INTO mysql_servers(hostgroup_id,hostname,port) VALUES (0,'10.10.100.186',3306);
Admin> INSERT INTO mysql_servers(hostgroup_id,hostname,port) VALUES (0,'10.10.100.194',3306);
Admin> INSERT INTO mysql_servers(hostgroup_id,hostname,port) VALUES (0,'10.10.100.195',3306);
Admin>insert into mysql_group_replication_hostgroups (writer_hostgroup,backup_writer_hostgroup,
reader_hostgroup, offline_hostgroup,active,max_writers,writer_is_also_reader,max_transactions_behind)
values (0,4,3,1,1,1,0,100);
Admin> save mysql servers to disk;
Query OK, 0 rows affected (0.01 sec)
Admin> load mysql servers to runtime;
Admin> insert into mysql_users(username, password) values('proxysql', 'BGT%6yhn');
Admin> INSERT INTO mysql_query_rules(rule_id,active,match_pattern,destination_hostgroup,apply) VALUES(1,1,'^SELECT.*FOR UPDATE$',0,1);
Admin> INSERT INTO mysql_query_rules(rule_id,active,match_pattern,destination_hostgroup,apply) VALUES(2,1,'^SELECT',0,1);
Admin> INSERT INTO mysql_query_rules(rule_id,active,match_pattern,destination_hostgroup,apply) VALUES(3,1,'^^[^SEL]',0,1);
Admin> LOAD MYSQL VARIABLES TO RUNTIME;
Admin> SAVE MYSQL VARIABLES TO DISK;
Admin> LOAD MYSQL SERVERS TO RUNTIME;
Admin> SAVE MYSQL SERVERS TO DISK;
Admin> LOAD MYSQL USERS TO RUNTIME;
Admin> SAVE MYSQL USERS TO DISK;
Admin>exit
2.6、在194和195上分别重启proxysql
service proxysql restart
2.7、此时实现了在 194和195上可以通过6033端口访问mysql:
mysql -u root -p -P 6033
三、在 194和195上安装keepalived:
3.1、分别执行安装
yum -y install openssl-devel
tar -xf keepalived-2.0.19.tar.gz
cd keepalived-2.0.19
./configure --prefix=/usr/local/keepalived
make && make install
cp /usr/local/keepalived/sbin/keepalived /etc/rc.d/init.d/keepalived
cp /usr/local/keepalived/etc/sysconfig/keepalived /etc/sysconfig/keepalived
mkdir /etc/keepalived
3.2、在mysql 194上创建arp.sh chk_proxy.sh keepalived.conf 文件
cat arp.sh
#!/bin/bash
VIP=10.10.100.111
GATEWAY=10.10.100.1
/sbin/arping -I eth0 -c 5 -s $VIP $GATEWAY > /dev/null 2>&1
cat chk_proxy.sh
#!/bin/bash
killall -0 proxysql
cat keepalived.conf
global_defs {
# Keepalived process identifier
router_id proxy_HA
# lvs_id proxy_HA
}
# Script used to check if Proxy is running
vrrp_script check_proxy {
script "/etc/keepalived/chk_proxy.sh"
interval 2
weight 10
user root
}
vrrp_sync_group VG001 {
group {
VI_001
}
notify_master "/etc/keepalived/arp.sh"
}
# Virtual interface
# The priority specifies the order in which the assigned interface to take over in a failover
vrrp_instance VI_001 {
# state MASTER
state BACKUP
interface eth0
virtual_router_id 75
priority 125
# nopreempt
# The virtual ip address shared between the two loadbalancers
virtual_ipaddress {
10.10.100.111
}
track_script {
check_proxy
}
}
3.3、在mysql 195上创建 arp.sh chk_proxy.sh keepalived.conf 文件:
cat arp.sh
#!/bin/bash
VIP=10.10.100.111
GATEWAY=10.10.100.1
/sbin/arping -I eth0 -c 5 -s $VIP $GATEWAY > /dev/null 2>&1
cat chk_proxy.sh
#!/bin/bash
killall -0 proxysql
cat keepalived.conf
global_defs {
# Keepalived process identifier
router_id proxy_HA
# lvs_id proxy_HA
}
# Script used to check if Proxy is running
vrrp_script check_proxy {
script "/etc/keepalived/chk_proxy.sh"
interval 2
weight 10
user root
}
vrrp_sync_group VG001 {
group {
VI_001
}
notify_master "/etc/keepalived/arp.sh"
}
# Virtual interface
# The priority specifies the order in which the assigned interface to take over in a failover
vrrp_instance VI_001 {
# state MASTER
state BACKUP
interface eth0
virtual_router_id 75
priority 120
# nopreempt
# The virtual ip address shared between the two loadbalancers
virtual_ipaddress {
10.10.100.111
}
track_script {
check_proxy
}
}
3.4、分别重启mysql194 mysql195上的keepalived:
/etc/init.d/keepalived
3.5、在mysql194 MYSQL195 查看vip及keepalived状态:
ip addr list |grep 10.10.100.111
3.6、可通过vip访问proxysql和mysql:
mysql -h 10.10.100.111 -u proxysql -p -P 6033