PolarDB | PostgreSQL 高并发队列处理业务的数据库性能优化实践

简介: 在电商业务中可能涉及这样的场景, 由于有上下游关系的存在, 1、用户下单后, 上下游厂商会在自己系统中生成一笔订单记录并反馈给对方, 2、在收到反馈订单后, 本地会先缓存反馈的订单记录队列, 3、然后后台再从缓存取出订单并进行处理. 如果是高并发的处理, 因为大家都按一个顺序获取, 容易产生热点, 可能遇到取出队列遇到锁冲突瓶颈、IO扫描浪费、CPU计算浪费的瓶颈. 以及在清除已处理订单后, 索引版本未及时清理导致的回表版本判断带来的IO浪费和CPU运算浪费瓶颈等.本文将给出“队列处理业务的数据库性能优化”优化方法和demo演示. 性能提升10到20倍.

作者

digoal

日期

2023-08-05

标签

PostgreSQL , PolarDB , 队列 , 锁 , hash mod , advisory lock , cte , update limit , delete limit , vacuum , index , IO浪费 , CPU浪费


背景

在电商业务中可能涉及这样的场景, 由于有上下游关系的存在, 1、用户下单后, 上下游厂商会在自己系统中生成一笔订单记录并反馈给对方, 2、在收到反馈订单后, 本地会先缓存反馈的订单记录队列, 3、然后后台再从缓存取出订单并进行处理.

这个过程的核心流程: 高速写入队列、从队列按先后顺序提取并高速处理、从队列清除已处理订单记录.

如果是高并发的处理, 因为大家都按一个顺序获取, 容易产生热点, 可能遇到取出队列遇到锁冲突瓶颈、IO扫描浪费、CPU计算浪费的瓶颈. 以及在清除已处理订单后, 索引版本未及时清理导致的回表版本判断带来的IO浪费和CPU运算浪费瓶颈等.

  • 文末的《打车与宇宙大爆炸的关系》一文有相似问题和优化方法, 思路类似.

本文将给出“队列处理业务的数据库性能优化”优化方法和demo演示. 性能提升10到20倍.

想体验一下的同学, 也可以通过云起实验启动环境来进行体验, 这个实验室是永久免费的.

DEMO

1、测试环境

MacBook Pro (15-inch, 2018)  
2.2 GHz 六核Intel Core i7  
32 GB 2400 MHz DDR4  
PostgreSQL 15.1

因为是macos, 可能需要设置一下ulimit.

ulimit -n 1000000

2、上游写入订单处理队列表

create table t_order_q (  
  id serial8 primary key,   -- 自增主键  
  order_id uuid unique,     -- 上游传递过来的订单号  
  cts timestamp not null    -- 上游传递过来的订单创建时间   
);   
-- create index on t_order_q (cts); -- 如果按订单时间先后取出处理, 则需要创建时间字段索引.  也可以按自增主键顺序处理, 则不需要时间索引.

3、取出并处理后的订单状态表

create table t_order_u (  
  id serial8 primary key,   -- 自增主键  
  order_id uuid unique,     -- 上游传递过来的订单号  
  cts timestamp not null,    -- 上游传递过来的订单创建时间   
  uts timestamp not null,   -- 订单处理时间  
  status int not null       -- 订单处理状态标记   
);

4、写入100万条订单队列

insert into t_order_q (order_id, cts) select gen_random_uuid(), clock_timestamp() from generate_series(1,1000000);

5、写pgbench压测脚本, 从队列取出, 并且使用ad lock对队列ID加事务锁, 判断是否正在处理, 事务结束自动释放ad lock. ad lock也经常被用于秒杀场景泄压.

vi t.sql  
with tmp as   
  (delete from t_order_q where ctid = (select ctid from t_order_q where pg_try_advisory_xact_lock(id) order by id limit 1) returning order_id, cts)  
insert into t_order_u (order_id,cts,uts,status) select tmp.order_id, tmp.cts, now(), 1 from tmp;
begin;
select id as v_id from t_order_q where pg_try_advisory_xact_lock(id) order by id limit 1 \gset
with tmp as (delete from t_order_q where id = :v_id returning order_id, cts) 
  insert into t_order_u (order_id,cts,uts,status) select tmp.order_id, tmp.cts, now(), 1 from tmp;   
end;
或(sleep 模拟应用拿到需要处理的订单后的应用端操作增加的耗时.)
begin; 
select id as v_id from t_order_q where pg_try_advisory_xact_lock(id) order by id limit 1 \gset
\sleep 10ms
with tmp as (delete from t_order_q where id = :v_id returning order_id, cts) 
  insert into t_order_u (order_id,cts,uts,status) select tmp.order_id, tmp.cts, now(), 1 from tmp;   
end;

6、压测256个并发消耗队列, 平均每个连接处理3906个事务.

select 1000000/256.0;  
3906.2500000000000

7、压测结果

pgbench -M extended -f ./t.sql -n -r -P 1 -c 256 -j 2 -t 3906
transaction type: ./t.sql  
scaling factor: 1  
query mode: extended  
number of clients: 256  
number of threads: 2  
maximum number of tries: 1  
number of transactions per client: 3906  
number of transactions actually processed: 999936/999936  
number of failed transactions: 0 (0.000%)  
latency average = 8.111 ms  
latency stddev = 5.376 ms  
initial connection time = 429.698 ms  
tps = 25379.081141 (without initial connection time)  
statement latencies in milliseconds and failures:  
         8.114           0  with tmp as

未优化前的性能如何?

1、写pgbench压测脚本, 从队列取出, 并且使用ad lock对队列ID加事务锁, 判断是否正在处理, 事务结束自动释放ad lock. ad lock也经常被用于秒杀场景泄压.

vi t1.sql  
begin;  
select id as vid from t_order_q order by id for update limit 1 \gset  
with tmp as   
  (delete from t_order_q where id = :vid returning order_id, cts)  
insert into t_order_u (order_id,cts,uts,status) select tmp.order_id, tmp.cts, now(), 1 from tmp;   
end;

2、压测结果

pgbench -M extended -f ./t1.sql -n -r -P 1 -c 256 -j 2 -t 3906
TPS 约 1200.

增加了skip locked后, TPS也只能到2500左右. 降低并发后使用skip locked性能可提升到8K tps左右.

begin;  
select id as vid from t_order_q order by id for update skip locked limit 1 \gset  
with tmp as   
  (delete from t_order_q where id = :vid returning order_id, cts)  
insert into t_order_u (order_id,cts,uts,status) select tmp.order_id, tmp.cts, now(), 1 from tmp;   
end;

还有什么可以提升性能的点?

1、减少浪费的IO和cpu计算:

  • 在并发的情况下, order by id limit 1需要扫描若干行, 而不是1行, 因为可能有些ID已经被ad lock touch了, 浪费的pg_try_advisory_xact_lock() cpu ops计算次数约等于 n + n-1 + n-2 + ... + n-n, 浪费的IO约等于N.

优化方法:

  • 固定N个链接, 按ID hash mod 取不同的数据分片, 从而减少浪费的IO和cpu计算.
  • 或者将队列表拆分成几个分区表, 入库的时候 按id hash mode, 每个分区分配给不同的进程取数, 从而减少冲突和浪费的扫描提高并发.

2、提高index vacuum的频率, 减少因没有index version导致的垃圾数据判断带来的cpu和回表的IO浪费. 提升autovacuum_work_mem, 容纳下所有dead tuple ctid避免多次扫描index.

优化方法:

  • 配置参数autovacuum_naptime、autovacuum_work_mem(或者老版本 maintenance_work_mem)即可.

3、使用并行vacuum, 配置max_parallel_maintenance_workers.

4、配置vacuum使用prefetch blocks, 减少io delay带来的vacuum 比较久的问题. (适合 单次IO delay较高, 但是吞吐没有瓶颈的云盘)

5、一次取出多条, 批量处理.

6、使用IOPS较高, 单次IO delay较低的本地nvme SSD.

更多请参考末尾文章.

参考

《DB吐槽大会,第69期 - PG 不支持update | delete limit语法》

《在PostgreSQL中实现update | delete limit - CTID扫描实践 (高效阅后即焚)》

《PostgreSQL skip locked与CTE妙用 - 解决并发批量更新锁冲突带来的锁等待,提高处理吞吐》

《PostgreSQL SELECT 的高级用法(CTE, LATERAL, ORDINALITY, WINDOW, SKIP LOCKED, DISTINCT, GROUPING SETS, ...) - 珍藏级》

《PostgreSQL 秒杀4种方法 - 增加 批量流式加减库存 方法》

《HTAP数据库 PostgreSQL 场景与性能测试之 30 - (OLTP) 秒杀 - 高并发单点更新》

《聊一聊双十一背后的技术 - 不一样的秒杀技术, 裸秒》

《PostgreSQL 垃圾回收参数优化之 - maintenance_work_mem , autovacuum_work_mem》

《[直播]为什么打车和宇宙大爆炸有关?》

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍如何基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
5月前
|
存储 监控 NoSQL
140_异步推理:队列管理框架 - 使用Celery处理高并发请求的独特设计
在大型语言模型(LLM)部署的实际场景中,推理服务的并发处理能力直接影响用户体验和系统稳定性。随着LLM应用的普及,如何高效处理大量并发请求成为部署优化中的关键挑战。传统的同步请求处理方式在面对突发流量时容易导致系统过载,响应延迟增加,甚至服务崩溃。异步推理通过引入队列管理机制,能够有效缓冲请求峰值,平滑系统负载,提高资源利用率,从而为LLM服务提供更稳定、更高效的并发处理能力。
|
8月前
|
存储 关系型数据库 分布式数据库
喜报|阿里云PolarDB数据库(分布式版)荣获国内首台(套)产品奖项
阿里云PolarDB数据库管理软件(分布式版)荣获「2024年度国内首版次软件」称号,并跻身《2024年度浙江省首台(套)推广应用典型案例》。
|
9月前
|
关系型数据库 分布式数据库 数据库
再获殊荣,阿里云PolarDB数据库蝉联SIGMOD最佳论文奖
内存池化技术新突破,阿里云PolarDB蝉联SIGMOD最佳论文奖
|
6月前
|
Cloud Native 关系型数据库 MySQL
免费体验!高效实现自建 MySQL 数据库平滑迁移至 PolarDB-X
PolarDB-X 是阿里云推出的云原生分布式数据库,支持PB级存储扩展、高并发访问与数据强一致,助力企业实现MySQL平滑迁移。现已开放免费体验,点击即享高效、稳定的数据库升级方案。
免费体验!高效实现自建 MySQL 数据库平滑迁移至 PolarDB-X
|
6月前
|
关系型数据库 MySQL 分布式数据库
阿里云PolarDB云原生数据库收费价格:MySQL和PostgreSQL详细介绍
阿里云PolarDB兼容MySQL、PostgreSQL及Oracle语法,支持集中式与分布式架构。标准版2核4G年费1116元起,企业版最高性能达4核16G,支持HTAP与多级高可用,广泛应用于金融、政务、互联网等领域,TCO成本降低50%。
|
9月前
|
关系型数据库 MySQL 分布式数据库
Super MySQL|揭秘PolarDB全异步执行架构,高并发场景性能利器
阿里云瑶池旗下的云原生数据库PolarDB MySQL版设计了基于协程的全异步执行架构,实现鉴权、事务提交、锁等待等核心逻辑的异步化执行,这是业界首个真正意义上实现全异步执行架构的MySQL数据库产品,显著提升了PolarDB MySQL的高并发处理能力,其中通用写入性能提升超过70%,长尾延迟降低60%以上。
|
8月前
|
关系型数据库 分布式数据库 数据库
阿里云PolarDB数据库蝉联SIGMOD最佳论文奖
阿里云PolarDB凭借全球首创基于CXL Switch的分布式内存池技术,在SIGMOD 2025上荣获工业赛道“最佳论文奖”,连续两年蝉联该顶会最高奖项。其创新架构PolarCXLMem打破传统RDMA技术瓶颈,性能提升2.1倍,并已落地应用于内存池化场景,推动大模型推理与多模态存储发展,展现CXL Switch在高速互联中的巨大潜力。
阿里云PolarDB数据库蝉联SIGMOD最佳论文奖
|
9月前
|
Cloud Native 关系型数据库 分布式数据库
客户说|知乎基于阿里云PolarDB,实现最大数据库集群云原生升级
近日,知乎最大的风控业务数据库集群,基于阿里云瑶池数据库完成了云原生技术架构的升级。此次升级不仅显著提升了系统的高可用性和性能上限,还大幅降低了底层资源成本。
|
6月前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
|
存储 NoSQL Java
探索Java分布式锁:在高并发环境下的同步访问实现与优化
【6月更文挑战第30天】Java分布式锁在高并发下确保数据一致性,通过Redis的SETNX、ZooKeeper的临时节点、数据库操作等方式实现。优化策略包括锁超时重试、续期、公平性及性能提升,关键在于平衡同步与效率,适应大规模分布式系统的需求。
529 1

热门文章

最新文章

相关产品

  • 云原生数据库 PolarDB