一、前言
Math.abs函数是jdk中提供的一个用来返回入参绝对值的函数,也就是你输入一个负数,它会返回其对应绝对值正数,这个在大部分情况下是这样,但是特殊情况下,还是会返回负数,为何那?且往下看。
java学习交流:737251827 进入可领取学习资源及对十年开发经验大佬提问,免费解答!
二、场景介绍
在数据库中当数据量比较大时,我们会把一个表分为多个分表,或者把一个库分为多个库,那么分表按照什么来分那?这里就有一个分表键的概念,比如用户信息表,可以按照用户id作为分表键,比如消息重试表,可以使用消息id来做。
当分表键是字符串时候,我们可以使用字符串的hash值作为分表键。比如我们有100个分表,那么我们可使用hash(分表键)%100取模,根据取模后的值确定当前记录放到哪个分表。
但是字符串的hash值有可能是负数,所以我们需要使用Math.abs取分表键hash值的绝对值%100。这样看起来很好,但是还是会有问题。
因为字符串的hash值是int类型的,所以会取Math.abs(int a)作为取绝对值函数,当a为0x80000000时候,我们会看到其结果为:-2147483648,竟然为负数,然后如果对100取模,则会得到-48,根据-48则我们无法找到对应的分表,因为分表后缀为0-99,所以会出错。
为何会出现上面问题那?其实Math.abs(int a)函数注释已经说明了:
Note that if the argument is equal to the value of Integer.MIN_VALUE, the most negative representable int value, the result is that same value, which is negative.
也就是如果参数是整数最小负数,则Math.abs(int a)方法会返回最小负数本身,那么该方法为啥这样做那。其实是因为最大正数为2147483647,而最小负数为-2147483648,对最小负数加绝对值后,已经超过了最大正正数所表达的范围。
那么如何解决那?第一我们可以使用Math.abs(long a)函数,也就是把hash值从整形转换为long型。第二我们可以对hash值做映射,如果hash值为正数最小负数则把其映射为一个固定的正数值即可。
三、总结
细节决定成败,多看,多思考,养成看源码,看使用的工具的注释的习惯,可以极大程度减少线上故障的可能。