MySQL借助DB实现分布式锁思路示例代码详解
文章主要给大家介绍了关于MySQL借助DB实现分布式锁思路的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用MySQL具有一定的参考学习价值,需要的朋友们下面来一起学...
文章主要给大家介绍了关于MySQL借助DB实现分布式锁思路的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用MySQL具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧。
前言
无论是单机锁还是分布式锁,原理都是基于共享的数据,判断当前操作的行为。对于单机则是共享RAM内存,对于集群则可以借助Redis,ZK,DB等第三方组件来实现。Redis,ZK对分布式锁提供了很好的支持,基本上开箱即用,然而这些组件本身要高可用,系统也需要强依赖这些组件,额外增加了不少成本。DB对于系统来说本身就默认为高可用组件,针对一些低频的业务使用DB实现分布式锁也是一个不错的解决方案,比如控制多机器下定时任务的起调,针对审批回调处理等,本文将给出DB实现分布式锁的一些场景以及解决方案,希望对你启发。
表设计
首先要明确DB在系统中仍然需要认为是最脆弱的一环,因此在设计时需要考虑压力问题,即能应用实现的逻辑就不要放到DB上实现,也就是尽量少使用DB提供的锁能力,如果是高并发业务则要避免使用DB锁,换成Redis等缓存锁更加有效。如清单1所示,该表中唯一的约束为lock_name,timestamp,version三者组合主键,下文会利用这三者实现悲观锁,乐观锁等业务场景。
清单1: 分布式锁表结构
CREATE TABLE `lock` (
`lock_name` varchar(32) NOT NULL DEFAULT '' COMMENT '锁名称',
`resource` bigint(20) NOT NULL COMMENT '业务主键',
`version` int(5) NOT NULL COMMENT '版本',
`gmt_create` datetime NOT NULL COMMENT '生成时间',
PRIMARY KEY (`lock_name`,`resource`,`version`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
观锁实现
对于悲观锁业务中常见的操作有以下两种:
针对A:
A场景当一台机器获取到锁后,其他机器处于排队状态,锁释放后其他机器才能够继续下去,这种应用层面解决是相当麻烦,因此一般使用DB提供的行锁能力,即select xxx from xxx for update。A场景一般都和业务强关联,比如库存增减,使用业务对象作为行锁即可。需要注意的是,该方案本质上锁压力还是在数据库上,当阻塞住的线程过多,且操作耗时,最后会出现大量锁超时现象。
针对B:
针对B场景(tryLock)举个具体业务,在集群下每台机器都有定时任务,但是业务上要求同一时刻只能有一台能正常调度。
解决思路是利用唯一主键约束,插入一条针对TaskA的记录,版本则默认为1,插入成功的算获取到锁,继续执行业务操作。这种方案当机器挂掉就会出现死锁,因此还需要有一个定时任务,定时清理已经过期的锁,清理维度可以根据lock_name设置不同时间清理策略。
定时任务清理策略会额外带来复杂度,假设机器A获取到了锁,但由于CPU资源紧张,导致处理变慢,此时锁被定时任务释放,因此机器B也会获取到锁,那么此时就出现同一时刻两台机器同时持有锁的现象,解决思路:把超时时间设置为远大于业务处理时间,或者增加版本机制改成乐观锁。
insert into lock set lock_name='TaskA' , resource='锁住的业务',version=1,gmt_create=now()
success: 获取到锁
failed:放弃操作
释放锁
乐观锁实现
针对乐观锁场景,举个具体业务,在后台系统中经常使用大json扩展字段存储业务属性,在涉及部分更新时,需要先查询出来,合并数据,写入到DB,这个过程中如果存在并发,则很容易造成数据丢失,因此需要使用锁来保证数据一致性,相应操作如下所示,针对乐观锁,不存在死锁,因此这里直接存放业务id字段,保证每一个业务id有一条对应的记录,并且不需要对应的定时器清除
。
select * from lock where lock_name='业务名称', resource='业务id';
不存在: insert into lock set lock_name='业务名称', resource='业务id' , version=1;
获取版本: version
业务操作: 取数据,合并数据,写回数据
写回到DB: update lock set version=version+1 where lock_name='业务名称' and resource='业务id' and version= #{version};
写回成功: 操作成功
写回失败: 回滚事务,从头操作
分布式锁的原理实际上很容易理解,难的是如何在具体业务场景上选择最合适的方案。无论是哪一种锁方案都是与业务密切关联,总之没有完美的分布式锁方案,只有最适合当前业务的锁方案。乐观锁写入失败会回滚整个事务,因此如果写入冲突很频繁的场景不适合使用乐观锁,大量的事务回滚会给DB巨大压力,最终影响到具体业务系统。
总结
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,
-
MySQL多版本并发控制MVCC的实现示例代码介绍
主要介绍了mysql多版本并发控制MVCC的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。事务隔...
-
mysql group_concat 实现把分组字段写成一行的方法
文章主要介绍了mysql group_concat实现把分组字段写成一行的方法,结合实例形式分析了group_concat函数的功能、查询用法及相关操作技巧,需要的朋友可以参考下。本文实例讲述...
-
mysql数据库实现多表关联统计、子查询统计示例
文章主要介绍了mysql实现多表关联统计(子查询统计),结合具体案例形式分析了mysql多表关联统计的原理、实现方法及相关操作注意事项,需要的朋友可以参考下。本文实例讲述了my...
-
MySQL InnoDB数据库如何保证事务特性示例详解
文章主要给大家介绍了关于MySQL InnoDB如何保证事务特性的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用MySQL具有一定的参考学习价值,需要的朋友们下面来一起...
-
数据库 MySQL8.0+常用命令及操作命令详解
文章主要介绍了MySQL8.0+常用命令及操作命令,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下。开启远程访问通过以下命令开启root用户远程访问权限:CREATE USER 'root'...
-
Mysql8.0.17数据库安装图文教程
本文通过图文并茂的形式给大家介绍了Mysql8.0.17安装,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下...
-
SQL Server 2000/2005/2008删除或压缩数据库日志的方法
最近win2008 r2的服务器比较卡,打开服务器显示也特别慢,sqlserver业务费正常执行,服务器桌面操作也比较卡,经过多方研究发现原来是sqlserver日志文件已经达到了84G导致,这里就为大家分享一下解决方法,需要的朋友可以参考一下...
-
SQL Server数据库查看login所授予的具体权限问题
在SQL Server数据库中如何查看一个登录名(login)的具体权限呢,下面脚本之家小编给大家带来了SQL Server查看login所授予的具体权限问题,感兴趣的朋友一起看看吧...
-
SQL Server数据库怎么找出一个表包含的页信息(Page)
文章主要给大家介绍了关于SQL Server是如何找出一个表包含的页信息(Page)的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用SQL Server具有一定的参考学习价值,...
-
Windows server 2016服务器上安装oracle数据库的教程图解
文章主要介绍了Windows server 2016 安装oracle的教程图解,本文图文并茂给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下 1.安装oracle Oracle的安...