advisory lock 实现高并发非堵塞式 业务锁

标签

PostgreSQL , advisory lock , 锁


背景

某些业务会利用数据库来作为一种可靠的锁,例如任务调度系统,或者其他需要可靠的锁机制的系统。

通常他们可能会使用数据库的一条记录来实现锁的SLOT和状态信息。

例如

create table lock_test (
  tid int primary key,   -- 任务ID
  state int default 1,   -- 任务状态,1表示初始状态,-1表示正在处理, 0表示处理结束
  retry int default -1,   -- 重试次数
  info text,   -- 其他信息
  crt_time timestamp  -- 时间
);

任务处理系统到数据库获取任务

例如

update lock_test set state=-1 , retry=retry+1 where tid=? and state=1;

处理失败

update lock_test set state=1 where tid=? and state=-1;

处理成功

update lock_test set state=0 where tid=? and state=-1;

当多个客户端并行获得同一个任务时,就会引发冲突,导致等待(虽然等待时间可能不长,但是在大型任务调度系统中,一点点的等待都无法忍受)。

如何解决这个冲突等待呢?

advisory lock登场,实际上在秒杀业务中我们也看到了它的踪影。

《PostgreSQL 使用advisory lock实现行级读写堵塞》

《PostgreSQL 无缝自增ID的实现 - by advisory lock》

《PostgreSQL 使用advisory lock或skip locked消除行锁冲突, 提高几十倍并发更新效率》

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

advisory lock 实现高并发非堵塞式 业务锁

事务级或会话级,根据业务形态选择。

                                        List of functions
   Schema   |               Name               | Result data type | Argument data types |  Type
------------+----------------------------------+------------------+---------------------+--------
 pg_catalog | pg_try_advisory_lock             | boolean          | bigint              | normal
 pg_catalog | pg_try_advisory_xact_lock        | boolean          | bigint              | normal

SQL改造如下

开始处理任务

update lock_test set state=-1 , retry=retry+1 where tid=? and state=1 and pg_try_advisory_xact_lock(?) returning *;

处理失败

update lock_test set state=1 where tid=? and state=-1 and pg_try_advisory_xact_lock(?);

处理成功

update lock_test set state=0 where tid=? and state=-1 and pg_try_advisory_xact_lock(?);

性能压测对比

为了体现冲突的问题,我们使用一条记录来表示一个任务,大家都来抢一个任务的极端场景。

create table lock_test (
  tid int primary key,   -- 任务ID
  state int default 1,   -- 任务状态,1表示初始状态,-1表示正在处理, 0表示处理结束
  retry int default -1,   -- 重试次数
  info text,   -- 其他信息
  crt_time timestamp  -- 时间
);  

insert into lock_test values (1, 1, -1, 'test', now());

1、传统模式压测

vi test1.sql  

update lock_test set state=-1 , retry=retry+1 where tid=1 and state=1;
update lock_test set state=1 where tid=1 and state=-1;  

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

query mode: prepared
number of clients: 64
number of threads: 64
duration: 120 s
number of transactions actually processed: 966106
latency average = 7.940 ms
latency stddev = 6.840 ms
tps = 8050.081170 (including connections establishing)
tps = 8054.812052 (excluding connections establishing)
script statistics:
 - statement latencies in milliseconds:
         3.978  update lock_test set state=-1 , retry=retry+1 where tid=1 and state=1;
         3.962  update lock_test set state=1 where tid=1 and state=-1;

2、advisory lock模式压测

vi test2.sql  

update lock_test set state=-1 , retry=retry+1 where tid=1 and state=1 and pg_try_advisory_xact_lock(1) returning *;
update lock_test set state=1 where tid=1 and state=-1 and pg_try_advisory_xact_lock(1);  

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

query mode: prepared
number of clients: 64
number of threads: 64
duration: 120 s
number of transactions actually processed: 23984594
latency average = 0.320 ms
latency stddev = 0.274 ms
tps = 199855.983575 (including connections establishing)
tps = 199962.502494 (excluding connections establishing)
script statistics:
 - statement latencies in milliseconds:
         0.163  update lock_test set state=-1 , retry=retry+1 where tid=1 and state=1 and pg_try_advisory_xact_lock(1) returning *;
         0.156  update lock_test set state=1 where tid=1 and state=-1 and pg_try_advisory_xact_lock(1);

8000 TPS提升到20万 TPS。开不开心、意不意外。

小结

1、使用advisory lock时,需要注意一点,因为它是库级别的轻量级锁,所以对于不同的业务(无需相互堵塞的业务),建议设计不同的advisory lock的ID空间,例如A业务的LOCK空间是1-1000000, B业务的LOCK空间是1000001-2000000的空间。诸如此类等等。

2、update, insert, delete都带returning语法,可以返回NEW, OLD value。

3、advisory 的其他应用:

《PostgreSQL 使用advisory lock实现行级读写堵塞》

《PostgreSQL 无缝自增ID的实现 - by advisory lock》

《PostgreSQL 使用advisory lock或skip locked消除行锁冲突, 提高几十倍并发更新效率》

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

4、advisory lock的级别分事务级和会话级,根据业务的需求进行选择。

时间: 2024-11-01 11:12:58

advisory lock 实现高并发非堵塞式 业务锁的相关文章

PostgreSQL 使用advisory lock或skip locked消除行锁冲突, 提高几十倍并发更新效率

PostgreSQL 使用advisory lock或skip locked消除行锁冲突, 提高几十倍并发更新效率 作者 digoal 日期 2016-10-18 标签 PostgreSQL , advisory lock , 高并发更新 背景 通常在数据库中最小粒度的锁是行锁,当一个事务正在更新某条记录时,另一个事务如果要更新同一条记录(或者申请这一条记录的锁),则必须等待锁释放. 通常持锁的时间需要保持到事务结束,也就是说,如果一个长事务持有了某条记录的锁,其他会话要持有这条记录的锁,可能要

电商那些年,我摸爬打滚出的高并发架构实战精髓

一.关于高并发   高并发是指在同一个时间点,有很多用户同时访问URL地址,比如:淘宝的双11.双12,就会产生高并发.又如贴吧的爆吧,就是恶意的高并发请求,也就是DDOS攻击,再屌丝点的说法就像玩LOL被ADC暴击了一样,那伤害你懂的.   1 高并发会来带的后果  服务端:导致站点服务器/DB服务器资源被占满崩溃,数据的存储和更新结果和理想的设计是不一样的,比如:出现重复的数据记录,多次添加了用户积分等. 用户角度:尼玛,这么卡,老子来参加活动的,刷新了还是这样,垃圾网站,再也不来了! 我的

【转】聊聊java高并发系统之异步非阻塞

在做电商系统时,流量入口如首页.活动页.商品详情页等系统承载了网站的大部分流量,而这些系统的主要职责包括聚合数据拼装模板.热点统计.缓存.下游功能降级开关.托底数据等等.其中聚合数据需要调用其它多个系统服务获取数据.拼装数据/模板然后返回给前端,聚合数据来源主要有依赖系统/服务.缓存.数据库等:而系统之间的调用可以通过如http接口调用(如HttpClient).SOA服务调用(如dubbo.thrift)等等.   在Java中,如使用Tomcat,一个请求会分配一个线程进行请求处理,该线程负

优秀开源项目之三:高性能、高并发、高扩展性和可读性的网络服务器架构State Threads

译文在后面. State Threads for Internet Applications Introduction State Threads is an application library which provides a foundation for writing fast and highly scalable Internet Applications on UNIX-like platforms. It combines the simplicity of the multi

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

标签 PostgreSQL , HTAP , OLTP , OLAP , 场景与性能测试 背景 PostgreSQL是一个历史悠久的数据库,历史可以追溯到1973年,最早由2014计算机图灵奖得主,关系数据库的鼻祖Michael_Stonebraker 操刀设计,PostgreSQL具备与Oracle类似的功能.性能.架构以及稳定性. PostgreSQL社区的贡献者众多,来自全球各个行业,历经数年,PostgreSQL 每年发布一个大版本,以持久的生命力和稳定性著称. 2017年10月,Pos

Java 高并发九:锁的优化和注意事项详解_java

摘要 本系列基于炼数成金课程,为了更好的学习,做了系列的记录. 本文主要介绍: 1. 锁优化的思路和方法 2. 虚拟机内的锁优化 3. 一个错误使用锁的案例 4. ThreadLocal及其源码分析 1. 锁优化的思路和方法 在[高并发Java 一] 前言中有提到并发的级别. 一旦用到锁,就说明这是阻塞式的,所以在并发度上一般来说都会比无锁的情况低一点. 这里提到的锁优化,是指在阻塞式的情况下,如何让性能不要变得太差.但是再怎么优化,一般来说性能都会比无锁的情况差一点. 这里要注意的是,在[高并

Java 高并发八:NIO和AIO详解_java

IO感觉上和多线程并没有多大关系,但是NIO改变了线程在应用层面使用的方式,也解决了一些实际的困难.而AIO是异步IO和前面的系列也有点关系.在此,为了学习和记录,也写一篇文章来介绍NIO和AIO. 1. 什么是NIO NIO是New I/O的简称,与旧式的基于流的I/O方法相对,从名字看,它表示新的一套Java I/O标 准.它是在Java 1.4中被纳入到JDK中的,并具有以下特性: NIO是基于块(Block)的,它以块为基本单位处理数据 (硬盘上存储的单位也是按Block来存储,这样性能

断网故障时Mtop触发tomcat高并发场景下的BUG排查和修复(已被apache采纳)

该文章来自阿里巴巴技术协会(ATA)精选集 目录 现象 NIO模式背景介绍 排查过程 结合业务场景解释问题产生的原因 进一步的发现 解决办法 向Apache社区的反馈 总结 现象 mtop的机器,环境为Ali-Tomcat 7.0.54.2,连接器采用的是NIO模式,在高流量(约1000 qps)的情况下,在Tomcat的启动后一段时间内,抛出ConcurrentModificationException,然后再过一段时间后,Tomcat无法再接受新的请求. 异常堆栈如下: Exception

java-并发-ConcurrentHashMap高并发机制-jdk1.6

ConcurrentHashMap 是 util.concurrent 包的重要成员.本文将结合 Java 内存模型,分析 JDK 源代码,探索 ConcurrentHashMap 高并发的具体实现机制. 由于 ConcurrentHashMap 的源代码实现依赖于 Java 内存模型,所以阅读本文需要读者了解 Java 内存模型.同时,ConcurrentHashMap 的源代码会涉及到散列算法和链表数据结构,所以,读者需要对散列算法和基于链表的数据结构有所了解. 回页首 Java 内存模型