galera mariadb集群恢复策略

1 galera mariadb
首先MariaDB是一个数据库,可以看成是MySQL的一个分支,由于MySQL被SUN收购,所以MySQL面临着闭源的风险,当时MySQL之父Widenius并没有加入SUN,而是基于MySQL的代码开发新的分支,命名为MariaDB,并全部开源。

Galera是Galera Cluster,是一种为数据库设计的新型的、数据不共享的、高度冗余的高可用方案,galera mariadb就是集成了Galera插件的MariaDB集群,Galera本身是具有多主特性的,所以galera mariadb不是传统的主备模式的集群,而是多主节点架构。

2 galera mariadb的配置方式
我的一篇OpenStack高可用模块博客中其中有一段是描述搭建galera mariadb的(2.2.1数据库服务高可用配置):OpenStack高可用方案及配置

3 galera mariadb的一些基本概念
(1)当前节点数据库状态

MariaDB [(none)]> show status like ‘wsrep_local_state_comment‘;
+---------------------------+--------+
| Variable_name | Value |
+---------------------------+--------+
| wsrep_local_state_comment | Synced |
+---------------------------+--------+

状态查询表:

状态 状态说明
Open 节点启动成功,尝试连接到集群
Primary 节点已处于集群中,在新节点加入时,选取donor进行数据库同步时会产生的状态
Joiner 节点处于等待接收或正在接收同步文件的状态
Joined 节点完成数据同步,但还有部分数据不是最新的,在追赶与集群数据一致的状态
Synced 节点正常提供服务的状态,表示当前节点数据状态与集群数据状态是一致的
Donor 表示该节点被选为Donor节点,正在为新加进来的节点进行全量数据同步,此时该节点对客户端不提供服务

(2)Primary Component
在网络发生故障时,由于网络连接原因,集群可能被分成好几个小集群,但只能有一个集群可以继续进行数据修改,集群的这部分称为Primary Component

(3)GTID
英文全称为Global Transaction ID,由UUID和sequence number偏移量组成,wsrep api中定义的集群内部全局事务id,一个顺序id,用来集群集群中状态改变的唯一标志及队列中的偏移量

(4)SST
英文全称为State Snapshot Transfer,即状态快照迁移:通过从一个节点到另一个节点迁移完整的数据拷贝(全量拷贝)。当一个新的节点加入到集群中,新的节点从集群中已有节点进行数据同步,开始进行状态快照迁移。
Galera中有两种不同的状态迁移方法:
<1>逻辑数据迁移:采用mysqldump命令,这是一个阻塞式的方法。
<2>物理数据迁移:该方法采用rsync、rsync_wan、xtrabackup等方法直接在服务器之间拷贝数据,接收的服务器在拷贝完数据后启动服务。
可以通过配置文件中修改SST的方式:
wsrep_sst_method=rsync

(5)IST
英文全称为Increamental State Transfer,即增量状态迁移:集群一个节点通过识别新加入的节点缺失的事务操作,将该操作发送,而并不像SST那样的全量数据拷贝。最常见情况就是该节点之前已经存在于该集群,只是关机重启了,重新加入该集群会使用IST进行同步。

(6)grastate.dat
可以通过该文件查看到该节点记录的uuid和seqno,也就是上面说的GTID,当节点正常退出Galera集群时,会将GTID的值更新到该文件中,如下:

[[email protected] ~]# cat /var/lib/mysql/grastate.dat
# GALERA saved state
version: 2.1
uuid: 30ae87da-8e8e-11e8-810c-6a8da854119b
seqno: 33557
safe_to_bootstrap: 0

如果该节点数据库服务正在运行,则seqno的值是-1的

(7)gvwstate.dat
当节点形成或改变Primary Component时,节点会创建或更新该文件,确保节点保留最新Primary Component的状态,如果节点正常关闭,该文件会被删除。

4 一些故障场景的恢复
(1)场景1

其中1个节点挂了,一般只需要重启A节点的服务即可

(2)场景2

所有节点都挂了,重启服务时不能单纯的全部重启,需要找状态最新的那个节点启动,且启动时需要加上--wsrep-new-cluster参数,该节点启动后其它节点再正常启动服务即可。
这里就涉及到一个关键点,那就是怎么找哪个是状态最新的那个节点,第5点介绍查找最新节点的策略。

5 恢复策略和自动恢复脚本
(1)恢复策略
<1>首先判断当前数据库集群中是否有服务在启动着,如果有则直接启动服务即可
<2>如果当前所有节点的数据库服务都挂了,则需要找状态最新的那个节点让它携带--wsrep-new-cluster参数启动,启动起来之后其它节点直接启动服务即可。
查找最新节点策略:
首先获取各节点的grastate.dat文件中的seqno值,值最大的那个就是最新的节点;如果所有节点的seqno都是-1,则去比较所有节点的gvwstate.dat文件中的my_uuid和view_id是否相等,相等的那个则作为第一个启动节点,第一个启动节点启动后,其它节点正常启动即可;如果依然未找到则需要人工干预来恢复了。
以下是我自己写的自动恢复脚本:

#!/usr/bin/python2
# -*- coding: utf-8 -*-

import os
import time
import traceback
import logging
import sys

# 初始化日志对象
logger = logging.getLogger("check-or-recover-galera")
log_file=‘/var/log/check-or-recover-galera/check-or-recover-galera.log‘
if not os.path.exists(log_file):
    os.system(‘mkdir -p /var/log/check-or-recover-galera/‘)
    os.system(‘touch ‘ + log_file)

formatter = logging.Formatter(‘%(asctime)s (filename)s[line:%(lineno)d] %(levelname)s %(message)s‘)
file_handler = logging.FileHandler(log_file)
file_handler.setFormatter(formatter)

logger.addHandler(file_handler)
logger.setLevel(logging.DEBUG)

import socket

PORT = 10000
BUFF_SIZE = 10240

def test_connect_ok(ip):
    client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client_sock.settimeout(3)
    client_sock.connect((ip, PORT))
    client_sock.close()

# 这个方法要求在要远程的节点上需要有个进程在监听PORT端口等待处理命令
def send_request(ip, data, timeout=60):
    test_connect_ok(ip)
    client_sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    client_sock.settimeout(timeout)
    client_sock.connect((ip, PORT))
    client_sock.send(data)
    ret_data = client_sock.recv(BUFF_SIZE)
    client_sock.close()
    return ret_data

def remote_send_request(ip, data, timeout=60):
    res_remote = send_request(ip, json.dumps(data), timeout=timeout)
    if res_remote is None or res_remote == ‘‘:
        raise Exception(‘res_remote is null‘)
    res_remote = json.loads(res_remote)
    if res_remote[‘ret_state‘] != ‘success‘:
        raise Exception(‘ret_state is not success‘)
    return res_remote

# 默认vmbr0是本地ip
def get_local_ip():
    cmd_out = os.popen(‘cat /etc/sysconfig/network-scripts/ifcfg-vmbr0 2>/dev/null |grep IPADDR‘).read()
    if cmd_out and cmd_out != ‘‘:
        cmd_out = cmd_out.strip()
        cmd_out = cmd_out.replace(‘"‘, ‘‘).replace(‘ ‘, ‘‘)
        tmp = cmd_out.split(‘=‘)
        if len(tmp) >= 2:
            ip = tmp[1]
            return ip
    return None

# 获取各节点的seqno值
def get_all_nodes_seqno(node_ips_arr):
    seqno_dict = {}
    data = {‘req_type‘: ‘get_seqno‘}
    for node_ip in node_ips_arr:
        try:
            res_remote = remote_send_request(node_ip, data)
            seqno_dict[node_ip] = res_remote[‘seqno‘]
        except Exception,e:
            seqno_dict[node_ip] = -1
            logger.error(traceback.format_exc())
    return seqno_dict

# 获取各节点的gvwstate.dat文件的my_uuid和view_id的比对值结果
def get_all_nodes_uv_is_equal(node_ips_arr):
    uv_equal_dict = {}
    data = {‘req_type‘: ‘get_uv_equal_value‘}
    for node_ip in node_ips_arr:
        try:
            res_remote = remote_send_request(node_ip, data)
            uv_equal_dict[node_ip] = res_remote[‘equal‘]
        except Exception,e:
            uv_equal_dict[node_ip] = 0
            logger.error(traceback.format_exc())
    return uv_equal_dict

# 检查自身mariadb服务是否已经启动
def check_is_active_now():
    is_active = os.popen(‘systemctl is-active mysqld_safe 2>/dev/null‘).read()
    is_active = is_active.strip()
    if is_active and is_active == ‘active‘:
        logger.info(‘the mariadb is already up‘)
        return True
    return False

# 第一个启动的节点
def start_mariadb_with_wsrep():
    os.system("sed -i ‘s/--wsrep-new-cluster//‘ /usr/lib/systemd/system/mysqld_safe.service")
    os.system("sed -i ‘s/user=mysql/user=mysql --wsrep-new-cluster/‘ /usr/lib/systemd/system/mysqld_safe.service")
    os.system("sed -i ‘s/safe_to_bootstrap:.*/safe_to_bootstrap: 1/‘ /var/lib/mysql/grastate.dat")
    os.system(‘systemctl daemon-reload‘)
    os.system(‘systemctl start mysqld_safe‘)
    # 将配置文件恢复回去
    os.system("sed -i ‘s/--wsrep-new-cluster//‘ /usr/lib/systemd/system/mysqld_safe.service")
    os.system(‘systemctl daemon-reload‘)
    time.sleep(10)
    if check_is_active_now() is True:
        return True
    else:
        logger.error(‘use option wsrep-new-cluster start mariadb failed‘)
    return False

def main():
    while True:
        try:
            time.sleep(10)
            # 先检测自己的mariadb是否已经自己启动
            if check_is_active_now() is True:
                time.sleep(60)
                continue

            # 这里应该先检测下thintaskd服务是否已经启动,如果还没启动则需等待
            is_thintaskd_active = os.popen(‘/etc/init.d/thintaskd status 2>/dev/null |grep active |grep running‘).read()
            if not is_thintaskd_active or is_thintaskd_active == ‘‘:
                logger.info(‘wait thintaskd service start‘)
                time.sleep(5)

            # 获取当前galera的集群的各节点的ip
            node_ips_info = os.popen("cat /etc/my.cnf.d/mariadb-server.cnf |grep ‘^wsrep_cluster_address‘").read()
            node_ips_str = node_ips_info.split(‘gcomm://‘)[1]
            node_ips_str = node_ips_str.strip()
            node_ips_arr = node_ips_str.split(‘,‘)

            # 检测其它节点是否已经有在运行着的
            data = {‘req_type‘: ‘check_mariadb_service‘}
            has_mariadb_service_on = False
            for node_ip in node_ips_arr:
                try:
                    res_remote = remote_send_request(node_ip, data)
                    state = res_remote[‘state‘]
                    if state == ‘active‘:
                        has_mariadb_service_on = True
                        # 找到在运行着的节点
                        logger.info(‘find the running mariadb service node:‘ + node_ip)
                        # 直接启动自己服务
                        os.system(‘systemctl start mysqld_safe‘)
                        time.sleep(10)
                        if check_is_active_now() is True:
                            time.sleep(60)
                        else:
                            logger.info(‘start mariadb service error‘)
                        break
                except Exception,e:
                    logger.error(traceback.format_exc())
                    logger.error(‘check_mariadb_service for ‘ + node_ip + ‘ failed, error:‘ + e.message)
            if has_mariadb_service_on is True:
                continue

            # 如果所有节点的mariadb都没在运行,则需要寻找一个节点进行启动
            seqno_dict = get_all_nodes_seqno(node_ips_arr)
            logger.info(‘get seqno_dict:%s‘, seqno_dict)
            # 根据seqno值判断哪个节点为启动节点
            first_boot_node = None
            max_seqno = -2
            for key in seqno_dict:
                if seqno_dict[key] > max_seqno:
                    max_seqno = seqno_dict[key]
                    first_boot_node = key
            if first_boot_node is not None:
                logger.info(‘find the first_boot_node by seqno, first_boot_node:‘ + first_boot_node)
                # 判断这个启动节点是不是自己,如果是就启动,否则等待其它节点启动起来
                if first_boot_node == get_local_ip():
                    if start_mariadb_with_wsrep() is True:
                        time.sleep(60)
                else:
                    logger.info(‘wait node ‘ + first_boot_node + ‘ start mariadb service‘)
                    time.sleep(5)
                continue
            else:
                logger.info("all node‘s seqno is -1")

            # 如果所有节点的seqno都是-1则说明可能是全部主机非正常停止的,比如断电等
            # 这时则通过比对gvwstate.dat文件的my_uuid和view_id是否相等来决定从这个节点启动
            # 当集群时干净状态停止的时候该文件是被删除的
            uv_equal_dict = get_all_nodes_uv_is_equal(node_ips_arr)
            # 根据返回的值判断哪个是启动节点,1表示是,0表示否
            for key in uv_equal_dict:
                if uv_equal_dict[key] == 1:
                    first_boot_node = key
                    logger.info(‘find the first_boot_node by uv_equal_dict, first_boot_node:‘ + first_boot_node)
                    break
            if first_boot_node is not None:
                # 判断这个启动节点是不是自己,如果是就启动,否则等待其它节点启动起来
                if first_boot_node == get_local_ip():
                    if start_mariadb_with_wsrep() is True:
                        time.sleep(60)
                    else:
                        logger.info(‘wait node ‘ + first_boot_node + ‘ start mariadb service‘)
                        time.sleep(5)
                continue
            else:
                logger.info("can not find first_boot_node by gvwstate.dat file")

            # 如果经过上述步骤依然找不到启动节点,需要人工进行干预了,或者可以随机挑选个节点进行启动
            logger.error(‘can not find first_boot_node, maybe you should ask admin to deal with this problem‘)
            time.sleep(5)
        except Exception,e:
            logger.error(traceback.format_exc())
            logger.error(‘error:‘ + e.message)

if __name__ == "__main__":
    sys.exit(main())

以下是自定义的mysqld_safe.service服务的文件,你可以将它放在/usr/lib/systemd/system/mysqld_safe.service

[Unit]
Description=Thinputer API Server
After=syslog.target network.target

[Service]
Type=notify
NotifyAccess=all
TimeoutStartSec=0
User=root

ExecStartPre=/usr/libexec/mysql-check-socket
ExecStartPre=/usr/libexec/mysql-prepare-db-dir %n
ExecStart=/bin/mysqld_safe --defaults-file=/etc/my.cnf.d/mariadb-server.cnf --user=mysql

[Install]
WantedBy=multi-user.target

原文地址:https://www.cnblogs.com/luohaixian/p/9426359.html

时间: 2024-10-31 18:01:22

galera mariadb集群恢复策略的相关文章

MariaDB集群Galera Cluster的研究与测试

MariaDB集群Galera Cluster的研究与测试 Galera Cluster是MariaDB的一个双活多主集群,其可以使得MariDB的所有节点保持同步,Galera为MariaDB提供了同步复制(相对于原生的异步复制),因此其可以保证HA,且其当前仅支持XtraDB/InnoDB存储引擎(扩展支持MyISAM),并且只可在Linux下使用.Galera Cluster拥有以下特性: 真正的多主架构,任何节点都可以进行读写 同步复制,各节点间无延迟且节点宕机不会导致数据丢失 紧密耦合

MariaDB Galera Cluster集群企业版编译安装与配置

安装环境 系统:CentOS 6.8 x86_64 软件:MariaDB 10.1.16 节点一:192.168.11.132 4C 8GB 节点二:192.168.11.133 4C 8GB 软件获取 访问MariaDB企业版下载地址 https://mariadb.com/my_portal/download/mariadb-enterprise 登录帐号后选择 10.1.16GA版本 源代码包下载. 从MariaDB Enterprise 10.1版本开始,企业版软件包与集群功能集成到一起

MariaDB Galera Cluster 部署(如何快速部署 MariaDB 集群)

MariaDB Galera Cluster 部署(如何快速部署 MariaDB 集群)  OneAPM蓝海讯通7月3日 发布 推荐 4 推荐 收藏 14 收藏,1.1k 浏览 MariaDB 作为 Mysql 的一个分支,在开源项目中已经广泛使用,例如大热的 openstack,所以,为了保证服务的高可用性,同时提高系统的负载能力,集群部署是必不可少的. MariaDB Galera Cluster 介绍 MariaDB 集群是 MariaDB 同步多主机集群.它仅支持 XtraDB/ Inn

MariaDB Galera Cluster 部署(如何快速部署MariaDB集群)

MariaDB作为Mysql的一个分支,在开源项目中已经广泛使用,例如大热的openstack,所以,为了保证服务的高可用性,同时提高系统的负载能力,集群部署是必不可少的. MariaDB Galera Cluster 介绍 MariaDB集群是MariaDB同步多主机集群.它仅支持XtraDB/ InnoDB存储引擎(虽然有对MyISAM实验支持 - 看wsrep_replicate_myisam系统变量). 主要功能: 同步复制 真正的multi-master,即所有节点可以同时读写数据库

mariadb集群与nginx负载均衡配置--centos7版本

这里配置得是单nginx主机..先准备4台主机,三台mariadb集群,一台nginx. ------------------------------------------------------------------------------------------------------------------------- mariadb集群配置 环境信息 MariaDB Server:MariaDB-10.2.10 CentOS:CentOS Linux release7.2.1511

mariadb集群配置(主从和多主)

mariadb主从 主从多用于网站架构,因为主从的同步机制是异步的,数据的同步有一定延迟,也就是说有可能会造成数据的丢失,但是性能比较好,因此网站大多数用的是主从架构的数据库,读写分离必须基于主从架构来搭建 主从架构主的数据可以同步到从上,从也可以读写数据,但是从上修改数据,不能同步到主的 主从同步数据会重在延迟(可能延迟一两个小时),延迟并不完全就只是缺点,加入主的数据丢失了,有可能因为延迟从的数据还未同步,可以通过从恢复一部分数据,减少损失 当一主多从时,为了减轻主服务器的压力一般我们会在主

负载均衡的mariadb集群搭建

集群介绍: Galera是一个MySQL(也支持MariaDB,Percona)的同步多主集群软件,目前只支持InnoDB引擎. 主要功能: 同步复制 真正的multi-master,即所有节点可以同时读写数据库 自动的节点成员控制,失效节点自动被清除 新节点加入数据自动复制 真正的并行复制,行级 用户可以直接连接集群,使用感受上与MySQL完全一致 优势: 因为是多主,所以不存在Slave lag(延迟) 不存在丢失交易的情况 同时具有读和写的扩展能力 更小的客户端延迟 节点间数据是同步的,而

记一次TokuMX数据库集群恢复

中午在昏睡中接到电话,服务器扩容后数据库启动不了了,于是不假思索的想到了TokuMX需要关闭对透明大页的使用才可以正常启动,而这点恰巧大家都不是很清楚,在这里写下关闭的shell:  echo never > /sys/kernel/mm/transparent_hugepage/enabled  一般情况下将其写入开机启动脚本,否则数据库不能正常启动. 然而当我远程查看了服务器后彻底懵逼了,数据库不见了,印象中服务安装在dev挂载点下,于是我翻遍了dev,全盘grep,find和tokumx相

elasticsearch(es) 集群恢复触发配置(Local Gateway参数)

elasticsearch(es) 集群恢复触发配置(Local Gateway) 当你集群重启时,几个配置项影响你的分片恢复的表现. 首先,我们需要明白如果什么也没配置将会发生什么. 想象一下假设你有 10 个节点,每个节点只保存一个分片,这个分片是一个主分片或者是一个副本分片,或者说有一个有 5 个主分片/1 个副本分片的索引.有时你需要为整个集群做离线维护(比如,为了安装一个新的驱动程序), 当你重启你的集群,恰巧出现了 5 个节点已经启动,还有 5 个还没启动的场景. 假设其它 5 个节