为何Redshift+psycopg2只读事务中使用select distinct会报错?
背景
我用Python的psycopg2连接Redshift,代码如下:
conn = psycopg2.connect(my_credentials) conn.set_session( readonly=True )
之后用这个连接创建游标执行查询。有个带CTE的查询示例:
with _cte_1 as ( select distinct some_identifier, some_metadata from my_schema.my_table ), _cte_2 as ( select some_identifier, some_different_metadata from my_schema.my_other_table ) select * from _cte_1 left join _cte_2 using (some_identifier)
执行这个查询时会触发ReadOnlySqlTransaction: transaction is read-only错误,但去掉_cte_1里的distinct后就能正常执行。注:无法提供最小复现示例,不清楚需要复现哪些数据库特性;my_schema.my_table和my_schema.my_other_table都是dbt流水线中的模型,psycopg2版本为2.9.9 (dt dec pq3 ext lo64)。
问题
为什么这里的SELECT DISTINCT会触发只读事务错误?
可能的原因及解释
临时表创建被误判为写操作
当查询包含DISTINCT时,Redshift的查询优化器可能会创建临时表存储去重后的中间结果(尤其是数据量较大时)。虽然临时表是会话级、结束后自动销毁的对象,但Redshift的READ ONLY事务规则可能将其创建操作误判为修改持久化状态的写操作,进而触发错误。自动统计信息更新触发写操作
Redshift默认开启自动统计信息更新,执行DISTINCT查询时,如果表的统计信息过期,系统可能尝试更新统计信息。而统计信息更新属于修改系统表的写操作,会被只读事务阻止,抛出错误。dbt模型的特殊配置影响
你的表是dbt生成的模型,若这些模型是增量表、物化视图或带有特殊的排序/分布键配置,DISTINCT操作可能触发模型背后的隐性写逻辑(比如增量模型的合并校验),不过这种情况概率较低,因为查询本身是只读的。psycopg2与Redshift的兼容性问题
你使用的psycopg2 2.9.9版本,可能与Redshift的SET SESSION READ ONLY设置存在兼容性问题。set_session(readonly=True)可能没有正确将只读属性传递到查询执行的子上下文,导致DISTINCT操作所在的子事务被判定为读写事务。
验证与解决方向
- 查看执行计划:用
EXPLAIN前缀你的查询,查看是否包含CREATE TEMP TABLE相关步骤,若有则说明是临时表触发的问题。 - 关闭自动统计信息:执行
SET AUTO_STATISTICS = OFF;后重试查询,若正常执行则说明是统计信息更新导致的。 - 升级psycopg2:尝试升级到最新稳定版,验证兼容性问题是否解决。
- 替代
DISTINCT:用GROUP BY实现去重(如SELECT some_identifier, some_metadata FROM ... GROUP BY some_identifier, some_metadata),看是否能绕过错误。
内容的提问来源于stack exchange,提问作者rxFt20

