我的朋友已经工作了 3 年,他过去一直担任前端工程师。
不幸的是,他被老板批评了,因为他在工作中犯了一个错误,这是一个非常简单但容易忽视的问题,我想也是很多朋友容易忽视的一个问题。
今天我把它分享出来,希望能够帮助到你,也希望大家遇到这个情况时,不要再放这个错误。
错误的描述
问题可能是这样的,Lily在公司负责一个重大项目,其中一个模块是显示一条与数字相关的信息,这是后端工程师界面返回的信息(仅举例)
@RestController
@RequestMapping("/getInfo")
public class YupiTestController {
@GetMapping
public Long getNum() {
return 123456789123456789L;
}
}
各位小伙伴,我们调用getInfo接口会返回什么信息呢?会是 123456789123456789 吗?
通过chrome浏览器的调试工具可以看到,似乎一切都和我们想象的一样,结果是123456789123456789。
但是,页面显示的结果是123456789123456780,最后一位是0而不是9。
这到底是怎么回事?这太奇怪了,是不是有点崩溃。
分析出现问题的原因
现在,我们一起来分析一下原因。
我尝试分析返回的数字,发现只有当数字超过16位时才会出现最后几位不一致的问题。
是不是因为数字太大,出现了精度损失?
Java语言中的Long类型是64位的,JavaScript语言中的Long类型是小于64位的吗?
天哪,JavaScript 似乎没有 Long 类型的数据!
实际上,在 JavaScript 中,我们使用 Number 来表示类型 number 的值。
Number 类型的总长度为 64 位。64位大致就是这样分配的,其中53位代表小数位,10位代表指数位,1位代表符号位。因此,Number 整数的表示范围为 -2^53 ~ 2^53。
让我们尝试在控制台上输出 JavaScript 中的最大值和最小值。
在其他语言中,例如 Java,Long 类型占用 64 个二进制位,最大值为 9223372036854774807 (2⁶³ — 1),长度约为 19 位。
在 JavaScript 中,由于 Number 类型的值也包含小数,所以最大值为 9007199254740993 (2^53 - 1),长度约为 16 位。
所以当Java向JSON返回16位以上的Long类型字段时,前端JavaScript获取的数据会因为溢出而失去精度。
如何解决这个问题呢?
也许我们可以尝试在前端解决这个问题,但我认为我们应该寻求后端工程师的帮助。
我们应该将可能超出范围的数字类型(Long)变量转换为字符串类型(String)。这个是我的个人处理方法。
遇到的问题
但随后没过多久,手机报警短信就飞来了。报警的服务器并不是刚刚的 Node 集群,而是『时序数据库』InfluxDB。
观察了下机器的内存与 CPU 开销走势,可以发现:应用接入后几小时内,机器的内存持续在上涨,最终造成 OOM,服务不可用。并且在管理后台上触发的一条 SQL 查询,几乎会耗尽整台数据库机器的 CPU 资源。
当时答主内心想的是:“完了,开发了几个月的产品, 上线后,尽然是这样的,年终奖没了不要说,明年回来,肯定要重新找工作了,我不仅坑了自己,还坑了我的主管” ,当时的内心可谓极度惶恐。
解决问题的方式
经历过一番内心挣扎后,觉得这样惶恐下去,解决不了问题,然后就冷静下来,尽可能地去查找时序数据库 InfluxDB 相关的分享文章,希望能从中找到一些性能瓶颈相关的资料。幸运的是,在饿了么团队的一次分享中,我找到一点引起问题的线索,然后开始从数据库表结构设计、计算查询优化等方面着手,逐步地进行了优化。
你有哪些好的处理方法呢,欢迎在留言分享。