10年架构师教你分布式锁的几种实现方式!

10年架构师教你分布式锁的几种实现方式!

锁机制是为了解决资源的并发操作带来的数据不一致问题:

单进程情况下:即多线程并发访问

在一个JVM进程中,JDK的API提供了丰富的解决方案,比如synchronized关键字(被动同步锁标记)、lock(主动加锁)、volatile(可见性)、concurrent工具包(原子类)

多进程情况下:分布式系统中的并发访问

在一个高并发业务的服务集群中,大量的业务请求被分流到集群中不同的进程中进行处理,会出现运行在多个JVM中的相同业务逻辑对同一业务对象进行访问和操作。这种情况下,无法使用JDK提供的并发api来保证数据的一致性。

10年架构师教你分布式锁的几种实现方式!

分布式锁就是将位于不同进程中的数据状态集中在一个统一的进程(公共资源池)中进行协调和管理。保证不同进程对同一个数据的操作互斥,保证对资源的顺序访问

关键特性

互斥

在任意时刻,只有一个客户端能持有锁

超时过期

锁状态添加一个过去时间,防止锁的持有进程异常退出无法解锁导致的死锁

可重入

对于已经获取资源锁状态的进程,还可以对该资源加锁,应锁状态中对应的一个计数器加一;解锁时,计数器减一,计数器归零时,锁状态解除

阻塞

当获取锁失败时,执行进入阻塞状态,直到获取锁再执行后续逻辑

数据库表乐观锁

加锁机制

原理

数据库中创建一张表来存放公共资源的状态,表的字段有:

主键

公共资源的唯一标识 - 唯一约束

创建时间

加锁时,向表中插入一条数据insert into lock_table ("my_lock_1","2018-12-08 15:15:28");

其他线程要获得my_lock_1锁的操作权限时,执行插入操作会抛出唯一约束异常,从而加锁失败。

解锁时只用删除之前插入的记录

缺点

锁的超时性、重入性、阻塞性,都需要在程序中解决

执行效率低,不适合高并发场景,尴尬的是分布式锁就是用在高并发的场景

超时解锁

当加锁的进程加锁之后抛出异常,没有执行解锁操作,就形成了死锁。需要建一个定时任务,根据创建时间将超时的锁记录删除

阻塞

只能在程序中do-while的方式不停的轮询尝试上锁,知道上锁成功,再进行后面的逻辑

重入

需要添加新的字段:获取锁线程的主机和进程信息以及上锁计数

同时还需要修改上锁和解锁逻辑:

上锁:第一次上锁insert,第二次上锁update计数

解锁:先检查计数,大于1时,计数减一;等于1时,删除记录

10年架构师教你分布式锁的几种实现方式!

Redis实现分布式锁

加锁机制

将锁状态信息以键值对的形式保存储

键:公共资源的唯一标识 lock-key

值:选择比较灵活

当前时间戳+过期时间:替代设置过期时间(redis维护过期),程序主动维护过期

加锁操作id:锁必须由加锁者解锁

map对象:存放键值对 加锁操作id-计数器 实现重入锁

使用setnx命令上锁,使用get或getset命令尝试获得锁

想要学习Dubbo框架、zookeper基本原理、redis分布式缓存、JVM性能优化,Nginx+apache+Tomcat集群部署、大数据hadoop,Hbase实时计算spark、storm、数据分析分词和权重等核心技术;需要的可以关注之后私信哈,记得要点赞转发噢!!!

相关推荐