介绍
员工改价应更新权威数据库,并移除该确切价格的旧缓存值。清空所有缓存商品会丢弃有用成果;在源写入失败前就移除缓存条目,也不能如实反映写入结果。
本独立实验准备了新的缓存工作台,以及正确使用完整标识、15 秒有效期的读取策略。你需要掌握上一实验中的旁路缓存、标识、TTL 和简短 Python 模板概念。你将完成 change_price,观察被拒绝的主存储写入,并比较显式失效与通过自然过期可见的外部更新。只需要 Terminal;上一 VM 的文件和进程都不会延续。
只编辑 /home/labex/project/cache-desk/policy.py,使用三个指定函数及源存储与缓存 API。保留所有工具、源表标识、无关价格及商品目录备注。允许的主存储变更为:通过策略把 North MUG USD 从 1200 改为 1350,一次被拒绝的负价格尝试,以及准备好的外部更新到 1450。学员操作流程在服务健康时顺序执行请求;这不代表强一致性、分布式缓存失效,也不承诺两个数据库原子提交。估计时长是作者为阅读和错误处理预留的时间,没有初学者计时数据。
提交后使对应价格缓存失效
本步骤中,你将让成功的源写入移除旧缓存价格,并在写入被拒绝时保留缓存。
从 Terminal 开始,进入本独立单元准备好的工作区:
cd /home/labex/project/cache-desk
./desk status
./desk policy
三个标识字段为店铺、SKU 和币种。North MUG USD 初始为 1200 分,South MUG USD 为 1500,North MUG EUR 为 1100,North FREE USD 为 0。先缓存一个正常价格,再提交合法改价:
./desk view north MUG USD
./desk view north MUG USD
./desk change north MUG USD 1350
./desk status
./desk view north MUG USD
状态现在显示已提交的主存储价格 1350,但及时查看时仍可能返回缓存的 1200。如果已经经过 15 秒,查看可能正常未命中并显示 1350;即使过期掩盖了问题,缺少失效操作仍是策略缺陷。成功消息只表示提交的策略函数已经返回;实际主存储和缓存观察才能说明发生了什么。
change_price 已调用 source.write(identity, cents)。这个预备方法在原生 SQLite 事务中更新一个已知主存储行,只在提交后返回。负价格违反源存储 CHECK 约束并抛错;未知标识抛出查找错误。Python 只在前一调用成功返回时运行下一行,因此应在调用后执行精确 Redis 删除。cache_key(prefix, identity) 复用与读取函数相同的标识映射。不要只按 SKU 删除,也不要使用 FLUSHDB/FLUSHALL。
与上一实验相同,带引号的 here-document 只替换 policy.py;保留缩进。再次给出完整读取函数,确保替换内容完整:
cat > policy.py <<'POLICY'
def cache_key(prefix, identity):
return f"{prefix}:{identity[0]}:{identity[1]}:{identity[2]}"
def read_price(source, cache, prefix, identity):
key = cache_key(prefix, identity)
value = cache.command("GET", key)
if value is not None:
return int(value), "cache"
cents = source.read(identity)
cache.command("SET", key, cents, "EX", 15)
return cents, "source"
def change_price(source, cache, prefix, identity, cents):
source.write(identity, cents)
cache.command("DEL", cache_key(prefix, identity))
POLICY
缓存两个无关价格,再重复允许的写入。再次写入相同的 1350,仍会真实提交已知行并检验失效操作:
./desk view south MUG USD
./desk view north MUG EUR
./desk change north MUG USD 1350
./desk inspect north MUG USD
./desk view north MUG USD
./desk view north MUG USD
Inspect 必须在写入后立即显示目标键缺失。下一次查看返回 source/1 和 1350,再下一次为 cache/0。其他键没有被主动失效,但仍可能自然过期。用 status 区分主存储值是否保留,以及缓存是否存在。
现在故意请求无效负价格。下面命令预期以非零状态退出,并打印带原生 CHECK 失败信息的 IntegrityError。这不是成功更新、缓存未命中或连接问题:
./desk change north MUG USD -1
./desk status
./desk inspect north MUG USD
./desk view north MUG USD
权威价格保持 1350。如果已缓存条目尚未自然过期,其值和原截止时间都保留,查看为 cache/0。阅读较慢时也可能看到正常过期;隔离检查在没有阅读耗时竞态的情况下测试失败写入后的保留行为。source.write 抛错时,Python 在 DEL 前停止。不要捕获错误并打印虚假成功。
shell 报告 ./deskk 缺失时,用 ls 并修正名称。策略有语法或缩进错误时,用 ./desk policy 检查,再用完整代码块替换。失败的负价格命令无需重置:预备源事务已回滚该尝试。之后读取数据,不要直接编辑数据库。
让外部更新在下一次填充中可见
本步骤中,你将观察真实外部源变更,以及旧缓存值有限的有效期。
继续使用 Terminal,工作目录为 /home/labex/project/cache-desk。重新写入允许的已提交价格,移除目标已有缓存,再填充 1350。这样可以明确起点,而不改变其他商品:
./desk change north MUG USD 1350
./desk view north MUG USD
./desk inspect north MUG USD
external 命令代表刻意受控的写入方,绕过本应用的失效钩子。它执行真实主存储提交,不是由定时器驱动的显示变化。本单元只允许以下外部目标和新价格:
./desk external north MUG USD 1450
./desk status
./desk view north MUG USD
./desk inspect north MUG USD
状态立即显示 1450。已有键过期前,查看仍可能以零次主存储读取返回缓存的 1350。截止时间继续倒计时,不会在命中时重启。如果阅读时间已超过有效期,未命中后看到 1450 符合预期。你可以及时重复本步骤开始的改价、填充、外部更新顺序,观察旧值仍可见的区间。
最终读取前,等待超过完整配置有效期:
sleep 16
./desk inspect north MUG USD
./desk view north MUG USD
./desk view north MUG USD
./desk status
Inspect 显示键缺失。下一次查看从主存储读取已提交的 1450 并填充 Redis;随后的查看可使用真实缓存的 1450。保留这一最终主存储状态。所有其他权威价格仍为 1500、1100 和 0。./redis GET catalog:admin:note 可检查保留的无关备注,不会发起主存储请求。
这是顺序执行的旁路缓存策略。填充后的外部更新,可能使这次填充的旧值持续可见,直到原有的 15 秒截止时间。该观察不能证明并发读写、缓慢填充或服务停止情况下的全局陈旧时间上限。并发读取方可能在写入前取到旧源值,却在失效后填入缓存;本课程不解决这种竞态,也不解决同时大量未命中引发的缓存击穿。
源提交和 Redis 删除是两个独立操作。如果成功提交后 Redis 拒绝删除,主存储已经改变。错误必须作为缓存操作失败向上传递,不能描述为主存储回滚或已完成失效。隔离评分器会测试这种原生失败,而不破坏实际服务。生产重试、持久可靠的失效机制和故障恢复需要额外设计,不在本练习范围内。
总结
你将真实主存储提交与精确缓存失效关联,在源写入被拒绝后保留缓存数据,并通过自然过期观察外部变更。策略区分了源写入失败与后续 Redis 失败,也明确说明了顺序执行的一致性边界。



