本文目录导读:

我来详细说明如何编写数据同步校验脚本,涵盖不同场景下的实现方案。
数据同步校验的核心目标
主要验证点:
- 数据完整性:记录数是否一致
- 数据准确性:字段值是否相同
- 数据一致性:主键和外键关系是否完整
- 时效性:同步延迟是否在可接受范围
简单计数校验脚本(Python)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import pymysql
import hashlib
import time
from datetime import datetime
class DataSyncValidator:
def __init__(self, source_config, target_config):
self.source_conn = pymysql.connect(**source_config)
self.target_conn = pymysql.connect(**target_config)
def count_check(self, table_name):
"""记录数校验"""
source_count = self._get_count(self.source_conn, table_name)
target_count = self._get_count(self.target_conn, table_name)
status = "PASS" if source_count == target_count else "FAIL"
print(f"[{status}] 表 {table_name}: 源库={source_count}, 目标库={target_count}")
return source_count == target_count
def _get_count(self, conn, table_name):
cursor = conn.cursor()
cursor.execute(f"SELECT COUNT(*) FROM {table_name}")
return cursor.fetchone()[0]
def checksum_check(self, table_name, primary_key, columns=None):
"""使用CHECKSUM进行数据一致性校验"""
if columns is None:
columns = "*"
# 源库校验和
source_checksum = self._get_checksum(self.source_conn, table_name, primary_key, columns)
target_checksum = self._get_checksum(self.target_conn, table_name, primary_key, columns)
status = "PASS" if source_checksum == target_checksum else "FAIL"
print(f"[{status}] 表 {table_name} Checksum: 源库={source_checksum}, 目标库={target_checksum}")
return source_checksum == target_checksum
def _get_checksum(self, conn, table_name, primary_key, columns):
cursor = conn.cursor()
if columns == "*":
cursor.execute(f"SELECT CHECKSUM(*) FROM {table_name}")
else:
cursor.execute(f"SELECT BIT_XOR(CRC32(CONCAT({columns}))) FROM {table_name}")
return cursor.fetchone()[0]
def close(self):
self.source_conn.close()
self.target_conn.close()
# 使用示例
if __name__ == "__main__":
# 数据库配置
source_config = {
'host': 'source_host',
'user': 'user',
'password': 'password',
'database': 'source_db',
'charset': 'utf8mb4'
}
target_config = {
'host': 'target_host',
'user': 'user',
'password': 'password',
'database': 'target_db',
'charset': 'utf8mb4'
}
validator = DataSyncValidator(source_config, target_config)
# 执行校验
tables = ['users', 'orders', 'products']
for table in tables:
validator.count_check(table)
validator.checksum_check(table, 'id', 'name,email,status')
validator.close()
完整数据对比脚本(支持分批处理)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import pandas as pd
from sqlalchemy import create_engine
import logging
from datetime import datetime
class DataSyncComparer:
def __init__(self, source_conn_str, target_conn_str):
self.source_engine = create_engine(source_conn_str)
self.target_engine = create_engine(target_conn_str)
self.setup_logging()
def setup_logging(self):
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('sync_validation.log'),
logging.StreamHandler()
]
)
self.logger = logging.getLogger(__name__)
def compare_tables(self, table_name, key_columns, batch_size=1000):
"""分批对比数据"""
source_count = self._get_count(self.source_engine, table_name)
target_count = self._get_count(self.target_engine, table_name)
self.logger.info(f"表 {table_name}: 源库={source_count}, 目标库={target_count}")
if source_count != target_count:
self.logger.error(f"记录数不匹配: {source_count} vs {target_count}")
return False
# 分批获取并比较数据
offset = 0
all_consistent = True
while offset < source_count:
source_data = self._get_batch_data(self.source_engine, table_name, key_columns, offset, batch_size)
target_data = self._get_batch_data(self.target_engine, table_name, key_columns, offset, batch_size)
# 按主键排序后比较
source_data = source_data.sort_values(by=key_columns).reset_index(drop=True)
target_data = target_data.sort_values(by=key_columns).reset_index(drop=True)
if not source_data.equals(target_data):
self.logger.error(f"数据不匹配: offset={offset}")
self._log_differences(source_data, target_data, key_columns)
all_consistent = False
offset += batch_size
self.logger.info(f"已校验 {min(offset, source_count)}/{source_count} 条记录")
return all_consistent
def _get_count(self, engine, table_name):
query = f"SELECT COUNT(*) FROM {table_name}"
return pd.read_sql(query, engine).iloc[0, 0]
def _get_batch_data(self, engine, table_name, key_columns, offset, limit):
query = f"""
SELECT * FROM {table_name}
ORDER BY {', '.join(key_columns)}
LIMIT {limit} OFFSET {offset}
"""
return pd.read_sql(query, engine)
def _log_differences(self, source_df, target_df, key_columns):
"""记录具体差异"""
# 找到不同行
differences = source_df != target_df
diff_rows = differences.any(axis=1)
for idx in diff_rows[diff_rows].index:
self.logger.error(f"差异记录 (行 {idx}):")
for col in source_df.columns:
if source_df.loc[idx, col] != target_df.loc[idx, col]:
self.logger.error(f" 字段 {col}: {source_df.loc[idx, col]} -> {target_df.loc[idx, col]}")
# 使用示例
if __name__ == "__main__":
source_conn = "mysql+pymysql://user:password@source_host/source_db"
target_conn = "mysql+pymysql://user:password@target_host/target_db"
comparer = DataSyncComparer(source_conn, target_conn)
# 比较多个表
tables_to_compare = [
{"name": "users", "keys": ["user_id"]},
{"name": "orders", "keys": ["order_id"]}
]
for table in tables_to_compare:
result = comparer.compare_tables(table["name"], table["keys"])
print(f"表 {table['name']}: {'通过' if result else '失败'}")
高级校验:支持多种数据源
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import redis
from elasticsearch import Elasticsearch
import json
class MultiSourceValidator:
def __init__(self):
self.validators = {
'mysql': self._validate_mysql,
'redis': self._validate_redis,
'elasticsearch': self._validate_elasticsearch
}
def validate(self, source_type, target_type, source_config, target_config, query_config):
"""多数据源校验"""
source_data = self._get_data(source_type, source_config, query_config)
target_data = self._get_data(target_type, target_config, query_config)
return self._compare_data(source_data, target_data, query_config.get('key_field'))
def _get_data(self, data_type, config, query_config):
if data_type == 'mysql':
return self._get_mysql_data(config, query_config)
elif data_type == 'redis':
return self._get_redis_data(config, query_config)
elif data_type == 'elasticsearch':
return self._get_es_data(config, query_config)
def _get_mysql_data(self, config, query_config):
conn = pymysql.connect(**config)
cursor = conn.cursor()
cursor.execute(query_config['query'])
return cursor.fetchall()
def _get_redis_data(self, config, query_config):
r = redis.Redis(**config)
keys = r.keys(query_config['pattern'])
data = {}
for key in keys:
data[key] = r.get(key)
return data
def _get_es_data(self, config, query_config):
es = Elasticsearch([config])
result = es.search(index=query_config['index'], body=query_config['query'])
return result['hits']['hits']
def _compare_data(self, source_data, target_data, key_field):
"""通用数据比较"""
inconsistencies = []
# 转换为字典便于比较
source_dict = {item[key_field]: item for item in source_data}
target_dict = {item[key_field]: item for item in target_data}
# 检查缺失和差异
for key in source_dict:
if key not in target_dict:
inconsistencies.append(f"目标缺少记录: {key}")
elif source_dict[key] != target_dict[key]:
inconsistencies.append(f"记录 {key} 数据不一致")
for key in target_dict:
if key not in source_dict:
inconsistencies.append(f"源缺少记录: {key}")
return inconsistencies
# 使用示例
validator = MultiSourceValidator()
# 比较MySQL和Redis中的数据
mysql_config = {
'host': 'localhost',
'user': 'root',
'password': 'password',
'database': 'test'
}
redis_config = {
'host': 'localhost',
'port': 6379
}
query_config = {
'query': 'SELECT user_id, name, email FROM users WHERE status = 1',
'pattern': 'user:*',
'key_field': 'user_id'
}
inconsistencies = validator.validate('mysql', 'redis', mysql_config, redis_config, query_config)
print(f"发现 {len(inconsistencies)} 处不一致")
Shell脚本实现(简单快速)
#!/bin/bash
# data_sync_check.sh
# 配置
SOURCE_HOST="source_host"
SOURCE_USER="root"
SOURCE_PASS="password"
SOURCE_DB="source_db"
TARGET_HOST="target_host"
TARGET_USER="root"
TARGET_PASS="password"
TARGET_DB="target_db"
# 表列表
TABLES=("users" "orders" "products")
check_table() {
local table=$1
echo "检查表: $table"
# 获取记录数
source_count=$(mysql -h $SOURCE_HOST -u $SOURCE_USER -p$SOURCE_PASS -D $SOURCE_DB -e "SELECT COUNT(*) FROM $table" | tail -1)
target_count=$(mysql -h $TARGET_HOST -u $TARGET_USER -p$TARGET_PASS -D $TARGET_DB -e "SELECT COUNT(*) FROM $table" | tail -1)
echo " 源库: $source_count"
echo " 目标库: $target_count"
if [ "$source_count" -eq "$target_count" ]; then
echo " ✓ 记录数一致"
# 检查校验和
source_checksum=$(mysql -h $SOURCE_HOST -u $SOURCE_USER -p$SOURCE_PASS -D $SOURCE_DB -e "SELECT CHECKSUM(*) FROM $table" | tail -1)
target_checksum=$(mysql -h $TARGET_HOST -u $TARGET_USER -p$TARGET_PASS -D $TARGET_DB -e "SELECT CHECKSUM(*) FROM $table" | tail -1)
if [ "$source_checksum" == "$target_checksum" ]; then
echo " ✓ 数据一致"
else
echo " ✗ 数据不一致"
return 1
fi
else
echo " ✗ 记录数不一致"
return 1
fi
}
# 主逻辑
echo "开始数据同步校验..."
failures=0
for table in "${TABLES[@]}"; do
if ! check_table $table; then
echo "表 $table 校验失败"
((failures++))
fi
echo ""
done
echo "校验完成: 成功 $(( ${#TABLES[@]} - $failures )), 失败 $failures"
exit $failures
定时校验(Crontab配置)
# 每天凌晨2点执行全量校验 0 2 * * * /path/to/data_sync_check.sh >> /var/log/sync_check.log 2>&1 # 每30分钟执行快速校验 */30 * * * * /path/to/quick_check.sh >> /var/log/quick_check.log 2>&1
关键建议
-
性能优化:
- 对大表使用分批处理
- 考虑使用并行处理
- 避免全表锁
-
错误处理:
- 实现重试机制
- 记录详细日志
- 发送告警通知
-
持续改进:
- 建立基线数据
- 自动化修复
- 监控趋势变化
根据你的具体需求选择合适的方案,MySQL和PostgreSQL场景推荐用Python脚本,简单的数量校验用Shell就够了。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播