NDB 可为您管理缓存。 有两个缓存级别:一个是上下文缓存,一个是使用 App Engine 标准缓存服务 - Memcache。 默认情况下,所有实体类型都会启用这两种缓存,您可通过配置这些缓存来满足高级需求。 此外,NDB 可实现一项名为自动批处理的功能,该功能会尝试将操作组合在一起,从而最大限度地减少服务器往返次数。
简介
缓存对大多数类型的应用都大有裨益。NDB 会自动缓存其写入或读取的数据(除非应用将其配置为不自动缓存)。 从缓存读取比从 Datastore 读取的速度要快。
您可以通过传递上下文选项参数来修改多个 NDB 函数的缓存行为。
例如,您可以调用 key.get(use_cache=False, use_memcache=False)
来绕过缓存。您还可以在 NDB 上下文中更改默认缓存政策,如下文所述。
注意:在您使用管理控制台的数据存储区查看器修改数据存储区内容时,缓存的值将不会更新。 因此,您的缓存可能会不一致。 对于上下文缓存,这通常不会造成问题。 对于 Memcache,我们建议使用管理控制台来刷新缓存。
上下文对象
缓存管理使用名为 Context
的类:每个线程和每个事务均在新的上下文中执行。每个传入 HTTP 请求都会启动一个新线程,各请求也会通过新的上下文执行。要访问当前上下文,请使用 ndb.get_context()
函数。
注意:在多个线程或请求之间共享 Context
对象是没有意义的。请勿将上下文保存为全局变量!
最好是将上下文存储在局部变量或线程局部变量中。
上下文对象具有用于设置缓存政策以及处理缓存的方法。
上下文缓存
上下文缓存仅在单个线程的持续时间内持续存在。这意味着系统会为每个传入 HTTP 请求指定一个新的上下文缓存,且该上下文缓存仅对处理相关请求的代码“可见”。在处理请求时,如果您的应用生成任何其他线程,则这些线程还将有一个新的单独上下文缓存。
上下文缓存速度很快;此缓存位于内存中。当 NDB 函数写入 Datastore 时,它也会写入上下文缓存。当 NDB 函数读取实体时,它会先检查上下文缓存。如果在上下文缓存中找到相应实体,则无需进行 Datastore 交互。
当 NDB 函数查询 Datastore 时,将从 Datastore 中检索结果列表。不过,如果上下文缓存中存在任何单个结果,则将使用该结果来代替通过 Datastore 查询检索到的值。 查询结果会写回上下文缓存,前提是缓存政策这样规定(但永不写回 Memcache)。
在后台任务中执行长时间运行的查询时,上下文缓存可能会消耗大量内存。这是因为缓存保存了在当前上下文中检索或存储的每个实体的副本。为避免长时间运行的任务中发生内存异常,您可以停用缓存或设置政策,排除消耗大部分内存的所有实体。
Memcache
Memcache 是 App Engine 的标准缓存服务,速度比 Datastore 快得多,但比上下文缓存慢(毫秒级与微秒级的区别)。
默认情况下,非事务性上下文会缓存 Memcache 中的所有实体。 应用的所有上下文都使用同一 Memcache 服务器,还会看到一组一致的缓存值。
Memcache 不支持事务。因此,旨在同时应用于 Datastore 和 Memcache 的更新可能只会对两者中的一个进行。 为了在此类情况下保持一致性(可能以牺牲性能为代价),更新后的实体将从 Memcache 中删除,然后再写入 Datastore。后续读取操作将发现 Memcache 中缺少该实体,从 Datastore 中检索它,然后在 Memcache 中更新该实体(这也是读取的一个“副作用”)。 此外,事务内的 NDB 读取会忽略 Memcache。
在事务中写入实体时,将不使用 Memcache;在提交事务时,其上下文将尝试从 Memcache 中删除所有此类实体。不过请注意,某些故障可能会阻止这些删除操作。
政策函数
自动缓存对大多数应用有利,但您的应用可能不常见,并且您需要对部分或所有实体关闭自动缓存。 您可以通过设置政策函数来控制缓存的行为。 以下是一个用于进程内缓存的政策函数,其设置如下:
还有一个用于 Memcache 的政策函数,其设置如下:
每个政策函数均接受键并返回布尔值结果。
如果政策函数返回 False
,则由该键标识的实体将不会保存在相应的缓存中。例如,要对所有 Account
实体绕过进程内缓存,您可以写入以下代码:
(不过,请继续阅读,找到一种更简单的方法来实现此目的。)
为方便起见,您可以传递 True
或 False
,而不是传递始终返回同一值的函数。默认政策会缓存所有实体。
还有一个 Datastore 政策函数,用于管理将哪些实体写入 Datastore 本身:
该函数的工作原理类似于上下文缓存和 Memcache 政策函数:如果 Datastore 政策函数为给定键返回 False
,则相应的实体将不会写入 Datastore。(该实体可能会写入进程内缓存或 Memcache,前提是其政策函数允许。)
如果您想要缓存类似实体的数据,但不需要将其存储在 Datastore 中,则此函数非常有用。
与缓存政策一样,您可以传递 True
或 False
,而不是传递始终返回同一值的函数。
在内存不足时,Memcache 会自动使项目过期。 您可以设置 Memcache 超时政策函数,以确定实体在缓存中的最长有效期:
此函数通过键参数调用,并且应返回一个指定最长有效期(以秒为单位)的整数;如果值为 0 或 None
,则表示无限期(只要 Memcache 服务器具有足够的内存即可)。为方便起见,您可以只传递一个整数常量,而不是传递始终返回同一值的函数。
如需详细了解超时,请参阅 Memcache 文档。
全新的上下文以一个空的进程内缓存开始。
虽然政策函数非常灵活,但实际上大多数政策都很简单。例如:
- 不要缓存属于特定模型类的实体。
- 将此模型类中的实体的 Memcache 超时设置为 30 秒。
- 无需将此模型类中的实体写入 Datastore。
为免去您编写和不断更新简单政策函数的工作(甚至是使用上下文选项替换每项操作的政策),默认政策函数可从传递给它们的键获取模型类,然后在模型类中查找特定的类变量:
类变量 | 类型 | 说明 |
---|---|---|
_use_cache | bool | 指定是否在进程内缓存中存储实体;替换默认进程内缓存政策。 |
_use_memcache | bool | 指定是否在 Memcache 中存储实体;替换默认 Memcache 政策。 |
_use_datastore | bool | 指定是否在数据存储区中存储实体;替换默认数据存储区政策。 |
_memcache_timeout | int | 实体在 Memcache 中的最长有效期;替换默认 Memcache 超时政策。 |
注意:这是每个政策的默认政策函数的一项功能。
如果您指定了自己的政策函数但还想要回退到默认政策,则可明确调用默认政策函数作为 Context
类的静态方法:
default_cache_policy(key)
default_memcache_policy(key)
default_datastore_policy(key)
default_memcache_timeout_policy(key)