Python 2 不再受社区支持。我们建议您将 Python 2 应用迁移到 Python 3

Memcache 概览

本页面简要介绍了 App Engine memcache 服务。在执行某些任务时,高性能的可扩缩 Web 应用通常先使用分布式内存中的数据缓存,然后才使用或完全不使用可靠的永久存储空间。为此,App Engine 提供了内存缓存服务。如需了解如何配置、监控和使用 Memcache 服务,请阅读使用 Memcache

何时使用内存缓存

内存缓存的一个用途是加快常见数据存储区查询的速度。如果许多请求使用相同的参数执行同一查询,并且结果更改不需要立即显示在网站上,则应用可以将结果缓存在 Memcache 中。后续请求可以直接检查 Memcache,只有在结果缺失或过期时才需要执行数据存储区查询。 会话数据、用户偏好设置以及网页查询返回的其他数据都非常适合进行缓存。

Memcache 还可用于存储其他临时值。不过,在考虑是否仅在 Memcache 中存储值而不在其他永久存储空间中进行备份时,应确保在值突然变得不可用时,您的应用仍能以可接受的行为正常运作。值在 Memcache 中可能随时失效,可能会在为值设置的失效截止期限之前就已失效。例如,如果用户会话数据突然缺失会导致会话出现故障,则应考虑除了在 Memcache 中存储该数据之外,可能还应将其存储在数据存储区中。

服务等级

App Engine 支持两种 Memcache 服务等级:

  • 共享 Memcache 是 App Engine 应用的免费默认等级。该服务等级会尽可能多地提供缓存容量,但会受限于使用此共享 Memcache 服务的所有 App Engine 应用的总体需求。

  • 专用 Memcache 提供专为您的应用分配的固定缓存容量。该服务等级按缓存大小(GB 小时)计费,并要求启用结算功能。能够控制缓存的大小,意味着应用能够以更可预测的方式执行操作,同时减少更昂贵的持久存储空间的读取次数。

这两种 Memcache 服务等级都使用同一 API。如需为应用配置 Memcache 服务,请参阅使用 Memcache

下表总结了这两种 Memcache 服务等级之间的区别:

特性 专用 Memcache 共享 Memcache
价格 每 GB 每小时 $0.06 免费
容量
us-central
1 到 100GB
其他区域
1 到 20GB
无保证容量
性能 每 GB 每秒最多 10000 次读取或 5000 次写入(独占)(内容 < 1KB)。如需了解详情,请参阅缓存统计信息 不保证
持久存储空间
服务等级协议

专用 Memcache 以 15 分钟为增量计费。 如果您使用非美元货币付费,请参阅 Cloud Platform SKU 上以您的币种列出的价格。

如果您的应用需要更多 Memcache 容量,请联系我们的销售团队

限制

使用 memcache 服务应遵循以下限制:

  • 缓存数据值的大小上限为 1 MB(10^6 个字节)。
  • 键不能大于 250 个字节。 在 Python 运行时中,字符串长度超过 250 个字节的键将进行哈希处理。(其他运行时的处理方式有所不同。)
  • “多”批量操作可以包含任意数量的元素。调用的总大小和提取数据的总大小不得超过 32 MB。
  • Memcache 键不能包含 null 字节。

缓存数据如何过期

Memcache 包含键值对。在缓存中写入和检索内容时,内存中的键值对随时会发生变化。

默认情况下,Memcache 中存储的值将保留尽可能长的时间。如果有新值添加到缓存中,而缓存内存不足,则系统可能会从缓存中逐出值。因内存压力而逐出值时,会先逐出最近最少使用的值。

在存储值时,应用可以提供以下形式的过期时间:从值添加时间起计算的秒数,或者将来的绝对 Unix 纪元时间(从 1970 年 1 月 1 日午夜算起的秒数)。值的逐出时间不会晚于过期时间,但也可能会因其他原因而提前逐出。增加为现有键存储的值不会更新其过期时间。

在极少数情况下,值也可能会因内存压力以外的原因而在过期之前从缓存中消失。虽然 Memcache 对于服务器故障具有很强的复原力,但 Memcache 值不会保存到磁盘,因此服务故障会导致值不可用。

一般来说,应用不应该期待缓存值始终可用。

您可以通过 API 或在 Google Cloud Console 的 Memcache 部分清空应用的整个缓存。

缓存统计信息

按内容大小的每秒操作次数

专用 Memcache 按每 GB 每秒操作次数确定操作速率,其中,一次操作的定义为访问一次个别缓存内容,例如 getsetdelete。操作速率因内容大小而异,大致如下表所示。如果超出这些速率,可能会导致 API 延迟时间延长或错误增加。

下表提供了每 GB 缓存的持续独占 get-hitset 操作次数上限。请注意,get-hit 操作是一种 get 调用,该调用会查找针对特定键存储的值并返回该值。

内容大小 (KB) 每秒 get-hit 操作次数上限 每秒 set 操作次数上限
≤1 10000 5000
100 2000 1000
512 500 250

理论上,配置了多个 GB 缓存的应用可达到聚合操作速率,即为 GB 数乘以每 GB 速率得出的总速率。 例如,对于 1KB 内容,配置了 5GB 缓存的应用可达到每秒 50000 次 Memcache 操作。 需要在 Memcache 键空间合理分配负载,才能达到这一水平。

对于每种 IO 模式,上面列出的限制适用于读取或写入。对于同时读取和写入,限制按比例增减。执行的读取次数越多,可执行的写入次数就越少,反之亦然。下面列出了每 1GB 缓存同时读取和写入 1KB 值的 IOPS 限制示例:

读取 IOPS 写入 IOPS
10000 0
8000 1000
5000 2500
1000 4500
0 5000

Memcache 计算单元 (MCU)

Memcache 吞吐量因您访问的内容大小以及您要对该内容执行的操作而异。大致来说,费用与操作次数相关联。您可以使用 Memcache 计算单元 (MCU) 估算专用 Memcache 预期可提供的流量容量。MCU 的定义如下:对于专用 Memcache,预期可提供每 GB 每秒 10000 个 MCU。Google Cloud Console 会显示您的应用当前正在使用的 MCU 数量。

请注意,MCU 是大致的统计估算,而且它不是线性单元。每个读取或写入值的缓存操作具有相应的 MCU 费用,此费用取决于该值的大小。set 的 MCU 取决于值的大小:其费用是成功执行 get-hit 操作所需费用的 2 倍。

值内容大小 (KB) get-hit 的 MCU 费用 set 的 MCU 费用
≤1 1.0 2.0
2 1.3 2.6
10 1.7 3.4
100 5.0 10.0
512 20.0 40.0
1024 50.0 100.0

不读取或写入值的操作其 MCU 费用是固定的:

操作 MCU
get-miss 1.0
delete 2.0
increment 2.0
flush 100.0
stats 100.0

请注意,get-miss 操作是一种 get 调用,该调用发现没有针对特定键存储的值。

比较和设置

比较和设置功能允许并发处理的多个请求以原子方式更新同一 Memcache 键的值,从而避免出现争用情况。

比较和设置的关键逻辑组件

如果要更新可能会收到其他并发写入请求的 Memcache 键值,您必须使用 Memcache Client 对象,该对象可存储支持比较和设置功能的方法所使用的特定状态信息。您不能使用 Memcache 函数 get()set(),因为它们是无状态的。Client 类本身不是线程安全的,因此您不应在多个线程中使用同一 Client 对象。

检索键时,您必须使用支持比较和设置功能的 Memcache Client 方法:gets()get_multi(),并将 for_cas 参数设置为 True

更新键时,您必须使用支持比较和设置功能的 Memcache Client 方法:cas()cas_multi()

另一个关键逻辑组件是 App Engine memcache 服务及其关于比较和设置的行为。App Engine memcache 服务本身以原子方式表现。也就是说,当两个并发请求(针对同一个应用 ID)使用 memcache 时,它们将转到相同的 memcache 服务实例,并且该 memcache 服务具有足够的内部锁定,这样对同一个键的并发请求就可以正确序列化。特别是,这意味着对同一个键的两个 cas() 请求实际上不会并行运行 - 服务先处理传入的第一个请求,完成后(即更新值和时间戳)才会开始处理第二个请求。

如需了解如何在 Python 中使用比较和设置,请阅读处理并发写入

最佳做法

以下是使用 Memcache 的一些最佳做法:

  • 正常处理 Memcache API 失败。 Memcache 操作可能会因各种原因而失败。在设计应用时,应该让它们能够捕获失败的操作,而不是向最终用户显示这些错误。特别是在涉及 Set 操作时,更要注意这一点。

  • 尽量使用 API 的批处理功能,尤其是对于较小的内容。这样做可以提高应用的性能和效率。

  • 在 Memcache 键空间分配负载。 如果单个或一小组 Memcache 内容占据了过大比例的流量,则会妨碍您的应用扩缩。这一指南同样适用于每秒操作次数和带宽。通常,您可以通过将数据明确分片来缓解此问题。

    例如,您可以在多个键之间拆分某个频繁更新的计数器,仅在您需要总计时才读回这些值并求和。 同样,您可以在多个键之间拆分在执行每个 HTTP 请求时都必须读取的一片大小为 500K 的数据,并使用单个批量 API 调用读回这些值 (更好的方法是在实例内存中缓存该值)。 对于专用 Memcache,单个键的峰值访问速率应该比每 GB 速率低 1-2 个数量级。

后续步骤