并发控制的问题分析和解决
2022-09-02 09:09:49 0 举报
AI智能生成
并发控制的问题分析和解决、案例分析、解决方案
作者其他创作
大纲/内容
设计思路
从几个熟悉的问题说起
实例
本质
问题分析
场景分类
数据同步
数据同步(不依赖原数据)
信息同步(业务数据各级缓存同步)
业务数据在Redis、ES、mongo中的各级缓存
简单业务状态的同步(状态流转无规则)
商品上下架、可售状态同步
活动状态同步
其他业务数据同...
业务结果依赖多个外部输入元素的计算结果
假设场景
数据同步(依赖原数据)
业务数据的自我更新
库存更新
更新其他业务结果
商品可售状态的维护
单品券预估面额
一个更加有趣的例子
一个太有趣的例子
竞争逻辑资源
业务状态流转
活动状态
活动单状态流转
单据审核
资源竞争
活动、运营位等的生效时间
核心关注的功能点
更新操作的准确性
目标值/条件的准确性
核心关注的场景
同样逻辑的并发执行问题
不同逻辑对共享资源的并发执行问题
Remark
QA:当前项目我写的是一个B端管理系统,这部分功能是给运营同学用的,并且已知所有权限只有一名业务同学在用,并发可能性极低,功能模块使用频率也很低,这时候还需要做并发控制么?如果做了功能看起来是否过重?
只谈是否有概率、不谈概率大小。即有概率则为有风险,不论概率大小。
锁
基本定位
锁的定位
实现对于共享资源的互斥(局部逻辑互斥)访问,以保证对于共享资源的并发操作结果的准确性。
分布式锁的概念
在集群或者分布式部署时,为了保证多个应用节点能够共享同一个锁,从而达到锁的设计初衷。
QA:
无论共享资源的具体存储形式是什么,只要涉及更新,在数据层都是互斥执行的,即one-by-one的执行,即数据更新本身即是互斥的,那这里的互斥是指什么?
无论共享资源的具体存储形式是什么,只要涉及更新,在数据层都是互斥执行的,即one-by-one的执行,即数据更新本身即是互斥的,那这里的互斥是指什么?
锁的分类
主流分类
锁的模式:乐观/悲观
【核心】
悲观:假定多数场景下会出现资源冲突的情况;每次执行数据操作前都进行上锁以控制并发。
乐观:假定多数场景下不会出现资源冲突的情况;即在执行数据操作前不上锁,只有在执行数据操作是校验数据状态的有效前提来判断本次数据操作是否有效。
悲观:假定多数场景下会出现资源冲突的情况;每次执行数据操作前都进行上锁以控制并发。
乐观:假定多数场景下不会出现资源冲突的情况;即在执行数据操作前不上锁,只有在执行数据操作是校验数据状态的有效前提来判断本次数据操作是否有效。
竞争策略:公平/非公平
指对于锁的竞争的策略,如是否支持先到先得甚至是否有偏重等规则。
但鉴于实现成本和满足业务需求的前提,非特殊明确要求之外,对该环节敏感度较低。
但鉴于实现成本和满足业务需求的前提,非特殊明确要求之外,对该环节敏感度较低。
锁属性:独享/共享
共享锁:满足并发读同时,不允许其他事务对当前数据进行修改操作,从而避免”不可重读”的问题的出现。
写锁:主要是为了解决在修改数据时,不允许其他事务对当前数据进行修改和读取操作,从而可以有效避免”脏读”问题的产生。
写锁:主要是为了解决在修改数据时,不允许其他事务对当前数据进行修改和读取操作,从而可以有效避免”脏读”问题的产生。
该锁的分类更多的是业务数据的读写控制,在锁的使用过程中并不过多考虑该环节。
是否可重入
同一线程在重复上统一把锁时是否支持
是否自旋
当没竞争到锁时是否作为最终结果
其他
分段锁
锁粗化/锁消除
设计要求
功能要求
互斥:基本要求,即同一时间最多只能有一个客户端获取锁;
多模式:提供Hard锁&Soft(可重试锁)多种功能模式;
服务能力要求
容错:分布式锁所依赖的服务需要有容错性,保证服务的健壮性;
吞吐量:高性能、不额外影响业务链路吞吐量
基本元素和设计考虑
设计元素
锁的模式:乐观/悲观
这个分类是所有分类中的最高优先级,即我们在进行设计锁时所需要优先进行选择的环节
设计考虑
确认资源竞争方,评估冲突频率。在保证最大吞吐量的策略下进行模式的选择。
需考虑共享资源的数据存储形式和控制形式
另外在并发量不大或者不敏感的场景下,可以放低对于吞吐量的要求。
竞争策略:公平/非公平
是否续期
续期时间段
探测时间间隔
功能元素
锁的粒度
通过识别共享/冲突资源,确定锁的最小粒度,可以是全局统一的固定值、也可以是具有局部变量的的形式。
设计考虑
如果单纯满足并发需求的角度来说,锁的粒度是向下兼容的。即“没什么是一个全局锁”解决不了的。
但,锁是一个双刃剑,约束了服务的吞吐量。以最小粒度进行表示是最优解,避免无效的竞争和阻塞。
锁持有时间
即线程获取锁之后对于当前锁的持有时间,当持有时间过期后则自动解锁或通过续期机制延长持有时间。
设计考虑
需满足正常业务处理的耗时,避免提前丧失对锁的控制,导致锁失效。
避免锁的持有时间过长,在异常无法解锁的情况会直接阻塞了业务。
是否自旋
自旋次数
间隔时间
设计考虑
依赖于具体业务场景而定,判断是否需要自旋。
特殊的,自旋次数和间隔时间的设定需要考虑整体等待时长。一般两个方向:(1)长间隔、少次数(2)短间隔、多次数,但整体探测时长一定要大于业务单次处理时长。
是否可重入
是否支持同一个线程重复获取同一把锁时,该场景更多的出现在复杂业务的嵌套。支持重入很大程度上能让锁的设计和功能的组合更加灵活。
设计考虑
在条件允许的情况下,尽可能提供重入功能。
是否续期
续期时间段
探测时间间隔
设计考虑
续期功能可以保证在业务功能出现处理时间过长的情况能够自动帮助业务线程延长锁的持有,避免锁时效。
但是,要结合业务场景需求作出选择,当某个线程出现满查询的情况下,选择A:保证锁的价值,其他线程等待。选择B:放弃当前锁,保证所有线程的平均响应时间。
设计理念
事务
事务的设计很大程度依赖于业务诉求,并且也会极大影响并发控制的设计方向,在整体设计阶段需明确各个业务数据之间的持久化事务关系。
可能是数据批量操作,也可能是跨表多表操作,实际业务可能更复杂,所以事务的设计需先行。
数据准确性
明确共享资源的各个竞争方/场景以及各自的访问形式(批量/单个),把问题也即目标设定明确。
性能/吞吐量
在完成基本的功能设计后,满足数据准确性的基本要求的基础上,需要全盘推演并发控制对于业务执行耗时的影响。
业务耗时的推演工作是我们在设计核心功能模块的必要工作,尤其是在设计并发控制时。
如果发现由于并发控制导致业务吞吐量造成较大影响不符合业务需要,优先审视锁的粒度定义。
最优解
多个正确的方案:整体方案涉及多个环节,也意味着我们可以通过组合得到N种可以满足需求整体方案。能满足所有业务需求的方案都是正确的方案。
寻找最优解:最优解是各环节解决方案的最优搭配,满足需求的同时最大化吞吐量,负载。
但最优解不一定是最终选择:(1)实现成本考虑。(2)结合研发环境考虑。
预防
死锁
工程相关
代码位置
自由主题
案例
常见方案
悲观锁
Redis相关
RedLock
Mysql
for update+主键&索引
乐观锁
Mysql
版本号
子主题
案例
CaseA:存在于概念上的共享资源,B端操作
【背景】B端管理系统运营同学准备“活动”数据列表,必要元素“活动开始时间”、“活动结束时间”。
【要求】活动的生效时间段不能重复。
【使用场景】制定单个运营同学使用。
【背景】B端管理系统运营同学准备“活动”数据列表,必要元素“活动开始时间”、“活动结束时间”。
【要求】活动的生效时间段不能重复。
【使用场景】制定单个运营同学使用。
设计步骤
锁的粒度
共享资源为同一的时间段。表现形式为概念上的。
锁的模式
确认资源竞争方,评估冲突频率。在保证最大吞吐量的策略下进行模式的选择。
共享资源的数据存储形式和控制形式
竞争策略
锁持有时间
即线程获取锁之后对于当前锁的持有时间,当持有时间过期后则自动解锁或通过续期机制延长持有时间。
是否可重入
是否自旋
根据
是否续期
探测时间间隔
方案A
insert into person_table (uid,pname,age)
select 2,"李四",20 from dual
where not exists
(select id from person_table where uid=2 )
select 2,"李四",20 from dual
where not exists
(select id from person_table where uid=2 )
方案B
子主题
CaseB: 具体数据形式的共享资源,B端操作
【背景】
【要求】
【使用场景】
【背景】
【要求】
【使用场景】
CaseC:具体数据形式的共享资源,B&C端操作
【背景】运营同学准备秒杀商品库存,同时可以在管理端动态调整库存。
【要求】C端下单对于库存扣减、运营调整库存结果的正确性。
【使用场景】
【背景】运营同学准备秒杀商品库存,同时可以在管理端动态调整库存。
【要求】C端下单对于库存扣减、运营调整库存结果的正确性。
【使用场景】
分布式锁的底层实现
0 条评论
下一页