Redis实战-Redisson-分布式锁
1. 简介
随着技术的快速发展,业务系统规模的不断扩大,分布式系统越来越普及。一个应用往往会部署到多台机器上,在一些业务场景中,为了保证数据的一致性,要求在同一时刻,同一任务只在一个节点上运行,保证同一个方法同一时刻只能被一个线程执行。这时候分布式锁就运用而生了。
分布式锁有很多的解决方案。常见的有:
基于数据库的:悲观锁,乐观锁。
基于zookeeper的分布式锁。
本章中讲的基于redis的分布式锁。
2. 超卖
下单减库存是互联网项目中必不可少的环节。然而,如果我么考虑不得当,将会带来很多问题。比如最不能忍受的:超卖
如下代码,一个初始化库存的方法和一个购买图书的方法,我们没有做任何的并发处理,查看下最终结果。
package com.ldx.redisson.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.Objects;
/**
* redis 实现分布式锁
*
* @author ludangxin
* @date 2021/8/15
*/
@Slf4j
@RestController
@RequestMapping("redis")
public class RedisLockTestController {
@Resource
private StringRedisTemplate stringRedisTemplate;
// 商品key
private static final String KEY = "book";
// 库存数量
private static final Long STOCK = 50L;
/**
* 初始化
*/
@GetMapping("init")
public String init() {
stringRedisTemplate.opsForValue().set(KEY, String.valueOf(STOCK));
return "初始化成功~";
}
/**
* 购买图书
*/
@GetMapping("buy")
public String buy() {
// 获取到当前库存
String buyBefore = stringRedisTemplate.opsForValue().get(KEY);
if(Objects.isNull(buyBefore)) {
log.error("未找到\"{}\"的库存信息~", KEY);
return "暂未上架~";
}
long buyBeforeL = Long.parseLong(buyBefore);
if(buyBeforeL > 0) {
// 对库存进行-1操作
Long buyAfter = stringRedisTemplate.opsForValue().decrement(KEY);
log.info("剩余图书==={}", buyAfter);
return "购买成功~";
}
else {
log.info("库存不足~");
return "库存不足~";
}
}
}
启动测试:
这里我们使用jemter
来进行并发请求。配置如下:
线程组配置:
请求配置:
请求结果:
只复制了部分日志
通过日志很明显的看到,即使在业务代码中判断了库存 > 0
但还是超卖了。
......
2021-08-15 21:01:22.614 INFO 66913 --- [io-8080-exec-30] c.l.r.c.RedisLockTestController : 库存不足~
2021-08-15 21:01:22.614 INFO 66913 --- [io-8080-exec-99] c.l.r.c.RedisLockTestController : 剩余图书===-42
2021-08-15 21:01:22.614 INFO 66913 --- [io-8080-exec-29] c.l.r.c.RedisLockTestController : 库存不足~
2021-08-15 21:01:22.622 INFO 66913 --- [io-8080-exec-89] c.l.r.c.RedisLockTestController : 剩余图书===-40
2021-08-15 21:01:22.622 INFO 66913 --- [io-8080-exec-90] c.l.r.c.RedisLockTestController : 剩余图书===-35
2021-08-15 21:01:22.622 INFO 66913 --- [o-8080-exec-135] c.l.r.c.RedisLockTestController : 库存不足~
2021-08-15 21:01:22.622 INFO 66913 --- [o-8080-exec-177] c.l.r.c.RedisLockTestController : 库存不足~
2021-08-15 21:01:22.622 INFO 66913 --- [io-8080-exec-92] c.l.r.c.RedisLockTestController : 剩余图书===-34
2021-08-15 21:01:22.622 INFO 66913 --- [io-8080-exec-86] c.l.r.c.RedisLockTestController : 剩余图书===-37
2021-08-15 21:01:22.642 INFO 66913 --- [io-8080-exec-11] c.l.r.c.RedisLockTestController : 库存不足~
2021-08-15 21:01:22.642 INFO 66913 --- [o-8080-exec-115] c.l.r.c.RedisLockTestController : 库存不足~
2021-08-15 21:01:22.642 INFO 66913 --- [io-8080-exec-72] c.l.r.c.RedisLockTestController : 剩余图书===-33
2021-08-15 21:01:22.643 INFO 66913 --- [nio-8080-exec-3] c.l.r.c.RedisLockTestController : 库存不足~
3. redis setnx
主要是用redis的 setnx (set not exists)命令实现分布式锁。
3.1 编写逻辑
在超买的场景中,我们了解了分布式锁的必要性。
上面的场景如果是单机的话,直接使用jvm锁就能解决问题,但是在分布式场景下下jvm锁无法处理。
接下来我们将使用redis命令来解决一下超卖问题。
新增了锁标识key。
在进行业务处理之前,给redis中
setIfAbsent(LOCK_KEY, clientId, 30, TimeUnit.SECONDS)
作为lock。LOCK_KEY
:锁的标识,比如秒杀的商品id_lock
:当对该商品进行秒杀下单时,加锁使其线性执行。clientId
:当前请求的唯一值,为了在删除锁时进行锁判断。即只能删除自己加的锁。防止误删锁。30
:失效时间,防止死锁(如果加锁时不设置过期时间,当系统执行完加锁还未进行解锁时系统宕机,那其他节点也无法进行下单,因为锁一直在)。解锁逻辑最好放在
finally
中进行,防止报错导致死锁。
// 锁标识
private static final String LOCK_KEY = "book_lock";
/**
* 购买图书
*/
@GetMapping("buy1")
public String buy1() {
String clientId = UUID.randomUUID().toString();
/*
* 给redis设置一个key,并设置过期时间防止死锁
* setIfAbsent(setnx):当key不存在时才设置值
* flag=true:值设置成功(获取锁) flag=false:设置值失败(获取锁失败)
*/
Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(LOCK_KEY, clientId, 30, TimeUnit.SECONDS);
try {
if(Objects.nonNull(flag) && flag) {
String buyBefore = stringRedisTemplate.opsForValue().get(KEY);
if(Objects.isNull(buyBefore)) {
log.error("未找到\"{}\"的库存信息~", KEY);
return "暂未上架~";
}
long buyBeforeL = Long.parseLong(buyBefore);
if(buyBeforeL > 0) {
Long buyAfter = stringRedisTemplate.opsForValue().decrement(KEY);
log.info("剩余图书==={}", buyAfter);
return "购买成功~";
}
else {
log.info("库存不足~");
return "库存不足~";
}
}
else {
log.error("获取锁失败~");
}
}
catch(Exception e) {
e.printStackTrace();
}
finally {
// 防止误删锁
if(clientId.equals(stringRedisTemplate.opsForValue().get(LOCK_KEY))) {
stringRedisTemplate.delete(LOCK_KEY);
}
}
return "系统错误~";
}
3.2 启动测试
启动两个服务,并配置nginx负载均衡。
nginx配置如下:
jemter配置如下:
启动测试:
部分日志如下:
redis中查看库存:
打完收工~
3.3 小节
这里主要是用了setnx
来实现分布式锁。虽然解决了超卖问题,但其中还是有很多缺陷。比如:
- 当请求获取锁失败时,能不能尝试重新获取锁或者阻塞等待获取锁,而不是直接返回
系统繁忙
之类的提示语。 - 如果持有锁的请求处理时间超过了设置的超时时间,也就是业务逻辑还没有处理完呢,但锁已经失效了,此时刚好又进来一个请求,又有并发问题了。
此时:redisson
:申请出战。
4. redisson
4.1 简介
Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。其中包括(BitSet
, Set
, Multimap
, SortedSet
, Map
, List
, Queue
, BlockingQueue
, Deque
, BlockingDeque
, Semaphore
, Lock
, AtomicLong
, CountDownLatch
, Publish / Subscribe
, Bloom filter
, Remote service
, Spring cache
, Executor service
, Live Object service
, Scheduler service
) Redisson提供了使用Redis的最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。
Redisson底层采用的是Netty框架。支持Redis2.8以上版本,支持Java1.6+以上版本。
Jedis 与 Redisson
Jedis:Jedis 只是简单的封装了 Redis 的API库,可以看作是Redis客户端,它的方法和Redis 的命令很类似,相比于Redisson 更原生一些,更灵活。
Redisson:Redisson 不仅封装了 redis ,还封装了对更多数据结构的支持,以及锁等功能,相比于Jedis 更加大。
4.2 quick start
4.2.1 添加依赖
springboot 基础上添加此依赖。
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.16.1</version>
</dependency>
4.2.2 application.yaml
因为使用的是单机redis,并且使用的是自动装配的依赖,所以直接使用redis的基本配置即可。
server:
port: ${port}
spring:
# redis 配置
redis:
# 地址
host: localhost
# 端口,默认为6379
port: 6379
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大数据库连接数
max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
4.2.3 controller
package com.ldx.redisson.controller;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
/**
* redisson test
*
* @author ludangxin
* @date 2021/8/15
*/
@Slf4j
@RestController
@RequestMapping("test")
@RequiredArgsConstructor
public class RedissonLockTestController {
private final RedissonClient redissonClient;
/**
* 没获取到锁的线程阻塞等待获取锁
*/
@GetMapping("/lock")
public void lock() {
log.info("进入了测试方法~");
RLock lock = null;
try {
lock = redissonClient.getLock("lock");
lock.lock();
log.info("获取到锁~");
Thread.sleep(2000);
}
catch(InterruptedException e) {
e.printStackTrace();
}
finally {
//如果当前线程保持锁定则解锁
if (null != lock && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
/**
* 没获取到锁的线程直接返回锁状态
*/
@GetMapping("tryLock")
public void tryLock() {
log.info("进入了测试方法~");
RLock lock = null;
try {
lock = redissonClient.getLock("lock");
if(lock.tryLock()) {
log.info("获取到锁~");
Thread.sleep(6000);
}
else {
log.error("获取锁失败~");
}
}
catch(InterruptedException e) {
e.printStackTrace();
}
finally {
//如果当前线程保持锁定则解锁
if (null != lock && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
/**
* 没获取到锁的线程尝试获取锁
*/
@GetMapping("tryLockWithBlock")
public void tryLockWithBlock() {
log.info("进入了测试方法~");
RLock lock = null;
try {
//非公平锁,随机取一个等待中的线程分配锁
lock = redissonClient.getLock("lock");
//公平锁,按照先后顺序依次分配锁
//lock=redissonClient.getFairLock("lock");
//最多等待锁3秒,5秒后强制解锁
if(lock.tryLock(3, 5, TimeUnit.SECONDS)) {
log.info("获取到锁~");
Thread.sleep(1000);
}
else {
log.error("获取锁失败~");
}
}
catch(InterruptedException e) {
e.printStackTrace();
}
finally {
//如果当前线程保持锁定则解锁
if (null != lock && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
4.2.4 启动测试
jemter 配置如下:
启动9个线程并同一时刻进行请求。
请求lock方法日志如下:
所有请求同一时刻进入方法,并且请求阻塞每隔两秒获取到锁。
2021-08-15 16:58:26.435 INFO 63602 --- [nio-8080-exec-5] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 16:58:26.435 INFO 63602 --- [nio-8080-exec-2] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 16:58:26.435 INFO 63602 --- [nio-8080-exec-7] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 16:58:26.435 INFO 63602 --- [nio-8080-exec-1] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 16:58:26.435 INFO 63602 --- [nio-8080-exec-6] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 16:58:26.435 INFO 63602 --- [nio-8080-exec-4] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 16:58:26.437 INFO 63602 --- [io-8080-exec-10] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 16:58:26.437 INFO 63602 --- [io-8080-exec-11] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 16:58:26.437 INFO 63602 --- [nio-8080-exec-9] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 16:58:26.443 INFO 63602 --- [nio-8080-exec-2] c.l.r.c.RedissonLockTestController : 获取到锁~
2021-08-15 16:58:28.474 INFO 63602 --- [io-8080-exec-11] c.l.r.c.RedissonLockTestController : 获取到锁~
2021-08-15 16:58:30.499 INFO 63602 --- [nio-8080-exec-6] c.l.r.c.RedissonLockTestController : 获取到锁~
2021-08-15 16:58:32.523 INFO 63602 --- [nio-8080-exec-5] c.l.r.c.RedissonLockTestController : 获取到锁~
2021-08-15 16:58:34.548 INFO 63602 --- [io-8080-exec-10] c.l.r.c.RedissonLockTestController : 获取到锁~
2021-08-15 16:58:36.569 INFO 63602 --- [nio-8080-exec-7] c.l.r.c.RedissonLockTestController : 获取到锁~
2021-08-15 16:58:38.595 INFO 63602 --- [nio-8080-exec-4] c.l.r.c.RedissonLockTestController : 获取到锁~
2021-08-15 16:58:40.616 INFO 63602 --- [nio-8080-exec-9] c.l.r.c.RedissonLockTestController : 获取到锁~
2021-08-15 16:58:42.643 INFO 63602 --- [nio-8080-exec-1] c.l.r.c.RedissonLockTestController : 获取到锁~
请求tryLock方法日志如下:
所有请求同一时刻进入方法,并且只有一个请求获取到了锁,其他请求直接返回结果。
2021-08-15 16:59:17.925 INFO 63602 --- [nio-8080-exec-9] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 16:59:17.925 INFO 63602 --- [nio-8080-exec-1] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 16:59:17.925 INFO 63602 --- [nio-8080-exec-5] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 16:59:17.928 INFO 63602 --- [nio-8080-exec-4] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 16:59:17.931 INFO 63602 --- [nio-8080-exec-6] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 16:59:17.931 INFO 63602 --- [io-8080-exec-11] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 16:59:17.933 INFO 63602 --- [nio-8080-exec-2] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 16:59:17.933 INFO 63602 --- [nio-8080-exec-8] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 16:59:17.933 INFO 63602 --- [io-8080-exec-10] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 16:59:17.937 ERROR 63602 --- [nio-8080-exec-9] c.l.r.c.RedissonLockTestController : 获取锁失败~
2021-08-15 16:59:17.937 INFO 63602 --- [nio-8080-exec-5] c.l.r.c.RedissonLockTestController : 获取到锁~
2021-08-15 16:59:17.938 ERROR 63602 --- [nio-8080-exec-4] c.l.r.c.RedissonLockTestController : 获取锁失败~
2021-08-15 16:59:17.937 ERROR 63602 --- [nio-8080-exec-1] c.l.r.c.RedissonLockTestController : 获取锁失败~
2021-08-15 16:59:17.939 ERROR 63602 --- [nio-8080-exec-8] c.l.r.c.RedissonLockTestController : 获取锁失败~
2021-08-15 16:59:17.939 ERROR 63602 --- [nio-8080-exec-2] c.l.r.c.RedissonLockTestController : 获取锁失败~
2021-08-15 16:59:17.939 ERROR 63602 --- [nio-8080-exec-6] c.l.r.c.RedissonLockTestController : 获取锁失败~
2021-08-15 16:59:17.939 ERROR 63602 --- [io-8080-exec-11] c.l.r.c.RedissonLockTestController : 获取锁失败~
2021-08-15 16:59:17.939 ERROR 63602 --- [io-8080-exec-10] c.l.r.c.RedissonLockTestController : 获取锁失败~
请求tryLockWithBlock方法日志如下:
所有请求同一时刻进入方法,并且只有三个请求获取到了锁。
2021-08-15 23:34:41.617 INFO 70413 --- [nio-8080-exec-6] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 23:34:41.617 INFO 70413 --- [nio-8080-exec-4] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 23:34:41.617 INFO 70413 --- [nio-8080-exec-7] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 23:34:41.617 INFO 70413 --- [nio-8080-exec-8] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 23:34:41.618 INFO 70413 --- [nio-8080-exec-5] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 23:34:41.618 INFO 70413 --- [nio-8080-exec-2] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 23:34:41.618 INFO 70413 --- [nio-8080-exec-9] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 23:34:41.618 INFO 70413 --- [nio-8080-exec-1] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 23:34:41.617 INFO 70413 --- [nio-8080-exec-3] c.l.r.c.RedissonLockTestController : 进入了测试方法~
2021-08-15 23:34:41.658 INFO 70413 --- [nio-8080-exec-8] c.l.r.c.RedissonLockTestController : 获取到锁~
2021-08-15 23:34:42.681 INFO 70413 --- [nio-8080-exec-7] c.l.r.c.RedissonLockTestController : 获取到锁~
2021-08-15 23:34:43.697 INFO 70413 --- [nio-8080-exec-3] c.l.r.c.RedissonLockTestController : 获取到锁~
2021-08-15 23:34:44.624 ERROR 70413 --- [nio-8080-exec-9] c.l.r.c.RedissonLockTestController : 获取锁失败~
2021-08-15 23:34:44.624 ERROR 70413 --- [nio-8080-exec-1] c.l.r.c.RedissonLockTestController : 获取锁失败~
2021-08-15 23:34:44.624 ERROR 70413 --- [nio-8080-exec-5] c.l.r.c.RedissonLockTestController : 获取锁失败~
2021-08-15 23:34:44.624 ERROR 70413 --- [nio-8080-exec-2] c.l.r.c.RedissonLockTestController : 获取锁失败~
2021-08-15 23:34:44.625 ERROR 70413 --- [nio-8080-exec-6] c.l.r.c.RedissonLockTestController : 获取锁失败~
2021-08-15 23:34:44.630 ERROR 70413 --- [nio-8080-exec-4] c.l.r.c.RedissonLockTestController : 获取锁失败~
4.2.5 小节
redisson 提供了lock()
和tryLock()
,tryLock(long time, TimeUnit unit)
,tryLock(long waitTime, long leaseTime, TimeUnit unit)
方法。
lock()
:会阻塞未获取锁的请求,默认持有30s
锁,但当业务方法在30s内没有执行完时,会有看门狗(默认每隔10s)
给当前锁续时30s
。tryLock()
:尝试获取锁,获取不到则直接返回获取失败,默认持有30s
锁,但当业务方法在30s内没有执行完时,会有看门狗(默认每隔10s)
给当前锁续时30s
。tryLock(long time, TimeUnit unit)
:尝试获取锁,等待time TimeUnit
,默认持有30s
锁,但当业务方法在30s内没有执行完时,会有看门狗(默认每隔10s)
给当前锁续时30s
。tryLock(long waitTime, long leaseTime, TimeUnit unit)
:尝试获取锁,等待waitTime TimeUnit
,锁最长持有leaseTime TimeUnit
,当业务方法在leaseTime TimeUnit
时长内没有执行完时,会强制解锁。
4.3 解决超卖
private static final String KEY = "book";
/**
* 购买图书
*/
@GetMapping("buy1")
public String buy1() {
RLock lock = null;
try {
lock = redissonClient.getLock("lock");
if(lock.tryLock(3, TimeUnit.SECONDS)) {
RAtomicLong buyBefore = redissonClient.getAtomicLong(KEY);
if(Objects.isNull(buyBefore)) {
log.error("未找到\"{}\"的库存信息~", KEY);
return "暂未上架~";
}
long buyBeforeL = buyBefore.get();
if(buyBeforeL > 0) {
Long buyAfter = buyBefore.decrementAndGet();
log.info("剩余图书==={}", buyAfter);
return "购买成功~";
}
else {
log.info("库存不足~");
return "库存不足~";
}
}
else {
log.error("获取锁失败~");
}
}
catch(Exception e) {
e.printStackTrace();
}
finally {
//如果当前线程保持锁定则解锁
if(null != lock && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
return "系统错误~";
}
经测试不会存在超卖问题。
并且避免了3.3
小节中提到的问题。
4.4 小节
方便,好用。
Redis实战-Redisson-分布式锁的更多相关文章
- Redisson 分布式锁实战与 watch dog 机制解读
Redisson 分布式锁实战与 watch dog 机制解读 目录 Redisson 分布式锁实战与 watch dog 机制解读 背景 普通的 Redis 分布式锁的缺陷 Redisson 提供的 ...
- Redisson 分布式锁实现之前置篇 → Redis 的发布/订阅 与 Lua
开心一刻 我找了个女朋友,挺丑的那一种,她也知道自己丑,平常都不好意思和我一块出门 昨晚,我带她逛超市,听到有两个人在我们背后小声嘀咕:"看咱前面,想不到这么丑都有人要." 女朋友 ...
- Redisson分布式锁的简单使用
一:前言 我在实际环境中遇到了这样一种问题,分布式生成id的问题!因为业务逻辑的问题,我有个生成id的方法,是根据业务标识+id当做唯一的值! 而uuid是递增生成的,从1开始一直递增,那么在同一台机 ...
- Redisson分布式锁实现
转: Redisson分布式锁实现 2018年09月07日 15:30:32 校长我错了 阅读数:3303 转:分布式锁和Redisson实现 概述 分布式系统有一个著名的理论CAP,指在一个分布 ...
- redis客户端、分布式锁及数据一致性
Redis Java客户端有很多的开源产品比如Redission.Jedis.lettuce等. Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持:Redis ...
- Redis系列(二)--分布式锁、分布式ID简单实现及思路
分布式锁: Redis可以实现分布式锁,只是讨论Redis的实现思路,而真的实现分布式锁,Zookeeper更加可靠 为什么使用分布式锁: 单机环境下只存在多线程,通过同步操作就可以实现对并发环境的安 ...
- [转帖]SpringBoot集成redisson分布式锁
SpringBoot集成redisson分布式锁 https://www.cnblogs.com/yangzhilong/p/7605807.html 前几天同事刚让增加上这一块东西. 百度查一下 啥 ...
- redis系列:分布式锁
redis系列:分布式锁 1 介绍 这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁.会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁. 本篇文章会将分布式锁的实现分 ...
- 又长又细,万字长文带你解读Redisson分布式锁的源码
前言 上一篇文章写了Redis分布式锁的原理和缺陷,觉得有些不过瘾,只是简单的介绍了下Redisson这个框架,具体的原理什么的还没说过呢.趁年前项目忙的差不多了,反正闲着也是闲着,不如把Rediss ...
- Redisson 分布式锁源码 02:看门狗
前言 说起 Redisson,比较耳熟能详的就是这个看门狗(Watchdog)机制. 本文就一起看看加锁成功之后的看门狗(Watchdog)是如何实现的? 加锁成功 在前一篇文章中介绍了可重入锁加锁的 ...
随机推荐
- 抽象队列同步器AQS
AQS是AbstractQueuedSynchronizer的简称,即抽象队列同步器,从字面上可以这样理解: 抽象:抽象类,只实现一些主要逻辑,有些方法由子类实现: 队列:使用先进先出(FIFO)的队 ...
- 一文彻底弄懂MySQL优化之深度分页
深度分页(Deep Pagination)在MySQL中指的是对大型数据集进行分页查询时,尤其是当需要获取较后页的数据时,性能可能会受到影响.传统的分页方法在数据量较大时,随着页数的增加,性能会迅速下 ...
- CerberusDet:不同任务共享不同的部分,新多任务目标检测方案
传统的目标检测模型通常受到其训练数据和定义的类别逻辑的限制.随着语言-视觉模型的近期兴起,出现了不受这些固定类别限制的新方法.尽管这些开放词汇检测模型具有灵活性,但与传统的固定类别模型相比,仍然在准确 ...
- 使用wxpython开发跨平台桌面应用,对常用消息对话框的封装处理
在很多程序中,封装常用消息对话框有很多好处,尤其是在 GUI 应用程序中,本篇随笔结合.net 开发Winform界面的经验,对使用wxpython开发中 wx.MessageDialog 和 wx. ...
- 基于sqli-labs Less-7 的sql高权读写注入详解
1. MySQL高权限读写简介 1.1 前置知识 数据库的高权用户对服务器上的文件进行读取写入操作,从而可以进行写入一句话木马来获得服务器权限或者读取服务器上的配置型文件等注入行为. select l ...
- IIC通信协议详解 & PCF8591应用(Verilog实现)
该文章结合PCF8591 8-bit AD/DA 模数/数模转换器来详细介绍IIC通信协议,尽量做到条理清晰,通俗易懂.该文图片均从PCF8591手册中截取,一定程度上引导读者学习阅读data she ...
- C# 串口读取并转换字符串
public string ReadString() { ASCIIEncoding ascii = new ASCIIEncoding(); byte[] readBuffer = new byte ...
- OpenGL编程指南(原书第9版)
这本书是<OpenGL编程指南(原书第9版)>,也称为<OpenGL Programming Guide: The Official Guide to Learning OpenGL ...
- Python中函数或者类对象带()与不带()的区别——闭包和函数返回时的常见现象
Python中函数或者类对象带()与不带()的区别-----闭包和函数返回时的常见现象 - 函数不带括号时,调用的是这个函数本身 ,是整个函数体,是一个函数对象,不需等该函数执行完成,返回一个已定义函 ...
- mysql之编译安装
在CentOS7中编译安装MySQL 5.7.29 一.依赖包安装 yum install gcc gcc-c++ ncurses ncurses-devel cmake bison -y 二.下载源 ...