Python脚本如何批量同步批量数据变更

wen python案例 30

本文目录导读:

Python脚本如何批量同步批量数据变更

  1. 基于SQL的批量同步
  2. 基于变更捕获(CDC)的同步
  3. 基于文件的批量同步
  4. 企业级批量同步方案
  5. 实用同步工具类
  6. 使用建议

我来介绍几种批量同步数据变更的Python方案:

基于SQL的批量同步

import pandas as pd
from sqlalchemy import create_engine
import logging
# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class DataSyncSQL:
    def __init__(self, source_db, target_db):
        self.source_engine = create_engine(source_db)
        self.target_engine = create_engine(target_db)
    def sync_by_timestamp(self, table_name, timestamp_column, batch_size=1000):
        """基于时间戳的增量同步"""
        try:
            # 获取目标表最大时间戳
            query = f"SELECT MAX({timestamp_column}) FROM {table_name}"
            last_sync = pd.read_sql(query, self.target_engine).iloc[0, 0]
            if last_sync is None:
                last_sync = '1970-01-01'
            # 分批读取源数据
            offset = 0
            while True:
                query = f"""
                    SELECT * FROM {table_name} 
                    WHERE {timestamp_column} > '{last_sync}'
                    ORDER BY {timestamp_column}
                    LIMIT {batch_size} OFFSET {offset}
                """
                df = pd.read_sql(query, self.source_engine)
                if df.empty:
                    break
                # 写入目标数据库
                df.to_sql(table_name, self.target_engine, 
                         if_exists='append', index=False)
                offset += batch_size
                logger.info(f"已同步 {offset} 条记录")
        except Exception as e:
            logger.error(f"同步失败: {e}")
            raise
# 使用示例
sync = DataSyncSQL('mysql://user:pass@source_host/db', 
                   'mysql://user:pass@target_host/db')
sync.sync_by_timestamp('orders', 'update_time', batch_size=5000)

基于变更捕获(CDC)的同步

import hashlib
import json
from datetime import datetime
class CDCDataSync:
    def __init__(self, source_conn, target_conn):
        self.source = source_conn
        self.target = target_conn
        self.sync_meta_table = 'sync_metadata'
    def calculate_row_hash(self, row):
        """计算行哈希值用于变更检测"""
        row_str = json.dumps(row, sort_keys=True, default=str)
        return hashlib.md5(row_str.encode()).hexdigest()
    def detect_changes(self, table_name, primary_keys, batch_size=1000):
        """检测并同步变更"""
        source_rows = self.get_all_source_rows(table_name, batch_size)
        target_hashes = self.get_target_hashes(table_name, primary_keys)
        changes = {
            'insert': [],
            'update': [],
            'delete': []
        }
        for row in source_rows:
            pk = tuple(row[k] for k in primary_keys)
            current_hash = self.calculate_row_hash(row)
            if pk not in target_hashes:
                changes['insert'].append(row)
            elif target_hashes[pk] != current_hash:
                changes['update'].append(row)
        # 检测删除
        source_pks = set(tuple(row[k] for k in primary_keys) 
                        for row in source_rows)
        for pk in target_hashes:
            if pk not in source_pks:
                changes['delete'].append(pk)
        return changes
    def apply_changes(self, table_name, changes, primary_keys):
        """应用变更到目标表"""
        with self.target.cursor() as cursor:
            # 批量插入
            if changes['insert']:
                insert_sql = self.build_insert_sql(table_name, 
                                                   changes['insert'][0].keys())
                cursor.executemany(insert_sql, 
                                   [list(row.values()) for row in changes['insert']])
            # 批量更新
            if changes['update']:
                for row in changes['update']:
                    update_sql = self.build_update_sql(table_name, 
                                                       row.keys(), primary_keys)
                    cursor.execute(update_sql, list(row.values()) + 
                                   [row[k] for k in primary_keys])
            # 批量删除
            if changes['delete']:
                delete_sql = f"DELETE FROM {table_name} WHERE " + \
                            " AND ".join([f"{k} = %s" for k in primary_keys])
                cursor.executemany(delete_sql, changes['delete'])
            self.target.commit()

基于文件的批量同步

import csv
import os
from concurrent.futures import ThreadPoolExecutor, as_completed
import time
class FileBasedSync:
    def __init__(self, source_files, target_dir, chunk_size=1000):
        self.source_files = source_files
        self.target_dir = target_dir
        self.chunk_size = chunk_size
    def process_chunk(self, chunk, file_index, chunk_index):
        """处理单个数据块"""
        output_file = os.path.join(
            self.target_dir, 
            f"chunk_{file_index}_{chunk_index}.csv"
        )
        with open(output_file, 'w', newline='') as f:
            writer = csv.writer(f)
            writer.writerow(chunk[0].keys())  # 写入header
            for row in chunk:
                writer.writerow(row.values())
        return output_file
    def sync_files(self, max_workers=4):
        """并行同步多个文件"""
        all_files = []
        with ThreadPoolExecutor(max_workers=max_workers) as executor:
            futures = []
            chunk_index = 0
            for file_index, file_path in enumerate(self.source_files):
                with open(file_path, 'r') as f:
                    reader = csv.DictReader(f)
                    chunk = []
                    for row in reader:
                        chunk.append(row)
                        if len(chunk) >= self.chunk_size:
                            future = executor.submit(
                                self.process_chunk, 
                                chunk.copy(), 
                                file_index, 
                                chunk_index
                            )
                            futures.append(future)
                            chunk = []
                            chunk_index += 1
                    # 处理剩余数据
                    if chunk:
                        future = executor.submit(
                            self.process_chunk, 
                            chunk, 
                            file_index, 
                            chunk_index
                        )
                        futures.append(future)
            # 收集结果
            for future in as_completed(futures):
                try:
                    result = future.result()
                    all_files.append(result)
                except Exception as e:
                    print(f"处理失败: {e}")
        return all_files

企业级批量同步方案

from typing import List, Dict, Any
import asyncio
import aiohttp
import aiomysql
class EnterpriseDataSync:
    def __init__(self, config: Dict):
        self.config = config
        self.batch_size = config.get('batch_size', 1000)
        self.retry_count = config.get('retry_count', 3)
        self.sync_strategy = config.get('sync_strategy', 'incremental')
    async def sync_table(self, table_name: str, 
                        where_condition: str = None):
        """异步同步单个表"""
        try:
            # 1. 获取源数据
            source_data = await self.fetch_source_data(
                table_name, where_condition
            )
            # 2. 数据转换和验证
            validated_data = await self.validate_data(source_data)
            # 3. 批量写入目标
            await self.batch_write_target(validated_data, table_name)
            # 4. 记录同步日志
            await self.log_sync_result(table_name, len(validated_data))
        except Exception as e:
            await self.handle_sync_error(table_name, e)
    async def batch_sync_all_tables(self, tables: List[str]):
        """批量同步多个表"""
        tasks = []
        for table in tables:
            task = asyncio.create_task(
                self.sync_table(table)
            )
            tasks.append(task)
        # 等待所有同步任务完成
        results = await asyncio.gather(*tasks, return_exceptions=True)
        # 处理结果
        successful = []
        failed = []
        for table, result in zip(tables, results):
            if isinstance(result, Exception):
                failed.append((table, str(result)))
            else:
                successful.append(table)
        return {
            'successful': successful,
            'failed': failed,
            'timestamp': datetime.now().isoformat()
        }
    async def monitor_sync_progress(self):
        """监控同步进度"""
        while True:
            status = await self.get_sync_status()
            print(f"同步进度: {status['progress']}%")
            if status['completed']:
                break
            await asyncio.sleep(5)

实用同步工具类

import json
import dask.dataframe as dd
from typing import Optional
class DataSyncToolkit:
    def __init__(self):
        self.sync_config = {}
    def compare_and_sync(self, source_df, target_df, keys):
        """比较并同步数据框"""
        # 找出新增记录
        new_records = source_df.merge(
            target_df, 
            on=keys, 
            how='left', 
            indicator=True
        ).query('_merge == "left_only"')
        # 找出变更记录
        merged = source_df.merge(
            target_df, 
            on=keys, 
            suffixes=('_new', '_old'),
            how='inner'
        )
        changed = merged[merged['_merge'] == 'both']
        return {
            'new': new_records,
            'changed': changed
        }
    def large_dataset_sync(self, source_path: str, 
                          target_path: str, 
                          chunk_size: int = 100000):
        """大数据集同步(使用Dask)"""
        # 读取大数据集
        ddf = dd.read_csv(source_path, blocksize=chunk_size)
        # 数据转换
        # 添加同步标记
        ddf['sync_timestamp'] = datetime.now()
        ddf['sync_status'] = 'pending'
        # 保存到目标
        ddf.to_parquet(
            target_path,
            compression='snappy',
            overwrite=True
        )
        return True
    def incremental_sync_with_state(self, source_query_func, 
                                     sync_state_file: str = 'sync_state.json'):
        """带状态记录的增量同步"""
        # 读取同步状态
        try:
            with open(sync_state_file, 'r') as f:
                last_sync_state = json.load(f)
        except FileNotFoundError:
            last_sync_state = {'last_id': 0}
        # 执行增量查询
        new_data = source_query_func(
            last_id=last_sync_state['last_id']
        )
        if not new_data.empty:
            # 执行同步逻辑
            max_id = new_data['id'].max()
            # 更新同步状态
            last_sync_state['last_id'] = max_id
            last_sync_state['last_sync_time'] = datetime.now().isoformat()
            with open(sync_state_file, 'w') as f:
                json.dump(last_sync_state, f, indent=2)
        return new_data

使用建议

  1. 选择合适的同步策略

    • 全量同步:数据量小,首次同步
    • 增量同步:数据量大,定期同步
    • 实时同步:需要即时一致性
  2. 错误处理

    • 实现重试机制(指数退避)
    • 记录失败记录,单独处理
    • 事务回滚保证数据一致性
  3. 性能优化

    • 使用批量操作(BATCH)
    • 并行处理不依赖的数据
    • 使用索引加速查询
  4. 监控告警

    • 记录同步延迟
    • 监控数据一致性
    • 异常告警通知

根据你的具体场景(数据量、实时性要求、数据库类型等)选择合适的方案。

抱歉,评论功能暂时关闭!