long乘法溢出静默绕回致负数,常见于ID生成、计费等场景;线索包括数据库bigint突变负值、时间戳排序错乱、ArithmeticException告警骤降、指标跳变;高危写法如字面量缺L后缀。
long 类型乘法溢出不会报错,结果直接“绕回”,比如
得到一个巨大负数。这种静默溢出在高频累加、id生成、时间差计算、计费统计等场景中极易埋雷,且上线后难以复现和定位。
看日志与监控中的异常信号
不依赖代码审查,先从运行态找线索:
数据库 bigint 字段写入值突变为负数(如订单金额、累计PV),尤其集中在某批任务或某类用户行为后
排序逻辑错乱:比如按时间戳升序查出的结果里,后面一条记录的时间戳反而比前面小几十亿
告警中出现
的频率骤降——反而是危险信号,说明本该触发的
被注释或绕过了
监控图表中某指标(如单日总耗时纳秒、分片步长)突然归零或跳变,但无服务重启记录
查代码中三类高危乘法模式
重点扫描以下写法,它们几乎都隐含 int→long 的中间截断或符号误判:
字面量未带 L 后缀
:如
,全程按 int 算,溢出后再转 long;应写成
或
DTO/接口参数类型不一致
:RPC 接口定义为
,但消费者传的是
,序列化框架(如 Hessian、Kryo)配置不当,自动截断高位,日志里看不出异常
混合符号乘法未预检
:如
,其中
,
,结果正溢出,但
判断失效,需用
或手动分情况检查
用工具链主动暴露问题
别等线上崩了再查,把检测左移到开发和测试阶段:
编译期:启用
,JDK 会警告类似
的字面量风险
单元测试:对所有乘法逻辑补边界用例,例如输入
,验证是否抛出预期异常或返回合理降级值
静态扫描:用 SonarQube 或 Alibaba Java Coding Guidelines 插件,规则
(危险的整数运算)可识别未防护的
运行时防护:在核心路径(如计费引擎)统一封装
,内部调用
,捕获
并打标 + 告警 + 切 BigInteger 降级
数据库与序列化层必须对齐精度
很多溢出其实在落地前就已发生,只是被掩盖:
MyBatis 映射中,
→ 即使数据库是
,Java 端也早被截成
,后续任何乘法都基于错误值
JSON 序列化(如 Jackson)默认把 long 当数字处理,若前端 JS 用 Number 接收(最大安全整数
),超限后精度丢失,后端再乘一次就彻底失真
解决方案:所有涉及数值传输的环节,显式声明类型——DTO 字段用
包装类而非
;数据库字段映射强制指定
;JSON 接口对大数字段加
转字符串传输
long.max_value * 2ArithmeticExceptionMath.multiplyExact1024 * 1024 * 1024 * 21024L * 1024 * 1024 * 21L longinta * ba = -1000000000000Lb = -1000000000000La * b > 0Math.multiplyExact-Xlint:allinteger number too largeLong.MAX_VALUE, 2, -1, 0, Long.MIN_VALUEjava:S2184long * longSafeLongMath.multiply(a, b)Math.multiplyExactArithmeticExceptionBIGINTint2^53-1LonglongjavaType="java.lang.Long"@JsonFormat(shape = JsonFormat.Shape.STRING)