insert on conflict - 合并写 (消除不必要更新)

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:

标签

PostgreSQL , 合并写 , insert on conflict , 不必要更新


背景

合并更新的应用非常广泛,存在则更新,不存在则写入。

但是在某些场景中,存在并不一定要更新,原因是新的内容可能和老的内容完全一致。这种更新操作是完全没有必要的。

因为PG是多版本的,更新会产生新的TUPLE版本,如果这种没必要的更新很多,只会给数据库带来额外的负担同时影响性能。特别体现在批量操作中。

例如电商场景中,运营人员会将需要操作的一批店铺导入到操作表,在操作表中再进行数据的操作。导入动作就是一个合并写的动作,每一批导入的店铺可能在操作表中已存在并且记录完全没有变化,如果不注意INSERT INTO ON CONFLICT语法的使用,会导致写入放大。

例子

1、新建测试表

create table tbl(  
  c1 int,   
  c2 int,   
  c3 int,   
  c4 int,   
  c5 timestamp,   
  unique (c1,c2)  
);  

2、使用普通的insert into on conflict合并写入,存在写入放大

insert into tbl   
  select id,id,1,random(),now() from generate_series(1,1000000) t(id)   
  on conflict(c1,c2)   
  do update   
  set   
  c3=excluded.c3,c4=excluded.c4,c5=excluded.c5;  

每一次操作都会更新所有记录

INSERT 0 1000000  
  
INSERT 0 1000000  
  
INSERT 0 1000000  

3、优化方法,加入更新条件,避免未变化的记录被更新

例如当c3,c4没有变化时,不更新。

  where  
  tbl.c3 is distinct from excluded.c3 or  
  tbl.c4 is distinct from excluded.c4;  

SQL如下

insert into tbl   
  select id,id,1,random(),now() from generate_series(1,1000000) t(id)   
  on conflict(c1,c2)   
  do update   
  set   
  c3=excluded.c3,c4=excluded.c4,c5=excluded.c5  
  where  
  tbl.c3 is distinct from excluded.c3 or  
  tbl.c4 is distinct from excluded.c4;  

此时每次更新的就是那些真正发生了变化的记录

INSERT 0 500172  
  
INSERT 0 500383  
  
INSERT 0 500664  

参考

https://www.postgresql.org/docs/10/static/sql-insert.html

[ WITH [ RECURSIVE ] with_query [, ...] ]  
INSERT INTO table_name [ AS alias ] [ ( column_name [, ...] ) ]  
    [ OVERRIDING { SYSTEM | USER} VALUE ]  
    { DEFAULT VALUES | VALUES ( { expression | DEFAULT } [, ...] ) [, ...] | query }  
    [ ON CONFLICT [ conflict_target ] conflict_action ]  
    [ RETURNING * | output_expression [ [ AS ] output_name ] [, ...] ]  
  
where conflict_target can be one of:  
  
    ( { index_column_name | ( index_expression ) } [ COLLATE collation ] [ opclass ] [, ...] ) [ WHERE index_predicate ]  
    ON CONSTRAINT constraint_name  
  
and conflict_action is one of:  
  
    DO NOTHING  
    DO UPDATE SET { column_name = { expression | DEFAULT } |  
                    ( column_name [, ...] ) = [ ROW ] ( { expression | DEFAULT } [, ...] ) |  
                    ( column_name [, ...] ) = ( sub-SELECT )  
                  } [, ...]  
              [ WHERE condition ]  

is distinct from是一种不等于的用法,其中包括NULL值的处理(认为null is not distinct from null is TRUE)。两个NULL值返回FALSE,一个NULL值返回TRUE。

null is distinct from null 返回false

null is distinct from nonnull 返回true

nonnull is distinct from nonnull 根据实际的VALUE来判断是否相等,相等返回false,不相等返回true。

当要判断NULL时,这个比=操作符好用,使用=等操作符时,通常底层函数是strict的,所以当操作数包含NULL值时,操作符的结果也返回NULL而不是我们想要的true或false。

相关文章
|
10月前
|
SQL 关系型数据库 MySQL
操作delete或者update语句,加个limit或者循环分批次删除
操作delete或者update语句,加个limit或者循环分批次删除
|
关系型数据库 MySQL Java
MySQL 巨坑:update 更新慎用影响行数做判断!!!
MySQL 巨坑:update 更新慎用影响行数做判断!!!
753 0
MySQL 巨坑:update 更新慎用影响行数做判断!!!
|
SQL 数据库管理
【SQL开发实战技巧】系列(九):一个update误把其他列数据更新成空了?Merge改写update!给你五种删除重复数据的写法!
本篇文章讲解的主要内容是:***你有没有经历过一个update把其他列数据清空了、使用merge更新合并记录、删除违反参照完整性的记录、给你五种删除重复数据的写法*** 【SQL开发实战技巧】这一系列博主当作复习旧知识来进行写作,毕竟SQL开发在数据分析场景非常重要且基础,面试也会经常问SQL开发和调优经验,相信当我写完这一系列文章,也能再有所收获,未来面对SQL面试也能游刃有余~。
【SQL开发实战技巧】系列(九):一个update误把其他列数据更新成空了?Merge改写update!给你五种删除重复数据的写法!
|
SQL 关系型数据库 MySQL
当并发insert on duplicate key update遇见死锁:更新丢失
数据库死锁问题,是一个老生常谈且很常见的问题,网上也有非常多对于各类死锁场景的解析和复现,但凡和死锁有关,无外乎不涉及数据库隔离等级、索引、以及innodb锁等相关原因。但是我这个案例我搜遍了全网也没能找到比较相似情况。于是我想尽可能的复现出这种情况,找出死锁的原因,找出可能出现的隐患。问题的背景:我们的数据库中的发生死锁的表是具有”多列组合构建的唯一索引“(不包含
17269 2
|
SQL 索引
深入解析:Row Movement 的原理和性能影响与关联
ROW MOVEMENT特性最初是在8i时引入的,其目的是提高分区表的灵活性——允许更新Partition Key。这一特性默认是关闭,只是在使用到一些特殊功能时会要求打开。除了之前提到的更新Partition Key,还有2个要求打开的ROW MOVEMENT的功能就是flushback table和Shrink Segment。
3808 0
|
关系型数据库 PostgreSQL 索引
随机记录并发查询与更新(转移、删除)的"无耻"优化方法
背景       某张表有一批记录,A用户说,这批记录是我要的,但是我只要一条,B用户也说,这批记录是我要的,我也只要一条。 是不是有点像一群男人去逛怡红院,妹子们都是目标,但是今晚只要一位,至于是谁暂时还不确定,虽然不需要抢,但是得锁单。
3105 0