PostgreSQL 大宽表,全列索引,高并发合并写入(insert into on conflict, upsert, merge insert) - 实时adhoc query

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
简介: 标签 PostgreSQL , 全列索引 , 大宽表 , 写测试 , insert on conflict , upsert , merge insert , adhoc query 背景 OLAP系统中,adhoc query非常场景(任意维度查询分析)。 adhoc query,通常来说,可以加GIN倒排,或者每一列都加一个索引来实现。 《PostgreSQL 设计优化case

2000元阿里云代金券免费领取,2核4G云服务器仅664元/3年,新老用户都有优惠,立即抢购>>>


阿里云采购季(云主机223元/3年)活动入口:请点击进入>>>,


阿里云学生服务器(9.5元/月)购买入口:请点击进入>>>,

标签

PostgreSQL , 全列索引 , 大宽表 , 写测试 , insert on conflict , upsert , merge insert , adhoc query


背景

OLAP系统中,adhoc query非常场景(任意维度查询分析)。

adhoc query,通常来说,可以加GIN倒排,或者每一列都加一个索引来实现。

《PostgreSQL 设计优化case - 大宽表任意字段组合查询索引如何选择(btree, gin, rum) - (含单个索引列数超过32列的方法)》

加索引和不加索引,实时的upsert(有则更新,无则写入),性能差别如何呢?每列都有索引时是否可以支撑实时的合并写入呢?

测试

测试数据upsert合并写入

100个字段不含索引

do language plpgsql $$     
declare    
  sql text := 'create table tbl_test1( id int primary key,';    
begin    
  for i in 1..100 loop    
    sql := sql||' c'||i||' int default random()*10000000,';    
  end loop;    
  sql := rtrim(sql,',');    
  sql := sql || ')';    
  execute sql;    
end;    
$$;    
vi test1.sql    
\set id random(1,100000000)    
\set c1 random(1,20000)    
insert into tbl_test1 (id, c1) values (:id,:c1) on conflict(id) do update set c1=excluded.c1;    
pgbench -M prepared -n -r -P 1 -f ./test1.sql -c 32 -j 32 -T 120    
    
transaction type: ./test1.sql    
scaling factor: 1    
query mode: prepared    
number of clients: 32    
number of threads: 32    
duration: 120 s    
number of transactions actually processed: 14433112    
latency average = 0.266 ms    
latency stddev = 3.919 ms    
tps = 120275.287837 (including connections establishing)    
tps = 120303.256409 (excluding connections establishing)    
statement latencies in milliseconds:    
         0.001  \set id random(1,100000000)    
         0.000  \set c1 random(1,20000)    
         0.265  insert into tbl_test1 (id, c1) values (:id,:c1) on conflict(id) do update set c1=excluded.c1;    

100个字段含索引

do language plpgsql $$     
declare    
  sql text := 'create table tbl_test2( id int primary key,';    
begin    
  for i in 1..100 loop    
    sql := sql||' c'||i||' int default random()*10000000,';    
  end loop;    
  sql := rtrim(sql,',');    
  sql := sql || ')';    
  execute sql;    
  for i in 1..100 loop    
    execute 'create index idx_tbl_test2_'||i||' on tbl_test2 (c'||i||')';    
  end loop;    
end;    
$$;    
vi test2.sql    
\set id random(1,100000000)    
\set c1 random(1,20000)    
insert into tbl_test2 (id, c1) values (:id,:c1) on conflict(id) do update set c1=excluded.c1;    
pgbench -M prepared -n -r -P 1 -f ./test2.sql -c 32 -j 32 -T 120    
    
    

分区表大宽表,全索引测试

do language plpgsql $$     
declare    
  sql text := 'create table tbl_test3(sid int, id int,';    
begin    
  for i in 1..100 loop    
    sql := sql||' c'||i||' int default random()*10000000,';    
  end loop;    
  sql := rtrim(sql,',');    
  sql := sql || ') partition by list (sid)';    
  execute sql;    
end;    
$$;    

创建索引和唯一约束

alter table tbl_test3 add constraint uk_tbl_test3 unique (sid,id);

do language plpgsql $$
declare
begin
  for i in 1..100 loop
    execute 'create index idx_tbl_test3_'||i||' on tbl_test3 (c'||i||')';
  end loop;
end;
$$;

创建分区

do language plpgsql $$
declare
begin
  for sid in 1..60 loop
    execute format('create table tbl_test3_%s partition of tbl_test3 for values in (%s)', sid, sid);  
  end loop;
end;
$$;

合并写的语法

insert into tbl_test3 values (1,1) 
on conflict (sid,id) 
do update 
set c1=excluded.c1;

create or replace function upsert_tbl_test3 (v_sid int, v_id int) returns void as $$
declare
  
begin
  execute format('execute p_%s (%s, %s)', v_sid, v_sid, v_id);
  exception when others then 
    execute format('prepare p_%s (int,int) as insert into tbl_test3_%s(sid,id) values ($1,$2) on conflict (sid,id) do update set c1=excluded.c1', v_sid, v_sid);
    execute format('execute p_%s (%s, %s)', v_sid, v_sid, v_id);
end;
$$ language plpgsql strict;


vi test.sql
\set id random(1,2000000000)
\set sid random(1,60)
select upsert_tbl_test3(:sid,:id);

pgbench -M prepared -n -r -P 1 -f ./test.sql -c 28 -j 28 -T 120

优化

《PostgreSQL 分区表如何支持多列唯一 - 枚举、hash哈希 分区, 多列唯一, insert into on conflict, update, upsert, merge insert》

性能对比

场景 合并写入QPS
1个索引 120303
101个索引 19485

小结:

1、瓶颈分析,多个索引时,注意瓶颈在索引的RT上面,为提升性能,可以使用分区表,将RT打平,提升整体写入吞吐。

2、频繁更新可能导致索引膨胀,如果发现索引膨胀严重,在空闲期间需要对索引进行rebuild操作(是一样concurrently并发创建,不影响DML)。

《PostgreSQL CREATE INDEX CONCURRENTLY 的原理以及哪些操作可能堵塞索引的创建》

《PostgreSQL 垃圾回收原理以及如何预防膨胀 - How to prevent object bloat in PostgreSQL》

参考

《PostgreSQL 设计优化case - 大宽表任意字段组合查询索引如何选择(btree, gin, rum) - (含单个索引列数超过32列的方法)》

《PostgreSQL 快速给指定表每个字段创建索引 - 2 (近乎完美)》

《PostgreSQL 快速给指定表每个字段创建索引 - 1 (不够完美)》

 

免费领取阿里云RDS PostgreSQL实例、ECS虚拟机

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
5天前
|
关系型数据库 PostgreSQL
postgresql将没有关联关系的两张表合并成一张
【5月更文挑战第4天】postgresql将没有关联关系的两张表合并成一张
23 5
|
9月前
|
关系型数据库 分布式数据库 数据库
沉浸式学习PostgreSQL|PolarDB 2: 电商高并发秒杀业务、跨境电商高并发队列消费业务
业务场景介绍: 高并发秒杀业务 秒杀业务在电商中最为常见, 可以抽象成热点记录(行)的高并发更新. 而通常在数据库中最细粒度的锁是行锁, 所以热门商品将会被大量会话涌入, 出现锁等待, 甚至把数据库的会话占满, 导致其他请求无法获得连接产生业务故障. 业务场景介绍: 高并发队列消费业务 在跨境电商业务中可能涉及这样的场景, 由于有上下游产业链的存在, 1、用户下单后, 上下游厂商会在自己系统中生成一笔订单记录并反馈给对方, 2、在收到反馈订单后, 本地会先缓存反馈的订单记录队列, 3、然后后台再从缓存取出订单并进行处理.
332 1
|
6月前
|
关系型数据库 PostgreSQL
postgresql insert into插入记录时使用select子查询
postgresql insert into插入记录时使用select子查询
|
9月前
|
关系型数据库 测试技术 分布式数据库
PolarDB | PostgreSQL 高并发队列处理业务的数据库性能优化实践
在电商业务中可能涉及这样的场景, 由于有上下游关系的存在, 1、用户下单后, 上下游厂商会在自己系统中生成一笔订单记录并反馈给对方, 2、在收到反馈订单后, 本地会先缓存反馈的订单记录队列, 3、然后后台再从缓存取出订单并进行处理. 如果是高并发的处理, 因为大家都按一个顺序获取, 容易产生热点, 可能遇到取出队列遇到锁冲突瓶颈、IO扫描浪费、CPU计算浪费的瓶颈. 以及在清除已处理订单后, 索引版本未及时清理导致的回表版本判断带来的IO浪费和CPU运算浪费瓶颈等. 本文将给出“队列处理业务的数据库性能优化”优化方法和demo演示. 性能提升10到20倍.
613 4
|
12月前
|
SQL 缓存 数据管理
开源分布式数据库PolarDB-X源码解读——PolarDB-X源码解读(五):DML之Insert流程.
开源分布式数据库PolarDB-X源码解读——PolarDB-X源码解读(五):DML之Insert流程.
630 0
|
搜索推荐 关系型数据库 数据库
《阿里云RDS PostgreSQL实践课 2 实时用户画像数据库实践》电子版地址
阿里云RDS PostgreSQL实践课 2 实时用户画像数据库实践
114 0
《阿里云RDS PostgreSQL实践课 2 实时用户画像数据库实践》电子版地址
|
关系型数据库 PostgreSQL
PostgreSQL 合并字符串函数CONCAT(s1, s2, ...)、CONCAT_WS(x, s1, s2, ...)
PostgreSQL 合并字符串函数CONCAT(s1, s2, ...)、CONCAT_WS(x, s1, s2, ...)
1438 0
|
关系型数据库 PostgreSQL
PostgreSQL INSERT INTO 语句
PostgreSQL INSERT INTO 语句
290 0
|
SQL 存储 关系型数据库
索引设计—高并发场景微服务实战(六)
你好,我是程序员Alan,很高兴遇见你. 我在上一篇文章《 表结构设计—高并发场景微服务实战(五)》中,详细的写了如何选择合适的类型创建一张表,但表结构设计只是设计数据库最初的环节之一,我们还缺少数据库设计中最为重要的一个环节——索引设计,只有正确设计索引,业务才能达到上线的初步标准。 索引如果展开来讲有很多需要关注的地方,例如索引设计、业务应用与调优等等,本篇文章我会重点讲一下索引设计相关知识
124 0
索引设计—高并发场景微服务实战(六)
|
SQL AliSQL 数据库连接
PolarDB-X 源码解读系列:DML 之 INSERT IGNORE 流程
本文将进一步介绍 PolarDB-X 中 INSERT IGNORE 的执行流程,其根据插入的表是否有 GSI 也有所变化。
PolarDB-X 源码解读系列:DML 之 INSERT IGNORE 流程
http://www.vxiaotou.com