导航:首页 > 源码编译 > 大厂面试题源码

大厂面试题源码

发布时间:2023-09-18 22:36:46

Ⅰ 【面试题解析】从 Vue 源码分析 key 的作用

最近看了面试题中有一个这样的题, v-for 为什么要绑定 key?

Vue 中 key 很多人都弄不清楚有什么作用,甚至还有些人认为不绑定 key 就会报错。

其实没绑定 key 的话,Vue 还是可以正常运行的,报警告是因为没通过 Eslint 的检查。

接下来将通过源码一步步分析这个 key 的作用。

Virtual DOM 最主要保留了 DOM 元素的层级关系和一些基本属性,本质上就是一个 JS 对象。相对于真实的 DOM,Virtual DOM 更简单,操作起来速度更快。

如果需要改变 DOM,则会通过新旧 Virtual DOM 对比,找出需要修改的节点进行真实的 DOM 操作,从而减小性能消耗。

传统的 Diff 算法需要遍历一个树的每个节点,与另一棵树的每个节点对比,时间复杂度为 O(n²)。

Vue 采用的 Diff 算法则通过逐级对比,大大降低了复杂性,时间复杂度为 O(n)。

VNode 更新首先会经过 patch 函数, patch 函数源码如下:

vnode 表示更新后的节点,oldVnode 表示更新前的节点,通过对比新旧节点进行操作。

1、vnode 未定义,oldVnode 存在则触发 destroy 的钩子函数

2、oldVnode 未定义,则根据 vnode 创建新的元素

3、oldVnode 不为真实元素并且 oldVnode 与 vnode 为同一节点,则会调用 patchVnode 触发更新

4、oldVnode 为真实元素或者 oldVnode 与 vnode 不是同一节点,另做处理

接下来会进入 patchVnode 函数,源码如下:

1、vnode 的 text 不存在,则会比对 oldVnode 与 vnode 的 children 节点进行更新操作

2、vnode 的 text 存在,则会修改 DOM 节点的 text

接下来在 updateChildren 函数内就可以看到 key 的用处。

key 的作用主要是给 VNode 添加唯一标识,通过这个 key,可以更快找到新旧 VNode 的变化,从而进一步操作。

key 的作用主要表现在以下这段源码中。

updateChildren 过程为:

1、分别用两个指针(startIndex, endIndex)表示 oldCh 和 newCh 的头尾节点

2、对指针所对应的节点做一个两两比较,判断是否属于同一节点

3、如果4种比较都没有匹配,那么判断是否有 key,有 key 就会用 key 去做一个比较;无 key 则会通过遍历的形式进行比较

4、比较的过程中,指针往中间靠,当有一个 startIndex > endIndex,则表示有一个已经遍历完了,比较结束

从 VNode 的渲染过程可以得知,Vue 的 Diff 算法先进行的是同级比较,然后再比较子节点。

子节点比较会通过 startIndex、endIndex 两个指针进行两两比较,再通过 key 比对子节点。如果没设置 key,则会通过遍历的方式匹配节点,增加性能消耗。

所以不绑定 key 并不会有问题,绑定 key 之后在性能上有一定的提升。

综上,key 主要是应用在 Diff 算法中,作用是为了更快速定位出相同的新旧节点,尽量减少 DOM 的创建和销毁的操作。

希望以上内容能够对各位小伙伴有所帮助,祝大家面试顺利。

Vue 的文档中对 key 的说明如下:

关于就地修改,关键在于 sameVnode 的实现,源码如下:

可以看出,当 key 未绑定时,主要通过元素的标签等进行判断,在 updateChildren 内会将 oldStartVnode 与 newStartVnode 判断为同一节点。

如果 VNode 中只包含了文本节点,在 patchVnode 中可以直接替换文本节点,而不需要移动节点的位置,确实在不绑定 key 的情况下效率要高一丢丢。

某些情况下不绑定 key 的效率更高,那为什么大部分Eslint的规则还是要求绑定 key 呢?

因为在实际项目中,大多数情况下 v-for 的节点内并不只有文本节点,那么 VNode 的字节点就要进行销毁和创建的操作。

相比替换文本带来的一丢丢提升,这部分会消耗更多的性能,得不偿失。

了解了就地修改,那么我们在一些简单节点上可以选择不绑定 key,从而提高性能。

如果你喜欢我的文章,希望可以关注一下我的公众号【前端develop】

Ⅱ 大厂数据分析面试题,大数据结构化面试

作为程序员,你认为代码只要实现功能就可以了吗?
其实,工作2~3年后,你会陪蠢发现随着工作的深入,工作中遇到的问题会变大,处理的数据量也会变大。
一开始,我可能会耐心加班,等机器处理好了再回家,但最后,处理完这些数据通常是在深夜。
面对这样的问题,其实可以用数据结构解决。 仔细整理开发中遇到的问题,会发现很多工作中的问题,用简单的逻辑就能解决。
举个例子,你很熟悉。 如何实时统计99%的业务接口响应时间?
您可能会首先想到,每次查询时,都会按照从小到大的顺序对所有响应时间进行排序。 如果总共有1200个数据,第1188个数据将有99%的响应时间。
很明显,每次用这种方法查询都要排序,效率非常低。
但是,如果知道“堆”数据结构,两个堆就可以非常有效地解决这个问题。
因此,数据结构是提高我们程序员工作效率的利器!
另外,已经工作了2到3年的你,可能想跳槽进入大工厂。
但是,当你去面试时,你经常会碰到数据结构和算法的主题。
目前,数据结构和算法是许多知名企业面试的必考问题。
国内外各大互联网公司在面试过程中,都多少听说了一些有关数据结构和算法的主题。
而且,规模越大的公司,越重视数据结构和算法。
例如,2019年6月,阿里面试中涉及的数据结构主题:
2019年华为面试涉及的数据结构主题:
目前,许多中小企业的面试问题都涉瞎盯及数据结构知识。
其实,你会发现,即使是大小公司,为了筛选更优秀的人磨乱和才,面试问题的难度也会越来越大。
因此,数据结构是进入大厂的重要门槛。
总之,如果你想提高工作效率,进入更大的公司,数据结构和算法是你必须跨越的一道坎。
从易传传媒、亚信、奥鹏教育、程序员到架构师再到技术经理樊延欣老师,前后六年通过各种工作方式打好数据结构基础,在过程中梳理了许多心得,进行了深入思考。
和樊延欣老师一起,死战数据结构,跳过代码陷阱,尽快完成数据结构通关,有机会升职更好。
扫描堆场上的二维码,点击组,立即抢购
原价69元,限时优惠49元
老师怎么解释这门课?#
老师介绍枯燥抽象的结构规则用详细的方法映射到实际项目中。 然后尽量脱离复杂的数学基础,在许多常见的应用场合映射相关理论,降低学习者的理解门槛,使其零基础也能学习。
同时,该课程至少涵盖了50%常见互联网公司中数据结构方面的面试问题纲领,序列和栈是基础性主题,树是更高级的主题,可以理解和把握,发挥面试信心,更上一层楼
#课程介绍#
#我能得到什么? #
1、提高编程效率和质量
熟悉数据结构原理,复杂的项目无需为需求实现原理而烦恼。
2、优化能力提升
随着了解的加深,能够发现与工作中数据结构特性相违背的代码,并具有优化修改的能力。
3、提高面试成功率
学习50%以上互联网公司数据结构的面试问题纲领,提高面试合格率。
#使用者群组#
1、开发业务系统2年,有相关项目经验,不断重复制作业务车轮希望提高的程序员。
有2、3~5年开发经验,但基础不牢固,想改变体系结构的程序员。
3、基础扎实,需要大量用例和思考才能巩固基础的优秀毕业生/在校生。
#新课初优惠#
限时49元
(成本69 )。
每百人加价十元
第26节课,平均每课2元,持续一个月,改变报关大厂面试机会
享受七折的折扣

自考/成考有疑问、不知道自考/成考考点内容、不清楚当地自考/成考政策,点击底部咨询官网老师,免费领取复习资料:https://www.87dh.com/xl/

Ⅲ 把大厂都面试一遍后,我总结了13条面试经验和面试题(附答案)

近期大大小小面了十几家公司,花了一点时间整理了14条面试经验,希望能帮助到你。

1.面试前要认真准备,及时梳理

你会的和面试中你能讲清楚完全是两码事。不是为了背知识点出去吹牛逼,而是要把自己会的梳理好思路,组织好语言。哪怕几个月前才做过的项目,如果没有做项目复盘,突然让你讲,恐怕未必所有细节都能记得清楚。

2.每面完一家一定要复盘

面试完把没表达好的地方再打磨打磨,一些高频问题的答案组织好提纲,尤其是关于项目的。现场的答案通常逻辑都会很混乱,笔者自己面到最后,依然感觉很难把项目说出亮点来,毕竟我前东家是外包公司,很多技术点没机会做得深入。

3.简历不是一成不变的,要及时调整

基本每面一家都建议调整一下细节,应聘岗位不同,简历所呈现的重点也应不同,所以针对投递的职位,简历要做不同程度的优化。比较心仪的岗位需要根据岗位描述有偏向性地单独准备简历。这样通过率才会更高,在面试中也会让你过面试官更容易抓到你与该岗位的契合点。

4.不要把内推想的太神奇,除非你有够硬的关系。

对大部分人而言,内推的作用就是过简历关,其他方面的作用我不太好评估,每个人的资源不一样。你拿Offer了,内推人拿推荐奖金,你面挂了,推荐人也不会怎么样,打铁还需自身硬,当然这只是自己的看法。

5.控制自己的面试节奏,否则可能会非常辛苦。

每个公司的面试节奏不一样,可以提前找HR问清楚,比如阿里就是平均一个岗位要面一个月,而滴滴我当时早晨一面下午二面,2天后就三面了,进度很快。笔者面试中节奏安排基本是错乱的,中间有几天平均每天1.5个电话面,真的很考验体能。建议综合调研一下可以投的岗位,按照【保底】【满意】【挑战】三个档拉开梯度投简历,毕竟你最后只能选一个,都投到一个档次里意义不大。

6.谨慎面对各大招聘软件上的邀约

招聘软件上会有很多人问你要简历,他们并不是真的对你感兴趣,只是群发消息,建议不要随便给简历,很吓人的。笔者自己第一个蚂蚁金服的面试,就是招聘软件里对方说“先看一下简历”,结果转手就给内推了,当时没有经验,完全不知道面的什么部门什么要求,也不知道面试预约了还可以推掉,结果赶鸭子上架第一个面试一面就栽了,面评表上吃了个很难看的差评,很影响心情。

7.面试中对待不同的面试官要有好的心态

不要把人想的太坏,也不要把人想的太好,想让你过的会帮你找亮点,让你有阐述的机会,不想让你过的就会盯着你不会的点一直问,随时等着放大你任何一个漏洞,面试的过程中你会遇到各种各样的人,当成一种阅历,不卑不亢就好。给我印象比较深刻的是政采云的技面官,花名堂主,面试之余给了我很多忠告和指点,受益匪浅。

8.在面试过程中尽量不要话太多

每个面试官喜好不同,有的人希望你不知道的就说不知道,有的人希望听你的思路和推测,我自己在面试中,有的面试官就说“不知道没关系,你可以讲讲思路,或者如果让你来做,你会怎么做”,也有的面试官开场就说“我问的每个点你简单陈述就行,如果我感兴趣就会自己展开问”,在不明确对方偏好的情况下,建议话少点,说的越多漏洞越多,也容易碰到雷区引起反感。

9.简历尽量投给HR和你认识的搞技术的朋友

这样即便最终没有通过,也大概能知道问题出在哪,原则上面评结果是保密的,但是可以拜托对方针对自己的不足点提供一些关键信息,以便明确下一步努力的方向,面试从来都不是一次决定最终结果的事情,一次栽了,再面其他的就行,如果真的非常向往某个公司或岗位,过一两年再来就行了,重点是你要先敢面。如果简历给到猎头,那么当你面对同等资历的竞争者时可能就会被Pass,因为如果招了你,是需要给猎头付费的,如果简历给了不认识的人内推,极有可能后续什么消息都打听不到,干着急。

10.我强烈建议大家去尝试参加面试

定期去了解大厂的技术动向和对于你这个水平的面试者的评定条件,每个厂都不一样,你的学校、专业技术、工作履历、工作年限、项目管理经验等等都可能被作为指标,尤其是像笔者这种半路出家的野路子码农,千万不要用网上那些经验往自己身上套,差别太大了。很多人都会觉得自己“水平差”,感觉自己“啥都不会”,但是自己感觉自己差和面试中被人吊打完全是两种感觉,后者虽然不好受,但能迫使你做出改变,走出舒适区。

11.面试是一场匹配游戏

不是你越牛逼结果就越理想,面试的关键词是【匹配】。大多数面试者都是劣势的一方,请对自己好点,通过了,告诉自己"运气不错,继续努力,要对得起别人的赏识",没过,也没关系,告诉自己"运气不太好,继续努力就好,总有一天会有人赏识"。对面试中暴露出的技能短板要足够重视,但对于结果,真的没必要太较真,有时候公司的想法并没有那么复杂,就是想招个更年轻更便宜的而已。

12.大厂对于大龄程序员是有“把控”的,不管招聘方承不承认。

13.一定要做一个有亮点的程序员

无论是什么,一定得有跟普通应聘者不一样的地方,可能是业务梳理能力,资源协调能力,跨端开发,跨栈开发,或者某个专项的技术玩的很溜都可以,但一定得有,如果还没有,那就开始培养一个。

你近期面试了吗?欢迎在评论区一起讨论,我这里整理的一些大厂面试资料(附答案)可免费提供,添加QQ群:1020139748  备注即可。

java面试题,请大家帮忙看一下,请排列下列源代码顺序,完成枚举定义:

public enum Signal {

YELLOW, BLUE, RED {
public String info() {
return "Stop";
}
};
public String info() {
return "Signal Data";
}

}

B和E答案有误。。。。
两个答案是一样的。。。
其中有一个应该是};
后面有个分号。。。

假设B.}; E.}
那么排序结果应该是: DCBAE

希望能帮到你。。。。仍有问题可以HI我。。。

Ⅳ 2022史上最全android面试题归纳汇总(附答案解析)

我经历过这么多年的摸爬滚打,面试过也被面试过。现总结与归纳Android开发相关面试题:

1、Activity启动模式有哪些,分别有什么不同?

2、Service启动模式有哪些,对应的生命周期?IntentService呢?

3、ContentProvider的作用,是否支持多线程和多进程

4、Broadcast的注册方式,对应的生命周期是什么,有序和无序那种可以中断广播?

5、AsyncTask的作用,如何使用(包括有哪些方法,能说出同步异步,能说出不同Android版本下的区别加分)

6、有哪些异步的方式?

7、Handler机制

8、Dialog的使用及其生命周期

9、Activity的生命周期,能否改?

10、Fragment的生命周期,能否改?

11、Activity和Fragment如何通信

12、View的绘制机制

13、View的事件传递机制

14、如何监听手势

15、ImageView设置图片显示有哪几种模式,有什么区别?

16、有哪些存储方式

17、SharedPreferences是否支持多进程、多线程

别看以上常问的是入门级的,但是有两三年开发经验能回答圆满的人不多。

1、如何理解Activity的任务亲和性

2、如何让Service为单独的进程

3、IntentService的实现原理

4、LocalBroadcast的作用,实现原理,相对于Broadcast的优势在哪,劣势在哪

5、Handler的缺点,会不会造成内存泄漏,有则如何解决

6、Fragment与Activity的区别和联系

7、Fragment如何缓存布局

8、Fragment与ViewPager的搭配使用,有没有问题重叠问题,怎么解决

9、同时提供侧滑和上下滑动,如何解决事件传播问题

10、是否使用过Design包

11、嵌套滑动理解

12、behavior的原理

13、对设计模式有什么看法,经常使用的有哪些?

中级的稍微偏底层一些,这个主要考察平时是否关注而不是一味地怼业务需求

1、Activity的启动过程

2、Service创建为单独进程会有哪些问题?

3、简述AIDL的构建过程

4、IPC机制有哪些?

5、android多进程通信方式,内部原理

6、App启动的入口在哪?

7、LRU缓存算法

8、Bitmap的有哪几种压缩算法,有啥区别?

9、图片在手机本地存储大小和在内存大小是否一致,为什么,Android默认像素一般占几个字节?

10、第三方框架的熟练程度,如:

11、SharedPreference内部实现原理

12、模块化、插件话、组件化等分别有什么区别,对用有什么好处

13、说说MV * 模式,并画出做过项目的架构图

14、对跨平台方案有哪些了解,使用过哪些? 比如RN

15、对大前端有什么看法,了解多少?使用过什么?

16、对其他语言的了解,kotlin,pythonphp、c++等

17、兴趣爱好是什么?对未来有什么规划?

目前是一些经常会被问到的,当然只是列举了Android 开发方向的,Java的一些还没列举,比如异常、网络、多线程、JCF等等

以上问题的答案在下面都有详细解答,我们不仅整理了这些资料,而且还有一份长达"635页"的Android资料汇总:

包括:底层原理+项目实战+面试专题

虽说Android早已不像过去那般火爆,但各大厂对于中高级开发者仍旧是求贤若渴,想要获取更丰厚的薪资,打铁还得自身硬。对于框架、源码、原理、项目实操经验,都必须有足够的知识储备,才可以在面试中击败面试官。但是由于网上的资料鱼龙混杂,也不成体系,很多人在自我提升的过程中都头疼不已。 这里就给大家分享一份字节大佬整理的《Android中高级面试题汇总(2022)》,帮助大家系统的梳理中高级Android知识!里面包含了所有Android面试的知识点,刷完进大厂妥妥的

(含:静态内部类和非静态内部类的比较,多态的理解与应用, java方法的多态性理解,java中接口和继承的区别,线程池的好处,详解,单例,线程池的优点及其原理,线程池的优点,为什么不推荐通过Executors直接创建线程池,创建线程或线程池时请指定有意义的线程名称,方便出错时回溯,深入理解ReentrantLock与Condition,Java多线程:线程间通信之Lock,Synchronized 关键字原理,ReentrantLock原理,HashMap中的Hash冲突解决和扩容机制, JVM常见面试题, JVM内存结构,类加载机制/双亲委托…)

(含:Activity知识点, Fragment知识点, Service知识点, Intent知识点…)

(含:屏幕适配,主要控件优化,事件分发与嵌套滚动…)

(含:MVP架构设计,组件化架构…)

(含:启动优化,内存优化,绘制优化,安装包优化…)

(含:开源库源码分析,Glide源码分析,OkHttp源码分析,Retrofit源码分析,RxJava源码分析…)

(含:开源文档,面试合集…)

Ⅵ 已拿32k小米Android高级开发offer(面试题回顾)

到现在我入职也有一段时间了,这才有空梳理一下当时的面试题。简单说下我的情况:这是一次比较平常的跳槽,不是什么逆袭大厂的剧本,只是薪资有所涨幅。

个人经历不详说,面试题对大家来说可能更有参考性,本篇先整理小米的面试题,我前后也面了很多个大厂,有空把其他几个大厂的面试题也总结一下。

Java基础肯定是少不了要问的,这轮面试Kotlin相对来说是我这些面试中问得比较多的,所以说准备面试还是要面面俱到。

我有点佩服我的记忆力了。这部分涉及到更多的 源码、原理和优化 方面的问题,Android高级开发需要具备一些什么能力大家也应该有所衡量了。

最后给大家分享一份 2246页 Android大厂高频面试题解析大全 ,基本上把我的面试内容都涵盖到了: Android、性能优化、Java、Kotlin、网络、插件化、热修复、模块化、组件化、增量更新、Gradle、图片、Flutter等。

这份资料免费提供给大家复习,文末查看领取方式,搞定Android面试这一份肯定够了。

第一章 Android相关 (源码分析、性能优化、Framework等)

第二章 性能优化 (GC原理、布局优化、绘制优化、内存优化等)

第三章 Java相关 (四种线程池、JVM、内存管理、垃圾回收、引用等)

第四章 Kotlin相关 (延迟初始化、Reified、Extension Functions、函数等)

第五章 网络相关 (HTTP 知识体系、HttpDns 原理、TCP,UDP,HTTP,SOCKET 之间的区别等)

第六章 插件化&热修复&模块化&组件化&增量更新&Gradle

第七章 图片相关 (图片库对比、LRUCache原理、图片加载原理、Glide等)

第八章 Flutter相关 (Flutter原理、Flutter Hot Reload、Flutter 动态化 探索 、Flutter Platform Channel等)

需要这份资料的朋友私信我【面试题】就可以免费领取。

希望大家都可以把握住每一次自我提升的机会,把每一步都走踏实了,涨薪升职什么的都会迎你而来。

也欢迎大家和我一起交流Android方面的事情。

Ⅶ 腾讯大佬整理推荐《Android Framework 开发揭秘》突破面试!(附面试宝典)

随着 Android 开发者越来越多,企业在筛选 Android 程序员时越来越看中一个程序员对于 Android 底层原理的理解和思考。

经常面试的人就知道,现在 Framework 算是面试必问知识点了,比如下面一些大厂面试题:

Framework 为开发应用程序提供了非常多的 API,通过调用特殊的 API 构造 APP,满足业务上的需求。正因为有了 Framework 层,应用开发才能事半功倍,专注于业务逻辑实现。

这里给大家分享一份由 腾讯大佬整理推荐的《Android Framework 开发揭秘》以及《2022最新Android中高级面试题合集》。

这份1932页的《2022Android中高级面试题汇总》是总结了2020-2021期间大厂面试中的高频面试题汇总,其中包括腾讯、字节、美团、阿里、网络…等一线互联网大厂。

资料包含: Java基础、Android基础、UI控件、网络通信、架构设计、性能优化、源码流程…

想要深入学习了解 Framework ,突破面试难关,那么这两份《Android Framework 开发揭秘》《2022最新Android中高级面试题合集》一定不要错过。

Ⅷ 大厂面试题详解:如何用Redis实现分布式锁

说一道常见面试题:

一个很简单的答案就是去使用 Redission 客户端。Redission 中的锁方案就是 Redis 分布式锁得比较完美的详细方案。

那么,Redission 中的锁方案为什么会比较完美呢?

正好,我用 Redis 做分布式锁经验十分丰富,在实际工作中,也 探索 过许多种使用 Redis 做分布式锁的方案,经过了无数血泪教训。

所以,在谈及 Redission 锁为什么比较完美之前,先给大家看看我曾经使用 Redis 做分布式锁是遇到过的问题。

我曾经用 Redis 做分布式锁是想去解决一个用户抢优惠券的问题。这个业务需求是这样的:当用户领完一张优惠券后,优惠券的数量必须相应减一,如果优惠券抢光了,就不允许用户再抢了。

在实现时,先从数据库中先读出优惠券的数量进行判断,当优惠券大于 0,就进行允许领取优惠券,然后,再将优惠券数量减一后,写回数据库。

当时由于请求数量比较多,所以,我们使用了三台服务器去做分流。

这个时候会出现一个问题:

如果其中一台服务器上的 A 应用获取到了优惠券的数量之后,由于处理相关业务逻辑,未及时更新数据库的优惠券数量;在 A 应用处理业务逻辑的时候,另一台服务器上的 B 应用更新了优惠券数量。那么,等 A 应用去更新数据库中优惠券数量时,就会把 B 应用更新的优惠券数量覆盖掉。

看到这里,可能有人比较奇怪,为什么这里不直接使用 SQL:

原因是这样做,在没有分布式锁的协调下,优惠券数量可能直接会出现负数。因为当前优惠券数量为 1 的时候,如果两个用户通过两台服务器同时发起抢优惠券的请求,都满足优惠券大于 0 每个条件,然后都执行这条 SQL 说了句,结果优惠券数量直接变成 -1 了。

还有人说可以用乐观锁,比如使用如下 SQL:

这种方式就在一定几率下,很可能出现数据一直更新不上,导致长时间重试的情况。

所以,经过综合考虑,我们就采用了 Redis 分布式锁,通过互斥的方式,以防止多个客户端同时更新优惠券数量的方案。

当时,我们首先想到的就是使用 Redis 的 setnx 命令,setnx 命令其实就是 set if not exists 的简写。

当 key 设置值成功后,则返回 1,否则就返回 0。所以,这里 setnx 设置成功可以表示成获取到锁,如果失败,则说明已经有锁,可以被视作获取锁失败。

如果想要释放锁,执行任务 del 指令,把 key 删除即可。

利用这个特性,我们就可以让系统在执行优惠券逻辑之前,先去 Redis 中执行 setnx 指令。再根据指令执行结果,去判断是否获取到锁。如果获取到了,就继续执行业务,执行完再使用 del 指令去释放锁。如果没有获取到,就等待一定时间,重新再去获取锁。

乍一看,这一切没什么问题,使用 setnx 指令确实起到了想要的互斥效果。

但是,这是建立在所有运行环境都是正常的情况下的。一旦运行环境出现了异常,问题就出现了。

想一下,持有锁的应用突然崩溃了,或者所在的服务器宕机了,会出现什么情况?

这会造成死锁——持有锁的应用无法释放锁,其他应用根本也没有机会再去获取锁了。这会造成巨大的线上事故,我们要改进方案,解决这个问题。

怎么解决呢?咱们可以看到,造成死锁的根源是,一旦持有锁的应用出现问题,就不会去释放锁。从这个方向思考,可以在 Redis 上给 key 一个过期时间。

这样的话,即使出现问题,key 也会在一段时间后释放,是不是就解决了这个问题呢?实际上,大家也确实是这么做的。

不过,由于 setnx 这个指令本身无法设置超时时间,所以一般会采用两种办法来做这件事:

1、采用 lua 脚本,在使用 setnx 指令之后,再使用 expire 命令去给 key 设置过期时间。

2、直接使用 set(key,value,NX,EX,timeout) 指令,同时设置锁和超时时间。

以上两种方法,使用哪种方式都可以。

释放锁的脚本两种方式都一样,直接调用 Redis 的 del 指令即可。

到目前为止,我们的锁既起到了互斥效果,又不会因为某些持有锁的系统出现问题,导致死锁了。这样就完美了吗?

假设有这样一种情况,如果一个持有锁的应用,其持有的时间超过了我们设定的超时时间会怎样呢?会出现两种情况:

出现第一种情况比较正常。因为你毕竟执行任务超时了,key 被正常清除也是符合逻辑的。

但是最可怕的是第二种情况,发现设置的 key 还存在。这说明什么?说明当前存在的 key,是另外的应用设置的。

这时候如果持有锁超时的应用调用 del 指令去删除锁时,就会把别人设置的锁误删除,这会直接导致系统业务出现问题。

所以,为了解决这个问题,我们需要继续对 Redis 脚本进行改动……毁灭吧,累了……

首先,我们要让应用在获取锁的时候,去设置一个只有应用自己知道的独一无二的值。

通过这个唯一值,系统在释放锁的时候,就能识别出这锁是不是自己设置的。如果是自己设置的,就释放锁,也就是删除 key;如果不是,则什么都不做。

脚本如下:

或者

这里,ARGV[1] 是一个可传入的参数变量,可以传入唯一值。比如一个只有自己知道的 UUID 的值,或者通过雪球算法,生成只有自己持有的唯一 ID。

释放锁的脚本改成这样:

可以看到,从业务角度,无论如何,我们的分布式锁已经可以满足真正的业务需求了。能互斥,不死锁,不会误删除别人的锁,只有自己上的锁,自己可以释放。

一切都是那么美好!!!

可惜,还有个隐患,我们并未排除。这个隐患就是 Redis 自身。

要知道,lua 脚本都是用在 Redis 的单例上的。一旦 Redis 本身出现了问题,我们的分布式锁就没法用了,分布式锁没法用,对业务的正常运行会造成重大影响,这是我们无法接受的。

所以,我们需要把 Redis 搞成高可用的。一般来讲,解决 Redis 高可用的问题,都是使用主从集群。

但是搞主从集群,又会引入新的问题。主要问题在于,Redis 的主从数据同步有延迟。这种延迟会产生一个边界条件:当主机上的 Redis 已经被人建好了锁,但是锁数据还未同步到从机时,主机宕了。随后,从机提升为主机,此时从机上是没有以前主机设置好的锁数据的——锁丢了……丢了……了……

到这里,终于可以介绍 Redission(开源 Redis 客户端)了,我们来看看它怎么是实现 Redis 分布式锁的。

Redission 实现分布式锁的思想很简单,无论是主从集群还是 Redis Cluster 集群,它会对集群中的每个 Redis,挨个去执行设置 Redis 锁的脚本,也就是集群中的每个 Redis 都会包含设置好的锁数据。

我们通过一个例子来介绍一下。

假设 Redis 集群有 5 台机器,同时根据评估,锁的超时时间设置成 10 秒比较合适。

第 1 步,咱们先算出集群总的等待时间,集群总的等待时间是 5 秒(锁的超时时间 10 秒 / 2)。

第 2 步,用 5 秒除以 5 台机器数量,结果是 1 秒。这个 1 秒是连接每台 Redis 可接受的等待时间。

第 3 步,依次连接 5 台 Redis,并执行 lua 脚本设置锁,然后再做判断:

再额外多说一句,在很多业务逻辑里,其实对锁的超时时间是没有需求的。

比如,凌晨批量执行处理的任务,可能需要分布式锁保证任务不会被重复执行。此时,任务要执行多长时间是不明确的。如果设置分布式锁的超时时间在这里,并没有太大意义。但是,不设置超时时间,又会引发死锁问题。

所以,解决这种问题的通用办法是,每个持有锁的客户端都启动一个后台线程,通过执行特定的 lua 脚本,去不断地刷新 Redis 中的 key 超时时间,使得在任务执行完成前,key 不会被清除掉。

脚本如下:

其中,ARGV[1] 是可传入的参数变量,表示持有锁的系统的唯一值,也就是只有持有锁的客户端才能刷新 key 的超时时间。

到此为止,一个完整的分布式锁才算实现完毕。总结实现方案如下:

这个分布式锁满足如下四个条件:

当然,在 Redission 中的脚本,为了保证锁的可重入,又对 lua 脚本做了一定的修改,现在把完整的 lua 脚本贴在下面。

获取锁的 lua 脚本:

对应的刷新锁超时时间的脚本:

对应的释放锁的脚本:

到现在为止,使用 Redis 作为分布式锁的详细方案就写完了。

我既写了一步一坑的坎坷经历,也写明了各个问题和解决问题的细节,希望大家看完能有所收获。

最后再给大家提个醒,使用 Redis 集群做分布式锁,有一定的争议性,还需要大家在实际用的时候,根据现实情况,做出更好的选择和取舍。

原文 https://www.cnblogs.com/siyuanwai/p/16011836.html

阅读全文

与大厂面试题源码相关的资料

热点内容
java的队列类 浏览:357
荣耀手机带方舟编译器 浏览:496
表达式最小值算法 浏览:601
指南针多空资金源码 浏览:894
菜单上有灰色的命令 浏览:120
如何区分原神服务器 浏览:453
php多ip 浏览:583
易语言编译后打开需要dll 浏览:301
eos对称加密技术 浏览:16
程序员老公生活 浏览:814
mq语言编译器打不开 浏览:378
微信图片怎么查看文件夹 浏览:764
魔性解压游戏冒险王者 浏览:546
多级压缩气体功耗 浏览:151
德国大众空调压缩机价格 浏览:647
服务器怎么解决停电问题 浏览:673
安卓抖音如何看好友是否在线 浏览:443
中国银行选择编译环境 浏览:61
3dmax教程pdf 浏览:502
手机写易语言代码不用编译 浏览:736