PostgreSQL 中的更新和插入与更新插入

Update and Insert vs Upsert in PostgreSQL

比如说,我正在构建一个相机应用程序。每次用户点击照片时,图像都会存储在云端。因为我想限制在云端存储的图像数量,所以应用程序在初始化时在名为 listURLs 的数组中获取 10 URLs。

前 10 次点击 PUT 进入云端,耗尽 listURLs。然后,每发生一次点击,就通过抛硬币来决定最近一次点击是否替换云端已有的一次点击。典型的数字是 50 次点击,前 10 次点击被分配 URL,在剩余的 40 次点击中,其中 20 次覆盖现有的 URL。

我将每个应用程序会话的记录存储在 Postgres 数据库中。每个会话都有一个 ID 和 所有 个点击实例(可能有也可能没有对应的 url)。我还需要知道与每次点击对应的 url(如果存在的话)。所以,如果有 30 次点击,我需要知道其中哪 10 次有相应的 url

我可以想到两种存储这些数据的方法。

  1. tblClicksURLs 作为具有 click_idurlurl_active 作为其字段的 Table。每次需要插入 click_id 和非空 url 时,更新具有相同 url 的所有其他记录,使 url_activefalse 相同。

  2. 两个 tables tblClickstblURLstblURLs 有一个 click_id 外键。每次需要插入 click_id 和非空 url 时,click_id 就会插入到 tblClicksclick_id 以及 url 中得到 已将 插入 tblURLs。 upsert 基于 url 是否已经存在于 tblURLs 中。因此,对于给定的 urltblURLs

  3. 中只会有一个 click_id

因此,在案例 1 中,我将有一个 url_activeUPDATE,然后是相同的 table 的 INSERT。在案例 2 中,我会将 INSERT 转换为一个 table,将 UPSERT 转换为另一个。我需要在 click_id 上建立索引,但在 url.

上不需要

如果您正在查看每秒 > 10k 行的写入,甚至更多,这两者中哪一个效率更高?假设每个会话的数字与上面引用的数字相似(50 次点击等)

我还可以为案例 1 中的每条记录注册一个 created_at 日期时间,并且只使用按时间倒序排列的第一个非空 url。但是,我试图避免这种情况,除非性能优势是巨大的。

考虑了一段时间后,我决定在 tblClicksURLs 上使用 UPSERT 和唯一约束。约束在 (url, url_active) 上。每次需要添加新的 (click_id, url, url_active) 记录时,都会对该记录进行 UPSERT。冲突时 url_active 设置为 null。因此,所有具有此 url 的记录都将其 url_active 设置为 null

然后我用

RETURNING (xmax = 0) AS inserted

如讨论的那样 检查记录是否已插入或更新。如果更新了记录,我将最新记录的 url_active 字段更新为 true.

这样,如果第一次插入 url,它会在单个事务中发生。如果 url 存在,则通过两次交易发生。