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

判断PostgreSQL中以SELECT开头的查询是否为只读是否可靠?

仅靠"SELECT开头"判断只读查询的问题分析

直接通过SQL语句是否以SELECT开头来判断是否为只读查询,存在不少漏洞,会导致误判,具体问题包括:

一、漏判只读查询

很多不会修改数据的查询并不以SELECT开头,比如:

  • 系统查询类:SHOW timezone、EXPLAIN ANALYZE SELECT * FROM users
  • 数据导出类:COPY users TO '/tmp/users.csv'
  • 其他只读语句:FETCH ALL FROM my_cursor、VALUES (1, 'a'), (2, 'b')

这些语句都不会修改数据库,但按你的判断逻辑会被错误分发到主库,浪费主库资源。

二、误判写操作

有些以SELECT开头的语句实际会修改数据库状态,不能在只读副本执行:

  • 建表插入类:SELECT id, name INTO new_users FROM old_users(会创建new_users表并插入数据)
  • 调用修改类函数:SELECT pg_advisory_lock(123)(会获取排他锁,修改系统状态)
  • 加锁查询类:SELECT * FROM users FOR UPDATE(会对行加排他锁,副本不支持这类写操作)

如果把这些语句分发到副本,会直接执行失败。

三、注释干扰

如果查询开头带注释,比如:

/* 业务查询:获取用户列表 */ SELECT * FROM users WHERE status = 1

你的判断逻辑会因为开头不是SELECT而误判为非只读,导致本该走副本的查询跑到主库。

改进方案

  1. 语法解析判断
    用PostgreSQL内置的pg_parse_query()函数解析SQL语句,通过语法树判断语句类型。比如可以结合pg_stat_statements查看语句的命令类型:

    SELECT queryid, commandtype FROM pg_stat_statements WHERE query LIKE '%SELECT%';
    

    或者在应用端使用PostgreSQL语法解析库(比如libpg_query的封装),识别出SELECTStmt、ShowStmt、ExplainStmt等只读类型,同时排除带FOR UPDATE/SHARE的SELECT语句、SELECT INTO这类写操作。

  2. 补充业务规则过滤
    除了语法解析,还要结合业务场景调整:

    • 对强一致性要求的查询(比如用户刚提交订单后查询订单状态),强制走主库,避免副本数据延迟导致的不一致
    • 维护一份需要强制走主库的表/语句清单,作为兜底规则
  3. 利用只读连接属性兜底
    给副本连接设置default_transaction_read_only = on,即使误发了写操作,PostgreSQL会直接拒绝执行,避免破坏副本数据,但这只是兜底手段,核心还是要做好语句识别。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 22:05:21