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
。
我可以想到两种存储这些数据的方法。
tblClicksURLs
作为具有 click_id
、url
和 url_active
作为其字段的 Table。每次需要插入 click_id
和非空 url
时,更新具有相同 url
的所有其他记录,使 url_active
与 false
相同。
两个 tables tblClicks
和 tblURLs
。 tblURLs
有一个 click_id
外键。每次需要插入 click_id
和非空 url
时,click_id
就会插入到 tblClicks
和 click_id
以及 url
中得到 已将 插入 tblURLs
。 upsert 基于 url
是否已经存在于 tblURLs
中。因此,对于给定的 url
,tblURLs
中只会有一个 click_id
因此,在案例 1 中,我将有一个 url_active
的 UPDATE
,然后是相同的 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 存在,则通过两次交易发生。
比如说,我正在构建一个相机应用程序。每次用户点击照片时,图像都会存储在云端。因为我想限制在云端存储的图像数量,所以应用程序在初始化时在名为 listURLs
的数组中获取 10 URLs。
前 10 次点击 PUT
进入云端,耗尽 listURLs
。然后,每发生一次点击,就通过抛硬币来决定最近一次点击是否替换云端已有的一次点击。典型的数字是 50 次点击,前 10 次点击被分配 URL,在剩余的 40 次点击中,其中 20 次覆盖现有的 URL。
我将每个应用程序会话的记录存储在 Postgres 数据库中。每个会话都有一个 ID 和 所有 个点击实例(可能有也可能没有对应的 url)。我还需要知道与每次点击对应的 url
(如果存在的话)。所以,如果有 30 次点击,我需要知道其中哪 10 次有相应的 url
。
我可以想到两种存储这些数据的方法。
tblClicksURLs
作为具有click_id
、url
和url_active
作为其字段的 Table。每次需要插入click_id
和非空url
时,更新具有相同url
的所有其他记录,使url_active
与false
相同。两个 tables
tblClicks
和tblURLs
。tblURLs
有一个click_id
外键。每次需要插入click_id
和非空url
时,click_id
就会插入到tblClicks
和click_id
以及url
中得到 已将 插入tblURLs
。 upsert 基于url
是否已经存在于tblURLs
中。因此,对于给定的url
,tblURLs
中只会有一个
click_id
因此,在案例 1 中,我将有一个 url_active
的 UPDATE
,然后是相同的 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 存在,则通过两次交易发生。