不能用字符串拼接构造SQL查询,因易导致SQL注入;必须使用Psycopg2参数化查询(%s占位符),按tuple/dict/list规范传参,并显式管理事务和连接生命周期。

为什么不能用字符串拼接构造SQL查询
直接用 f-string 或 str.format() 拼接用户输入进 SQL,等于给 SQL 注入敞开大门。哪怕加了 strip() 或正则过滤,也挡不住绕过手段——比如用注释符 -- 切断校验逻辑,或用 Unicode 空格混淆检测。
Psycopg2 的参数化查询不是“可选优化”,而是唯一安全路径。它把参数交给 PostgreSQL 服务端解析,确保值永远被当作数据而非 SQL 语法处理。
- ✅ 正确:使用
%s占位符(注意不是 Python 的字符串格式化),由 Psycopg2 底层绑定参数 - ❌ 错误:用
f"SELECT * FROM users WHERE name = '{name}'" - ⚠️ 注意:
%s是 Psycopg2 的占位符语法,和 Python 的%格式化无关,也不能换成{}或$1
如何正确传入参数:tuple、dict 和 list 的区别
Psycopg2 对不同参数类型有明确约定,传错结构会导致 TypeError 或静默错误(如查询结果为空)。
- 单个参数:必须是
tuple,哪怕只有一个值也要加逗号 ——cursor.execute("SELECT %s", (user_id,)) - 命名参数:用
dict,键名需与 SQL 中的%(key)s完全一致 ——cursor.execute("SELECT * FROM logs WHERE level = %(level)s", {"level": "ERROR"}) - 批量插入:用
execute_many()或executemany(),参数是listoftuple或listofdict——cur.executemany("INSERT INTO t VALUES (%s, %s)", [(1, 'a'), (2, 'b')])
别用 list 直接传单条语句,cursor.execute("...", [x, y]) 会报错;也别在命名参数里混用大小写,%(Level)s 和 {"level": "ERROR"} 不匹配。
立即学习“Python免费学习笔记(深入)”;
事务控制不当导致的数据不一致
Psycopg2 默认开启隐式事务,但不会自动提交。忘记 commit() 或出错后没 rollback(),轻则数据滞留,重则锁表阻塞其他连接。
- 显式控制:所有写操作后必须调用
conn.commit();异常时立刻conn.rollback() - 避免裸
try/except:不要只捕获Exception后吞掉错误,否则事务状态混乱 - 用上下文管理器更可靠:
with conn: # 自动 commit / rollback,但仅适用于整个事务块,不适用于需要分步提交的场景
特别注意:连接池(如 psycopg2.pool)中取到的 conn 仍需手动管理事务,池本身不接管事务生命周期。
连接泄漏和游标未关闭的实际影响
不显式关闭 cursor 或 connection,会导致 PostgreSQL 连接数缓慢耗尽,最终新请求卡在 connect() 阻塞,错误提示常是 server closed the connection unexpectedly 或 too many clients already。
-
cursor推荐用with语句创建:with conn.cursor() as cur:,退出时自动close() -
connection必须显式conn.close(),尤其在长周期脚本或 CLI 工具中;Web 应用通常交由框架或连接池管理 - 别依赖
__del__或垃圾回收:CPython 可能延迟回收,PyPy 或多线程下更不可靠
最隐蔽的问题是:本地测试时一切正常,一上生产就间歇性超时——十有八九是连接没关干净,积累数小时后爆发。



















