mongodb双写解决TTL索引失效
症状
- 生产环境某个 mongodb 超大表(约6000w笔),配置了 TTL 索引(保留30天),但历史数据并没有自动过期删除
分析
- 登录该mongo实例,发现该表TTL索引并非建在date类型上,由于该表访问比较频繁且数据量太大,直接修改字段类型并不推荐。以下是针对不同日期类型的测试方案:
use db1;
db.createCollection("log_events");
//.以下满足ttl索引对date类型的要求
db.log_events.insert({"user_name":"aaa", "created_at":new Date(), "log_message":"Success"});
db.log_events.insert({"user_name":"aaa", "created_at":new Date('Jun 26, 2025 11:20:07'), "log_message":"Success"});
//.以下不满足ttl索引对date类型的要求
db.log_events.insert({"user_name":"aaa", "created_at":1748402407, "log_message":"Failed"});
db.log_events.insert({"user_name":"aaa", "created_at":"2025-05-26 11:20:07", "log_message":"Failed"});
// 使用 collMod 修改 expireAfterSeconds的属性
// db.runCommand({collMod: "log_events", index: { keyPattern: { created_at: 1 }, expireAfterSeconds: 60}});
//.创建ttl索引
db.log_events.createIndex({"created_at":1}, {expireAfterSeconds:60*60*24*7*30});
db.log_events.find();
解决方案
- 第1步,新增一个新表并对date类型字段创建配置TTL索引,比如 30 天
//.创建新表
use db1;
db.createCollection("log_events_new");
//.告知开发同事,写入的日期务必为date类型
db.log_events_new.insert({"user_name":"aaa", "created_at":new Date(), "log_message":"Success"});
//.保留30天的ttl索引
db.log_events_new.createIndex({"created_at":1}, {expireAfterSeconds:60*60*24*7*30});
- 第2步,择机发版配置双写方案(写入的日期务必为date类型),双写持续的时间务必覆盖ttl索引的时效,比如 40 天
- 第3步,择机对这2张表执行 rename 将名字互换,并发版取消双写方案
use db1;
db.log_events.renameCollection("log_events_2025");
db.log_events_new.renameCollection("log_events");
db.log_events_2025.renameCollection("log_events_new");