分布式锁Redis/MySQL
内容纲要

分布式锁

为什么需要分布式锁

锁这个名次在开发中很常见,操作系统、数据库这些软件中都有锁的实现。

我们在开发应用的时候,如果需要对某一个共享变量进行多线程同步访问的时候,可以使用我们学到的锁进行处理,并且可以完美的运行。说白了,锁就是一个“指挥交通“的存在,它可以规定谁可以通行(访问数据)。但是像操作系统、数据库等软件中的锁都只是在单机上,他不能指挥其他主机。

因为业务的需求,分布式、微服务出现了,那么在多台服务器的场景下,就需要一个锁来指挥全部的服务器。某台服务器上的锁不能管到其他服务器,分布式锁就出现。

分布式CAP理论

AP理论是分布式系统设计中的一个重要理论,它指出一个分布式系统不可能同时满足一致性、可用性和分区容错性这三个特性。一致性是指系统中的数据能够保持一致的状态,可用性是指系统能够对每个请求做出响应,分区容错性是指系统能够在网络分区的情况下继续运行。

分布式锁需要具备的条件

1、在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行;
2、高可用的获取锁与释放锁;
3、高性能的获取锁与释放锁;
4、具备可重入特性;
5、具备锁失效机制,防止死锁;
6、具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。

分布式锁的实现

基于数据库

数据库也是一个可以公共访问的资源,所有服务器都能够访问它。那么借助数据库的唯一索引和锁机制就能实现分布式锁的功能。

核心思想:

在数据库中创建一个表,表中包含方法名等字段,将方法名设置为唯一索引,用于区分争夺的是哪个方法的服务(抢占的资源)。要执行某个方法,就向表中插入一行数据,插入成功则代表获得锁,反之则获取失败。当使用完后就删除该行数据,代表释放锁。

基于数据库实现的优点:

  • 实现简单,直接由数据库的机制就能实现,不需要自己写额外的代码。

基于数据库实现的缺点:

  • 因为数据库读写涉及磁盘读写,而且数据库可能还涉及数据同步、主从复制等问题,所以分布式锁的性能及可用性会受到影响。
  • 因为是基于数据库自己的唯一索引机制,我们也不能实现锁可重入的需求。
  • 不能实现锁失效机制,如果在成功插入数据后服务器宕机了,那么锁就会一直存在。
  • 无法确保获取锁以及删除锁的服务器是同一个。

基于Redis的实现

Redis作为一个内存存储系统,在数据读写效率上远大于数据库。

setnx 命令:当key不存在时才插入,否则插入失败。那么插入是否成功可作为是否抢到锁的依据。

expire 命令:可设置锁的过期时间。

delete 命令:可之间删除锁

核心思想:

在获取锁时,使用setnx尝试取获得锁,并设置一个锁过期的时间,超过该事件则自动释放锁,锁的value可以为一个随机生成的UUID;最后删除锁时,可通过UUID判读是否同一服务器。

但是按照以上思想实现的话,还存在一些问题:

  1. 如果服务器还不想释放锁(因为还未使用完资源),怎么实现锁的可重入性。
  2. 如何实现A取到锁,只能由A释放锁
  3. 如何保证命令的原子性

实现锁的可重入性

可以使用Hash结构来实现,key表示公共资源,hashfield为UUID,hash field的value表示获取锁的次数。(类似于操作系统信号量)

实现A取到的锁只能由自己释放

// 以下为伪代码
setnx lock Alock; // A取到锁

if 'get lock' == Alock{
    delete lock;    // 如果是A自己的锁,则可以释放
} else{
    // 不是自己的锁
}

这个方法是可以解决自己的锁只能由自己释放,但是还存在问题。

因为getdelete两个命令不是原子执行的。

if 'get lock' == Alock{

// 如果这时,锁突然不再是自己的,而是变成别人的,就会出现误解锁 delete lock; // 如果是A自己的锁,则可以释放 } else{ // 不是自己的锁 }

通过lua脚本实现原子操作

// 获取锁的 value 与 ARGV[1] 是否匹配,匹配则执行 del
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

Redis执行Lua脚本:Redis执行Lua脚本

通过Redisson实现分布式锁

上述讨论Redis实现分布式锁会出现的问题,Redisson都已帮我们解决了,我们直接用就行了。

配置Redisson

SpringBoot整合Redis – GBlog

// 获得锁
// 拿到锁才会执行的代码
RLock lock = redissonClient.getLock("lockName");
try{
    lock.lock();
    // TODO job
}catch(Exception e){

}finally {
    // 解锁自己的锁
    if (lock.isHeldByCurrentThread()){
        lock.unlock();
    }
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇