KingbaseES数据库控制文件管理:从查看、备份到损坏恢复的实战指南

0 阅读6分钟

干了这么多年数据库运维,我越来越觉得控制文件这东西就像汽车的发动机舱——平时没人注意,但一旦出问题,整台车直接趴窝。今天我就把这几年跟金仓数据库控制文件打交道的经验,从查看、备份到损坏恢复,一次性聊透。

前言:为什么控制文件如此重要

记得去年有个项目,客户的生产环境突然崩溃,数据库起不来。我远程连上去一看,日志里明确写着“无法找到控制文件”。当时我就知道,这事儿麻烦了。控制文件丢了,数据库就像没了大脑的躯体,完全不知道自己是谁、该干嘛。

KingbaseES(金仓数据库)的控制文件默认存放在数据目录的global文件夹下,名字叫sys_control【turn0search2】【turn0search26】【turn0search40】。就这么个8KB的小文件【turn0search2】【turn0search26】【turn0search40】,却记录着数据库集群的整个状态。系统标识符、数据库块大小、WAL日志位置、检查点信息……这些关键数据全在里面。

一旦控制文件损坏或丢失,数据库绝对无法启动【turn0search2】【turn0search26】【turn0search40】。这不是危言耸听,是金仓数据库的设计如此。

一、控制文件里到底存了什么

在这里插入图片描述

要理解控制文件的重要性,先得知道它里面记录了什么。我用sys_controldata工具查看了一下测试环境的控制文件,输出信息挺多,我挑重点给你解释下【turn0search1】【turn0search31】:

[kingbase@node1 ~]$ sys_controldata -D /home/kingbase/data
sys_control version number:            1201
Catalog version number:                202312201
Database system identifier:            7368768981555329093
Database cluster state:                in production
sys_control last modified:             Thu May 16 04:09:02 2024
Latest checkpoint location:            0/26000088
Latest checkpoint's REDO location:     0/26000058
Latest checkpoint's REDO WAL file:     000000010000000000000026
...
Database block size:                   8192
Blocks per segment of large relation:  131072
WAL block size:                        8192
Bytes per WAL segment:                 16777216
Maximum length of identifiers:         64
Maximum columns in an index:           32
Maximum size of a TOAST chunk:         1988
...

这些信息可以分成三类【turn0search2】【turn0search26】【turn0search40】:

  1. 静态信息:初始化时生成,永远不变。比如系统标识符(那个长串数字)、数据库块大小(默认8KB)、WAL段大小(默认16MB)。
  2. 配置信息:初始化时可以定制,但之后不能改。比如字符集、排序规则。
  3. 动态信息:数据库运行时不断更新。比如最新检查点位置、下一个事务ID(NextXID)、下一个OID(NextOID)、多事务ID等。

特别提醒:控制文件路径是固定的,不能改变;控制文件大小也是固定的,物理大小始终是8KB【turn0search2】【turn0search26】【turn0search40】。

二、查看控制文件信息的实用方法

想了解数据库当前状态,控制文件是个很好的信息源。金仓提供了sys_controldata工具来读取这些信息【turn0search2】【turn0search26】【turn0search40】。

2.1 基本用法

最简单的用法就是指定数据目录:

sys_controldata -D /path/to/your/data/directory

如果设置了KINGBASE_DATA环境变量,直接运行sys_controldata就行【turn0search6】。

2.2 关键参数解读

输出信息里,有几个特别重要的:

  • Database cluster state:数据库状态。in production表示正常运行中;in archive recovery表示备库正在恢复;shut down表示正常关闭【turn0search0】【turn0search30】。
  • Latest checkpoint location:最新检查点位置。这个很重要,数据库非正常关闭后重启,会从这个位置开始恢复【turn0search4】。
  • NextXID:下一个事务ID。这个值接近2^31时,需要执行VACUUM FREEZE防止事务ID回卷【turn0search8】。
  • oldestXID:最老活跃事务ID。这个值跟事务ID回卷风险直接相关【turn0search8】。

2.3 实际应用场景

上周我用这个工具排查了一个主备同步延迟的问题。通过对比主库和备库的控制文件,发现备库的Latest checkpoint location比主库落后很多,这就解释了为什么备库读到的数据总是“旧”的。

三、控制文件的备份策略

控制文件这么重要,但金仓数据库不允许单独备份控制文件【turn0search6】。只能在做基础备份(如sys_rman或sys_basebackup)时,随着整个数据目录一起备份【turn0search2】【turn0search26】【turn0search40】。

不过,金仓提供了控制文件多路复用功能,通过control_file_copy参数实现控制文件冗余【turn0search6】【turn0search46】。

3.1 配置控制文件冗余

配置方法很简单,编辑kingbase.conf文件,添加control_file_copy参数【turn0search6】:

# 控制文件多副本配置示例
control_file_copy = '/cf_copy/sys_control_1;/cf_copy/sys_control_2'

注意:多个副本之间用分号分隔,最多支持8个副本【turn0search6】【turn0search34】。

修改后需要重启数据库才能生效【turn0search6】【turn0search34】:

sys_ctl restart -D /path/to/data

3.2 冗余控制文件的优势

配置了多副本后,原控制文件损坏时,可以直接从副本拷贝回来【turn0search6】【turn0search46】:

cp /cf_copy/sys_control_1 /data/global/sys_control

然后重新启动数据库就行【turn0search6】【turn0search46】。这比后面要讲的sys_resetwal方法简单太多了。

3.3 实际配置案例

去年给一家金融机构做高可用方案,我特意配置了控制文件冗余:

# 创建副本目录并设置权限
[root@node1 ~]# mkdir -p /cf_copy
[root@node1 ~]# chown kingbase:kingbase /cf_copy
[root@node1 ~]# chmod 700 /cf_copy
[root@node1 ~]# su - kingbase

# 配置kingbase.conf
[kingbase@node1 data]$ cat <<EOF >>/data/kingbase.conf
control_file_copy='/cf_copy/sys_control_1;/cf_copy/sys_control_2'
EOF

# 重启数据库
[kingbase@node1 data]$ sys_ctl restart

配置后验证一下:

[kingbase@node1 data]$ sys_controldata | grep "control_file_copy"
control_file_copy: /cf_copy/sys_control_1;/cf_copy/sys_control_2

四、控制文件损坏后的恢复方法

即使做了冗余配置,也难免有意外情况。下面讲讲控制文件损坏后的恢复方法,按推荐顺序介绍。

4.1 有冗余副本的情况(最推荐)

如果配置了control_file_copy,恢复超级简单【turn0search6】【turn0search46】:

# 直接从副本拷贝
cp /cf_copy/sys_control_1 /data/global/sys_control

# 启动数据库
sys_ctl start -D /data

去年那次控制文件丢失事故,我5分钟就搞定了,靠的就是这个方法。

4.2 没有冗余副本的情况(复杂)

没配置冗余的话,只能用sys_resetwal工具重建控制文件【turn0search6】【turn0search22】。这个过程有点复杂,参数很多,我一步步给你讲。

4.2.1 准备工作

首先需要创建一个空的控制文件,并删除kingbase.pid文件【turn0search6】:

touch /data/global/sys_control
rm -f /data/kingbase.pid

4.2.2 确定sys_resetwal参数

sys_resetwal有好几个关键参数,需要从现有文件中计算值【turn0search6】【turn0search28】:

  • -l 参数:设置新的WAL最小起始位置。查找sys_wal目录下最大的日志文件编号,加1【turn0search6】【turn0search28】。

    # 查看最大WAL文件
    ls -l /data/sys_wal/ | tail -n 1
    # 假设最大文件是000000010000000000000002
    # 那么下一个就是000000010000000000000003
    
  • -x 参数:设置下一个事务ID。查找sys_xact目录下最大编号,加1后末尾补5个0【turn0search6】【turn0search28】。

    # 查看最大事务文件
    ls -l /data/sys_xact/ | tail -n 1
    # 假设最大文件是0000
    # 那么下一个事务ID是0x000100000
    
  • -m 参数:设置下一个多事务ID和最旧的多事务ID。查找sys_multixact/offsets目录下最大和最小编号【turn0search6】【turn0search28】。

    # 查看多事务ID
    ls -l /data/sys_multixact/offsets/ | tail -n 2
    # 假设最大和最小都是0000
    # 那么下一个多事务ID是0x00010000
    # 最旧的多事务ID是0x00000001(因为最小值是0,需要+1处理)
    
  • -O 参数:设置下一个多事务偏移量。查找sys_multixact/members目录下最大编号,加1后乘以0xCC80【turn0search6】【turn0search28】。

    # 查看多事务成员
    ls -l /data/sys_multixact/members/ | tail -n 1
    # 假设最大文件是0000
    # 那么下一个偏移量是0xCC80
    
  • -g 参数:数据库模式。1表示Oracle风格,0表示标准风格【turn0search6】。

    # 查看当前数据库模式
    sys_controldata | grep "database mode"
    

4.2.3 执行恢复

参数确定后,执行恢复命令【turn0search6】:

sys_resetwal -f \
  -l 000000010000000000000003 \
  -x 0x000100000 \
  -m 0x00010000,0x00000001 \
  -O 0xCC80 \
  -D /data \
  -g 1

-f表示强制更新【turn0search6】。

4.2.4 启动数据库

执行完上述命令后,尝试启动数据库【turn0search6】:

sys_ctl start -D /data

如果正常启动,检查数据是否完整【turn0search6】。

4.3 使用脚本一键恢复

手动计算这些参数太麻烦,我写了个Python脚本自动完成这个过程【turn0search6】:

#!/usr/bin/env python3
import os
import glob
import re
import subprocess

def get_max_file(directory, pattern):
    """获取目录下符合模式的最大编号文件"""
    files = [f for f in glob.glob(os.path.join(directory, pattern)) 
             if re.match(r'^[0-9A-Fa-f]+$', os.path.basename(f))]
    if not files:
        return "0000"
    return max(os.path.basename(f) for f in files)

def calculate_resetwal_params(data_dir):
    """计算sys_resetwal所需参数"""
    # 计算WAL参数
    wal_dir = os.path.join(data_dir, "sys_wal")
    max_wal = get_max_file(wal_dir, "*")
    next_wal = f"{int(max_wal, 16) + 1:024X}"
    
    # 计算事务ID参数
    xact_dir = os.path.join(data_dir, "sys_xact")
    max_xact = get_max_file(xact_dir, "*")
    next_xid = (int(max_xact, 16) + 1) * 0x100000
    next_xid_hex = f"0x{next_xid:09X}"
    
    # 计算多事务ID参数
    multixact_offsets_dir = os.path.join(data_dir, "sys_multixact", "offsets")
    max_offset = get_max_file(multixact_offsets_dir, "*")
    min_offset = min([os.path.basename(f) for f in glob.glob(os.path.join(multixact_offsets_dir, "*"))], 
                     key=lambda x: int(x, 16)) if glob.glob(os.path.join(multixact_offsets_dir, "*")) else "0000"
    
    next_multixact_id = (int(max_offset, 16) + 1) * 0x10000
    next_multixact_id_hex = f"0x{next_multixact_id:08X}"
    
    oldest_multixact_id = (int(min_offset, 16) * 0x10000) + (1 if int(min_offset, 16) == 0 else 0)
    oldest_multixact_id_hex = f"0x{oldest_multixact_id:08X}"
    
    # 计算多事务偏移量参数
    multixact_members_dir = os.path.join(data_dir, "sys_multixact", "members")
    max_member = get_max_file(multixact_members_dir, "*")
    next_member_number = int(max_member, 16) + 1
    next_offset = next_member_number * 0xCC80
    next_offset_hex = f"0x{next_offset:X}"
    
    return {
        'next_wal': next_wal,
        'next_xid': next_xid_hex,
        'next_multixact_id': next_multixact_id_hex,
        'oldest_multixact_id': oldest_multixact_id_hex,
        'next_offset': next_offset_hex
    }

def recover_controlfile(data_dir):
    """恢复控制文件"""
    # 1. 创建空控制文件
    control_file = os.path.join(data_dir, "global", "sys_control")
    if not os.path.exists(control_file):
        with open(control_file, 'w') as f:
            pass
        print(f"已创建空控制文件: {control_file}")
    
    # 2. 删除kingbase.pid文件
    pid_file = os.path.join(data_dir, "kingbase.pid")
    if os.path.exists(pid_file):
        os.remove(pid_file)
        print(f"已删除PID文件: {pid_file}")
    
    # 3. 计算参数
    params = calculate_resetwal_params(data_dir)
    
    # 4. 构建并执行sys_resetwal命令
    cmd = [
        'sys_resetwal',
        '-f',
        '-l', params['next_wal'],
        '-x', params['next_xid'],
        '-m', f"{params['next_multixact_id']},{params['oldest_multixact_id']}",
        '-O', params['next_offset'],
        '-D', data_dir,
        '-g', '1'
    ]
    
    print("生成sys_resetwal恢复命令:")
    print(' '.join(cmd))
    
    # 执行命令
    result = subprocess.run(cmd, capture_output=True, text=True)
    print(result.stdout)
    if result.stderr:
        print("错误:", result.stderr)
    
    # 5. 尝试启动数据库
    print("尝试启动数据库...")
    start_cmd = ['sys_ctl', 'start', '-D', data_dir]
    start_result = subprocess.run(start_cmd, capture_output=True, text=True)
    print(start_result.stdout)
    
    if start_result.returncode == 0:
        print("数据库启动成功!")
    else:
        print("数据库启动失败,请检查日志")

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(description='KingbaseES控制文件一键恢复工具')
    parser.add_argument('-kd', '--kingbase-data', required=True, help='KingbaseES数据目录路径')
    args = parser.parse_args()
    
    recover_controlfile(args.kingbase_data)

这个脚本会自动计算所有需要的参数,然后执行恢复操作。

五、控制文件损坏的常见原因与预防

5.1 常见损坏原因

根据我的经验,控制文件损坏主要有这几个原因【turn0search3】【turn0search35】:

  1. 非正常关机:突然断电、强制kill进程等导致控制文件写了一半。
  2. 磁盘故障:存储控制文件的磁盘出现坏道。
  3. 双写冲突:在共享存储集群环境中,多个节点同时写控制文件导致文件损坏【turn0search39】。
  4. 文件系统损坏:操作系统层面的文件系统错误。

5.2 预防措施

  • 配置控制文件冗余:这是最有效的预防措施【turn0search6】【turn0search46】。
  • 定期备份:使用sys_rman或sys_basebackup做基础备份,控制文件会一起备份【turn0search2】【turn0search26】【turn0search40】。
  • 避免非正常关机:使用sys_ctl stop -m fast正常关闭数据库【turn0search12】。
  • 监控磁盘健康:定期检查磁盘SMART信息,及时发现潜在问题。

六、控制文件与其他组件的关系

控制文件不是孤立存在的,它跟数据库其他组件关系密切【turn0search48】:

  • 与WAL日志的关系:控制文件记录最新检查点位置,WAL日志记录数据变更。恢复时,先从控制文件找到最新检查点,然后从WAL日志中重放检查点之后的事务【turn0search29】。
  • 与数据文件的关系:控制文件记录数据文件的位置和状态。数据文件存储实际数据,控制文件告诉数据库去哪里找这些数据【turn0search5】。
  • 与检查点进程的关系:检查点进程定期触发检查点操作,更新控制文件中的检查点信息【turn0search48】。

七、高级管理技巧

7.1 查看控制文件详细信息

除了sys_controldata,还可以用sys_controlfile工具查看控制文件内容【turn0search10】【turn0search16】:

sys_controlfile -D /path/to/data

这个工具能查看控制文件的二进制内容,对排查深度问题有帮助。

7.2 控制文件校验

金仓还提供了sys_checksums工具,可以检查数据页校验和【turn0search14】。控制文件中记录了校验和版本信息【turn0search1】。

7.3 在线备份控制文件

虽然不能单独备份控制文件,但可以在线查看其内容【turn0search2】【turn0search26】【turn0search40】:

sys_controldata -D /path/to/data > control_file_backup_$(date +%Y%m%d).txt

定期导出控制文件信息,可以作为备份的补充。

总结

控制文件是KingbaseES数据库的“大脑”,记录着数据库集群的关键状态信息。管理好控制文件,是数据库稳定运行的基础。

我的建议是:

  1. 一定要配置控制文件冗余,这是最简单有效的预防措施
  2. 定期做基础备份,确保控制文件和数据一起备份
  3. 避免非正常关机,减少控制文件损坏的风险
  4. 掌握恢复方法,即使出现问题也能快速恢复

希望这篇分享能帮助你更好地管理金仓数据库的控制文件。如果你有具体问题,欢迎留言交流。