分布式锁之数据库锁

本文介绍了一种基于数据库的分布式锁实现方案,详细解释了其实现原理,并探讨了其优缺点。该方案通过在数据库中创建资源锁表并利用唯一索引来确保同一时刻只有一个节点能够获取特定资源。

在分布式环境下经常会出现这样的需求,多个服务器节点调用远程服务器的某项资源,但是这样的资源在同一时间点只允许一个服务器节点使用,类似于这样机器与机器之间的并发无法通过传统java并发API来解决.于是便有了分布式锁

数据库锁是并发锁的一种实现

分布式锁需要满足以下两个条件

  1. 在分布式环境下,在同一时间只能被一台机器的一个线程执行
  2. 为了避免死锁,分布式锁是一把可重入锁
  3. 锁的获取和释放必须高性能
CREATE TABLE `resource_lock` (
  `id` int(11) NOT NULL AUTO_INCREMENT
  `resource_name` varchar(64) NOT NULL ,
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '保存数据时间,自动生成',
  `holder_info` varchar(64) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uidx_resource` (`resource_name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT '资源锁表';

锁表的关键点在于唯一索引 UNIQUE KEY,这一条件保证了在任何时候每个资源只能有一个主机能够获取该资源.而holder_info记录了获取资源的是谁,这个字段使分布式锁支持可重入性,从而避免了死锁

如果我们锁住某个资源资源的时候,我们首先获取资源,获取资源的方法是在该表中添加一个资源,如果添加成功,则获取资源成功,反之失败

这个分布式锁仍然存在如下问题:
1.数据库必须是集群状态,若是单点数据库,一旦数据库瘫痪,整个系统都将处于不可用状态
2.如果某台主机获取了该锁,但是因为意外情况宕机,导致该资源一直处于占用状态.所以需要在应用层加上定时task,根据updateTime清除到时但是没有被释放的锁

如果我们需要该锁支持阻塞,也就是当节点没有获取到锁的时候就等待,直到获取锁,我们在插入数据(也就是获取锁)失败的情况下,使用mysql悲观锁锁定该锁,mysql代码如下

-- 获取锁代码如下
begin;
select * from resource_lock where resource_name='resource' for update
-- 业务逻辑
dosomething()
-- 释放锁代码如下
commit;

该方法有几个注意点:

1.务必对resource_name添加索引,否则mysql将会锁全表,这会造成其它线程都无法申请锁
2.其它线程等待获取锁的时候阻塞时间是有限制的,超时后mysql会报如下的错误

 insert into resource_lock values(1,'resource',current_timestamp,'holder_info');

lock wait time可以自己更改mysql配置

3.我们要使用排它锁进行分布式锁的lock,如果一个排他锁不提交,会长时间占用数据库连接池连接,如果这样的连接变多,可能会造成其它线程获取mysql连接失败

基于数据库的分布式锁优缺点分析
优点:
1.直接借助数据库容易理解
2.数据库锁具有持久化特性
缺点
1.操作数据库会有性能开销
2.数据库阻塞锁的实现可能会造成占用大量数据库连接池连接导致其它线程无连接可用

Redis分布式锁是一种常见的并发控制机制,用于在分布式系统中解决多个请求并发访问资源时的互斥问题。当多个客户端同时尝试获取同一时,只有一个客户端能够成功获取,其他客户端则需要等待,直到被释放。这通常通过在Redis中设置一个过期时间的键来实现,例如使用`SETNX`命令设置一个唯一的标识,如果该键不存在则设置并返回true,表示获得数据库事务则是数据库操作的一种执行方式,它保证了对数据库的一组操作要么全部成功,要么全部回滚,从而保持数据的一致性。在一个事务中,所有相关的操作被视为一个原子操作,这有助于避免并发修改导致的数据不一致问题。 当Redis分布式锁数据库事务同时使用时,通常会在以下几个场景中: 1. **分布式事务管理**:如果系统需要支持跨数据库的操作,可以先尝试获取Redis,成功后开始一个数据库事务。只有在事务内完成所有操作并提交时,才释放。如果事务失败(如部分操作出错),也会被自动释放,防止数据不一致。 2. **限流与熔断**:在高并发场景下,可能需要使用分布式锁来实现限流或熔断机制。先获取,如果获取成功,再检查是否超过阈值,如果在事务范围内完成这些操作后,释放。 3. **临时存储**:在需要临时存储数据,等待其他服务处理完成后进行持久化的情况,可以先在Redis中获取,然后在事务中进行数据写入,事务完成后,如果一切正常,释放
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值