多源异构数据库统一采集方案:某教育平台6大校区数据中心实战

13 阅读7分钟

一、背景

某教育集团业务覆盖海淀、朝阳、西城、东城、丰台、通州6大校区,每个校区都有独立的MySQL数据库实例,存储着学员、课程、成绩等核心业务数据。数据中台团队需要将这6个校区的数据统一采集到数据仓库中,供下游分析和教研团队使用。

数据分布情况

校区数据库实例过滤条件数据量级
海淀rm-haidian.mysql.rds.aliyuncs.comcampus_id = 1约8万学员
朝阳rm-chaoyang.mysql.rds.aliyuncs.comcampus_id != 0约6万学员
西城rm-xicheng.mysql.rds.aliyuncs.comcampus_id != 0约5万学员
东城rm-dongcheng.mysql.rds.aliyuncs.comcampus_id != 0约4万学员
丰台rm-fengtai.mysql.rds.aliyuncs.comcampus_id != 0约3万学员
通州rm-tongzhou.mysql.rds.aliyuncs.comcampus_id != 0约2万学员

看起来不复杂?天真了


二、踩坑历程

坑1:直连源库做ETL——一个挂全部挂

最初方案是直接连6个源库做ETL,代码大概长这样:

for campus in ['haidian', 'chaoyang', 'xicheng', 'dongcheng', 'fengtai', 'tongzhou']:
    conn = create_connection(campus)
    data = fetch_data(conn)
    process_data(data)
    conn.close()

结果上线第一天就翻车了——朝阳校区数据库因为选课高峰,连接直接超时,整个采集任务卡死在那一步,后面4个校区的数据一个都没采。

教训:多源采集最大的坑不是技术实现,而是容错隔离。一个源挂了,不能拖垮整个流程。

坑2:UNION ALL一把梭——临时表炸了

第二次尝试把6个校区的数据用UNION ALL合并后一次性写入ODS层:

INSERT INTO ods_student (...)
SELECT ... FROM haidian.student WHERE ...
UNION ALL
SELECT ... FROM chaoyang.student WHERE ...
UNION ALL
SELECT ... FROM xicheng.student WHERE ...
-- ... 6个UNION ALL

MySQL在执行UNION ALL时会产生巨大的临时表,6个校区的数据加起来数十万行,RDS实例配置不高,磁盘直接告警。

教训:即使数据量不算海量,UNION ALL在资源有限的环境下依然是临时表爆炸的元凶。

坑3:连接配置硬编码——运维噩梦

6套数据库连接信息直接写在代码里,每次密码变更、实例迁移都要改代码重新部署,运维同学差点提刀来见。


三、最终解决方案

经过3次迭代,最终形成了临时库解耦 + 分校区采集 + 配置外置的三层架构:

3.1 架构设计

6大校区源库 → 临时库(staging_{campus}) → ODS层 → DW层 → DM层

核心思路:引入临时库作为缓冲层,解耦采集与处理

3.2 临时库按校区分区

每个校区的数据写入独立的临时表,互不干扰:

# 临时库表命名规则:{业务表}_{校区标识}
# 例如:student_haidian, student_chaoyang, student_xicheng ...
#       course_haidian, course_chaoyang, course_xicheng ...

6个校区 × 3张业务表 = 18张临时表,结构完全一致,数据完全隔离。

3.3 分校区独立采集 + 容错

class MultiSourceSync:
    def __init__(self):
        self.campuses = ['haidian', 'chaoyang', 'xicheng', 'dongcheng', 'fengtai', 'tongzhou']
        self.campus_filters = {
            'haidian': 'campus_id = 1',
            'chaoyang': 'campus_id != 0',
            # ...
        }
        self.failed_campuses = []

    def run(self):
        for campus in self.campuses:
            try:
                data = self.fetch_from_source(campus)
                self.sync_to_staging(data, campus)
            except Exception as e:
                logger.error(f"校区 {campus} 采集失败: {e}")
                self.failed_campuses.append(campus)
                continue  # 不阻塞其他校区

        if self.failed_campuses:
            logger.warning(f"以下校区采集失败: {self.failed_campuses}")

关键点:每个校区独立try-catch,失败的校区记录到列表中,不影响其他校区继续执行。

3.4 分校区写入ODS层

替代UNION ALL的方案——逐个校区执行INSERT:

for campus, campus_filter in campus_configs:
    sql = f"""
        INSERT INTO ods_student (...)
        SELECT ... FROM staging.student_{campus}
        WHERE is_deleted = 0 AND {campus_filter}
    """
    execute_sql(conn, sql)
    release_temp_tables(conn)  # 每个校区执行完立即释放临时表

3.5 配置外置到.env

# config/.env
DB_HAIDIAN_HOST=rm-haidian.mysql.rds.aliyuncs.com
DB_HAIDIAN_PORT=3306
DB_HAIDIAN_USER=reader
DB_HAIDIAN_PASSWORD=***
DB_HAIDIAN_DATABASE=edu_core

DB_CHAOYANG_HOST=rm-chaoyang.mysql.rds.aliyuncs.com
# ... 其他校区配置
from dotenv import load_dotenv
load_dotenv('config/.env')

def get_db_config(campus: str) -> dict:
    prefix = f'DB_{campus.upper()}_'
    return {
        'host': os.getenv(f'{prefix}HOST'),
        'port': int(os.getenv(f'{prefix}PORT', '3306')),
        'user': os.getenv(f'{prefix}USER'),
        'password': os.getenv(f'{prefix}PASSWORD'),
        'database': os.getenv(f'{prefix}DATABASE'),
    }

四、效果对比

指标优化前优化后
单校区故障影响全流程中断仅该校区数据缺失
ODS层临时表峰值6校区合并,超2GB单校区,约200-400MB
配置变更改代码+重新部署改.env文件即可
采集耗时约15分钟约10分钟

五、总结

多源异构数据采集的核心不是技术多高深,而是架构设计要留足容错空间

  1. 缓冲层必不可少:临时库解耦采集与处理,是整个方案的基石
  2. 隔离是第一原则:一个源挂了不能拖垮全局
  3. 分而治之替代大而全:UNION ALL看起来优雅,资源有限环境下是灾难
  4. 配置外置是基本素养:硬编码是运维的敌人

💡 一句话总结:多源采集的精髓在于"分"——分校区采集、分表存储、分批处理,任何"合"的操作都要慎之又慎。


如果这篇文章对你有帮助,点赞收藏不迷路~ 有问题欢迎评论区交流!


世局板块

1. 哈马斯新任军事领导人遭以军空袭身亡。

以色列国防军与安全局27日联合宣布,巴勒斯坦伊斯兰抵抗运动(哈马斯)新任军事领导人穆罕默德·奥达,在26日针对加沙地带北部的空袭行动中身亡。以军称,经过数月情报追踪,精准打击了奥达的藏身处。奥达曾深度参与策划2023年10月7日的突袭行动,并于近期刚接替遭暗杀的前任领导人。截至目前,哈马斯方面尚未对此作出公开回应。

2. 美媒曝美军“悄悄”协助船只通过霍尔木兹海峡。

据《华尔街日报》26日援引美军官员消息,鉴于霍尔木兹海峡局势紧张,美国海军正低调协助多艘商船通过该海域。报道称,一艘滞留已久的希腊油轮近期已在美军引导下驶往印度,后续还将协助十余艘船只通行。不过,美军中央司令部随后紧急辟谣,明确表示此前暂停的“自由计划”护航行动并未恢复,官方否认了“正式护送”的说法。

3. 以色列正在持续开展地面行动,黎巴嫩南部战略要地已被纳入规划。

当地时间26日内,塔尼亚胡召集了安全内阁会议,宣称以色列国防军正在加大在黎巴嫩的行动力度,在纵深地区行动时要要求占领一些战略要地并巩固所谓的安全缓冲区。

4. 瑞银看多美光科技,目标价直指1.8万亿美元市值。

5月26日,瑞银发布重磅研报,将存储芯片巨头美光科技的目标价从535美元大幅上调至1625美元,创下华尔街最高估值预期。受此利好提振,美光股价单日暴涨逾19%,总市值强势突破1万亿美元大关。瑞银指出,在AI算力需求的强劲驱动下,存储芯片行业正迎来结构性变革,长期协议模式将显著削弱行业周期属性,预计美光市值未来有望进一步攀升至1.8万亿美元。

5. 美团发布“跑腿Skill”,全面接入AI助手生态。

5月26日,美团跑腿正式推出“跑腿Skill”功能,将线下即时配送的下单能力封装成标准化接口,向各大AI助手生态开放。目前,用户已可在OpenClaw、Cursor、微信、飞书等多种接入该Skill的AI应用中,直接通过自然语言描述需求,即可一键完成跑腿下单。此举标志着美团正加速融入AI应用生态,通过技术赋能进一步降低用户获取本地生活服务的门槛。

6. 全球6G竞赛白热化,中国专利占比居首领跑赛道。

随着全球通信产业聚焦下一代技术,6G竞赛已呈白热化态势。目前,全球6G产业形成中美双极领跑格局,中国已率先批复6G试验频率使用许可。截至2025年,中国6G核心专利申请量占比高达40.3%,稳居全球第一。依托频率资源、专利储备与试验网络三大优势,我国正深度参与国际标准制定,并规划于2030年启动商用,2035年实现规模化部署。