1、前言
1.1、本地锁和分布式锁区别
锁我想对大家都不陌生,在我们刚学习 Java 的时候,肯定知道synchronized和Lock锁
;这两者都是本地锁。
何为本地锁呢?本地锁
就是该锁只针对当前节点有效,也就是当 node A 获取锁时,那么 node B 同样还可以获取锁,这种情况就是本地锁。
如果服务只部署了一个节点的话,用这种本地锁是没有问题的。
现现在很多系统为了抗高并发、高可用和高性能,会部署多节点(集群部署),那么此时如果还用本地锁的话就会出现问题,因此分布式锁
就诞生了。
分布式锁就是当有一个节点获取到锁后,其它节点是不可以获取锁的。
1.2、Redis 分布式锁和 Zookeeper 分布式锁区别
谈起分布式集群,就绕不开CAP理论
,也就是强一致性、可用性和分区容错性
。三者只能选其二,不可兼容。这里我就不具体分析其原因之类了,直接步入两把分布式锁区别。
Redis分布式锁:
它追求的高可用性和分区容错性。Redis 在追求高可用性时会在 Redis 写入主节点数据后,立即返回成功,不关心异步主节点同步从节点数据是否成功。Redis 是基于内存的,性能极高,官方给的指标是每秒可达到 10W 的吞吐量。
Zookeeper分布式锁:
它追求的是强一致性和分区容错性。Zookeeper 在写入主节点数据后会等到从节点同步完数据成后才会返回成功。为了数据的强一致性牺牲了一部分可用性。
两者综合对比下来,技术派为了追求用户体验度
,就采用了 Redis 分布式锁来实现。
2、使用 Redis 分布式锁背景
技术派使用 Redis 分布式锁的背景是,用户根据 articleId 查询文章详情,查询出结果后返回。
查询文章详情流程图如下所示:
如果并发量不是特别高的情况下没有问题,但就怕并发量高;会出现什么问题呢?什么时候出现呢?
当缓存中没有数据,需要到 MySQL 中查询这一步。
问题出现点如下所示:
因为当高并发时,如果查询缓存中没有数据,大量的用户会同时去访问 DB 层 MySQL,MySQL 的资源是非常珍贵的,并且性能没有 Redis 好,很容易将我们的 MySQL 打宕机,进而影响整个服务。
针对这种问题,该怎么解决呢?
当大量用户同时访问同一篇文章时,只允许一个用户去 MySQL 中获取数据。由于服务是集群化部署,就需要用到 Redis 分布式锁。
逻辑如下所示:
采用加锁的方式就能很好地保护 DB 层数据库,进而保证系统的高可用性。
3、Redis 分布式锁几种实现方式
其实可以直接给大家讲最终的实现方式,这样我也比较省事;但是心里总感觉少点什么,所以接下来我就用几种方式由简到繁一点一点的推出最佳的实现方式。
3.1、Redis 实现分布式锁
代码如下图所示:
3.1.1、第一种 setIfAbsent(key,value,time)
redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS)
对应的 Redis 命令是set key value EX time NX
。
set key value EX time NX
是一个复合操作,setNx + setEx,底层采用的是 lua 脚本来保证其原子性,要么全部成功,否则加锁失败。
redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS)
含义就是:如果key不存在则加锁成功,返回true;否则加锁失败,返回false
。
第一种加锁逻辑如下图所示:
主要逻辑就是:当缓存中没有数据时,开始加锁,加锁成功则允许访问数据库,加锁失败则自旋重新访问。
主要代码如下所示:
/**
* Redis分布式锁第一种方法
*
* @param articleI
回复