Laravel 10 Redis标签缓存并发刷新异常问题求助
问题:Laravel 10标签缓存高并发下数据不一致及标签损坏问题
我开发了一个Laravel 10应用,支持候选人投票并展示按票数排序的候选人列表。该列表从数据库获取计算后,以标签形式存入Redis缓存(实际含多个标签,但单个标签也存在问题)。用户投票后会刷新该标签下的所有缓存数据,后续列表请求会重新从数据库拉取数据并写入带标签的Redis缓存。低流量时一切正常:缓存能正确刷新,用户看到最新数据;无投票时从缓存读取提升性能。
Laravel标签缓存的Redis存储机制
Laravel 10处理标签缓存的代码如下:
Cache::tags(['object-1'])->put('cand-votes', $votes, $TTL);
执行后会在Redis中生成两个键:
- prefix:tag:tagName:entries:存储该标签下所有缓存键的信息,用于刷新整个标签时定位需清除的键
- prefix:uniqid:cacheKey:存储实际缓存数据
低流量正常状态
Redis键列表:
keys app_cache* 1) "app_cache:tag:object-1:entries" 2) "app_cache:6461bc79646c190a1c986d8ca1ab917e8ddf0d8f:cand-votes"
app_cache:tag:object-1:entries的内容:
zscan app_cache:tag:object-1:entries 0 1) "0" 2) 1) "6461bc79646c190a1c986d8ca1ab917e8ddf0d8f:cand-votes" 2) "1689766717"
此时能明确标签下的缓存键及其TTL,读取数据正常:
get app_cache:6461bc79646c190a1c986d8ca1ab917e8ddf0d8f:cand-votes "150"
高流量异常表现
当并发量达到约15次投票/秒(即15次Redis刷新请求/秒)时出现问题:Redis中的标签损坏,无法通过标签刷新清除数据,只能用artisan cache:clear清空整个缓存。
8秒内15位用户投票150次后的异常状态:
Redis键列表仅剩数据键:
keys app_cache* 1) "app_cache:6461bc79646c190a1c986d8ca1ab917e8ddf0d8f:cand-votes"
读取该键得到旧数据:
get app_cache:6461bc79646c190a1c986d8ca1ab917e8ddf0d8f:cand-votes "38"
此时仅存存储数据的缓存键(已过期),存储缓存键列表的entries键丢失。
应用内调用:
Cache::tags(['object-1'])->get('cand-votes');
始终返回错误数据;调用:
Cache::tags(['object-1'])->flush();
无法清除该键(因entries键丢失),导致旧数据在TTL周期内无法清除,仅能通过artisan cache:clear解决。
推测是缓存写入操作重建了刚被删除的键,但entries键随即被删除。
已尝试方案
- 为投票流程添加缓存锁:略有改善但会导致请求等待变慢甚至失败
- 为缓存标签刷新操作添加Redis事务:无明显效果
临时Workaround
- 高负载时禁用缓存,实时刷新数据
- 将缓存刷新和预热操作移入队列,降低并发
但这些方案都不够理想,属于临时措施。请问有使用标签缓存遇到类似问题的吗?寻求处理缓存并发刷新场景的解决方案。
内容的提问来源于stack exchange,提问作者Lothid
相关产品推荐
相关产品推荐

