0

0

MySQL MGR集群单主模式的自动搭建和自动化故障修复

雪夜

雪夜

发布时间:2025-07-15 08:54:14

|

864人浏览过

|

来源于php中文网

原创

随着mysql mgr版本的升级和技术的成熟,mgr在取代mha成为mysql高可用首选方案后,其搭建过程虽然不算复杂,但仍涉及一系列手动操作。为了简化mgr的搭建和故障诊断,开发了一个自动化脚本,用于实现mgr的自动化搭建、自动化故障诊断及修复。

为了简化起见,MGR的自动化搭建测试采用单机多实例模式,预先安装了三个MySQL实例,端口号分别为7001、7002和7003,其中7001作为写入节点,其余两个节点作为读取节点,8000节点为作者的另一个测试节点,可忽略。在指定主从节点的情况下,以下是mgr_tool.py一键搭建MGR集群的测试demo。

MySQL MGR集群单主模式的自动搭建和自动化故障修复MGR故障模拟1

MGR节点故障的自动监测和自愈实现如下图所示,搭建完成后的MGR集群目前处于完全正常的状态。

MySQL MGR集群单主模式的自动搭建和自动化故障修复主观造成主从节点间binlog的丢失

MySQL MGR集群单主模式的自动搭建和自动化故障修复在主节点上对从节点丢失的数据进行操作,GTID无法找到对应的数据,组复制立即停止。

MySQL MGR集群单主模式的自动搭建和自动化故障修复非写入节点出现错误

MySQL MGR集群单主模式的自动搭建和自动化故障修复查看errorlog

MySQL MGR集群单主模式的自动搭建和自动化故障修复如果手动解决,仍需使用GTID跳过错误事务的常规方法,获取master上的GTID信息。

MySQL MGR集群单主模式的自动搭建和自动化故障修复尝试跳过最新的一个事务ID,然后重新连接到组,可以正常连接到组,另一个节点仍处于错误状态。

stop group_replication;

SET GTID_NEXT='6c81c118-e67c-4416-9cb0-2d573d178c1d:13';

BEGIN; COMMIT;

set gtid_next='automatic';

MVM mall 网上购物系统
MVM mall 网上购物系统

采用 php+mysql 数据库方式运行的强大网上商店系统,执行效率高速度快,支持多语言,模板和代码分离,轻松创建属于自己的个性化用户界面 v3.5更新: 1).进一步静态化了活动商品. 2).提供了一些重要UFT-8转换文件 3).修复了除了网银在线支付其它支付显示错误的问题. 4).修改了LOGO广告管理,增加LOGO链接后主页LOGO路径错误的问题 5).修改了公告无法发布的问题,可能是打压

下载

MySQL MGR集群单主模式的自动搭建和自动化故障修复另一个节点类似,依次解决。

MGR故障模拟2

从节点脱离Group

MySQL MGR集群单主模式的自动搭建和自动化故障修复这种情况相对简单,重新启动组复制即可,执行start group_replication。

MGR故障自动检测和修复

对于上述两种情况,1,如果是从节点丢失主节点的事务,尝试在从节点上跳过GTID,重新开始复制即可;2,如果是从节点非丢失主节点事务,尝试在从节点重新开始组复制即可。

实现代码如下:

def auto_fix_mgr_error(conn_master_dict,conn_slave_dict):
    group_replication_status = get_group_replication_status(conn_slave_dict)
    if(group_replication_status[0]["MEMBER_STATE"]=="ERROR" or group_replication_status[0]["MEMBER_STATE"] == "OFFLINE"):
        print(conn_slave_dict["host"]+str(conn_slave_dict["port"])+'------>'+group_replication_status[0]["MEMBER_STATE"])
        print("auto fixing......")
        while 1 > 0:
            master_gtid_list = get_gtid(conn_master_dict)
            slave_gtid_list = get_gtid(conn_slave_dict)
            master_executed_gtid_value = int((master_gtid_list[-1]["Executed_Gtid_Set"]).split("-")[-1])
            slave_executed_gtid_value = int(slave_gtid_list[-1]["Executed_Gtid_Set"].split("-")[-1])
            slave_executed_gtid_prefix = slave_gtid_list[-1]["Executed_Gtid_Set"].split(":")[0]
            slave_executed_skiped_gtid = slave_executed_gtid_value + 1
            if (master_executed_gtid_value > slave_executed_gtid_value):
                print("skip gtid and restart group replication,skiped gtid is "
                      + slave_gtid_list[-1]["Executed_Gtid_Set"].split(":")[-1].split("-")[0]
                      + ":"+str(slave_executed_skiped_gtid))
                slave_executed_skiped_gtid = slave_executed_gtid_prefix+":"+str(slave_executed_skiped_gtid)
                skip_gtid_on_slave(conn_slave_dict,slave_executed_skiped_gtid)
                time.sleep(10)
                start_group_replication(conn_slave_dict)
                if(get_group_replication_status(conn_slave_dict)[0]["MEMBER_STATE"]=="ONLINE"):
                    print("mgr cluster fixed,back to normal")
                    break
            else:
                start_group_replication(conn_slave_dict)
                if(get_group_replication_status(conn_slave_dict)[0]["MEMBER_STATE"]=="ONLINE"):
                    print("mgr cluster fixed,back to normal")
                break
    elif (group_replication_status[0]['MEMBER_STATE'] == 'ONLINE'):
        print("mgr cluster is normal,nothing to do")
        check_replication_group_members(conn_slave_dict)

对于故障类型1,GTID事务不一致的自动化修复

MySQL MGR集群单主模式的自动搭建和自动化故障修复 对于故障类型2从节点offline的自动化修复

MySQL MGR集群单主模式的自动搭建和自动化故障修复完整的实现代码

该过程要求MySQL实例必须满足MGR的基本条件,如果环境本身无法满足MGR,一切都无从谈起,因此要非常清楚MGR环境的最基本要求。

完成的实现代码如下,花了一个下午编写,目前存在以下不足:1,创建复制用户时,未指定具体的slave机器,目前直接指定为%:create user repl@'%' identified by repl2;2,对于slave的修复,目前无法整体修复,只能一台一台修复,实际上是缺少了一个循环slave机器判断的过程;3,目前搭建之前都会reset master(不管主从,主要是清理可能的残留GTID),因此只适合新环境的搭建;4,目前只支持offline和gtid事务冲突的错误类型修复,无法支持其他MGR错误类型的修复;5,开发环境是单机多实例模式测试,没有在多机单实例模式下充分测试。这些问题将逐步改善和加强。

# -*- coding: utf-8 -*-

import pymysql import logging import time import decimal

def execute_query(conn_dict,sql): conn = pymysql.connect(host=conn_dict['host'], port=conn_dict['port'], user=conn_dict['user'], passwd=conn_dict['password'], db=conn_dict['db']) cursor = conn.cursor(pymysql.cursors.DictCursor) cursor.execute(sql) list = cursor.fetchall() cursor.close() conn.close() return list

def execute_noquery(conn_dict,sql): conn = pymysql.connect(host=conn_dict['host'], port=conn_dict['port'], user=conn_dict['user'], passwd=conn_dict['password'], db=conn_dict['db']) cursor = conn.cursor() cursor.execute(sql) conn.commit() cursor.close() conn.close() return list

def get_gtid(conn_dict): sql = "show master status;" list = execute_query(conn_dict,sql) return list

def skip_gtid_on_slave(conn_dict,gtid): sql_1 = 'stop group_replication;' sql_2 = '''set gtid_next='{0}';'''.format(gtid) sql_3 = 'begin;' sql_4 = 'commit;' sql_5 = '''set gtid_next='automatic';'''

try:
    execute_noquery(conn_dict, sql_1)
    execute_noquery(conn_dict, sql_2)
    execute_noquery(conn_dict, sql_3)
    execute_noquery(conn_dict, sql_4)
    execute_noquery(conn_dict, sql_5)
except:
    raise

def get_group_replication_status(conn_dict): sql = '''select MEMBER_STATE from performance_schema.replication_group_members where (MEMBER_HOST = '{0}' or ifnull(MEMBER_HOST,'') = '') AND (MEMBER_PORT={1} or ifnull(MEMBER_PORT,'') ='') ; '''.format(conn_dict["host"], conn_dict["port"]) result = execute_query(conn_dict,sql) if result: return result else: return None

def check_replication_group_members(conn_dict): print('-------------------------------------------------------') result = execute_query(conn_dict, " select * from performance_schema.replication_group_members; ") if result: column = result[0].keys() current_row = '' for key in column: current_row += str(key) + " " print(current_row)

    for row in result:
        current_row = ''
        for key in row.values():
            current_row += str(key) + " "
        print(current_row)
print('-------------------------------------------------------')

def auto_fix_mgr_error(conn_master_dict,conn_slave_dict): group_replication_status = get_group_replication_status(conn_slave_dict) if(group_replication_status[0]["MEMBER_STATE"]=="ERROR" or group_replication_status[0]["MEMBER_STATE"] == "OFFLINE"): print(conn_slave_dict["host"]+str(conn_slave_dict["port"])+'------>'+group_replication_status[0]["MEMBER_STATE"]) print("auto fixing......") while 1 > 0: master_gtid_list = get_gtid(conn_master_dict) slave_gtid_list = get_gtid(conn_slave_dict) master_executed_gtid_value = int((master_gtid_list[-1]["Executed_Gtid_Set"]).split("-")[-1]) slave_executed_gtid_value = int(slave_gtid_list[-1]["Executed_Gtid_Set"].split("-")[-1]) slave_executed_gtid_prefix = slave_gtid_list[-1]["Executed_Gtid_Set"].split(":")[0] slave_executed_skiped_gtid = slave_executed_gtid_value + 1 if (master_executed_gtid_value > slave_executed_gtid_value): print("skip gtid and restart group replication,skiped gtid is "

  • slave_gtid_list[-1]["Executed_Gtid_Set"].split(":")[-1].split("-")[0]
  • ":"+str(slave_executed_skiped_gtid)) slave_executed_skiped_gtid = slave_executed_gtid_prefix+":"+str(slave_executed_skiped_gtid) skip_gtid_on_slave(conn_slave_dict,slave_executed_skiped_gtid) time.sleep(10) start_group_replication(conn_slave_dict) if(get_group_replication_status(conn_slave_dict)[0]["MEMBER_STATE"]=="ONLINE"): print("mgr cluster fixed,back to normal") break else: start_group_replication(conn_slave_dict) if(get_group_replication_status(conn_slave_dict)[0]["MEMBER_STATE"]=="ONLINE"): print("mgr cluster fixed,back to normal") break elif (group_replication_status[0]['MEMBER_STATE'] == 'ONLINE'): print("mgr cluster is normal,nothing to do") check_replication_group_members(conn_slave_dict)

'''reset master''' def reset_master(conn_dict): try: execute_noquery(conn_dict, "reset master;") except: raise

def install_group_replication_plugin(conn_dict): get_plugin_sql = "SELECT name,dl FROM mysql.plugin WHERE name = 'group_replication';" install_plugin_sql = '''install plugin group_replication soname 'group_replication.so'; ''' try: result = execute_query(conn_dict, get_plugin_sql) if not result: execute_noquery(conn_dict, install_plugin_sql) except: raise

def create_mgr_repl_user(conn_master_dict,user,password): try: reset_master(conn_master_dict) sql_exists_user = '''select user from mysql.user where user = '{0}'; '''.format(user) user_list = execute_query(conn_master_dict,sql_exists_user) if not user_list: create_user_sql = '''create user {0}@'%' identified by '{1}'; '''.format(user,password) grant_privilege_sql = '''grant replication slave on . to {0}@'%';'''.format(user) execute_noquery(conn_master_dict,create_user_sql) execute_noquery(conn_master_dict, grant_privilege_sql) execute_noquery(conn_master_dict, "flush privileges;") except: raise

def set_super_read_only_off(conn_dict): super_read_only_off = '''set global super_read_only = 0;''' execute_noquery(conn_dict, super_read_only_off)

def open_group_replication_bootstrap_group(conn_dict): sql = '''select variable_name,variable_value from performance_schema.global_variables where variable_name = 'group_replication_bootstrap_group';''' result = execute_query(conn_dict, sql) open_bootstrap_group_sql = '''set @@global.group_replication_bootstrap_group=on;''' if result and result[0]['variable_value']=="OFF": execute_noquery(conn_dict, open_bootstrap_group_sql)

def close_group_replication_bootstrap_group(conn_dict): sql = '''select variable_name,variable_value from performance_schema.global_variables where variable_name = 'group_replication_bootstrap_group';''' result = execute_query(conn_dict, sql) close_bootstrap_group_sql = '''set @@global.group_replication_bootstrap_group=off;''' if result and result[0]['variable_value'] == "ON": execute_noquery(conn_dict, close_bootstrap_group_sql)

def start_group_replication(conn_dict): start_group_replication = '''start group_replication;''' group_replication_status = get_group_replication_status(conn_dict) if not (group_replication_status[0]['MEMBER_STATE'] == 'ONLINE'): execute_noquery(conn_dict, start_group_replication)

def connect_to_group(conn_dict,repl_user,repl_password): connect_to_group_sql = '''change master to master_user='{0}', master_password='{1}' for channel 'group_replication_recovery'; '''.format(repl_user,repl_password) try: execute_noquery(conn_dict, connect_to_group_sql) except: raise

def start_mgr_on_master(conn_master_dict,repl_user,repl_password): try: set_super_read_only_off(conn_master_dict) reset_master(conn_master_dict) create_mgr_repl_user(conn_master_dict,repl_user,repl_password) connect_to_group(conn_master_dict,repl_user,repl_password)

    open_group_replication_bootstrap_group(conn_master_dict)
    start_group_replication(conn_master_dict)
    close_group_replication_bootstrap_group(conn_master_dict)

    group_replication_status = get_group_replication_status(conn_master_dict)
    if (group_replication_status[0]['MEMBER_STATE'] == 'ONLINE'):
        print("master added in mgr and run successfully")
        return True
except:
    raise
print("############start master mgr error################")
exit(1)

def start_mgr_on_slave(conn_slave_dict,repl_user,repl_password): try: set_super_read_only_off(conn_slave_dict) reset_master(conn_slave_dict) connect_to_group(conn_slave_dict,repl_user,repl_password) start_group_replication(conn_slave_dict)

wait for 10

    time.sleep(10)
    # then check mgr status
    group_replication_status = get_group_replication_status(conn_slave_dict)
    if (group_replication_status[0]['MEMBER_STATE'] == 'ONLINE'):
        print("slave added in mgr and run successfully")
    if (group_replication_status[0]['MEMBER_STATE'] == 'RECOVERING'):
        print("slave is recovering")
except:
    print("############start slave mgr error################")
    exit(1)

def auto_mgr(conn_master,conn_slave_1,conn_slave_2,repl_user,repl_password): install_group_replication_plugin(conn_master) master_replication_status = get_group_replication_status(conn_master)

if not (master_replication_status[0]['MEMBER_STATE'] == 'ONLINE'):
    start_mgr_on_master(conn_master,repl_user,repl_password)

slave1_replication_status = get_group_replication_status(conn_slave_1)
if not (slave1_replication_status[0]['MEMBER_STATE'] == 'ONLINE'):
    install_group_replication_plugin(conn_slave_1)
    start_mgr_on_slave(conn_slave_1, repl_user, repl_user)

slave2_replication_status = get_group_replication_status(conn_slave_2)
if not (slave2_replication_status[0]['MEMBER_STATE'] == 'ONLINE'):
    install_group_replication_plugin(conn_slave_2)
    start_mgr_on_slave(conn_slave_2, repl_user, repl_user)

check_replication_group_members(conn_master)

if name == 'main': conn_master = {'host': '127.0.0.1', 'port': 7001, 'user': 'root', 'password': 'root', 'db': 'mysql', 'charset': 'utf8mb4'} conn_slave_1 = {'host': '127.0.0.1', 'port': 7002, 'user': 'root', 'password': 'root', 'db': 'mysql', 'charset': 'utf8mb4'} conn_slave_2 = {'host': '127.0.0.1', 'port': 7003, 'user': 'root', 'password': 'root', 'db': 'mysql', 'charset': 'utf8mb4'} repl_user = "repl" repl_password = "repl"

auto_mgr(conn_master,conn_slave_1,conn_slave_2,repl_user,repl_password)

auto_fix_mgr_error(conn_master,conn_slave_1)
check_replication_group_members(conn_master)

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

683

2023.10.12

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

322

2023.10.27

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

348

2024.02.23

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

1095

2024.03.06

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

358

2024.03.06

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

677

2024.04.07

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

575

2024.04.29

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

417

2024.04.29

Java JVM 原理与性能调优实战
Java JVM 原理与性能调优实战

本专题系统讲解 Java 虚拟机(JVM)的核心工作原理与性能调优方法,包括 JVM 内存结构、对象创建与回收流程、垃圾回收器(Serial、CMS、G1、ZGC)对比分析、常见内存泄漏与性能瓶颈排查,以及 JVM 参数调优与监控工具(jstat、jmap、jvisualvm)的实战使用。通过真实案例,帮助学习者掌握 Java 应用在生产环境中的性能分析与优化能力。

19

2026.01.20

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL 教程
PostgreSQL 教程

共48课时 | 7.5万人学习

Git 教程
Git 教程

共21课时 | 2.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号