Django Redis缓存get方法耗时过高求助(约1700ms)
问题背景
在Django项目中使用Redis缓存货币兑换汇率,缓存有效期设为24小时,Redis与Django容器通过Docker Compose部署在同一环境。实现函数如下:
def getExchangeRate(currencyCode: str) -> float: # Define a cache key based on the currency code cache_key = f'exchange_rate_{currencyCode}' # Check the cache first for the exchange rate exchangeRate = cache.get(cache_key) if exchangeRate is None: try: # Fetch the exchange rate from the database exchangeRate = float(ExchangeRate.objects.get(currency__code=currencyCode).rate) # Cache the fetched value with an appropriate TTL (e.g., 24 hours) cache.set(cache_key, exchangeRate, 86400) # 86400 seconds = 24 hours except (ExchangeRate.DoesNotExist, ValueError): # Handle the case where the currency code is not found in the database # or the rate cannot be converted to a float exchangeRate = None cache.close() return exchangeRate
目前cache.get()方法耗时约1700ms,远超预期。Atatus监控数据显示该查询在事务中占用大量时间,原本希望通过缓存减少数据库查询次数,但Redis缓存获取操作性能异常,暂未找到有效解决方案。
排查与优化步骤
1. 移除手动关闭缓存连接的操作
Django的Redis缓存后端默认使用连接池管理连接,手动调用cache.close()会强制关闭当前连接,后续请求需要重新建立TCP连接,这是导致性能飙升的核心原因。连接池会自动复用空闲连接,无需手动关闭。
修改后的函数:
def getExchangeRate(currencyCode: str) -> float: cache_key = f'exchange_rate_{currencyCode}' exchangeRate = cache.get(cache_key) if exchangeRate is None: try: exchangeRate = float(ExchangeRate.objects.get(currency__code=currencyCode).rate) cache.set(cache_key, exchangeRate, 86400) except (ExchangeRate.DoesNotExist, ValueError): exchangeRate = None return exchangeRate
2. 检查Docker网络配置
确保Redis与Django容器处于同一自定义Docker网络,使用容器名称作为Redis的连接地址(而非localhost),避免跨网络通信的DNS解析延迟或路由损耗。
示例Docker Compose配置片段:
services: redis: image: redis:alpine networks: - app-internal django: build: . networks: - app-internal environment: - REDIS_URL=redis://redis:6379/0 networks: app-internal: driver: bridge
3. 优化Redis客户端连接池参数
在Django的settings.py中调整Redis缓存的连接池配置,增加最大连接数、设置合理的超时时间,减少连接等待和超时概率:
CACHES = { 'default': { 'BACKEND': 'django_redis.cache.RedisCache', 'LOCATION': 'redis://redis:6379/0', 'OPTIONS': { 'CLIENT_CLASS': 'django_redis.client.DefaultClient', 'CONNECTION_POOL_KWARGS': { 'max_connections': 100, 'socket_timeout': 2, 'socket_connect_timeout': 2, 'socket_keepalive': True, }, } } }
4. 验证Redis服务本身性能
进入Redis容器,检查服务响应速度和是否存在慢查询:
# 进入Redis容器 docker exec -it <redis-container-id> redis-cli # 测试响应时间 ping # 查看最近慢查询记录 slowlog get 10
正常情况下ping响应耗时应在1ms以内,若存在慢查询需针对性优化Redis配置或查询逻辑。
5. 分离事务与缓存操作
如果该函数在数据库事务上下文内被调用,事务的锁等待或提交延迟可能被误统计到Redis操作耗时中。尝试将缓存操作移出事务,或对相关视图使用@transaction.non_atomic_requests装饰器,避免事务对缓存性能的干扰。
预期效果
移除cache.close()后,Redis连接会被连接池复用,cache.get()耗时应降至10ms以内,大幅减少事务中的时间占用,同时实现缓存减少数据库查询的预期目标。
内容的提问来源于stack exchange,提问作者Saifullah Saeed

