4. 特征存储故障:Redis/MySQL/对象存储等后端存储的常见故障与数据一致性校验
特征平台跑起来之后,最怕什么?我个人最怕的是存储层出问题。你想想看,特征数据全在背后撑着,一旦存储挂了,线上推理直接断粮。今天我们就聊聊三种主流后端存储——Redis、MySQL、对象存储——的常见故障模式,以及怎么保证数据一致性。
4.1 Redis 特征缓存:丢了数据怎么办?
Redis 在特征平台里通常扮演热缓存角色。特征值从离线算好,灌进 Redis,线上服务毫秒级读取。但 Redis 本身是内存型数据库,故障场景很典型。
4.1.1 常见故障一:缓存雪崩与击穿
缓存雪崩说白了就是大量特征 key 在同一时间过期,请求全部打到下游 MySQL 或对象存储。我在项目中遇到过,双十一大促前特征刷新任务没做散列,所有 key 的 TTL 设成了同一个时间点。结果零点一过,Redis 瞬间空了一半,后端存储被打到 OOM。
避坑指南:
- TTL 加随机偏移量,比如基础值 3600s,再随机 ±300s
- 热点特征用本地缓存兜底(Caffeine 或 Guava Cache)
- 开启 Redis 的 lazy-free 和异步淘汰
4.1.2 常见故障二:主从切换导致数据丢失
Redis 主从架构下,如果主节点宕机,从节点晋升为主。但异步复制可能导致部分特征数据没同步过去。我记得有一次线上特征回刷,主节点写入一批新特征值,还没来得及同步就挂了。切换后,线上读取到的全是旧值,模型推理结果直接偏了。
4.1.3 常见故障三:内存打满与 OOM
特征量增长快,Redis 内存配置没跟上,就会触发 OOM killer。我见过一个案例,特征平台每天新增 2000 个特征,但 Redis maxmemory 一直没调。三个月后某个凌晨,Redis 进程直接被系统杀掉。
解决方案:
- 设置 maxmemory-policy allkeys-lru,避免 OOM
- 监控 used_memory / maxmemory 比例,超过 80% 告警
- 定期清理过期特征,别让僵尸 key 占着内存
4.2 MySQL 特征存储:关系型数据库的坑
MySQL 在特征平台里通常存特征元数据、特征版本、以及部分离线特征宽表。它的问题往往出在写入压力和事务冲突上。
4.2.1 常见故障一:写入瓶颈与死锁
特征平台每天有大量特征写入任务,如果多个任务同时更新同一张特征表,很容易出现死锁。我遇到过最夸张的一次,8 个特征刷新任务同时 UPDATE 同一行记录,MySQL 直接报 Deadlock found。结果那批特征全部写入失败,线上推理用了旧数据。
避坑指南:
- 特征表设计时,按特征 ID 做分片,避免热点行
- 写入任务加随机延迟,错开执行时间
- 使用 INSERT ... ON DUPLICATE KEY UPDATE 代替先查后改
4.2.2 常见故障二:主从延迟导致特征不一致
特征平台通常读写分离:写入主库,读取从库。但主从延迟会导致刚写入的特征读不到。我记得有一次特征上线,写入主库后立即查询从库,结果返回空。线上服务直接降级,用了默认值。
4.2.3 常见故障三:大事务与锁等待
特征批量导入时,如果单事务涉及的行数太多,会长时间持有行锁,阻塞其他写入。我见过一个特征导入任务,一次 INSERT 10 万行,事务执行了 30 秒,导致其他特征写入全部超时。
解决方案:
- 批量写入拆成小批次,每批 1000-5000 行
- 使用 LOAD DATA LOCAL INFILE 代替逐行 INSERT
- 监控 innodb_row_lock_current_waits,超过阈值自动熔断
4.3 对象存储:特征文件丢了怎么办?
对象存储(S3、OSS、COS)在特征平台里存离线特征快照、模型特征映射文件、以及历史特征数据。它的故障模式跟 Redis/MySQL 完全不同。
4.3.1 常见故障一:文件损坏与校验失败
特征文件上传过程中网络闪断,可能导致文件不完整。我遇到过特征快照上传到 OSS 后,ETag 跟本地 MD5 对不上。线上加载时直接报错,特征服务重启失败。
避坑指南:
- 上传完成后,主动调用 HeadObject 校验 Content-MD5
- 使用分片上传,每个分片独立校验
- 特征文件加版本号,回滚时能切到上一版本
4.3.2 常见故障二:访问权限与限流
对象存储的访问策略配置错误,会导致特征读取失败。我见过一个案例,特征平台迁移到新 Bucket,但 RAM 角色没更新,线上服务一直报 AccessDenied。排查了半小时才发现是权限问题。
4.3.3 常见故障三:跨区域复制延迟
如果特征平台跨地域部署,对象存储的跨区域复制可能有分钟级延迟。我记得有一次,北京地域写入特征快照,上海地域读取时还是旧文件。线上推理用了过期的特征数据。
解决方案:
- 对时效性敏感的特征,使用同地域读写
- 跨地域场景,写入后主动等待复制完成(ListObjectVersions 校验)
- 使用对象存储的事件通知,复制完成后触发回调
4.4 数据一致性校验:怎么保证特征不脏?
三种存储都可能出现数据不一致。我总结了一套校验方法,核心是「写入时校验 + 定时对账 + 异常自动修复」。
4.4.1 写入时校验
每次特征写入后,立即回读校验。比如写入 Redis 后,用 GET 命令读回来比对值。写入 MySQL 后,SELECT 出来校验。写入对象存储后,下载文件比对 MD5。
// 伪代码:Redis 写入后校验
String key = "feature:user:1001:age";
String value = "28";
jedis.set(key, value);
String readBack = jedis.get(key);
if (!value.equals(readBack)) {
// 告警并重试
alert("特征写入不一致: " + key);
retryWrite(key, value);
}
4.4.2 定时对账
每天凌晨跑对账任务,对比不同存储之间的特征数据。比如 Redis 里的热特征 vs MySQL 里的元数据,或者对象存储里的特征快照 vs MySQL 里的版本记录。
| 对账维度 | 源存储 | 目标存储 | 校验方式 |
|---|---|---|---|
| 特征值一致性 | Redis | MySQL | 逐 key 比对值 |
| 特征版本一致性 | 对象存储 | MySQL | 比对版本号 + 文件 MD5 |
| 特征数量一致性 | Redis | MySQL | 比对 key 总数 |
4.4.3 异常自动修复
发现不一致后,自动触发修复流程。比如 Redis 里特征值跟 MySQL 不一致,就从 MySQL 拉取正确值覆盖 Redis。修复完成后,再校验一次,确保修复成功。
4.5 知识体系总览
下面这张图总结了特征存储故障的核心链路和校验方法。你可以把它当作排查时的思维导图。
4.6 小结
特征存储故障排查,说白了就是三件事:知道每种存储的典型故障模式,掌握数据一致性校验方法,建立自动修复机制。我个人建议,先把 Redis 的缓存雪崩和 MySQL 的死锁搞定,这两个是线上最常遇到的。对象存储的故障相对少,但一出就是大事,文件损坏或权限问题可能导致整个特征服务不可用。
嗯,这一章的内容就到这里。记住,存储层是特征平台的根基,根基不稳,上面再好的模型也是白搭。