Django权限校验频繁查询PostgreSQL数据库的优化方案咨询
你遇到的高耗时查询是Django原生权限校验的典型性能问题:默认情况下每次调用user.has_perm都会执行一次多表左连接查询,且仅在单次请求生命周期内缓存结果,高并发场景下很容易成为数据库性能瓶颈。以下是可落地的优化方案:
解决方案汇总
1. 成熟第三方权限缓存包
- 可以直接使用
django-cache-permissions包,它默认支持将用户全量权限缓存到你配置的任意缓存后端(包括Memcached、Redis等),缓存粒度支持用户级、权限级,会自动监听auth_user、auth_group、权限相关表的变更事件自动失效缓存,完全兼容Django原生的权限校验API,无需修改业务层的权限校验代码。 - 另外
django-guardian作为常用的精细化权限管理包,也内置了可选的权限缓存配置,开启后同样支持将权限结果写入Memcached等缓存后端,适合同时需要对象级权限控制的场景。
2. 轻量自定义缓存实现(无需引入第三方包)
如果不想额外引入依赖,可以自行基于Django的缓存框架实现权限缓存,只需要重写默认的权限校验后端即可,核心逻辑如下:
- 自定义权限校验后端继承Django默认的
ModelBackend - 重写
has_perm方法,先以user_id:perm_codename为键去缓存中查询结果,缓存命中则直接返回 - 缓存未命中时执行原生的权限校验逻辑,将结果写入缓存并设置合理的过期时间
- 监听权限相关表的
post_save、post_delete信号,用户/组/权限变更时主动删除对应用户的权限缓存,避免脏数据
示例核心代码片段:
from django.contrib.auth.backends import ModelBackend from django.core.cache import cache from django.db.models.signals import post_save, post_delete from django.dispatch import receiver from django.contrib.auth.models import User, Group, Permission class CachedModelBackend(ModelBackend): def has_perm(self, user_obj, perm, obj=None): # 有对象级权限校验需求时走原生逻辑 if obj is not None: return super().has_perm(user_obj, perm, obj) cache_key = f"perm:{user_obj.id}:{perm}" res = cache.get(cache_key) if res is not None: return res res = super().has_perm(user_obj, perm, obj) # 缓存有效期设为1天,权限变更时主动失效 cache.set(cache_key, res, timeout=86400) return res # 监听权限相关表变更,主动清空缓存 @receiver([post_save, post_delete], sender=User) @receiver([post_save, post_delete], sender=Group) @receiver([post_save, post_delete], sender=Permission) def clear_perm_cache(sender, instance, **kwargs): # 可根据实际变更范围精准删除对应缓存,减少缓存失效范围 cache.delete_pattern("perm:*")
之后在settings.py中将AUTHENTICATION_BACKENDS配置为你自定义的后端即可生效。
3. 额外优化建议
- Django 3.2及以上版本可以先在
settings.py中开启内置的请求级权限缓存AUTH_PERMISSION_CACHE = True,先减少单次请求内的重复权限查询次数,改造成本为0。 - 如果你的系统权限变更频率极低,也可以直接将所有用户的权限预计算后批量写入缓存,定期全量更新即可,进一步降低数据库查询量。
内容的提问来源于stack exchange,提问作者Quentin Mortier
相关产品推荐
相关产品推荐

