使几个分组的缓存键无效
Invalidating several grouped cache keys
我的模型 TicketType
有大约 500 个实例。
它每周只更改几次。
但如果它发生变化,我需要使所有使用旧 TicketTypes 的缓存值无效。
遗憾的是,一些缓存键未修复。它们包含计算数据。
我看到了这些解决方案:
使用 version
参数并在 TicketType
的 post 保存信号处理程序上更新版本值。
对基于 TicketType 的所有缓存键使用通用前缀。
然后使 post 保存信号处理程序中的所有缓存键无效。
我想还有第三种更好的方法...
示例:
TicketType 是一棵树。 TicketTypes 的可见性与权限绑定。如果两个用户具有不同的权限,他们可能会以不同的方式查看树。我们根据权限缓存它。用户的权限被序列化和散列。通过创建包含哈希和固定部分的字符串来创建缓存键:
hash_key='ticket-type-tree--%s' % hashed_permissions
如果 TicketType 树发生变化,我们需要确保不会从缓存中加载旧数据。只要不使用旧数据,就不需要主动失效。
嗯,基本上你的问题只是缓存键的表达能力。当你必须做一些像散列集合这样复杂的事情来获取密钥时,它一定是一个提示,缺少一些东西。
在你的情况下,我相信缺少的只是一个 "permission set" 对象。你可以称它为一个组,一个角色(如在 RBAC 中)......这就是为什么我问你集合是否重复 - 实际上,你的哈希键只是一种重新创建不存在的集合对象的 ID 的方法。
所以解决方案是:
- 创建一个榜样,M2M 与用户相关,M2M 与权限相关(据我所知,这与您的 TicketType 相关联)
- 使用事件处理程序来捕获对 TicketType 的保存。
- 获取所有受影响的角色(通过权限)
- 生成密钥(类似于 ticket-type-TREEID-ROLEID)并使其失效
最后两点:
- 有时 cache.clear() 是解决方案 - 特别是如果您不将缓存用于其他用途时
- 您说在树中导航时您的 SQL 查询计数很大。如果您还没有尝试过,您可能只是想通过预取和 select_related(参见文档)对其进行优化。
在 TicketType post 中保存信号处理程序:
a) 根据所有用户的权限生成密钥并使密钥无效
b)为每个排列(许可)生成密钥(如果你可以计算它们)并使密钥无效
c) 使用第二个 memcached 实例仅存储这些缓存并清除它(最简单)
P.S.: 专业提示是刷新缓存而不是仅仅使它们无效。但是,django 信号中未捕获的异常可能会很麻烦,所以要小心
您可以将工单修改时间用作缓存键的一部分。
hash_key = 'ticket-type-tree--%s-%s' % (hashed_permissions, tree.lastmodified)
您可以添加 DateTimeField
和 auto_now=True
。如果从数据库获取修改时间太昂贵,你也可以缓存它。
通常,在 post_save
信号处理程序中更新缓存就可以了。除非你想一直拥有一致的数据,并且想为交易支付额外的费用。
使用 redis 来缓存你的模型
我缓存实例的方式如下:
1-确保您每次都收到一件商品。例如:Model.objects.get(foo='bar'),并且您每次都使用属性 foo
从数据库中获取模型。这将用于确保数据稍后失效。
2-覆盖方法 save() 并确保它使用 foo 属性将数据保存到缓存中。
例如:
class Model(model.Model):
foo = models.CharField()
bar = models.CharField()
def save(self, *args, **kwargs):
redis.set(foo, serialize_model())
super(Model, self).save(*args, **kwargs)
def serialize_model():
return serilized_object
3-覆盖 get 方法以在访问数据库之前获取序列化对象。
例如:
class Model(model.Model):
...
def get(self, *args, **kwargs):
if redis.get(self.foo):
return redis.get(self.foo)
else:
return super(Model).get(*args, **kwargs)
4-如果实例被移除或删除,覆盖您的删除方法以移除缓存
例如
class Model(model.Model):
...
def delete(self,*args, **kwargs):
redis.delete(self.foo)
super(Model, self).delete(*args, **kwargs)
将模型 class 替换为您的模型,在本例中为 Ticket Type
有一件事,我假设您不会在 Django 应用程序之外接触数据库。如果您在任何其他地方使用原始 sql,这将不起作用。
在他们的网站上找redis functions,他们有删除,设置和获取的功能。如果您使用其他缓存方式。寻找如何设置、获取和删除。
我的模型 TicketType
有大约 500 个实例。
它每周只更改几次。
但如果它发生变化,我需要使所有使用旧 TicketTypes 的缓存值无效。
遗憾的是,一些缓存键未修复。它们包含计算数据。
我看到了这些解决方案:
使用 version
参数并在 TicketType
的 post 保存信号处理程序上更新版本值。
对基于 TicketType 的所有缓存键使用通用前缀。 然后使 post 保存信号处理程序中的所有缓存键无效。
我想还有第三种更好的方法...
示例:
TicketType 是一棵树。 TicketTypes 的可见性与权限绑定。如果两个用户具有不同的权限,他们可能会以不同的方式查看树。我们根据权限缓存它。用户的权限被序列化和散列。通过创建包含哈希和固定部分的字符串来创建缓存键:
hash_key='ticket-type-tree--%s' % hashed_permissions
如果 TicketType 树发生变化,我们需要确保不会从缓存中加载旧数据。只要不使用旧数据,就不需要主动失效。
嗯,基本上你的问题只是缓存键的表达能力。当你必须做一些像散列集合这样复杂的事情来获取密钥时,它一定是一个提示,缺少一些东西。
在你的情况下,我相信缺少的只是一个 "permission set" 对象。你可以称它为一个组,一个角色(如在 RBAC 中)......这就是为什么我问你集合是否重复 - 实际上,你的哈希键只是一种重新创建不存在的集合对象的 ID 的方法。
所以解决方案是:
- 创建一个榜样,M2M 与用户相关,M2M 与权限相关(据我所知,这与您的 TicketType 相关联)
- 使用事件处理程序来捕获对 TicketType 的保存。
- 获取所有受影响的角色(通过权限)
- 生成密钥(类似于 ticket-type-TREEID-ROLEID)并使其失效
最后两点:
- 有时 cache.clear() 是解决方案 - 特别是如果您不将缓存用于其他用途时
- 您说在树中导航时您的 SQL 查询计数很大。如果您还没有尝试过,您可能只是想通过预取和 select_related(参见文档)对其进行优化。
在 TicketType post 中保存信号处理程序:
a) 根据所有用户的权限生成密钥并使密钥无效
b)为每个排列(许可)生成密钥(如果你可以计算它们)并使密钥无效
c) 使用第二个 memcached 实例仅存储这些缓存并清除它(最简单)
P.S.: 专业提示是刷新缓存而不是仅仅使它们无效。但是,django 信号中未捕获的异常可能会很麻烦,所以要小心
您可以将工单修改时间用作缓存键的一部分。
hash_key = 'ticket-type-tree--%s-%s' % (hashed_permissions, tree.lastmodified)
您可以添加 DateTimeField
和 auto_now=True
。如果从数据库获取修改时间太昂贵,你也可以缓存它。
通常,在 post_save
信号处理程序中更新缓存就可以了。除非你想一直拥有一致的数据,并且想为交易支付额外的费用。
使用 redis 来缓存你的模型
我缓存实例的方式如下:
1-确保您每次都收到一件商品。例如:Model.objects.get(foo='bar'),并且您每次都使用属性 foo
从数据库中获取模型。这将用于确保数据稍后失效。
2-覆盖方法 save() 并确保它使用 foo 属性将数据保存到缓存中。
例如:
class Model(model.Model):
foo = models.CharField()
bar = models.CharField()
def save(self, *args, **kwargs):
redis.set(foo, serialize_model())
super(Model, self).save(*args, **kwargs)
def serialize_model():
return serilized_object
3-覆盖 get 方法以在访问数据库之前获取序列化对象。
例如:
class Model(model.Model):
...
def get(self, *args, **kwargs):
if redis.get(self.foo):
return redis.get(self.foo)
else:
return super(Model).get(*args, **kwargs)
4-如果实例被移除或删除,覆盖您的删除方法以移除缓存
例如
class Model(model.Model):
...
def delete(self,*args, **kwargs):
redis.delete(self.foo)
super(Model, self).delete(*args, **kwargs)
将模型 class 替换为您的模型,在本例中为 Ticket Type
有一件事,我假设您不会在 Django 应用程序之外接触数据库。如果您在任何其他地方使用原始 sql,这将不起作用。
在他们的网站上找redis functions,他们有删除,设置和获取的功能。如果您使用其他缓存方式。寻找如何设置、获取和删除。