SQLite中UPSERT(ON CONFLICT)与UPDATE/INSERT双查询的性能对比
回答
性能差异
原生UPSERT确实比双查询方案更快,但差距大小要看具体场景:
- 单条操作:差异微乎其微。原生UPSERT是单次SQL请求、单轮索引查找;双查询是两次请求,但因为
name有唯一索引,UPDATE的查找速度极快,实际使用中几乎感知不到区别。 - 批量操作:当大量执行upsert时,原生UPSERT的优势会凸显——它不需要在PHP层做分支判断,减少了PHP与SQLite的交互次数;同时SQLite内部处理冲突的逻辑更高效,避免了两次重复的索引查找,批量场景下性能能提升20%-50%(具体取决于数据量)。
是否值得做版本适配?
分两种情况:
- 如果你的业务有高频批量upsert需求(比如每秒数百次以上),或者对性能有极致要求,那绝对值得做版本判断。只需要几行代码就能获取SQLite版本并分支执行:
$sqliteVersion = SQLite3::version()['versionNumber']; // 3024000对应SQLite 3.24.0版本 $useNativeUpsert = $sqliteVersion >= 3024000;
- 如果只是低频单条upsert,双查询方案的性能完全够用,没必要增加代码复杂度,维护成本反而更高。
双查询方案在新版本的表现
在新版本SQLite中,双查询方案的性能依然能满足绝大多数日常需求:
- 事务包裹下,两次操作的磁盘IO会被合并(SQLite事务会延迟写操作到提交时执行),不会额外增加磁盘开销。
- 唯一索引的存在让UPDATE的查找效率拉满,几乎没有额外损耗。
- 除非你有每秒数千次以上的upsert请求,否则双查询的性能瓶颈不会显现。
额外提醒
- 无论用哪种方案,一定要包裹在事务中!如果没有事务,双查询方案会触发两次磁盘刷新,性能直接暴跌;原生UPSERT虽然是单条语句,但无事务的话每次操作都会刷盘,同样影响性能。
- 双查询方案要注意并发场景:高并发下可能出现UPDATE后、INSERT前,其他进程插入了同一条数据,导致唯一索引冲突。这种情况可以在INSERT时捕获异常,重试一次UPDATE即可——而原生UPSERT本身内置了冲突处理,不会出现这个问题。
内容的提问来源于stack exchange,提问作者O. Jones
相关产品推荐
相关产品推荐

