缓存失效瞬间,请求会怎样?get_or_set 能保证只算一次吗?怎么让用户感觉不到回源的等待?这篇用线程并发,把这几个问题都数出来。
compute 被执行了 20 次;返回了 1 个不同的值: ['result-2'] 缓存里最终留下: result-2
各线程各自 get 不到、各自计算、最后 add 只有第一个成功——所以结果一致,但计算重复了。
compute 被执行了 1 次;所有线程拿到同一个值: True ;总耗时 110 ms
同第 96 篇的击穿防护,锁要带过期时间。
阻塞式回源 请求 537 次;计算 16 次;p50 0.02 ms;p99 205.1 ms;最慢 205 ms stale-while-revalidate 请求 635 次;计算 7 次;p50 0.02 ms;p99 0.3 ms;最慢 205 ms
stale-while-revalidate 的做法:缓存里的值带一个「软过期时间」,硬 TTL 比它长得多;读到过期(软)的值时先原样返回,同时让一个线程去刷新。本文的实现是单进程内的简化版(用一个标志位防止重复刷新),多进程要用缓存里的锁。
set 后 1.2s 再 set(2s),再过 1.2s(距第一次 2.4s): a2 (第二次 set 重置了 TTL) 读-改-写竞态:另一个线程在中间写入了 v1-from-other,最终 = v0+writer ← 覆盖了对方的写入(lost update)
要原子地累加用 cache.incr;要保证「只有一个人在改」需要锁。