Is this code vulnerable to the expired cache race condition? How would you improve it?
是.如果两个(或多个)并发客户端尝试从缓存中获取相同的密钥并最终从数据库中提取它.您将在数据库上出现峰值,并且数据库将在一段时间内处于高负载状态.这称为缓存标记.有几种方法可以解决这个问题:
>对于新项目预热缓存(基本上意味着您在网站上线之前预加载所需的对象).
>对于定期过期的项目,创建一个未来的过期时间,而不是实际过期时间(比如说5-10分钟).然后,当您从缓存中提取对象时,请检查过期时间是否已关闭,将来是否缓存以防止任何其他客户端更新缓存,以及从数据库进行更新.为了能够在没有缓存标记的情况下工作,您需要实现密钥锁定或使用cas标记(需要最新的客户端库才能工作).
Let’s say that query X gets 100 rows. A little after row #50 is modified by another process (lets say that the retail price gets increased).
缓存中有三种类型的数据:
>对象
>物体清单
>生成的数据
我通常做的是将对象保持为单独的键,然后在列表中使用缓存“指针”.在你的情况下你在缓存中的某个地方有N个objets(假设键是1,2..N),然后你有一个数组数组中的对象列表(1,2,3,10,42 …) .当您决定使用对象加载列表时,从缓存加载列表键,然后从缓存加载实际对象(使用getMulti减少请求).在这种情况下,如果任何对象得到更新,您只能在一个位置更新它,并且它会自动更新到任何地方(更不用说使用此技术可以节省大量空间).
编辑:决定添加更多关于超前时间到期的信息.
使用到期数据x设置对象并将其保存到数据库中,其有效期为x 5分钟.这是从缓存加载对象时执行的步骤:
>检查是否需要更新(时间() – x <0)
>如果是这样,请锁定密钥,以便在刷新项目时没有人可以更新它.如果您无法锁定密钥,那么其他人已经在更新密钥,它就变成了SEP(Somebody Else的问题).由于memcached没有锁的解决方案,因此您必须设计自己的机制.我通常通过在末尾添加一个单独的键和原始键值“:lock”来完成此操作.您必须将此键设置为以尽可能短的数量到期(对于1秒的memcached).
>如果您获得了密钥的锁定,则首先使用新的到期时间保存对象(这样您确定没有其他客户端会尝试锁定密钥),然后继续您的业务并从数据库更新密钥,使用适当的超前期限再次保存新值(请参阅第1点).
希望这清除一切:)