读操作实现的修正 , 以防突发流量和陈旧值保护 。
这种方法能使缓存有效地保护记录系统以免遭突发流量的冲击 。 在缓存未命中的情况下 , 只有一个幸运的请求能够成功添加租约并与真实源交互 , 而其他请求将被降级为轮询租约 , 直到幸运的请求将其计算出来的值填充完缓存为止 。
这种机制还可以保护我们免受上述竞争状况的影响 。 当记录系统发生突变 , 并且读操作从真实源中获取数据并将其放入缓存的过程中产生了缓存无效 , 就会发生缓存中毒 。 该模型可以防止读操作毒害缓存 , 因为如果写操作更改了缓存下面的记录 , 则其原子检查和设置就会失败 。
文章插图
尽管它暂时感到困惑 , 但不幸的是 , 缓存“恶魔”确实有更多的锦囊妙计 。 考虑这样一个用例:数据始终被频繁地读取 , 但是部分数据也会被频繁地周期性写入:
文章插图
读操作 1 徒劳地等待读操作 3 和读操作 2 填充感兴趣的缓存键时 , 会经历一段荒谬的延迟 。
在冗长的写操作过程中 , 可能会出现一种病态的情况 , 读操作最终会轮流获得租约 , 并查询记录系统 , 结果却被一个写操作清除了 。 实际上 , 这会序列化来自真实源的读取 , 但不会消除重复数据 , 最终会导致过高的读延迟和超时 , 因为读需要等待轮流从真实源中获取所需的值 。
我们在 Box 的分布式关系数据服务中遇到了这个问题 , 并想出了一些与之相关的解决方案 。 我们最终采用的方法是基于这样一种观点:任何正在等待租约的读操作都可以安全地使用持有租约的读操作检索到的值 , 即使租约最终被写操作清除 , 并且atomic_check_and_set失败 。 实际上 , 如果某个读操作遇到了另一个读操作的租约 , 则该读操作必须在写操作清除缓存值之前到达 , 因此在确认写操作之前 , 这两个读操作都可以返回由租约持有者检索到的值 , 而不会牺牲读写(read-after-write)一致性 。 为了利用这一观点 , 除了执行atomic_check_and_set尝试将根据真实源计算出来的值存储到缓存中之外 , 获得租约的读操作还需将该值存储在缓存的其他位置 , 从而可以被等待租约的读操作发现 。
使用流程图来说明该算法会变得复杂且难以阅读 , 但下面是执行该算法的代码片段 。 该代码片段是用 Java 过程编写的 , 旨在使高级方法更加清晰明了 , 而没有考虑错误处理、编译时的安全性、可维护性等 。
public class LookasideCache {private CacheCluster cacheCluster;private LeaseUtils leaseUtils;/*** 使用 lookaside 缓存读取某个值** @param key 要返回谁的值* @param sourceOfTruthComputation 如果有必要的话 ,*用于从真实源中获取值的函数 。*计算被认为是昂贵的*并且会加大真实数据存储源的负载 。** @return a byte[] 表示与查找 key 相关联的值 。*/public byte[] read(byte[] key, Function sourceOfTruthComputation) {return read(key, sourceOfTruthComputation, Collections.EMPTY_SET);}/*** 用于上述 read 方法的私用辅助方法 , 允许我们在堆栈上携带之前看到的租约集 **/private byte[] read(byte[] key,Function sourceOfTruthComputation,Set previouslySeenLeases) {// 首先查找所提供的 key 以及以前看到的所有租约 。List cacheKeysToLookUp = new ArrayList<>();cacheKeysToLookUp.add(key);for (Lease previouslySeenLease : previouslySeenLeases) {cacheKeysToLookUp.add(previouslySeenLease.getNonce());}List valuesFromCacheServer = cacheCluster.get(cacheKeysToLookUp);byte[] valueForKey = valuesFromCacheServer.remove(0);// 检查该值是否隐藏在我们前面看到的某个租约的后面 。for (byte[] valueForPreviouslySeenLease : valuesFromCacheServer) {if (valueForPreviouslySeenLease != null) {return valueForPreviouslySeenLease;}}if (valueForKey == null) {// 我们试着获取该 key 的租约 。Lease newLease = leaseUtils.createNew();boolean leaseAddSucceeded = cacheCluster.atomicAdd(key, newLease.getNonce());if (leaseAddSucceeded) {// 我们设法获取该 key 的租约 , 这意味着需要我们去寻找真实源 ,// 然后用真实源里的值填充缓存 。byte[] valueFromSourceOfTruth = sourceOfTruthComputation.apply(key);boolean checkAndSetSuccceeded = cacheCluster.atomicCheckAndSet(key,newLease.getNonce(),valueFromSourceOfTruth);// 让我们使用租约的 nonce 作为 key ,并将我们计算的值与之关联 。cacheCluster.set(newLease.getNonce(), valueFromSourceOfTruth);return valueFromSourceOfTruth;} else {// 另一个请求比我们先获得了租约 。 让我们重试 。return read(key, sourceOfTruthComputation, previouslySeenLeases);}} else if (leaseUtils.isCacheValueLease(valueForKey)) {// 另一个缓存请求持有该 key 的租约 ,// 让我们给它一些时间来填满混存 , 然后再重试 。sleep(100);previouslySeenLeases.add(leaseUtils.fromCacheValue(valueForKey));return read(key, sourceOfTruthComputation, previouslySeenLeases);} else {// 从缓存服务中获取值 , 然后返回 。return valueForKey;}}private void sleep(int millis) {try {Thread.sleep(millis);} catch (InterruptedException e) {System.err.println("Sleep interrupted.");e.printStackTrace();}}}interface CacheCluster {/*** 从缓存集群获取键的列表 。* @param keys 要从缓存集群中获取键* @return byte[] 列表表示与提供的键相关联的值*返回的列表将与提供的键列表并行 ,* 换句话说 , 与某个键关联的值在返回列表中的位置与键在键列表中的位置相同 。* 返回列表中的空值表示缓存未命中 。* 虽然这不是一个好的设计 API 的方法* 但对于这个算法的高层演示 , 它能很好的运行 。*/List get(List keys);/*** 设置值与缓存集群中所提供的键的关联关系*/void set(byte[] key, byte[] value);/*** 当且仅当键尚未被设置时 , 为该键设置所提供的值 。* 否则 , 操作失败 。* @return 如果操作成功且设置了值 , 则返回 true , 否则返回 false 。*/boolean atomicAdd(byte[] key, byte[] value);/*** 当且仅当键刚好与 expectedValue 相关关联时 , 为所提供的键设置 valueToSet 。* @return 如果操作成功且设置了值 , 则返回 true , 否则返回 false 。*/boolean atomicCheckAndSet(byte[] key, byte[] expectedValue, byte[] valueToSet);}interface Lease {byte[] getNonce();}interface LeaseUtils {Lease createNew();Lease fromCacheValue(byte[] nonce);boolean isCacheValueLease(byte[] value);}
推荐阅读
-
也无心在此流连停歇|穿一身白宛若王子,气质神似胡歌,周华健混血儿子瘦了居然这么帅
-
『墨小颜看体育』让她感受到人情冷暖,网坛巨星李娜心中的痛!少年时发生的这件事
-
【小侃说军事】还是人人生而平等本就是缥缈的,是特朗普错了
-
【实锤社】人又留下了?,德云色黑莓KTV事件再度反转!lol笑笑发微博秒删
-
-
-
「skt」LCK:半决赛不敌SKT,DRX官博写下114个字,粉丝为Deft鸣不平
-
沃尔玛|沃尔玛:重启出售英国连锁商超阿斯达多数股权计划
-
『资深娱乐家』车长4670mm,整车进口身份,价如奥迪A5!,又一豪车有诚意
-
芊微防务观察为何中国航天进度放缓?原因其实很简单,新飞船返回成功
-
-
-
「iQOO」iQOO新机曝光,高刷新率+骁龙865
-
贷款不用征信哪里可以贷款(纯私人放款10000联系)
-
热门搞笑神吐槽■家里比较穷,放学回来就带着书本出去放牛,开心一笑:小学的时候
-
【趣淘文化】与贝克汉姆一家为邻,哈里梅根夫妇计划花费300万英镑新筑爱巢
-
琼斯爱生活|李沁为什么一直没有男朋友?当照片被曝出后,网友们:这谁敢娶啊,
-
亚盛医药|亚盛医药:APG-2575获得第二张FDA“孤儿药认证”门票
-
蔡英文|与蔡英文会面,美国卫生部长肉麻吹捧“台湾防疫成就”
-
【佳作有约】你都不知道爱买衣服的女人有多好!,讲真