帮我看看这个首次全量后面增量数据同步的方法可行吗?

【TDengine 使用环境】
生产环境 /测试/ Poc/预生产环境

【TDengine 版本】

3.3.8.4-oss

【操作系统以及版本】

centonOS7.9

【部署方式】容器/非容器部署

非容器部署

【集群节点数】

【集群副本数】

【描述业务影响】
源服务器A:td_1,源服务器B:td_2,定时同步到服务器C:td_1,首次全量后面增量,两个服务器不会有相同的超级表和其子表,数据字段不变。

【问题复现路径/shan】做过哪些操作出现的问题

【遇到的问题:问题现象及影响】
下面是我的shell脚本,看看行吗?
源A:

#!/bin/bash

源A:td_8bu 备份同步脚本(修复时间差,避免丢数据)

set -euo pipefail

配置项

DB_NAME=“td_8bu”
SOURCE_ID=“src_a”
LOCAL_BACKUP_ROOT=“/taos/data/taos_incr_backup_${SOURCE_ID}”
RECORD_FILE=“${LOCAL_BACKUP_ROOT}/last_backup_endtime.txt”
TAOS_HOST=“localhost”
TAOS_PORT=“6030”
LOCAL_LOG_FILE=“${LOCAL_BACKUP_ROOT}/backup_sync.log”
KEEP_DAYS=180
TIME_OFFSET=30  # 时间回退秒数,覆盖备份耗时的时间差

目标服务器配置

TARGET_IP=“xxx.xx.xxx.xx”
TARGET_USER=“root”
TARGET_BACKUP_ROOT=“/taos/data/taos_backup_sync/${SOURCE_ID}”
TARGET_RECORD_FILE=“${TARGET_BACKUP_ROOT}/last_backup_endtime.txt”

1. 初始化目录

mkdir -p ${LOCAL_BACKUP_ROOT}
chmod 755 ${LOCAL_BACKUP_ROOT}
ssh ${TARGET_USER}@${TARGET_IP} “mkdir -p ${TARGET_BACKUP_ROOT} && chmod 755 ${TARGET_BACKUP_ROOT}” >> ${LOCAL_LOG_FILE} 2>&1

函数:时间回退(ISO8601格式回退N秒)

offset_time() {
local ISO_TIME=$1
local OFFSET=$2

转换为时间戳 → 减OFFSET秒 → 转回ISO8601

TIMESTAMP=$(date -d “${ISO_TIME}” +%s)
NEW_TIMESTAMP=$((TIMESTAMP - OFFSET))
date -d “@${NEW_TIMESTAMP}” +“%Y-%m-%dT%H:%M:%S+0800”
}

2. 备份逻辑

echo -e “\n===== $(date +”%Y-%m-%d %H:%M:%S") 源${SOURCE_ID}-${DB_NAME} 开始备份 =====" >> ${LOCAL_LOG_FILE}
if [ ! -f ${RECORD_FILE} ]; then
FULL_PATH=“${LOCAL_BACKUP_ROOT}/${SOURCE_ID}full$(date +”%Y%m%d%H%M%S")"
mkdir -p ${FULL_PATH}

全量备份:E参数用当前时间,记录时回退TIME_OFFSET秒

BACKUP_END=$(date +“%Y-%m-%dT%H:%M:%S+0800”)
taosdump -h ${TAOS_HOST} -P ${TAOS_PORT} -D ${DB_NAME} -E “${BACKUP_END}” -o ${FULL_PATH} >> ${LOCAL_LOG_FILE} 2>&1

同步全量备份到目标服务器

scp -r ${FULL_PATH} ${TARGET_USER}@${TARGET_IP}:${TARGET_BACKUP_ROOT}/ >> ${LOCAL_LOG_FILE} 2>&1

记录回退后的时间(避免漏数据)

RECORD_END=$(offset_time “${BACKUP_END}” ${TIME_OFFSET})
echo “${RECORD_END}” > ${RECORD_FILE}

同步记录文件到C端

scp ${RECORD_FILE} ${TARGET_USER}@${TARGET_IP}:${TARGET_RECORD_FILE} >> ${LOCAL_LOG_FILE} 2>&1

echo “源${SOURCE_ID}全量备份完成,记录时间(回退${TIME_OFFSET}秒):${RECORD_END}” >> ${LOCAL_LOG_FILE}
else
LAST_END=$(cat ${RECORD_FILE})

增量备份:E参数用当前时间,S参数用上次记录的时间(已回退,无需再改)

BACKUP_END=$(date +“%Y-%m-%dT%H:%M:%S+0800”)
INCR_PATH=“${LOCAL_BACKUP_ROOT}/${SOURCE_ID}incr$(echo ${LAST_END} | sed ‘s/[-T:+]//g’ | cut -c1-14)_$(date +”%Y%m%d%H%M%S")"
mkdir -p ${INCR_PATH}

增量备份:-S 用上次记录的时间(已回退),-E 用当前时间

taosdump -h ${TAOS_HOST} -P ${TAOS_PORT} -S “${LAST_END}” -E “${BACKUP_END}” -D ${DB_NAME} -o ${INCR_PATH} >> ${LOCAL_LOG_FILE} 2>&1

同步增量备份到目标服务器

scp -r ${INCR_PATH} ${TARGET_USER}@${TARGET_IP}:${TARGET_BACKUP_ROOT}/ >> ${LOCAL_LOG_FILE} 2>&1

记录回退后的结束时间

RECORD_END=$(offset_time “${BACKUP_END}” ${TIME_OFFSET})
echo “${RECORD_END}” > ${RECORD_FILE}

同步记录文件到C端

scp ${RECORD_FILE} ${TARGET_USER}@${TARGET_IP}:${TARGET_RECORD_FILE} >> ${LOCAL_LOG_FILE} 2>&1

echo “源${SOURCE_ID}增量备份完成,记录时间(回退${TIME_OFFSET}秒):${RECORD_END}” >> ${LOCAL_LOG_FILE}
fi

3. 清理180天前备份

find ${LOCAL_BACKUP_ROOT} -type d -name “${SOURCE_ID}full" -o -name "${SOURCE_ID}incr” -mtime +${KEEP_DAYS} -exec rm -rf {} ;
ssh ${TARGET_USER}@${TARGET_IP} “find ${TARGET_BACKUP_ROOT} -type d -name ‘${SOURCE_ID}full’ -o -name '${SOURCE_ID}incr’ -mtime +${KEEP_DAYS} -exec rm -rf {} ;”

源B:

#!/bin/bash

源B:td_8bu_1 备份同步脚本(修复时间差,避免丢数据)

set -euo pipefail

配置项(仅修改SOURCE_ID和DB_NAME,其余与源A一致)

DB_NAME=“td_8bu_1”
SOURCE_ID=“src_b”  # 源B标识
LOCAL_BACKUP_ROOT=“/taos/data/taos_incr_backup_${SOURCE_ID}”
RECORD_FILE=“${LOCAL_BACKUP_ROOT}/last_backup_endtime.txt”
TAOS_HOST=“localhost”
TAOS_PORT=“6030”
LOCAL_LOG_FILE=“${LOCAL_BACKUP_ROOT}/backup_sync.log”
KEEP_DAYS=180
TIME_OFFSET=30  # 时间回退秒数,覆盖备份耗时的时间差(与源A保持一致)

目标服务器配置(与源A一致)

TARGET_IP=“xxx.xx.xxx.xx”
TARGET_USER=“root”
TARGET_BACKUP_ROOT=“/taos/data/taos_backup_sync/${SOURCE_ID}”
TARGET_RECORD_FILE=“${TARGET_BACKUP_ROOT}/last_backup_endtime.txt”

1. 初始化目录

mkdir -p ${LOCAL_BACKUP_ROOT}
chmod 755 ${LOCAL_BACKUP_ROOT}
ssh ${TARGET_USER}@${TARGET_IP} “mkdir -p ${TARGET_BACKUP_ROOT} && chmod 755 ${TARGET_BACKUP_ROOT}” >> ${LOCAL_LOG_FILE} 2>&1

函数:时间回退(ISO8601格式回退N秒,与源A完全一致)

offset_time() {
local ISO_TIME=$1
local OFFSET=$2

转换为时间戳 → 减OFFSET秒 → 转回ISO8601

TIMESTAMP=$(date -d “${ISO_TIME}” +%s)
NEW_TIMESTAMP=$((TIMESTAMP - OFFSET))
date -d “@${NEW_TIMESTAMP}” +“%Y-%m-%dT%H:%M:%S+0800”
}

2. 备份逻辑(仅日志标识适配src_b,其余逻辑与源A一致)

echo -e “\n===== $(date +”%Y-%m-%d %H:%M:%S") 源${SOURCE_ID}-${DB_NAME} 开始备份 =====" >> ${LOCAL_LOG_FILE}
if [ ! -f ${RECORD_FILE} ]; then
FULL_PATH=“${LOCAL_BACKUP_ROOT}/${SOURCE_ID}full$(date +”%Y%m%d%H%M%S")"
mkdir -p ${FULL_PATH}

全量备份:E参数用当前时间,记录时回退TIME_OFFSET秒

BACKUP_END=$(date +“%Y-%m-%dT%H:%M:%S+0800”)
taosdump -h ${TAOS_HOST} -P ${TAOS_PORT} -D ${DB_NAME} -E “${BACKUP_END}” -o ${FULL_PATH} >> ${LOCAL_LOG_FILE} 2>&1

同步全量备份到目标服务器C

scp -r ${FULL_PATH} ${TARGET_USER}@${TARGET_IP}:${TARGET_BACKUP_ROOT}/ >> ${LOCAL_LOG_FILE} 2>&1

记录回退后的时间(避免漏数据)

RECORD_END=$(offset_time “${BACKUP_END}” ${TIME_OFFSET})
echo “${RECORD_END}” > ${RECORD_FILE}

同步记录文件到C端

scp ${RECORD_FILE} ${TARGET_USER}@${TARGET_IP}:${TARGET_RECORD_FILE} >> ${LOCAL_LOG_FILE} 2>&1

echo “源${SOURCE_ID}全量备份完成,记录时间(回退${TIME_OFFSET}秒):${RECORD_END}” >> ${LOCAL_LOG_FILE}
else
LAST_END=$(cat ${RECORD_FILE})

增量备份:E参数用当前时间,S参数用上次记录的时间(已回退,无需再改)

BACKUP_END=$(date +“%Y-%m-%dT%H:%M:%S+0800”)
INCR_PATH=“${LOCAL_BACKUP_ROOT}/${SOURCE_ID}incr$(echo ${LAST_END} | sed ‘s/[-T:+]//g’ | cut -c1-14)_$(date +”%Y%m%d%H%M%S")"
mkdir -p ${INCR_PATH}

增量备份:-S 用上次记录的时间(已回退),-E 用当前时间

taosdump -h ${TAOS_HOST} -P ${TAOS_PORT} -S “${LAST_END}” -E “${BACKUP_END}” -D ${DB_NAME} -o ${INCR_PATH} >> ${LOCAL_LOG_FILE} 2>&1

同步增量备份到目标服务器C

scp -r ${INCR_PATH} ${TARGET_USER}@${TARGET_IP}:${TARGET_BACKUP_ROOT}/ >> ${LOCAL_LOG_FILE} 2>&1

记录回退后的结束时间

RECORD_END=$(offset_time “${BACKUP_END}” ${TIME_OFFSET})
echo “${RECORD_END}” > ${RECORD_FILE}

同步记录文件到C端

scp ${RECORD_FILE} ${TARGET_USER}@${TARGET_IP}:${TARGET_RECORD_FILE} >> ${LOCAL_LOG_FILE} 2>&1

echo “源${SOURCE_ID}增量备份完成,记录时间(回退${TIME_OFFSET}秒):${RECORD_END}” >> ${LOCAL_LOG_FILE}
fi

3. 清理180天前备份(与源A逻辑一致)

find ${LOCAL_BACKUP_ROOT} -type d -name “${SOURCE_ID}full" -o -name "${SOURCE_ID}incr” -mtime +${KEEP_DAYS} -exec rm -rf {} ;
ssh ${TARGET_USER}@${TARGET_IP} “find ${TARGET_BACKUP_ROOT} -type d -name ‘${SOURCE_ID}full’ -o -name '${SOURCE_ID}incr’ -mtime +${KEEP_DAYS} -exec rm -rf {} ;”

目标服务器C:

#!/bin/bash

目标服务器C:合并两个源库到td_8bu(全量+增量标记均独立存储,适配3.3.8.4)

set -euo pipefail

配置项

TARGET_DB=“td_8bu”
BACKUP_ROOT=“/taos/data/taos_backup_sync”
SRC_A_DIR=“${BACKUP_ROOT}/src_a”  # 源A备份目录
SRC_B_DIR=“${BACKUP_ROOT}/src_b”  # 源B备份目录

源端备份结束时间文件(源端同步过来)

SRC_A_BACKUP_END=“${SRC_A_DIR}/last_backup_endtime.txt”
SRC_B_BACKUP_END=“${SRC_B_DIR}/last_backup_endtime.txt”

独立标记文件(全量+增量,完全脱离日志)

SRC_A_FULL_RESTORED=“${SRC_A_DIR}/restored_full.txt”  # src_a全量恢复记录
SRC_A_INCR_RESTORED=“${SRC_A_DIR}/restored_incr.txt”  # src_a增量恢复记录
SRC_B_FULL_RESTORED=“${SRC_B_DIR}/restored_full.txt”  # src_b全量恢复记录
SRC_B_INCR_RESTORED=“${SRC_B_DIR}/restored_incr.txt”  # src_b增量恢复记录

TAOS_HOST=“localhost”
TAOS_PORT=“6030”
LOG_FILE=“${BACKUP_ROOT}/merge_restore.log”
KEEP_DAYS=180

关键:初始化目录+所有独立标记文件(首次执行自动创建空文件)

mkdir -p ${BACKUP_ROOT} ${SRC_A_DIR} ${SRC_B_DIR}
chmod 755 ${BACKUP_ROOT} ${SRC_A_DIR} ${SRC_B_DIR}

初始化全量/增量标记文件(避免文件不存在报错)

touch ${SRC_A_FULL_RESTORED} ${SRC_A_INCR_RESTORED}
touch ${SRC_B_FULL_RESTORED} ${SRC_B_INCR_RESTORED}
chmod 644 ${SRC_A_FULL_RESTORED} ${SRC_A_INCR_RESTORED}
chmod 644 ${SRC_B_FULL_RESTORED} ${SRC_B_INCR_RESTORED}

初始化日志(仅用于记录过程,不再用于恢复判断)

echo -e “\n===== $(date +”%Y-%m-%d %H:%M:%S") 开始合并恢复 =====" >> ${LOG_FILE}

函数:时间格式转换(ISO8601 → 纯数字,仅日志展示)

convert_time() {
local TIME_STR=$1
echo “${TIME_STR}” | sed ‘s/[-T:+]//g’ | cut -c1-14
}

函数:恢复单个源(全量+增量均查独立标记文件,完全脱离日志)

restore_source() {
local SRC_DIR=“$1”          # 源备份目录(src_a/src_b)
local SRC_BACKUP_END=“$2”   # 源端备份结束时间文件
local FULL_RESTORED=“$3”    # 全量恢复标记文件
local INCR_RESTORED=“$4”    # 增量恢复标记文件
local RENAME_RULE=“$5”      # 库重命名规则(源B用)
local SOURCE_ID=“$6”        # 源标识(src_a/src_b)

echo -e “\n===== 开始处理${SOURCE_ID}数据 =====” >> ${LOG_FILE}

步骤1:检查源端是否有备份记录

if [ ! -f “${SRC_BACKUP_END}” ]; then
echo “【${SOURCE_ID}】警告:未找到源端备份记录文件,跳过” >> ${LOG_FILE}
return
fi

读取源端最新备份结束时间(仅日志展示)

SRC_LAST_BACKUP_ISO=$(cat “${SRC_BACKUP_END}”)
SRC_LAST_BACKUP_NUM=$(convert_time “${SRC_LAST_BACKUP_ISO}”)
echo “【${SOURCE_ID}】源端最新备份结束时间:${SRC_LAST_BACKUP_ISO}(纯数字:${SRC_LAST_BACKUP_NUM})” >> ${LOG_FILE}

步骤2:恢复最新全量备份(查独立全量标记文件,不再查日志)

set +e

找源端最新全量备份(按时间降序取第一个)

SRC_FULL=$(ls -d “${SRC_DIR}/${SOURCE_ID}full”* 2>/dev/null | sort -r | head -n 1)
set -e

if [ -n “${SRC_FULL}” ]; then
FULL_NAME=$(basename “${SRC_FULL}”)
# 核心改:查全量标记文件,判断是否已恢复
FULL_HAS_RESTORED=$(grep -Fq “${FULL_NAME}” “${FULL_RESTORED}” && echo “yes” || echo “no”)

if [ "${FULL_HAS_RESTORED}" = "no" ]; then
  echo "【${SOURCE_ID}】首次恢复,执行全量备份恢复:${FULL_NAME}" >> ${LOG_FILE}
  eval "taosdump -h ${TAOS_HOST} -P ${TAOS_PORT} -i ${SRC_FULL} -D ${TARGET_DB} ${RENAME_RULE}" >> ${LOG_FILE} 2>&1
  
  if [ $? -eq 0 ]; then
    echo "【${SOURCE_ID}】全量备份恢复完成:${FULL_NAME}" >> ${LOG_FILE}
    # 写入全量标记文件(追加模式,避免覆盖)
    echo "${FULL_NAME}" >> "${FULL_RESTORED}"
    echo "【${SOURCE_ID}】全量标记已写入:${FULL_RESTORED}" >> ${LOG_FILE}
  else
    echo "【${SOURCE_ID}】错误:全量备份恢复失败,终止" >> ${LOG_FILE}
    exit 1
  fi
else
  echo "【${SOURCE_ID}】全量备份已恢复,跳过:${FULL_NAME}" >> ${LOG_FILE}
fi

else
echo “【${SOURCE_ID}】警告:无全量备份,跳过” >> ${LOG_FILE}
return
fi

步骤3:筛选增量备份(查独立增量标记文件)

set +e
INCR_LIST=$(ls -d “${SRC_DIR}/${SOURCE_ID}incr”* 2>/dev/null | sort)
set -e

过滤已恢复的增量(查独立增量标记文件)

INCR_UNRESTORED=()
if [ -n “${INCR_LIST}” ]; then
for INCR in ${INCR_LIST}; do
INCR_NAME=$(basename “${INCR}”)
if ! grep -Fq “${INCR_NAME}” “${INCR_RESTORED}”; then
INCR_UNRESTORED+=(“${INCR}”)
fi
done
fi

if [ ${#INCR_UNRESTORED[@]} -eq 0 ]; then
echo “【${SOURCE_ID}】无未恢复的增量备份,跳过” >> ${LOG_FILE}
return
fi

遍历未恢复的增量,执行恢复

for INCR in “${INCR_UNRESTORED[@]}”; do
INCR_NAME=$(basename “${INCR}”)
INCR_END_NUM=$(echo “${INCR_NAME}” | awk -F ‘_’ ‘{print $5}’)

echo "【${SOURCE_ID}】开始恢复增量备份:${INCR_NAME}(增量结束时间:${INCR_END_NUM})" >> ${LOG_FILE}
eval "taosdump -h ${TAOS_HOST} -P ${TAOS_PORT} -i ${INCR} -D ${TARGET_DB} ${RENAME_RULE}" >> ${LOG_FILE} 2>&1

if [ $? -eq 0 ]; then
  echo "【${SOURCE_ID}】增量备份恢复完成:${INCR_NAME}" >> ${LOG_FILE}
  # 写入增量标记文件
  echo "${INCR_NAME}" >> "${INCR_RESTORED}"
  echo "【${SOURCE_ID}】增量标记已写入:${INCR_RESTORED}" >> ${LOG_FILE}
else
  echo "【${SOURCE_ID}】错误:增量备份${INCR_NAME}恢复失败,终止" >> ${LOG_FILE}
  exit 1
fi

done
}

1. 确保目标库存在(适配3.3.8.4语法)

echo “【初始化】创建/检查目标库${TARGET_DB}” >> ${LOG_FILE}
taos -s “CREATE DATABASE IF NOT EXISTS ${TARGET_DB} KEEP 365 BLOCK_SIZE 10;” >> ${LOG_FILE} 2>&1
if [ $? -ne 0 ]; then
echo “【错误】目标库创建失败,查看日志:${LOG_FILE}” >> ${LOG_FILE}
echo “目标库创建失败!”
exit 1
fi

2. 恢复源A(传入全量+增量标记文件)

restore_source “${SRC_A_DIR}” “${SRC_A_BACKUP_END}” “${SRC_A_FULL_RESTORED}” “${SRC_A_INCR_RESTORED}” “” “src_a”

3. 恢复源B(传入全量+增量标记文件)

restore_source “${SRC_B_DIR}” “${SRC_B_BACKUP_END}” “${SRC_B_FULL_RESTORED}” “${SRC_B_INCR_RESTORED}” “-W td_8bu_1=td_8bu” “src_b”

4. 清理180天前备份(仅删备份目录,保留所有标记文件)

echo -e “\n===== 开始清理${KEEP_DAYS}天前的备份 =====” >> ${LOG_FILE}
find “${BACKUP_ROOT}” -type d ( -name “src_full” -o -name “src_incr” ) -mtime +${KEEP_DAYS} -exec rm -rf {} ; >> ${LOG_FILE} 2>&1
echo “【清理】180天前备份清理完成(保留全量/增量恢复标记文件)” >> ${LOG_FILE}

收尾

echo -e “\n===== $(date +”%Y-%m-%d %H:%M:%S") 合并恢复完成 =====\n" >> ${LOG_FILE}
echo “两个源库合并恢复到${TARGET_DB}完成!(全量/增量标记均独立存储)”
echo “src_a全量恢复记录:${SRC_A_FULL_RESTORED}”
echo “src_a增量恢复记录:${SRC_A_INCR_RESTORED}”
echo “src_b全量恢复记录:${SRC_B_FULL_RESTORED}”
echo “src_b增量恢复记录:${SRC_B_INCR_RESTORED}”
echo “详细日志:${LOG_FILE}”

【资源配置】

【报错完整截图】(不要大段的粘贴报错代码,论坛直接看报错代码不直观)

如果源服务的表稳定,不会有任何的schema变化,思路是可以的。就是利用 taosdump 能够指定时间范围的特性,进行增量备份。