作为经常和Hologres/PostgreSQL打交道的数据人,你有没有遇到过这样的困扰?
想全量替换核心业务表的数据,直接用DELETE+INSERT吧,大数据量下不仅慢,还会锁表,下游查询要么查不到数据,要么查到脏数据;直接删表重建更不行,业务会直接中断,锅直接扣到自己头上。
直到我最近实操了一段同步ODPS数据到Hologres的脚本,才吃透了一个“神仙操作”——先写back表、再原子切表,全程低锁、业务无感知,还能保证数据一致性,今天就把这个实操干货分享给和我一样在踩坑的小伙伴,新手也能直接抄作业!
一、先搞懂:为什么要“先写back表,再原子切表”?
在讲操作之前,先明白核心逻辑:我们之所以不直接操作核心表,本质是为了规避3个致命问题:
- 锁表风险:DELETE+INSERT大数据量时,会持有表级锁,下游业务查询、写入会被阻塞,严重影响线上服务;
- 数据不一致:如果替换过程中脚本中断(比如超时、网络波动),会出现“删了旧数据但新数据没写进去”的空表,或者部分数据写入的脏数据;
- 业务中断:直接删表、重命名核心表,若不做处理,正在执行的查询会直接报错,影响业务正常运转。
而“先写back表、再原子切表”的核心思路,就是绕开直接修改核心表:先把新数据放到“备用阵地”(back表,备份表),验证无误后,通过轻量的表重命名,一键切换核心表的指向,全程像“换灯泡”一样,旧灯泡(旧数据)取下、新灯泡(新数据)装上,中间不耽误照明(业务查询)。
二、实操拆解:一步一步教你做原子切表(附可复用模板)
我以“从ODPS同步数据,替换Hologres核心表”为例,拆解每一步操作,新手跟着走,不踩坑!(其他全量替换场景,直接替换“新数据来源”即可)
第一步:准备back表,写入新数据(筑牢备用阵地)
back表相当于我们的“新数据缓冲池”,先把要替换的新数据全部写入这里,避免直接触碰核心表。这里要注意:back表的结构必须和核心表完全一致(字段名、数据类型、分布键、索引都要一样),否则后续替换会报错。
-- 1. 清空back表(关键!避免残留上次的旧数据,导致新数据混入脏数据) TRUNCATE TABLE gl.核心表名_back; -- 2. 将新数据写入back表(这里以同步ODPS数据为例,可替换为其他数据源) INSERT INTO gl.核心表名_back SELECT 字段1, 字段2, 字段3, ... -- 复制核心表的所有字段,不要漏写 FROM ext.外部同步表 -- 比如从ODPS同步到Hologres ext模式的表 WHERE pdate = '${pdate}'; -- 筛选需要替换的日期(按需调整筛选条件)
第二步:事务包裹重命名,实现原子切表(核心操作)
这一步是整个方案的灵魂!我们用事务(BEGIN+COMMIT)包裹3次表重命名操作,保证这3步要么全部成功,要么全部失败,不会出现中间状态(比如核心表名没了、back表名错乱)。
表重命名是Hologres/PostgreSQL的轻量操作,只修改表的元数据(相当于给文件重命名,不改动文件内容),几乎不锁表,下游业务查询完全无感知——哪怕正在查询核心表,切换过程中也不会报错,无缝衔接新数据。
-- 开启事务:相当于“一键确认”,后续操作要么全成,要么全回滚
BEGIN;
-- ① 核心表临时“让位”:把核心表重命名为tmp临时表,腾出核心表名
ALTER TABLE gl.核心表名 RENAME TO 核心表名_tmp;
-- ② 新数据“上位”:把存好新数据的back表,重命名为核心表名(完成替换)
ALTER TABLE gl.核心表名_back RENAME TO 核心表名;
-- ③ 临时表“补位”:把原来的核心表(现在是tmp表),重命名为新的back表
ALTER TABLE gl.核心表名_tmp RENAME TO 核心表名_back;
-- 提交事务:所有重命名生效,原子切表完成!
COMMIT;
拆解一下这3步的逻辑:
- 重命名前:核心表(旧数据)、back表(新数据);
- 重命名后:核心表(新数据)、back表(旧数据,临时存着);
- 事务的作用:如果中间任何一步出错(比如表名写错、权限不足),会自动回滚到事务开始前的状态,不会出现表名混乱。