多模态预处理场景中抽帧和语音识别是流式返回的,它们的缓存是持续写入的,这个过程有两个问题需要解决
- 没有完全写入的缓存不能被读取,读取线程需要等待写入完成
- 同时只能有一个写缓存的线程
这两个问题的解决方法分别是引入临时键和分布式锁
临时键
在原key基础上增加temp后缀作为临时键,表示正在写入,写入完成后将临时键重命名为后缀为final,读写流程如下
写入
LPUSH key:temp "chunk_data_1"
LPUSH key:temp "chunk_data_2"
...
RENAME key:temp key:final
读取
LRANGE key:final 0 99
LRANGE key:final 100 199
...
读取前的判断逻辑
func cacheExists(ctx context.Context, key string) bool {
for redis.Exists(ctx, tempKey(key)) {
time.Sleep(time.Millisecond * 100)
}
final := finalKey(key)
return redis.Exists(ctx, final)
}
为防止无休止等待,这里有几种异常情况需要考虑
- 重命名失败需要多次重试
- 多次重试不成功,需要将临时键删除
- 删除临时键也失败,说明redis极有可能处于不可用状态,那么读取缓存也会失败
分布式锁
分布式锁的实现参考主页另一篇文章,如果缓存不存在则获取锁,上锁时用TryLock,不阻塞等待,逻辑比较简单,不赘述了