Upsert(冲突时)与 Read before Write 性能

Upsert (on conflict) vs Read before Write performance

每次登录用户访问我的一个网页时,我都会在 (user_id, page_id) 的 table 中添加一行,以标记访问过此页面的位置 ( user_id, page_id) 是这个table中的主键。 当然,如果用户再次来到这个页面,那么该行已经存在,所以不需要添加新的。

我目前使用 postgres ON CONFLICT 子句首先尝试写入,如果存在冲突,那么我什么也不做,因为该行已经存在。 但我担心,因为每次访问页面时都会发生这种写操作,所以它会给数据库增加不必要的负载。

这个想法对吗?如果是这样,那么我不是应该做更新插入 ON CONFLICT DO NOTHING,而是应该做一个 READ 来检查这个 (user_id, page_id) 是否已经存在于 table 中,如果不存在, 然后只做插入?

更新插入 ON CONFLICT 的好处是它只是一个单一的数据库查询。第二种先读后写的方法是 2 次 DB 调用,由于必须通过网络进行调用,因此速度会较慢。但第一种方法的缺点是它在每次访问页面时都进行写入(或者它实际上不被视为写入,因为 99% 的时间都会导致冲突?)。

我应该走哪条路?

您不需要单独的网络往返,您可以将其打包到一个语句中。但是,在我手中,ON CONFLICT 比使用两列键的“预读”稍微快一些,并且不会产生额外的 IO。使用带有自定义事务的 pgbench 进行测试(一旦 table 完全填充了所有 1e6 行):

\set a random(1, 1000)
\set b random(1, 1000)
insert into foo (a,b) values (:a,:b) on conflict do nothing;

对比

\set a random(1, 1000)
\set b random(1, 1000)
insert into foo (a,b) select :a,:b where not exists (select 1 from foo where a=:a and b=:b) 

此外,预读可能会受到竞争条件的影响。