MySQL Connector中WHERE NOT EXISTS插入与先查后插哪种性能更优?
结论
你提到的「先执行SELECT查询、不存在再插入」的方案性能会比当前的WHERE NOT EXISTS写法更差,还会额外引入并发重复插入的一致性问题,不是合理的优化方向。
性能差异原因分析
- 请求开销翻倍:原写法是单次数据库请求完成判断+插入逻辑,新方案需要两次跨网络的数据库交互,网络IO开销直接翻倍,多线程并发场景下会让数据库的QPS压力直接上涨一倍,反而会加重负载。
- 额外一致性开销:并发场景下SELECT和INSERT之间存在时间差,很容易出现两个线程同时查到同一条数据不存在、同时触发插入的情况,最终生成重复数据。如果为了避免这个问题加事务或加锁,又会额外引入锁竞争、事务管理的开销,性能会进一步下降。
你当前写法性能差的核心原因
- 缺少必要索引:你的判断条件是
cat_id = %s and d = '%s',如果没有给(cat_id, d)建立联合索引,不管是子查询还是单独的SELECT,每次都需要扫全表匹配数据,IO开销极高。 - SQL拼接导致执行计划无法复用:你现在的写法是直接用字符串拼接
%s生成SQL,每次执行的SQL语句结构都不一样,MySQL需要每次重新解析SQL、生成执行计划,高并发下会把数据库CPU资源全部占满,这是最主要的性能瓶颈。 - 子查询额外开销:你使用的MySQL5.x版本对
WHERE NOT EXISTS这类子查询的优化比较差,执行时会有额外的临时表生成开销。
最优优化方案
- 加联合唯一索引:给表建立
(cat_id, d)的联合唯一索引,SQL直接改为INSERT IGNORE语法,示例如下:
INSERT INTO `table` (a, b, c, cat_id, d) VALUES (%s, NOW(), 2, %s, %s)
用INSERT IGNORE时,如果命中唯一索引冲突会自动跳过插入,不需要自己写判断逻辑,性能是现有写法的3~10倍。
2. 改用参数化查询:不要自己拼接SQL,使用mysql.connector原生的参数绑定能力:
# 错误写法(字符串拼接) sql = f"INSERT ... VALUES ('{a_val}', {cat_id}, '{d_val}')" # 正确写法(参数化) sql = "INSERT INTO `table` (a, b, c, cat_id, d) VALUES (%s, NOW(), 2, %s, %s)" cursor.execute(sql, (a_val, cat_id_val, d_val))
参数化查询的SQL结构固定,MySQL可以复用执行计划,省去重复解析的开销,同时还能避免SQL注入风险。
3. 批量插入减少请求数:如果业务允许,攒100~500条数据做批量插入,进一步减少数据库请求次数,能把负载降到原来的1/100级别。
内容的提问来源于stack exchange,提问作者Yasmin Líbano
相关产品推荐
相关产品推荐

