MySQL源表更新/新增后如何自动刷新inner joins生成的表D
针对单表10w条数据量级、仅小批量行增改的场景,优先选低侵入、高性能的实现方式,不要每次全量重算关联结果,按实际业务实时性要求选以下方案即可:
方案1:原生触发器(零额外组件,强实时,最适配当前场景)
因为D是三表INNER JOIN的结果,不需要每次表变动就重算全量数据,只需要针对单表的变更行做增量处理:先删除D中与旧变更行关联的无效记录,再用新变更行关联另外两张表,把匹配join条件的结果写入D即可,单次操作仅涉及几行数据的计算,10w条量级下性能无压力。
以通用关联逻辑(三表通过id关联,A关联B、C的外键为b_id、c_id,D表存储三表关联后的业务字段)为例,触发器配置示例:
-- 先修改分隔符,避免和语句内分号冲突 DELIMITER // -- 表A更新时触发同步 CREATE TRIGGER tri_A_update AFTER UPDATE ON A FOR EACH ROW BEGIN -- 清除旧A记录关联生成的D表无效数据 DELETE FROM D WHERE a_id = OLD.id; -- 用更新后的A行关联B、C,匹配到的结果写入D INSERT INTO D(a_id, b_id, c_id, col1, col2) SELECT NEW.id, B.id, C.id, NEW.col1, B.col2 FROM A INNER JOIN B ON A.b_id = B.id INNER JOIN C ON A.c_id = C.id WHERE A.id = NEW.id; END // -- 表B新增行时触发同步 CREATE TRIGGER tri_B_insert AFTER INSERT ON B FOR EACH ROW BEGIN INSERT INTO D(a_id, b_id, c_id, col1, col2) SELECT A.id, NEW.id, C.id, A.col1, NEW.col2 FROM B INNER JOIN A ON A.b_id = B.id INNER JOIN C ON A.c_id = C.id WHERE B.id = NEW.id; END // -- 剩余触发器按相同逻辑补全即可: -- 表A/B/C的INSERT/UPDATE/DELETE三类操作都要建对应触发器 -- 核心逻辑统一为:先删旧关联记录,再插新匹配结果,保证D和三表数据一致 DELIMITER ;
这个方案延迟在毫秒级,不需要额外部署服务,完全满足当前场景需求;缺点是如果三表结构频繁调整,触发器需要同步修改,不适合单次批量变更上万行的场景(逐行触发会有锁等待)。
方案2:事件调度器增量同步(易维护,秒级延迟)
如果不想维护多个触发器,可以用MySQL自带的事件调度器做定时增量同步,适合对同步延迟容忍度在秒级的场景:
- 先给A、B、C三张表都加
update_time时间戳字段,自动记录行的最后变更时间:ALTER TABLE A ADD COLUMN update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP; ALTER TABLE B ADD COLUMN update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP; ALTER TABLE C ADD COLUMN update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP; - 开启MySQL事件调度器开关:
SET GLOBAL event_scheduler = ON; - 创建定时同步事件,比如设置每5秒同步一次,每次仅处理上次同步时间之后有变更的行,逻辑和触发器一致,先删旧关联数据再插新匹配结果即可。
这个方案维护成本比触发器低,不会因为大批量变更导致行锁长时间占用,缺点是不是强实时,存在调度间隔的延迟。
方案3:Binlog流同步(适合未来数据量扩容场景)
如果后续单表数据量涨到百万、千万级,或者单表变更频率很高,可以用Binlog订阅方案:同步三表的Binlog到流处理引擎,在引擎内完成三表流Join,将结果实时写入D表。这个方案性能强、和业务库解耦,不会影响线上业务,但需要额外部署组件,当前10w条量级下没必要使用。
避坑提醒:不要每次表有变更就清空D表全量重跑三表Join,虽然10w条数据全量关联也能跑出结果,但资源浪费严重,增量同步的性能比全量重算高两个数量级以上。
内容的提问来源于stack exchange,提问作者Shani

