腾讯后端一面:如果需要实现一个分布式锁,你该如何实现?

2024-04-21 23:36

本文主要是介绍腾讯后端一面:如果需要实现一个分布式锁,你该如何实现?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

更多大厂面试内容可见 -> http://11come.cn

腾讯后端一面:如果需要实现一个分布式锁,你该如何实现?

分布式锁

如果让你来实现一个分布式锁,该如何实现?

实现分布式锁的话,肯定需要一个地方去存储锁的信息,可以选择 Redis 或者 Zookeeper 来存储,这样所有的节点都可以感知到分布式锁的存在,达到多个节点互斥的效果

一般使用中,就是基于 Redis 或者 ZooKeeper 的分布式锁使用的比较多,这里说一下基于 Redis 如何实现一个分布式锁(也就是 Redis 的客户端工具 Redisson 如何实现的分布式锁)

首先,锁信息存在 Redis 中,要可以看得出来是哪个应用中的哪个线程持有的这把锁,这样是为了之后可以进行 重入锁 的操作,基于这样的考虑,在 Redis 中使用哈希结构来存储,如下:

"11_come": {ffa56698-e0f7-4412-ad5a-00669156d187:1: 1
}
  • 11_come :哈希结构的名称,也就是我们加的分布式锁的 Name

  • ffa56698-e0f7-4412-ad5a-00669156d187:1: :持有锁的线程的唯一标识,由 threadId 标识,但是多个应用的 threadId 可能重复,因此前边再加上一个 UUID ,即组成为:UUID + threadId ,这样就可以保证每个线程的标识都是唯一的,这个 UUID 需要存储下来,因为要保证同一个线程来获取锁的时候拿到的 UUID 都是相同的,这样才可以进行 锁的重入

  • 1 :哈希结构中,后边的 1 表示当前线程的重入次数

这样锁的存储就已经实现好了

如果多个应用同时去加锁,如果一起查询,锁结构并不存在,因此同时去加锁,这样会不会存在问题?

这里面试官问的意思就是如何去保证多个应用去同时加锁,判断锁结构是否存在以及加锁的操作如何保证原子性

可以通过 lua 脚本来保证这一系列操作的原子性,通过 lua 脚本可以保证命令执行的 原子性 ,即 Redis 在执行 lua 脚本时,不会被其他操作打断

锁的过期时间如何设置呢?

如果加锁的时候,指定了过期时间,就使用指定的时间,如果没有指定,就给一个默认的过期时间,可以设置 30s

会不会导致用户还没有执行完,锁就已经被释放了?

不会的,因为还有一个后台任务去进行一个 锁续期 的操作,比如过期时间为 30s,那么就在 1/3 的时间(10s)之后,就去判断用户线程是否还在执行任务,如果还在执行的话,就去进行一个锁续期的动作,即将锁的过期时间重置为 30s

如何去判断用户线程是否还在执行任务?

这个后台任务是在加锁的时候去启动的,其实就是一个延时任务,延时 10s 的时间去进行锁续期的操作,在执行完锁续期操作执行,再次去启动这个延时任务,进行下一次的锁续期操作

那么如果加锁的这个用户线程已经执行完毕之后,就不会去执行这个延时任务了,当然也就不会进行锁续期操作了

如果在高并发的场景下,用户加锁的机器宕机了,会导致其他线程一直拿不到锁,这个该如何去解决?

面试官问的意思是,想要如何在用户宕机之后,怎么去知道这个用户所在的机器宕机了,主动的去释放掉这个锁

我先回答的是每个锁有一个超时时间,超过这个时间锁就会被释放掉了,但是面试官说默认的超时时间 30s 太长了,在海量任务的场景下会将其他请求都阻塞 30s,这样是不可以接受的

之后我说,那就要感知到这个进程节点是否还存活,可以将这个线程注册一个节点到 ZooKeeper 中,注册为 临时节点 ,当这个机器宕机,线程也就没了,ZooKeeper 中的临时节点就会消失,当监听到这个节点消失,就可以去 Redis 中将这个节点的锁给主动删除掉

如果线程在获取锁之后,一直得不到调度,那么这个线程就不会进行锁续期,导致锁过期了该怎么办?

这个我说的是将执行锁续期的时间点放的靠前一些

因为这个线程可能暂时得不到调度,但是后边迟早要得到调度,因此锁续期的时间点在过期时间的 1/3 处,也就是过期时间为 30s,那么当过了 10s 之后就来执行锁续期的这个操作,而不是等到第 28、29 秒的时候再来执行锁续期,这样可能导致线程没有得到调度,没来得及执行锁续期,锁就已经过期了,但是这个线程的任务还没有执行完毕

如果进程并没有挂掉,而是线程出现问题,比如抢占不到 CPU 资源,此时去监听这个进程是否挂掉就没有用了,该怎么办?

这里问的是,上边我说将线程节点作为临时节点注册到 ZooKeeper 中,但是如果进程没挂,而是线程得不到 CPU 调度,那通过这种方式也无法感知到这个问题

我回答的是,既然要应对这种问题,就要探测这个线程是否在执行,那么就可以在加锁的用户线程中加一个发送心跳的操作,在另外一个服务中去接收这个心跳,如果在一段时间内都收不到用户线程的心跳,那么就说明这个用户线程出现问题了,一直没有得到执行,因此就可以主动的去将这个用户线程加的分布式锁给释放掉,那么这里判断线程出现问题的时间就可以设置的短一些,比如 5s 都没有收到该线程的心跳,那么就判断该线程得不到调度,出现问题,就主动去释放他的锁

面试到这里就结束了,之后出了一道算法题,实现 Compartor 排序就可以完成,比较简单

面试复盘:

问的分布式锁的最后一个问题,感觉还存在一些问题,线程得不到调度之后,我们去主动释放该线程的锁了,那么加入这个线程之后又重新被调度了,那就会出现问题了,所以在释放这个线程锁之后,还要将这个线程任务给杀掉,避免之后又重新得到调度从而出现并发安全问题

我觉得这种情况概率很低,在 Redisson 客户端中也没有进行这样的处理,如果有一些想法的话,可以在留言中指出,一起讨论一下!

这篇关于腾讯后端一面:如果需要实现一个分布式锁,你该如何实现?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/924312

相关文章

MySQL 横向衍生表(Lateral Derived Tables)的实现

《MySQL横向衍生表(LateralDerivedTables)的实现》横向衍生表适用于在需要通过子查询获取中间结果集的场景,相对于普通衍生表,横向衍生表可以引用在其之前出现过的表名,本文就来... 目录一、横向衍生表用法示例1.1 用法示例1.2 使用建议前面我们介绍过mysql中的衍生表(From子句

Mybatis的分页实现方式

《Mybatis的分页实现方式》MyBatis的分页实现方式主要有以下几种,每种方式适用于不同的场景,且在性能、灵活性和代码侵入性上有所差异,对Mybatis的分页实现方式感兴趣的朋友一起看看吧... 目录​1. 原生 SQL 分页(物理分页)​​2. RowBounds 分页(逻辑分页)​​3. Page

Python基于微信OCR引擎实现高效图片文字识别

《Python基于微信OCR引擎实现高效图片文字识别》这篇文章主要为大家详细介绍了一款基于微信OCR引擎的图片文字识别桌面应用开发全过程,可以实现从图片拖拽识别到文字提取,感兴趣的小伙伴可以跟随小编一... 目录一、项目概述1.1 开发背景1.2 技术选型1.3 核心优势二、功能详解2.1 核心功能模块2.

MYSQL查询结果实现发送给客户端

《MYSQL查询结果实现发送给客户端》:本文主要介绍MYSQL查询结果实现发送给客户端方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录mysql取数据和发数据的流程(边读边发)Sending to clientSending DataLRU(Least Rec

Java中实现线程的创建和启动的方法

《Java中实现线程的创建和启动的方法》在Java中,实现线程的创建和启动是两个不同但紧密相关的概念,理解为什么要启动线程(调用start()方法)而非直接调用run()方法,是掌握多线程编程的关键,... 目录1. 线程的生命周期2. start() vs run() 的本质区别3. 为什么必须通过 st

使用SpringBoot整合Sharding Sphere实现数据脱敏的示例

《使用SpringBoot整合ShardingSphere实现数据脱敏的示例》ApacheShardingSphere数据脱敏模块,通过SQL拦截与改写实现敏感信息加密存储,解决手动处理繁琐及系统改... 目录痛点一:痛点二:脱敏配置Quick Start——Spring 显示配置:1.引入依赖2.创建脱敏

基于Python实现一个简单的题库与在线考试系统

《基于Python实现一个简单的题库与在线考试系统》在当今信息化教育时代,在线学习与考试系统已成为教育技术领域的重要组成部分,本文就来介绍一下如何使用Python和PyQt5框架开发一个名为白泽题库系... 目录概述功能特点界面展示系统架构设计类结构图Excel题库填写格式模板题库题目填写格式表核心数据结构

C#之List集合去重复对象的实现方法

《C#之List集合去重复对象的实现方法》:本文主要介绍C#之List集合去重复对象的实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C# List集合去重复对象方法1、测试数据2、测试数据3、知识点补充总结C# List集合去重复对象方法1、测试数据

Linux实现线程同步的多种方式汇总

《Linux实现线程同步的多种方式汇总》本文详细介绍了Linux下线程同步的多种方法,包括互斥锁、自旋锁、信号量以及它们的使用示例,通过这些同步机制,可以解决线程安全问题,防止资源竞争导致的错误,示例... 目录什么是线程同步?一、互斥锁(单人洗手间规则)适用场景:特点:二、条件变量(咖啡厅取餐系统)工作流

SpringBoot读取ZooKeeper(ZK)属性的方法实现

《SpringBoot读取ZooKeeper(ZK)属性的方法实现》本文主要介绍了SpringBoot读取ZooKeeper(ZK)属性的方法实现,强调使用@ConfigurationProperti... 目录1. 在配置文件中定义 ZK 属性application.propertiesapplicati