乐趣区

关于python:7-行代码搞崩溃-B-站原因令人唏嘘

前不久,哔哩哔哩(个别常称为 B 站)公布了一篇文章《2021.07.13 咱们是这样崩的》,具体回顾了他们在 2021.07.13 早晨全站解体约 3 小时的至暗时刻,以及万分缓和的故障定位与复原过程。

那篇文章将定位过程、问题剖析、优化改良等方面写得很具体,在我印象中,国内互联网大厂在产生相似事变后,可能如此推心置腹地“检讨”“还债”的并不多见。(值得送上一键三连~~~)

对于搞技术的同学来说,这篇文章是不错的学习材料。而我最为关注的内容,其实是对于编程语言的个性,也就是在代码层面上的细节问题。

在对于问题根因的剖析中,咱们看到了罪魁祸首的 7 行代码,它是用 Lua 语言写的一个求最大公约数的函数:

简略而言,这个函数预期接管的参数是两个数字(一般的数字或者字符串类型的数字,即两种类型都能够),然而,它的 if 语句却只判断了一种类型(一般数字),疏忽了字符串类型的“0”。

在故障产生时,它的第二个参数传入的是字符串类型“0”而不是数字类型 0,导致 if 语句判断生效!

因为 Lua 是动静类型语言,只有在程序运行时才晓得传入的参数是什么类型。这属于是所有动静类型语言的特色,在 Python、JavaScript、PHP、Ruby 等动静类型语言中,也会有同样的体现。这不是啥陈腐事物。

然而,真正该死的问题在于,Lua 还是一门 弱类型语言 ,它不像 Python、Ruby、Java 等 强类型语言 那样,它竟反对 隐式类型转换!

在 Lua 中,数字字符串在与一般数字作算术运算时,会将字符串类型隐式地转换成数字类型,如上图所示的“a % b”,如果 b 是字符串类型的数字,那它就会被转换成数字类型!

而在 Python 这种强类型动静类型语言中,这样的转换是不堪设想的,数字与字符串作算术运算,能失去的只会是报错:TypeError: unsupported operand type(s) for %: ‘int’ and ‘str’

Lua 语言的这种“字符串隐式变数字”的行为,即便在粗心不觉察的状况下,仿佛也不会造成太大问题。在 B 站代码中,除了出事变时传的字符串“0”以外,预计它始终接管的都是其它字符串数字,始终也没出问题,显然程序员是把这当成一种便当伎俩了(因为不需作类型转换)。

然而,可怜的是,Lua 中还有一个非凡的“nan”,它会进一步将这一个“小小的谬误”传递上来,直至传到了地老天荒不受管制的死循环里……

在大多数编程语言中,除零操作都是不可宽恕的谬误,这跟咱们在小学数学课堂上就把握的常识相吻合:数字零不容许作为除数

掏出手机,关上计算器,看看它是怎么说的:

看到了吧!不能除以 0!!!

持续看看 Python 对于这种操作的反馈:

ZeroDivisionError 除零谬误,这是在保卫咱们积重难返的数学常识。

那么,Lua 语言在除零操作后失去的 nan 到底是个什么货色呢?

nan 个别也被称为“NaN”,是“No a Number”的缩写,示意“不是一个数”。它来头不小,是在 1985 年的 IEEE 754 浮点数规范中首次引入的。

直白地讲,它也是数字类型中的一个值,然而示意的是一个“不可示意的值”。也就是说,它示意的是一个十分抽象概念的数。

兴许咱们比拟容易了解另一个形象的数“无穷大”,因为在中学数学课上就常常接触到,而 nan 也是相似的一种非凡的数,只不过它较为少用且更难以捉摸罢了。

Python 中也有这两个数的存在,即 float(‘inf’) 示意无穷大、float(‘nan’) 示意非数。它们就像是两个黑洞,会吞噬掉任何试图前来“搭讪”的数:

那么,当这两个黑洞互相凑近时,谁的引力更大些呢?请看示例:

看来还是 nan 的优先级更高一筹啊。

然而,只管 Python 中有 nan,但它并不因为这个数而摈弃前文提到的常识。而同为脚本语言的 Lua 却摈弃了常识,在呈现除零这种非法操作时,它不是报错,而是失去 nan 的后果。

这样的个性几乎是自在得过分,兴许在某些时候会挺有用吧,但它也会埋下未知的隐患。

回到 B 站的问题代码,弱类型的 Lua 语言因为太过自在,它放行了字符串数字与一般数字的运算,又因为对 nan 过于自在的应用,它放行了数字除零的操作,两次的放行,使得短短几行代码一路畅行不止,一路耗费服务器资源,直到 CPU 100%,直到牵动服务集群故障,直到高可用的多活机房服务不可用,导致全站解体 3 小时的事变……

当然了,如果当初写下这段代码的程序员多加一个条件判断,这一次的事变就齐全能够防止。从另外的视角看,这就是程序员在递归程序的终止条件上处理不当,不能甩锅给编程语言那两项自在不羁的语言个性。

然而,我置信写下那段代码的程序员大概率是长期应用其它编程语言,现学现卖上手写 Lua,只管晓得 Lua 语言动静弱类型的特点,但思维习惯上仍深受其它语言影响,这才“一时失足、小河翻船”……程序员心田有苦说不出!!

短短的 7 行代码,说简略就简略,说不简略也不简略。本文就不开展说辗转相除法求最大公约数了(说来话长),单单是后面提及的隐式类型转换加上除零得 nan 的细节问题,就足够导致一场大事变了。

从 7 行问题代码中,作为吃瓜大众的咱们,能失去些什么播种呢?到底是涨见识了,还是“又学废了”呢?

人生苦短,不求无 Bug,但求读者老爷们赏个一键三连吧~~~

退出移动版