本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
设计注意事项
其他需要考虑的设计要点:
-
错误处理:如果缓存由于任何原因变得不可用,则应用程序应像所有项目都缺少缓存一样继续运行。缓存层的存在不应增加应用程序的脆弱性。
-
TTL:可以为所有缓存条目配置单个 TTL 值,也可以根据条目使用不同的 TTL 值(例如
get_item
query
、或scan
缓存)。对于负缓存条目(未返回任何项目的请求),也可以使用不同的 TTL 值。 -
已消耗的容量:当您返回缓存的响应时,我们建议您调整响应中的
ConsumedCapacity
指标以表明读取消耗量为零。 -
删除响应元数据:您还应删除响应中的
ResponseMetadata
部分。这样可以避免有人看到RequestId
并认为它是最新的,而实际上是几个小时前的风险。 -
添加缓存元数据:在响应中添加一个
CacheMetadata
部分会很有帮助。此部分可以报告存储该值的时间(用于衡量过时性)以及存储该值的客户端库和版本(在从一个版本无缝升级到另一个格式发生变化的版本时,这可能很有用)。 -
确定表架构:要通过写入操作确定缓存失效的主键,必须知道表的架构。您可以通过
describe_table
调用并在每个表首次使用时(仅一次)使用长期缓存来获取此信息。 ElastiCache -
接受而不是构造客户端:在构造函数中接受 DynamoDB 和 Redis 客户端实例(而不是根据传入的参数在 shim 中构建它们)的一个好处是,它允许构造函数的调用者确定细节,例如是否应该设置。
read_from_replicas=True
-
命名空间功能:在构造函数中支持一个可选的命名空间可以很方便,该命名空间将所有缓存读取和写入该命名空间隔离开来。使用命名空间是测试的理想选择,因为每次使用不同命名空间的运行似乎都是从空缓存开始的,并且与之前的运行没有副作用。您还可以通过更改命名空间来模拟在生产环境中清空全部缓存。这可以通过为每个查找键自动添加前缀来实现。
-
扫描缓存:很难知道是否应该缓存
scan
呼叫。执行一次性全表扫描时,您不想在缓存中填充大量不会被第二次读取的扫描数据。另一方面,许多工作负载会重复扫描,尤其是针对较小的表,以便向多个下游使用者提供最新的完整表数据。一种折衷方案是实现一个系统,在该系统中,它需要两个调用,并且每个呼叫都具有相同的签名(在 TTL 时间段内),才能缓存生成的扫描响应。这样可以避免在一次性全表扫描期间填满缓存,同时启用紧密的扫描循环以获得缓存的好处。第一次扫描会在缓存中放置一个小占位符,以将请求标记为已发出一次。第二次扫描将令牌值替换为实际有效负载,后者可能高达 1 MB。