本文目录导读:

- 核心策略:基于时间戳/版本号(最常见)
- 基于内容的强一致性同步:Checksum 对比
- 数据库专用的精准同步:Binlog/CDC + WAL
- 通用文件系统增量同步:inotify + 队列
- API 增量同步:分页 + 最新记录ID
- 工程最佳实践总结
针对增量精准同步场景,Python脚本需要解决几个核心问题:识别变更、记录同步状态、处理冲突或重复,根据数据源类型(文件、数据库、API),适配策略会有所不同,以下是几种主流且工程化的方案:
核心策略:基于时间戳/版本号(最常见)
这是最通用的增量同步方式,适用于文件(修改时间)、数据库记录(updated_at)或API(版本字段)。
实现要点:
- 记录上一次同步时间:保存到文件(JSON/CSV)、数据库或环境变量。
- 查询条件:
WHERE updated_at > last_sync_time - 处理边界:使用包含边界(
>=)或游标(id > last_id)防止漏数据。
import json
from datetime import datetime
import sqlite3
# 同步状态文件
STATE_FILE = 'sync_state.json'
DB_PATH = 'source.db'
def load_state():
try:
with open(STATE_FILE, 'r') as f:
return json.load(f)
except FileNotFoundError:
return {'last_updated': '1970-01-01 00:00:00', 'last_id': 0}
def save_state(state):
with open(STATE_FILE, 'w') as f:
json.dump(state, f)
def sync_records():
state = load_state()
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
# 增量查询: 使用时间戳 + ID 防止重复/漏数据
cursor.execute('''
SELECT * FROM users
WHERE updated_at >= ?
AND id > ? -- 游标,处理同一秒内多条记录
ORDER BY updated_at ASC, id ASC
''', (state['last_updated'], state['last_id']))
rows = cursor.fetchall()
for row in rows:
process_record(row) # 你的同步逻辑
# 更新状态(循环内更新,保证断点续传)
state['last_updated'] = row['updated_at']
state['last_id'] = row['id']
# 如果同步完全成功,再写入(事务性写法需更复杂)
save_state(state)
def process_record(record):
print(f"Syncing: {record['id']} - {record['name']}")
# 此处写目标数据库/文件的写入逻辑
缺点:依赖数据源提供可靠的、严格递增的更新时间。
的强一致性同步:Checksum 对比
对于不支持时间戳或时间戳不可靠的场景(如CSV文件、遗留系统),使用哈希校验。
方案:
- 存储上一次同步的文件哈希(MD5/SHA256)或记录指纹。
- 比较当前哈希与历史哈希,判断是否变更。
import hashlib
import json
def file_checksum(filepath):
"""计算文件SHA256"""
sha = hashlib.sha256()
with open(filepath, 'rb') as f:
for chunk in iter(lambda: f.read(4096), b''):
sha.update(chunk)
return sha.hexdigest()
def sync_files():
state = load_state() # 假设有 load_state 如上
file_list = get_file_list() # 获取所有文件
changed_files = []
for file, mtime in file_list:
# 检查文件是否被修改
if file not in state['files'] or state['files'][file]['mtime'] != mtime:
changed_files.append(file)
# 计算当前哈希
cur_hash = file_checksum(file)
if file not in state['files'] or state['files'][file]['hash'] != cur_hash:
# 真正变更了
upload_file(file)
state['files'][file] = {'mtime': mtime, 'hash': cur_hash}
save_state(state)
优点:100%精确检测变更。
缺点:对大规模文件系统/数据库性能开销大(每次全量扫描+哈希计算),适用于文件数量<10万或变化频率低的场景。
数据库专用的精准同步:Binlog/CDC + WAL
对于MySQL/PostgreSQL等数据库,增量同步的最佳实践是监听Redo Log或WAL。
Python工具链:
- mysql-replication (python-mysql-replication):解析MySQL binlog
- pg_easy_replicate 或 pg_wal_checkpoint:解析PG WAL
核心实现伪代码:
from pymysqlreplication import BinLogStreamReader
from pymysqlreplication.row_event import (
DeleteRowsEvent,
UpdateRowsEvent,
WriteRowsEvent,
)
def sync_from_binlog():
# 配置MySQL连接和binlog位置
stream = BinLogStreamReader(
connection_settings=settings,
server_id=100,
only_events=[DeleteRowsEvent, UpdateRowsEvent, WriteRowsEvent],
resume_stream=True, # 自动从上次断开位置恢复
)
for binlogevent in stream:
for row in binlogevent.rows:
if isinstance(binlogevent, WriteRowsEvent):
handle_insert(row['values'])
elif isinstance(binlogevent, UpdateRowsEvent):
handle_update(row['before_values'], row['after_values'])
elif isinstance(binlogevent, DeleteRowsEvent):
handle_delete(row['values'])
# 记录binlog位置用于断点续传
save_position(binlogevent.packet.log_pos)
优点:毫秒级延迟、无遗漏、无重复。
难点:需要开启数据库binlog(影响一定程度性能)、需要解析二进制格式。
通用文件系统增量同步:inotify + 队列
对于本地文件同步到远程(如rsync的Python替代),使用文件系统事件监控。
实现:
- 使用
watchdog库监控目录变化 - 结合去重窗口(Debounce,防抖动)
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import time
class SyncHandler(FileSystemEventHandler):
def __init__(self, sync_func, debounce_seconds=5):
self.sync_func = sync_func
self.debounce_seconds = debounce_seconds
self.last_trigger = 0
def on_modified(self, event):
if event.is_directory:
return
# 去重:避免短时间内多次触发
now = time.time()
if now - self.last_trigger > self.debounce_seconds:
self.sync_func(event.src_path)
self.last_trigger = now
def start_incremental_sync(watch_dir, sync_func):
event_handler = SyncHandler(sync_func)
observer = Observer()
observer.schedule(event_handler, watch_dir, recursive=True)
observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()
# 使用
def upload_file(path):
print(f"Uploading changed: {path}")
# 调用S3/OSS API或其他目标
start_incremental_sync('/data/source', upload_file)
缺点:进程存活期间有效,重启后需要全量比对。
API 增量同步:分页 + 最新记录ID
适用于REST API(如Salesforce、Zendesk等SaaS系统)。
策略:
- 使用
since或updated_after参数 - 或使用
cursor分页(防止数据量过大)
import requests
import time
def sync_orders(api_key, base_url):
last_sync_id = load_last_sync_id() # 从DB或文件读取
page = 1
while True:
params = {
'api_key': api_key,
'limit': 500,
'page': page,
'since_id': last_sync_id # 只获取比上次大的ID
}
resp = requests.get(f"{base_url}/orders", params=params)
orders = resp.json()
if not orders:
break
for order in orders:
process_order(order)
# 更新last_sync_id为最大ID
if order['id'] > last_sync_id:
last_sync_id = order['id']
if len(orders) < 500: # 最后一页
break
page += 1
save_last_sync_id(last_sync_id)
工程最佳实践总结
| 场景 | 推荐方案 | 注意事项 |
|---|---|---|
| 小文件/少量记录 | 时间戳 + 游标 | 保证时间戳严格递增 |
| 大文件/文件系统 | 文件监控(inotify) + 哈希校验 | 需处理启动时的全量比对 |
| MySQL实时同步 | Binlog流(CDC) | 依赖binlog格式,注意GTID |
| SaaS API | API分页 + since_id |
关注API速率限制 |
| 跨云存储 | AWS S3事件通知Lambda | 配合消息队列 |
推荐的Python库
- 数据库同步:
mysql-replication,pg_easy_replicate,sqlalchemy(ORM同步) - 文件系统:
watchdog,rsync(通过subprocess调用) - Hash校验:
checksumdir(目录哈希)、hashlib(单文件) - 状态管理:
sqlite3(小数据集中间状态)、redis(分布式同步)
最后建议
- 增加幂等性处理:目标端检查记录是否存在(
ON DUPLICATE KEY UPDATE或UPSERT) - 记录同步日志:便于排查漏同步或重复同步问题
- 监控延迟:比较数据源最新时间与同步状态的时间差
- 异常重试:使用
tenacity库实现重试机制
根据你的具体数据源(文件?数据库?API?)和使用场景,选择最合适的方案,如果数据量极大且要求严格一致性,建议优先考虑CDC方案。