如何安全的删除 xlog 文件

判定脚本

xlog_recycle_check.sh 点击查看代码
#!/bin/bash

usage()
{
    echo "Usage: "
    echo "  $0 [OPTION] ... [XLOGFILES]"
    echo
    echo "Options:"
    echo "  -x xlog_segsize,    xlog 文件段大小,单位 MB,默认 64 MB"
    echo "  -v,                 打印调试信息"
    echo

    exit 0
}

debug_echo()
{
    if [[ $verbose -eq 1 ]]; then
        echo "$@"
    fi
}

# 解析参数
while getopts "x:v" opt
do
    case $opt in
        x ) # 单位 MB ,默认 64 MB
            xlog_segsize="$OPTARG"
            ;;
        v )
            verbose=1
            ;;
        \? )
            usage
            ;;
    esac
done

shift $(($OPTIND-1))

# xlog 文件段
xlog_files=("$@")
[[ ${#xlog_files[@]} -eq 0 ]] && usage

# 设置默认值
verbose=${verbose:=0}
xlog_segsize="${xlog_segsize:=64}"

# 计算 xlog 分组进制 (4GB/xlog_segsize)
xlog_radix=$((0x100000000/($xlog_segsize*1024*1024)))
debug_echo "xlog radix: $xlog_radix"

# 遍历 xlog 文件段
for xlog_file in ${xlog_files[@]}
do
    xlog_base="$(basename $xlog_file)"
    if [[ ! $xlog_base =~ ^[0-9A-Z]{24}$ ]]; then
        debug_echo "$xlog_file is NOT xlog file"
        continue
    fi
    echo "xlog base: $xlog_base"
    echo "xlog file: $xlog_file"

    # 根据文件名计算当前文件的第一个 page 的LSN
    xlog_timeline="0x${xlog_base:0:8}"
    debug_echo "xlog timeline: $xlog_timeline"
    xlog_group="0x${xlog_base:8:8}"
    debug_echo "xlog group: $xlog_group"
    xlog_index="0x${xlog_base:16:8}"
    debug_echo "xlog index: $xlog_index"
    debug_echo "xlog calc expr: ($xlog_group*$xlog_radix+$xlog_index)*$xlog_segsize*1024*1024"
    xlog_first_page_lsn_calc=$((($xlog_group*$xlog_radix+$xlog_index)*$xlog_segsize*1024*1024))
    echo "LSN calc: $xlog_first_page_lsn_calc"
    printf "LSN calc hex: 0x%016x\n" $xlog_first_page_lsn_calc

    # 根据文件第 8~16 字节计算当前文件段第一个 page 的十六进制 LSN
    xlog_first_page_lsn_line=($(hexdump -s 8 -n 8 $xlog_file -C | head -n 1))
    debug_echo "LSN line: ${xlog_first_page_lsn_line[@]}"
    xlog_first_page_lsn_bytes=(${xlog_first_page_lsn_line[@]:1:8})
    debug_echo "LSN bytes: ${xlog_first_page_lsn_bytes[@]}"
    xlog_first_page_lsn_hex="0x"
    for((i=7;i>=0;i--))
    do
        xlog_first_page_lsn_hex+="${xlog_first_page_lsn_bytes[$i]}"
    done
    echo "LSN read hex: $xlog_first_page_lsn_hex"

    diff=$(($xlog_first_page_lsn_calc-$xlog_first_page_lsn_hex))
    if [[ $diff -ne 0 ]]; then
        echo "[RESULT] $xlog_base    recycled"
    else
        echo "[RESULT] $xlog_base    matched"
    fi
done

使用效果

  1. 查看一批 xlog 文件段
    sh ~/xlog_recycle_check.sh pg_xlog/* | grep RESULT
    image

  2. 查看指定 xlog 文件段
    sh ~/xlog_recycle_check.sh pg_xlog/00000001000000010000001B
    image

  3. 打印调试信息
    sh -v ~/xlog_recycle_check.sh pg_xlog/000000010000000100000032
    image

判定原理

WAL文件的命名包括三部分,每部分都是用8个16进制(4个字节)的字符串表示。

以 WAL 日志文件段64MB为例
● 第一部分,表示时间线,即WAL日志所在的时间线。
● 每4GB大小的日志文件段为一组,则每组有4GB/64MB=64个文件段,即每组的日志文件段至多64个,16进制的范围表示 00~3F,即第三部分的取值范围为 00~3F。
● 当日志的大小(LSN)每超过4GB,则向第二部分进一。

以 00000001000000110000001E 文件段为例:

● 第一部分:00000001 表示日志段所在的时间线为 0x00000001 = 1。
● 第二部分:00000011 表示日志段所在的组号(或者说第N个4GB日志文件,从0开始计数)为 0x00000011 = 17,即第18个4GB文件范围内。
● 第三部分:0000001E 表示日志段所在组的段号(即第N组的第M个文件段,从0开始计数)为 0000001E = 30

总结,00000001000000110000001E 为 第“0x00000011 * 64 + 0x0000001E = 17*64+30 = 1118”个64MB的文件段。

注:这里为什么要乘以64?

那么,该日志文件段,所表示的LSN的起始位置为:

1118 * 64 * 1024 * 1024 = 75027709952 = 0x1178000000

根据XLogPageHeaderData 结构体的定义,每一个XLOG的Page都会有这个页的一些头部信息。
● 前2字节,表示魔数,0xD07E
● 接着的2字节,表示一些flag
● 接着的4字节,表示时间线
● 接着的8字节,表示这个Page的起始LSN
image

再看下,00000001000000110000001E 文件的第8~16个字节,表示的是这个日志文件的第一个页的位置。(看样子是小端模式)。

hexdump -C -n 16 00000001000000110000001E
00000000 7e d0 06 00 01 00 00 00 00 00 00 78 11 00 00 00 |~..........x....|
00000010
根据日志文件的首页信息,可以看出,00000001000000110000001E 文件名与其内容是匹配的。

也可以用python3获取

python3 -c "
f=open('00000001000000110000001E', 'rb')
f.seek(8)
print(hex(int.from_bytes(f.read(8), byteorder='little')))
f.close()
"

那么,当日志文件被checkpoint进程recycle之后,会将旧的日志文件,重命名成新的日志文件,以减少文件的生成次数。

重命名的文件,其文件名与其内容是不匹配的。如 000000010000001100000026

hexdump -C -n 16 000000010000001100000026
00000000 7e d0 06 00 01 00 00 00 00 00 00 74 11 00 00 00 |~..........t....|
00000010

按照上述的说法:
000000010000001100000026的第一个首页的起始位置应该是:

(0x00000011 * 64 + 0x00000026) * (64 * 1024 * 1024) = 75564580864 = 0x1198000000

也就是说,000000010000001100000026 文件的第8~16个字节应该是:00 00 00 98 11 00 00 00,而不是现在的 00 00 00 74 11 00 00 00。

现在的 00 00 00 74 11 00 00 00,其表示的是 0x1174000000 这个首页的LSN地址。

反推,

0x1174000000/(6410241024) = 1117,即是第1117个64MB的文件。

按照每64个64MB文件为一组,其
● 组号为:1117 / 64 = 17 = 0x11
● 组内的段号为:1117 % 64 = 29 = 0x1D

即,这个文件的首页内容所在的WAL日志文件段名应该是:00000001000000110000001D

即,000000010000001100000026 是由 00000001000000110000001D 重命名的来。

🎈 所以,如果想要删除 WAL 文件段以回收空间时,尽量应该删除哪些 recycled 的 WAL 文件。

posted @ 2021-10-19 15:18  Zaclu  阅读(523)  评论(0编辑  收藏  举报