面试官必问的分布式锁面试题,你答得上来吗?

简介: 本文介绍了分布式锁的概念、实现方式及其在项目中的应用。首先通过黄金圈法则分析了分布式锁的“为什么”、“怎么做”和“做什么”。接着详细讲解了使用 Redisson 和 SpringBoot + Lettuce 实现分布式锁的具体方法,包括代码示例和锁续期机制。最后解释了 Lua 脚本的作用及其在 Redis 中的应用,强调了 Lua 保证操作原子性的重要性。文中还提及了 Redis 命令组合执行时的非原子性问题,并提供了 Lua 脚本实现分布式锁的示例。如果你对分布式锁感兴趣或有相关需求,欢迎关注+点赞,必回关!

添加图片注释,不超过 140 字(可选)


一、面试聊聊-分布式锁,如何回答?

要分析分布式锁这个问题,我们根据黄金圈法则来分析


添加图片注释,不超过 140 字(可选)

黄金圈法则是由美国营销顾问西蒙·斯涅克(Simon Sinek)提出的一种思维模型,用于帮助人们更好地理解和传达信息。黄金圈法则由三个圈组成,分别是:

  • 为什么(Why):为什么要做这件事?这是黄金圈的核心,是一切的起点。
  • 怎么做(How):怎么做这件事?这是黄金圈的中间部分,是实现目标的方法。
  • 做什么(What):做什么这件事?这是黄金圈的外围部分,是具体的行为。



使用3w分析问题思路来分析分布式锁,可以从以下几个方面进行分析:

What:分布式锁是什么?

分布式锁是控制分布式系统之间同步访问共享资源的机制。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。

回答时:先说一下概念。分布式锁是用于在分布式系统中控制对共享资源的访问,以避免数据竞争和并发问题。


How:如何实现分布式锁?

分布式锁的实现方法有很多,常见的有以下几种:

  • 数据库锁:使用数据库中的行锁或表锁来实现分布式锁。
  • 文件锁:使用文件来实现分布式锁。
  • Zookeeper锁:使用Zookeeper来实现分布式锁。
  • Redis锁:使用Redis来实现分布式锁。
  • 消息队列锁:使用消息队列来实现分布式锁。

回答时:说一下分布式锁以上的实现方式。

Why:为什么需要分布式锁?

一些需要分布式锁的场景:

  • 分布式数据库事务:在分布式系统中,通常需要使用分布式事务来保证数据的一致性。在分布式事务中,通常会使用分布式锁来保证事务的执行顺序。
  • 分布式资源分配:在分布式系统中,通常需要使用分布式锁来分配共享资源。例如,在抢购场景中,需要使用分布式锁来保证同一商品只能被一个用户购买。
  • 分布式数据同步:在分布式系统中,通常需要使用分布式锁来保证数据的同步。例如,在订单系统中,需要使用分布式锁来保证同一订单只能被一个系统修改。

回答时:说一下分布式锁的应用场景。


二、深入聊聊-分布式锁在项目中的应用

1、Redisson实现分布式锁


添加图片注释,不超过 140 字(可选)


Redisson 是一个基于 Redis 的 Java 分布式框架。Redisson 提供了丰富的功能,包括分布式锁、分布式集合、分布式队列等。

以下是使用 Redisson 实现分布式锁的示例:

@Autowired     private RedissonClient redissonClient;     public String lock() {         // 获取锁         RLock lock = redissonClient.getLock("lock");         boolean acquired = lock.tryLock(10,-1,TimeUnit.SECONDS);         if (acquired) {             // 获取锁成功,执行业务逻辑             return "获取锁成功,执行业务逻辑...";         } else {             // 获取锁失败,重试             return "获取锁失败,重试...";         }     }     public String unlock() {         // 释放锁         RLock lock = redissonClient.getLock("lock");         lock.unlock();         return "释放锁成功...";     }

另外,redisson支持锁续期。即在锁键值过期后任务还没执行完成,此时需要把锁键值的时间自动延长。

Redisson提供了的续期机制,只要客户端加锁成功,就会启动一个Watch Dog。可以看到源代码的实现leaseTime不设置为-1时开启监听。如果任务没完成就调用scheduleExpirationRenewal续期方法。


添加图片注释,不超过 140 字(可选)


tryLock() 方法用于尝试获取分布式锁,该方法有三个参数:

  • key:锁的键值
  • waitTime:等待获取锁的时间,单位为毫秒
  • leaseTime:锁的过期时间,单位为毫秒

waitTime 参数表示客户端最多等待多长时间来获取锁。如果在 waitTime 时间内没有获取到锁,则会返回 false。

leaseTime 参数表示锁的过期时间。如果锁在 leaseTime 时间内没有被释放,则会自动释放。如果 leaseTime 设置为 -1,则表示锁的过期时间由 renew() 方法来控制。这样,在业务逻辑执行过程中,可以定期调用 lock.renew() 方法来续期锁的过期时间。

tryLock() 方法的返回值是一个 Boolean 值,表示是否成功获取到锁。如果成功获取到锁,则返回 true。否则,返回 false。

2、Springboot+Lettuce


添加图片注释,不超过 140 字(可选)


在 SpringBoot 2.7 中,可以通过 spring-boot-starter-data-redis 默认依赖是Lettuce。那么Lettuce是如何实现分布式锁呢

  1. 在 Spring Boot 项目中添加 spring-boot-starter-data-redis 依赖。
  2. 定义一个 RedisLock 类来封装分布式锁的相关操作。
  3. 在 ServiceImpl类中使用 RedisLock 类来获取和释放分布式锁。

以下是 SpringBoot+Lettuce 实现分布式锁的完整代码:

@Component public class RedisLock {     private static final String LOCK_SCRIPT = "if redis.call('exists', KEYS[1]) == 0 then\n" +     "    redis.call('set', KEYS[1], ARGV[1], 'NX', 'PX', ARGV[2]);\n" +     "    return 1\n" +     "else\n" +     "    return 0\n" +     "end";     private final RedisTemplate<String, String> redisTemplate;     public RedisLock(RedisTemplate<String, String> redisTemplate) {         this.redisTemplate = redisTemplate;     }     //获得锁     public boolean acquireLock(String key, long timeout) {         String uuid = UUID.randomUUID().toString();         Object result = redisTemplate.execute(new DefaultRedisScript(LOCK_SCRIPT, Long.class), Arrays.asList(key), uuid, timeout);         return result != null && (long) result == 1;     } //释放锁     public void releaseLock(String key, String uuid) {         redisTemplate.delete(key);     } }


@Service public class TestServcieImpl implements TestServcie{   @Autowired     private StringRedisTemplate stringRedisTemplate;     // 加锁脚本     private static final String LOCK_SCRIPT = "if redis.call ('setnx', KEYS[1], ARGV[1]) == 1 then return redis.call ('expire', KEYS[1], ARGV[2]) else return 0 end";     // 解锁脚本     private static final String UNLOCK_SCRIPT = "if redis.call ('get', KEYS[1]) == ARGV[1] then return redis.call ('del', KEYS[1]) else return 0 end";     // 加锁方法     public boolean lock(String key, String value, Long expire) {         RedisScript<Long> redisScript = new DefaultRedisScript<>(LOCK_SCRIPT, Long.class);         Long result = stringRedisTemplate.execute(redisScript, Collections.singletonList(key), value, String.valueOf(expire));         return result.equals(Long.valueOf(1));     }     // 解锁方法     public boolean unlock(String key, String value) {         RedisScript<Long> redisScript = new DefaultRedisScript<>(UNLOCK_SCRIPT, Long.class);         Long result = stringRedisTemplate.execute(redisScript, Collections.singletonList(key), value);         return result.equals(Long.valueOf(1));     } }

在上述 demo 中,我们使用 RedisLock 类来封装分布式锁的相关操作。acquireLock() 方法用于获取分布式锁,releaseLock() 方法用于释放分布式锁。

在 ServcieImpl 类中,我们使用 RedisLock 类来获取和释放分布式锁。lock() 方法用于获取锁,unlock() 方法用于释放锁。不像Redisson封装好了相应的方法,Lettuuce如果要实现锁续期就需要自己写监听器及相应的lua脚本。


三、小结

1、什么是Lua?


添加图片注释,不超过 140 字(可选)


可以看到不论是Redisson还是Lettuce实现分布式锁都使用的Lua脚本,那我们先来了解一下什么是Lua脚本语言。

Lua 是一个小巧的脚本语言,由巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组于 1993 年开发的。Lua 使用标准 C 语言编写并以源代码形式开放,几乎在所有操作系统和平台上都能编译运行。Lua 脚本可以调用 C/C++ 的函数,也可以被 C/C++ 代码调用,所以 Lua 在应用程序中可以被广泛应用。

Lua 的特点如下:

  • 小巧灵活:Lua 的核心只有 200K,非常小巧,可以方便地嵌入到应用程序中。Lua 的语法也非常简单,易于学习和使用。
  • 可扩展性:Lua 可以调用 C/C++ 的函数,也可以被 C/C++ 代码调用,所以 Lua 可以很容易地扩展到应用程序的其他部分。
  • 高性能:Lua 的运行效率非常高,可以满足大多数应用程序的需求。

Lua 在游戏开发、Web 开发、嵌入式系统等领域都有广泛的应用。

以下是 Lua 的一些典型应用:

  • 游戏开发:Lua 常用于游戏中的脚本编写,用于实现游戏的逻辑和特效。
  • Web 开发:Lua 可以用于 Web 开发,用于实现动态页面和游戏。
  • 嵌入式系统:Lua 可以用于嵌入式系统的开发,用于实现控制逻辑和用户界面。

Lua 是一款非常实用的脚本语言,在众多领域都有广泛的应用。

2、为什么使用Lua?


添加图片注释,不超过 140 字(可选)


  • Redis 实现分布式锁中,获取锁、释放锁为什么要使用 Lua 脚本?
  • 使用 Lua 脚本的主要原因是为了保证操作的原子性,避免出现并发问题或误解锁的情况。
  • 使用 setnx 命令获取锁,然后使用 expire 命令设置过期时间,这两个命令之间可能会发生网络延迟或者其他异常,导致锁没有正确设置过期时间,从而造成死锁。
  • 使用 del 命令释放锁,需要先判断锁是否属于当前客户端,否则可能会误解其他客户端的锁。

   使用 Lua 脚本可以将判断和删除锁的操作合并为一个原子操作,避免了这些问题。Lua 脚本在 Redis 服务器端执行,不会受到网络延迟或者客户端故障的影响,也不会被其他命令打断,因此可以保证操作的原子性。

  • 为什么说 Redis 命令没有原子性?
  • Redis 命令本身是单线程执行的,所以单个命令是具有原子性的。
  • 但是如果要实现分布式锁的功能,通常需要多个命令组合起来执行,例如 setnx + expire 或者 get + del。
  • 这些命令组合在执行过程中可能会被其他客户端发送的命令打断,导致数据不一致或者逻辑错误。

   因此,Redis 命令没有原子性是指多个命令组合起来执行时没有原子性。

以下是使用 Lua 脚本实现分布式锁的示例:

-- 获取锁 function acquire_lock(key, uuid, timeout)   local value = redis.call("GET", key)   if value == nil then     redis.call("SET", key, uuid, "NX", "PX", timeout)     return 1   else     return 0   end end -- 释放锁 function release_lock(key, uuid)   redis.call("DEL", key) end

上述脚本实现了简单的 SETNX 和 DEL 操作,可以保证同一时刻只有一个客户端可以获取到锁。

在实际使用中,可以根据具体的业务场景来调整 Lua 脚本的实现。





如果文章对你有帮助,欢迎关注+点赞,必回关!!!

目录
相关文章
|
4天前
|
调度 云计算 芯片
云超算技术跃进,阿里云牵头制定我国首个云超算国家标准
近日,由阿里云联合中国电子技术标准化研究院主导制定的首个云超算国家标准已完成报批,不久后将正式批准发布。标准规定了云超算服务涉及的云计算基础资源、资源管理、运行和调度等方面的技术要求,为云超算服务产品的设计、实现、应用和选型提供指导,为云超算在HPC应用和用户的大范围采用奠定了基础。
179562 18
|
12天前
|
存储 运维 安全
云上金融量化策略回测方案与最佳实践
2024年11月29日,阿里云在上海举办金融量化策略回测Workshop,汇聚多位行业专家,围绕量化投资的最佳实践、数据隐私安全、量化策略回测方案等议题进行深入探讨。活动特别设计了动手实践环节,帮助参会者亲身体验阿里云产品功能,涵盖EHPC量化回测和Argo Workflows量化回测两大主题,旨在提升量化投研效率与安全性。
云上金融量化策略回测方案与最佳实践
|
13天前
|
人工智能 自然语言处理 前端开发
从0开始打造一款APP:前端+搭建本机服务,定制暖冬卫衣先到先得
通义灵码携手科技博主@玺哥超carry 打造全网第一个完整的、面向普通人的自然语言编程教程。完全使用 AI,再配合简单易懂的方法,只要你会打字,就能真正做出一个完整的应用。
9163 23
|
17天前
|
Cloud Native Apache 流计算
资料合集|Flink Forward Asia 2024 上海站
Apache Flink 年度技术盛会聚焦“回顾过去,展望未来”,涵盖流式湖仓、流批一体、Data+AI 等八大核心议题,近百家厂商参与,深入探讨前沿技术发展。小松鼠为大家整理了 FFA 2024 演讲 PPT ,可在线阅读和下载。
4850 12
资料合集|Flink Forward Asia 2024 上海站
|
17天前
|
自然语言处理 数据可视化 API
Qwen系列模型+GraphRAG/LightRAG/Kotaemon从0开始构建中医方剂大模型知识图谱问答
本文详细记录了作者在短时间内尝试构建中医药知识图谱的过程,涵盖了GraphRAG、LightRAG和Kotaemon三种图RAG架构的对比与应用。通过实际操作,作者不仅展示了如何利用这些工具构建知识图谱,还指出了每种工具的优势和局限性。尽管初步构建的知识图谱在数据处理、实体识别和关系抽取等方面存在不足,但为后续的优化和改进提供了宝贵的经验和方向。此外,文章强调了知识图谱构建不仅仅是技术问题,还需要深入整合领域知识和满足用户需求,体现了跨学科合作的重要性。
|
25天前
|
人工智能 自动驾驶 大数据
预告 | 阿里云邀您参加2024中国生成式AI大会上海站,马上报名
大会以“智能跃进 创造无限”为主题,设置主会场峰会、分会场研讨会及展览区,聚焦大模型、AI Infra等热点议题。阿里云智算集群产品解决方案负责人丛培岩将出席并发表《高性能智算集群设计思考与实践》主题演讲。观众报名现已开放。
|
13天前
|
人工智能 容器
三句话开发一个刮刮乐小游戏!暖ta一整个冬天!
本文介绍了如何利用千问开发一款情侣刮刮乐小游戏,通过三步简单指令实现从单个功能到整体框架,再到多端优化的过程,旨在为生活增添乐趣,促进情感交流。在线体验地址已提供,鼓励读者动手尝试,探索编程与AI结合的无限可能。
三句话开发一个刮刮乐小游戏!暖ta一整个冬天!
|
13天前
|
消息中间件 人工智能 运维
12月更文特别场——寻找用云高手,分享云&AI实践
我们寻找你,用云高手,欢迎分享你的真知灼见!
995 67