更新插入(冲突时)与先读后写性能对比

ras*_*n58 3 sql postgresql upsert

每当登录用户访问我的某个网页时,我都会向 (user_id, page_id) 表添加一行,以标记访问过该页面,其中 (user_id, page_id) 是该表中的主键。当然,如果用户再次访问此页面,则该行已经存在,因此不需要添加新行。

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

这种想法正确吗?如果是这样,那么我不应该在冲突时执行更新插入什么也不做,而是应该执行 READ 来检查表中是否已存在此(user_id,page_id),如果不存在,则只执行插入?

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

我应该走哪条路?

jja*_*nes 5

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

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

相对

\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) 
Run Code Online (Sandbox Code Playgroud)

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