You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级后Psycopg2偶发can't adapt type 'tuple'错误求助

你提到的“可以排除依赖或Psycopg2扩展问题”的判断不成立,该偶发现象恰恰指向Psycopg2 2.9.1版本的兼容问题,和数据库数据无关。

根因说明

这是Psycopg2 2.9.x系列版本在Django长连接复用场景下的已知偶发Bug:当同一个数据库连接被多次请求长时间复用后,Psycopg2内置的类型适配器会异常丢失元组类型的适配规则,导致IN子句的元组参数无法被正确解析,就会抛出can't adapt type 'tuple'错误。
你观察到的「重启Httpd后前几小时正常、仅涉及元组参数的查询报错」的特征完全匹配该Bug的触发规律:新启动的Httpd worker进程会重新初始化Psycopg2适配器,此时运行正常,连接复用一段时间后触发适配器异常,就会开始随机报错。

解决方案

方案1:降级Psycopg2版本(最便捷,无需修改业务代码)

直接将Psycopg2降级到2.8.6稳定版即可,该版本不存在该适配Bug,升级依赖后重启服务即可验证生效。

pip install psycopg2-binary==2.8.6
# 若使用源码安装则执行
pip install psycopg2==2.8.6

方案2:修改查询写法(适合必须保留Psycopg2 2.9.x版本的场景)

两种修改方式二选一即可:

  1. 改用Django ORM实现查询,ORM底层会自动处理参数适配,避免原生cursor的适配问题:
from your_app.models import XyzTableName
from django.db.models import Sum

rows = XyzTableName.objects.filter(
    employer_id=200176,
    approximate_age_band__in=['30-39', '50-59']
).values('month_key').annotate(
    eligible=Sum('eligible_count')
).order_by('month_key')
  1. 若必须使用原生SQL,手动拼接IN子句占位符,避免传入元组参数:
from django.db import connection

age_param = ['30-39', '50-59']
# 生成对应数量的占位符
placeholders = ', '.join(['%s'] * len(age_param))
sql = f"""SELECT month_key as cadence_val, sum(eligible_count) as eligible 
FROM xyz_table_name 
WHERE employer_id = %s 
AND approximate_age_band IN ({placeholders}) 
GROUP BY 1 ORDER BY 1;"""
# 参数扁平化处理,不嵌套元组
params = [200176] + age_param

with connection.cursor() as cursor:
    cursor.execute(sql, params)
    rows = cursor.fetchall()

方案3:关闭Django长连接复用(无代码修改成本)

在Django配置文件settings.py的数据库配置项中添加CONN_MAX_AGE=0,每次请求结束后自动关闭数据库连接,避免长连接复用触发的适配器异常:

DATABASES = {
    'default': {
        # 保留原有数据库配置(ENGINE、NAME、USER、PASSWORD等)
        'CONN_MAX_AGE': 0,
    }
}

注意该方案会略微增加数据库连接建立的开销,高并发场景建议配合数据库连接池中间件使用。


内容的提问来源于stack exchange,提问作者Naveen B

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 01:57:01