阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

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

417次阅读
没有评论

共计 12300 个字符,预计需要花费 31 分钟才能阅读完成。

随着 MySQL MGR 的版本的升级以及技术成熟,在把 MHA 拉下神坛之后,MGR 越来越成为 MySQL 高可用的首选方案。
MGR 的搭建并不算很复杂,但是有一系列手工操作步骤,为了简便 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,然后重新连接到组,可以正常连接到组,另外一个节点仍旧处于 error 状态

stop group_replication;
SET GTID_NEXT=6c81c118-e67c-4416-9cb0-2d573d178c1d:13;
BEGIN; COMMIT;
set gtid_next=‘automatic’; 

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

另外一个节点类似,依次解决。

MGR 故障模拟 2

从节点脱离 Group

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

这种情况倒是比较简单,重新开始组复制即可,start group_replication

 

MGR 故障自动检测和修复

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

实现代码如下

MySQL MGR 集群单主模式的自动搭建和自动化故障修复
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)
MySQL MGR 集群单主模式的自动搭建和自动化故障修复

 

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

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

 

 对于故障类型 2 从节点 offline 的自动化修复

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

 

完整的实现代码

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

完成的实现代码如下,花了一个下午写的,目前来说存在以下不足
1,创建复制用户的时候,没有指定具体的 slave 机器,目前直接指定的 %:create user repl@’%’ identified by repl
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)

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2022-01-22发表,共计12300字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中

星哥玩云

星哥玩云
星哥玩云
分享互联网知识
用户数
4
文章数
19348
评论数
4
阅读量
7803384
文章搜索
热门文章
开发者必备神器:阿里云 Qoder CLI 全面解析与上手指南

开发者必备神器:阿里云 Qoder CLI 全面解析与上手指南

开发者必备神器:阿里云 Qoder CLI 全面解析与上手指南 大家好,我是星哥。之前介绍了腾讯云的 Code...
星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛 NAS-6:抖音视频同步工具,视频下载自动下载保存 前言 各位玩 NAS 的朋友好,我是星哥!...
云服务器部署服务器面板1Panel:小白轻松构建Web服务与面板加固指南

云服务器部署服务器面板1Panel:小白轻松构建Web服务与面板加固指南

云服务器部署服务器面板 1Panel:小白轻松构建 Web 服务与面板加固指南 哈喽,我是星哥,经常有人问我不...
我把用了20年的360安全卫士卸载了

我把用了20年的360安全卫士卸载了

我把用了 20 年的 360 安全卫士卸载了 是的,正如标题你看到的。 原因 偷摸安装自家的软件 莫名其妙安装...
星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛 NAS-3:安装飞牛 NAS 后的很有必要的操作 前言 如果你已经有了飞牛 NAS 系统,之前...
阿里云CDN
阿里云CDN-提高用户访问的响应速度和成功率
随机文章
星哥带你玩飞牛NAS-5:飞牛NAS中的Docker功能介绍

星哥带你玩飞牛NAS-5:飞牛NAS中的Docker功能介绍

星哥带你玩飞牛 NAS-5:飞牛 NAS 中的 Docker 功能介绍 大家好,我是星哥,今天给大家带来如何在...
每年0.99刀,拿下你的第一个顶级域名,详细注册使用

每年0.99刀,拿下你的第一个顶级域名,详细注册使用

每年 0.99 刀,拿下你的第一个顶级域名,详细注册使用 前言 作为长期折腾云服务、域名建站的老玩家,星哥一直...
手把手教你,购买云服务器并且安装宝塔面板

手把手教你,购买云服务器并且安装宝塔面板

手把手教你,购买云服务器并且安装宝塔面板 前言 大家好,我是星哥。星哥发现很多新手刚接触服务器时,都会被“选购...
开源MoneyPrinterTurbo 利用AI大模型,一键生成高清短视频!

开源MoneyPrinterTurbo 利用AI大模型,一键生成高清短视频!

  开源 MoneyPrinterTurbo 利用 AI 大模型,一键生成高清短视频! 在短视频内容...
三大开源投屏神器横评:QtScrcpy、scrcpy、escrcpy 谁才是跨平台控制 Android 的最优解?

三大开源投屏神器横评:QtScrcpy、scrcpy、escrcpy 谁才是跨平台控制 Android 的最优解?

  三大开源投屏神器横评:QtScrcpy、scrcpy、escrcpy 谁才是跨平台控制 Andr...

免费图片视频管理工具让灵感库告别混乱

一言一句话
-「
手气不错
仅2MB大小!开源硬件监控工具:Win11 无缝适配,CPU、GPU、网速全维度掌控

仅2MB大小!开源硬件监控工具:Win11 无缝适配,CPU、GPU、网速全维度掌控

还在忍受动辄数百兆的“全家桶”监控软件?后台偷占资源、界面杂乱冗余,想查个 CPU 温度都要层层点选? 今天给...
一句话生成拓扑图!AI+Draw.io 封神开源组合,工具让你的效率爆炸

一句话生成拓扑图!AI+Draw.io 封神开源组合,工具让你的效率爆炸

一句话生成拓扑图!AI+Draw.io 封神开源组合,工具让你的效率爆炸 前言 作为天天跟架构图、拓扑图死磕的...
【开源神器】微信公众号内容单篇、批量下载软件

【开源神器】微信公众号内容单篇、批量下载软件

【开源神器】微信公众号内容单篇、批量下载软件 大家好,我是星哥,很多人都希望能高效地保存微信公众号的文章,用于...
每天一个好玩的网站-手机博物馆-CHAZ 3D Experience

每天一个好玩的网站-手机博物馆-CHAZ 3D Experience

每天一个好玩的网站 - 手机博物馆 -CHAZ 3D Experience 一句话介绍:一个用 3D 方式重温...
每年0.99刀,拿下你的第一个顶级域名,详细注册使用

每年0.99刀,拿下你的第一个顶级域名,详细注册使用

每年 0.99 刀,拿下你的第一个顶级域名,详细注册使用 前言 作为长期折腾云服务、域名建站的老玩家,星哥一直...