本文大纲: 1.什么是做姿势种分布式锁 2.解决方案 3.小结 Redis 简简单单的几种数据类型,一个 key/value 数据库,分布现在又是式锁绍两分布式锁、又是家介限流工具、又是做姿势种消息队列......,感觉都要被玩坏了。分布不过话说回来,式锁绍两Redis 在这么多场合被开发者们喜欢,家介还是做姿势种得益于它极高的性能与使用的简洁性。 在面试的分布时候,说到 Redis ,式锁绍两很多人第一反应就是家介缓存,其实除了缓存,做姿势种Redis 还有非常多丰富的分布使用场景,这些使用场景,式锁绍两松哥在未来都会和大家一一分享。 今天就先来看一个简单的,用 Redis 做分布式锁。 1.什么是分布式锁首先我们来看一个问题场景: 例如一个简单的用户操作,一个线程去修改用户的源码下载状态,首先从数据库中读出用户的状态,然后在内存中进行修改,修改完成后,再存回去。在单线程中,这个操作没有问题,但是在多线程中,由于读取、修改、存 这是三个操作,不是原子操作,所以在多线程中,这样会出问题。 解决这个问题,我们就需要锁,对于锁,大家应该不会陌生,在 Java 中的 synchronized 以及 ReentrantLock 可重入锁都是我们比较常见的,但是这种锁都是本地锁,现在微服务、分布式系统思想大行其道,在这样的系统中,云服务器提供商本地锁显然是不够用的,于是大家纷纷想办法,如何在分布式环境下解决锁的问题。想出来的办法很多,我们可以通过 MySQL、可以通过 ZK、也可以通过 Redis ,都可以用来解决分布式锁的问题,这里我们主要来看看如何通过 Redis 解决分布式锁问题。 2.解决方案 2.1 整体思路 「分布式锁实现的思路很简单,就是进来一个线城先占位,当别的线城进来操作时,发现已经有人占位了,就会放弃或者稍后再试。」 在 Redis 中,占位一般使用 setnx 指令,先进来的线程先占位,线程的操作执行完成后,再调用 del 指令释放位子。同时为了防止死锁,我们一般还要给锁加一个过期时间,到期了自动释放。服务器租用 基于这样的思路,我们来看两种不同的实现方式: 2.2 解决方案一 基于我们前面所说的思路,可以使用 setnx 和 expire 实现分布式锁,但是 setnx 和设置过期时间 expire 这是两个操作,这两个操作一起的话就不具备原子性(除非自己写 Lua 脚本),为了解决这个问题,从 Redis2.8 开始,setnx 和 expire 可以通过一个命令一起来执行了,这个命令就是 set,set 中多了一个参数: 从图中大家可以看到,在 key/value 之后,还有一个 EX 5 表示以秒计的过期时间(PX 表示以毫秒计的过期时间),最后的 NX 就是说如果 k1 不存在,这条命令执行成功,否则执行失败,这就相当于 setnx 的效果了。 因此,我们封装的锁如下: 对于上面这段代码,大家重点看思路,不必深究代码细节: 2.3 解决方案二 上面的代码写着还是蛮长的,那么有没有简单一点的办法呢?当然是有的!那就是 Redisson。 相对于 Jedis 这种原生态的应用,Redisson 对 Redis 请求做了较多的封装,对于锁,也提供了对应的方法可以直接使用: 在这段代码中,核心的就是 lock.tryLock(500, 1000, TimeUnit.MILLISECONDS);,第一个参数是尝试加锁的等待时间为 500 毫秒,第二个参数表示锁的超时时间为 1000 毫秒,也就是这个锁在 1000 毫秒后会自动失效。 小伙伴们发现,这和我们在方案一里边配置的参数是一样的,其实思路是不变的,Redisson 只不过是将我们写的和锁相关的方法封装起来了而已。 3.小结 当然,这里我只是先简单介绍下加锁的思路以及在 Redis 单机中如何加锁,后面松哥再和大家分享 Redis 集群中如何加锁。