事务并发解决方案

来源:互联网 发布:网络错误404是怎么办 编辑:程序博客网 时间:2024/06/05 01:54

设计目标

解决目前系统内部分业务使用 for update 锁单条或锁结果集合性能问题。

基本概念

for update:  数据库上锁关键字,在事务未提交之前其他需要更改相同记录的事务会排队等待。

设计方案

1.针对锁单条记录方案

1.数据库行锁机制 (悲观锁)

使用场景

1.订单状态变更场景,在状态变更SQL 语句后面加条件判断状态是否不等于更新目标状态,如果等于就代表已经更新过了。

例如: update table set status = 'fail' where status  !=  'fail'

2.库存加减场景, 在加减库SQL 语句采用在原字段递增或递减的方式

例如:update table set xxx = (xxx - 1) where xxx > 1   update table set xxx = (xxx - #{args}) where xxx > #{args}update table set xxx = (xxx + 1)

优缺点:

优点:相对于大事务重一开始就锁住(for update)需要的记录,在进行操作,到最后提交事务。 这种方式锁的成本低一些(只有在update时才会数据行锁)。

缺点:有局限性字段类型要支持加减,编码是需要明确知道流程走向和where条件值的判断。

 

2.基于数据版本号机制(乐观锁)

在需要锁的表加 version 字段,修改数据前先获取版本号,再执行修改语句判断版本号是否相同,如果不同则有其他任务修改过数据,此次修改失败可进行重试策略(定义一个重试次数超过重试次数还未竞争到锁提示系统繁忙)。

例如:select xxx, version from tableupdate set xxx = #{xxx} , version = (version+1) where version = #{version}

优缺点:

优点:采用版本号策略在高并发的情况下,可以避免行锁排队,(版本号过期where 条件匹配为0条)

缺点:并发情况下失败率较高,采用重试策略会增加数据查询量

3.分布式锁方案 (内存锁)

基于memcached, 或 redis 缓存服务加锁策略。

在操作数据库相关记录需要上锁时,针对此记录(key)在缓存服务里设置一个值并设置一定的失效时间(锁等待时长)。

如果设置成功代表拿到锁,此时进行业务想过操作,完毕或释放缓存服务里的锁。

如果设置失败,代表当前资源被占用,可重试去获取锁(定义获取锁重试次数)超过重试次数还未竞争到锁提示系统繁忙。

优缺点:

优点:将数据库的锁竞争压力转义到缓存服务器。

缺点:对于锁的key不好定义,且存在缓存系统宕机风险。

4.单机锁解决方案

单机使用JVM内存锁控制并发,并设置一定的锁超时

伪代码:

Lock lock = new ReentrantLock();for (int i = 0; i < 5; i++) {try {if (lock.tryLock(50, TimeUnit.MILLISECONDS)) {//TODO 处理逻辑 }} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}

 

优点:根据机器数来控制并发数

缺点:扩展机器带来并发数增高

 

2.针对锁结果集合的方案

参考方案 3.分布式锁方案 (内存锁) 

需要定义锁的key。

原创粉丝点击