TiDB AUTO_RANDOM 超大主键前端精度丢失排查:JavaScript Number 限制与解决方案

发布于:2025-06-28 ⋅ 阅读:(10) ⋅ 点赞:(0)

前端长整型主键“失踪”记

——一次 ArrayIndexOutOfBoundsException 的排查全过程


一、事故现场

最近在维护 SMS-OFFICE 后台系统时,运维同事反馈:
点击「短信详情」或「邮箱账号详情」时,偶尔弹窗空白、日志报错:

java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0

抓了一条完整链路,发现请求 URL 是:

/secured/lookSmsMessage.html?contentId=7205759403792883606

而数据库中明明存在这条记录!为什么 SELECT … WHERE content_id = ? 一条都查不到?


二、线索一:前端 JSON 对象异常

打开浏览器 DevTools ➜ Network ➜ Preview,注意到 DataTables 接口返回:

{
  "contentId": 7205759403792883606,
  …
}

字段是 裸数字,但和数据库里的大整数完全一致。那问题出在哪里?


三、线索二:JS Number 的“安全整数”

JavaScript 只有一种数值类型 Number,实现为 IEEE-754 双精度浮点。
最大安全整数

Number.MAX_SAFE_INTEGER === 9_007_199_254_740_991

任何大于此值的整数,低位全部失真

主键 十进制 是否 > 9e15
720 575 940 379 288 3606 ≈ 7.2 × 10¹⁸
288 230 376 151 801 751 ≈ 2.88 × 10¹⁷

在控制台验证一下:

JSON.stringify(7205759403792883606)    // "7205759403792884000"

浏览器早在解析 JSON 时,就把它“改写”成了一个近似值 ——
后端再跟这个错误 ID 去查数据库,当然一条也没有。


四、导致的连锁反应

  1. DataTables 渲染
    列内 contentId 被当成 number 存进 rowData,精度丢失。

  2. 按钮拼接 URL

    onclick="lookData(' + rdata.contentId + ')"
    

    结果 rdata.contentId 已经变成错误值。

  3. 后端查询为空list.get(0)IndexOutOfBoundsException.


五、最终定位:纯前端精度问题

  • 数据库:MySQL / TiDB 的 BIGINT,范围 ±9 × 10¹⁸,没问题。

  • 后端:Java long 同样能装下。

  • 真正掉链子 的是浏览器的 Number 精度。


六、修复方案

1. 让主键永远当「字符串」

- onclick="lookData(' + rdata.contentId + ')"
+ onclick="lookData(\'' + rdata.contentId + '\')"
  • 只要在拼接时加一对 引号,JS 就会把它当作字符串传递。

  • 后端 Spring MVC 可以自动把字符串转 Long
    如果你愿意,也可以把参数类型改成 String,然后 Long.valueOf()

2. Mapper / ResultMap 调整

<result column="content_id" property="contentId" jdbcType="VARCHAR"/>

3. 防御式编码

if (list.isEmpty()) {
    throw new NotFoundException("记录不存在");
}

4. 全链路自检脚本

// Chrome DevTools 快速检测 big int
function hasUnsafeId(json, key='contentId'){
  return json.some(r => Math.abs(r[key]) > Number.MAX_SAFE_INTEGER);
}

七、最佳实践小结

建议
前端 所有主键字段一律用字符串;不要对大整数做数学运算。
后端 接口、DTO、Mapper 保持与前端一致的 String/Long;空列表先判空。
数据库 如需水平分片可继续用 AUTO_RANDOM;只是传输层别当数值。
日志 记录原始请求参数,便于比对是否被截断。

八、一句话总结

当你在前端看到 18 位以上的主键时,第一反应:“加引号!”

避免 JS Number 精度地雷,你的分页、详情、批量操作都会更稳。


网站公告

今日签到

点亮在社区的每一天
去签到