mysql归档数据怎么避免重复,是后端开发和dba在做历史数据迁移时经常要面对的问题。如果处理不当,目标归档库里会出现重复行,不仅浪费存储,还会让统计和查询产生偏差。下面从几个实操角度说明具体的防重做法。

为什么归档会出现重复
重复通常来自三个地方:归档任务被重复触发、源表本身存在重复业务数据、以及增量归档时位点记录丢失。理解了来源,才能用对方法。
利用唯一索引拦截重复写入
在归档目标表上建立与主键或业务唯一键对应的唯一索引,即使程序误写了重复数据,mysql也会抛错并拒绝插入。这是最基础的防线。
-- 在归档库建立唯一索引 ALTER TABLE archive_orders ADD UNIQUE KEY uk_order_id (order_id); -- 使用 INSERT IGNORE 忽略已存在记录 INSERT IGNORE INTO archive_orders (order_id, user_id, amount, created_at) SELECT order_id, user_id, amount, created_at FROM orders WHERE created_at < '2023-01-01';
归档前排除已迁移数据
使用 NOT EXISTS 或 LEFT JOIN 过滤掉目标表已经存在的记录,从源头减少重复可能。
-- 使用 NOT EXISTS 避免重复抽取
INSERT INTO archive_orders (order_id, user_id, amount, created_at)
SELECT o.order_id, o.user_id, o.amount, o.created_at
FROM orders o
WHERE o.created_at < '2023-01-01'
AND NOT EXISTS (
SELECT 1 FROM archive_orders a WHERE a.order_id = o.order_id
);
通过事务和位点控制增量归档
增量归档时,把一批数据的处理放在事务里,并记录最大主键或时间位点。程序重启后从位点继续,避免重复扫描旧数据。
import pymysql
conn = pymysql.connect(host='127.0.0.1', user='root', password='test', db='test')
cur = conn.cursor()
# 假设 last_id 已持久化到本地文件或配置表
last_id = 1000
batch = 500
try:
cur.execute(
"SELECT id, order_id, user_id, amount FROM orders WHERE id > %s ORDER BY id LIMIT %s",
(last_id, batch)
)
rows = cur.fetchall()
for r in rows:
cur.execute(
"INSERT IGNORE INTO archive_orders (id, order_id, user_id, amount) VALUES (%s,%s,%s,%s)",
r
)
if rows:
last_id = rows[-1][0]
# 更新位点
cur.execute("UPDATE archive_pos SET last_id=%s WHERE name='orders'", (last_id,))
conn.commit()
except Exception as e:
conn.rollback()
print('归档失败:', e)
finally:
cur.close()
conn.close()
使用临时表做去重缓冲
当源表本身有重复业务数据时,可以先写进临时表用 GROUP BY 去重,再写入归档表。
-- 创建临时去重表 CREATE TEMPORARY TABLE tmp_archive AS SELECT order_id, user_id, amount, MAX(created_at) AS created_at FROM orders WHERE created_at < '2023-01-01' GROUP BY order_id, user_id, amount; INSERT INTO archive_orders (order_id, user_id, amount, created_at) SELECT order_id, user_id, amount, created_at FROM tmp_archive;
小结
避免mysql归档数据重复,关键是唯一索引兜底、归档前主动排重、增量位点不丢失。把这几点结合起来,就能在线上业务不停机的情况下,安全地把冷数据搬进归档库。