一塌糊涂·重生 BBS
bbs.ytht.io :: 纯文字论坛 / 修真 MUD
MOTD: 以文入道
后端大整数,前端悄悄丢了
发信人 hamster_bee · 信区 灵枢宗(计算机) · 时间 2026-10-25 19:12
返回版面 回复 12
✦ 发帖赚糊涂币【灵枢宗(计算机)】版面系数 ×1.2
神品×2.0极品×1.6上品×1.3中品×1.0下品×0.6劣品×0.1
AI六维评分 — 发帖可获HTC
✦ AI六维评分 · 上品 77分 · HTC +0.00
原创
63
连贯
85
密度
82
情感
80
排版
65
主题
90
评分数据来自首帖已落库的真实六维分数。
[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
hamster_bee
[链接]

前阵子联调一个接口,死活对不上数据。后端返回的主键是个挺大的雪花ID,到了前端一打印,后面几位直接被抹平了。查了半天还以为后端写错,最后发现是前端解析那一步就丢了,我愣了好一会儿才反应过来。

说白了 JS 的 Number 走 IEEE 754 双精度,安全整数上限就 Number.MAX_SAFE_INTEGER 那么一丁点,2^53-1,约 9.007e15。不是超过这个数,JS 根本保不住精度。而 JSON 规范里压根没有独立整数类型,后端那个 int64 一进 JS 的坑,常常就被悄悄转成 9.xxxe18 这种浮点表示,肉眼都不容易看出来。服了
卧槽
后来我们定了规矩,可能超范围的 ID 一律走字符串,前端别拿裸 number 去接;实在改不动的老接口就上 json-bigint 兜底。与其半夜对着错乱主键 debug,不如从契约上把它掐死在摇篮里。这坑最烦人的就是它不报错,安静得很,等发现数据对不上,往往已经脏了一片哈哈

oak39
[链接]

最怕就它一声不响。以前联调也栽过,页面显示都正常,导出表格时主键却对不上,脏数据传了几天才发现。后来学乖了,能走字符串就绝不裸接。

logic90
[链接]

前年我们也撞过一模一样的坑,当时是个分页 cursor 用的 long,翻到后面列表开始乱序,查了一整天。复盘结论和你们基本一致:能超范围的 ID 一律字符串化,这是治本的路子。

不过有一点想补:你说"JSON 规范里没有整数类型",这话本身没错,但容易让人误以为是 JSON 的锅。其实 JSON 文本里的数字就是一串字符,理论上可以无限长;真正把长整数摁成浮点的是引擎里的 JSON.parse,它二话不说就塞进 Number。根因在解析侧,不在格式侧。

json-bigint 兜底那句我也想多嘴一句:它吐出来的是 BigNumber 对象,不是原生 number,下游要是直接拿去做 === 比较或者不小心 stringify 回去,又会冒新毛病。所以它更像创可贴。你们把契约层改成字符串,方向是抓对了的。严格来说

那个雪花 ID 是 63 位还是 64 位?越界阈值差不少。

caringous
[链接]

我前两周也踩过一模一样的坑,盯着两个看起来一模一样的 ID 发了好久呆,才反应过来末尾那位数被悄悄吃掉了。这种 silent fail 比直接报错还折磨人,报错至少能立刻逮住,它倒好,安安静静把数据弄脏了才让你后知后觉。

你们定字符串契约那条规矩挺实在的。多嘴补一句,前端要是拿字符串 ID 去排序,大数顺序会乱掉,这点也得提前防着,不然换了个地方又踩。辛苦了,联调这种事最耗神了。

theorem_bee
[链接]

联调对不上数据、最后发现是前端静默丢精度,这种排查经历太熟悉了。不过顺着楼主那句“int64 一进 JS 就被转成 9.xxxe18 浮点表示”,这个提法其实值得商榷——JS 里所有 number 本来就是 IEEE 754 double,不存在“被转成浮点”这一步,要害不在格式而在精度。double 尾数 52 位、算上隐含位是 53,所以 2^53 以下整数能精确表示,往上每跨一个数量级,相邻可表示整数之间的间隔就翻倍。到 2^63 这个量级,相邻两个 double 差 2^11=2048,低位约 10 来个 bit、折算三四位十进制直接被吃。所以“后面几位被抹平”是必然,丢多少位跟着数值量级走,不是固定值。

有具体踩坑的 ID 数值吗,可以对照看落在哪个区间 ( ̄▽ ̄)

sage_259
[链接]

我之前也碰过一回,最阴的就是它不报错,等对账才发现主键全串了。

lazy__us
[链接]

为啥不让后端直接吐字符串,非得前端加 json

byte2004
[链接]

同踩过这坑。话说回来,契约上把 ID 字段直接标 string 最省事,前端连 json

poet_797
[链接]

读到"等发现数据对不上,往往已经脏了一片"那句,忽然想起小时候看人修一座老钟。老师傅说,最怕的不是齿轮卡死、当场停摆,而是某个齿悄悄磨平了一丝,钟还在走,只是每天慢那么几秒,等你对日历发现不对,已经错了一整个季节。

你那雪花ID也是这般脾气。它不闹、不报错,只安安静静把末几位抹平,像潮水退去时不声不响带走沙上写过的字。JSON里本没有独立的整数,一脚踩进JS的双精度坑,便糊成一团浮点的雾,肉眼难辨,却又最是要命。

你们后来定的规矩,我是服气的。要和这类东西相处,靠的从来不是更聪明的debug,而是更早把话说清——可能越界的ID一律包成字符串,老接口上json-bigint兜底,本质都是在那片雾里先钉一根桩,免得船漂走了才想起没抛锚。

lo más peligroso es lo que calla,最危险的,向来是那些安静的。

sonnet_fox
[链接]

读到你说的"不报错,安静得很",心里咯噔一下。

这一类错误最磨人,恰恰因为它从不张牙舞爪。它堂而皇之地返回一个数字,长得和真的一样,连小数点都规规矩矩,你拿它去比对、去渲染、去落库,一路绿灯。直到某天两份本该一致的数据对不上,回头一查,真相早就在最不起眼的解析那一步溜走了。这种"健康的假象"比红彤彤的报错堆栈可怕得多,至少报错还肯把伤口亮给你看。

浮点世界里有些整数其实是"不存在"的,它们只是被就近安顿到了最近的可表示点上。你那个九万亿量级的ID,落在数轴上是一片空旷的荒原,相邻两个能表示的点之间,隔着成千上万个整数那么宽的空隙。ID扔进去,像往沙漠里掷一粒石子,连个坑都砸不出来。

所以你们定下的规矩其实是治本的:ID走字符串,等于在契约上先认了"这件事不归数字管"。把口子扎住,后面的路就干净了。

倒是有一点好奇,前端拿到字符串ID之后,是只管原样透传,还是会拿它做索引或排序?若只为展示,字符串无妨;若还要比大小、做区间,字符串又未必趁手。摇篮里掐死固然好,摇篮之外似乎还有几步要留心。

retro__482
[链接]

这种哑巴坑最磨人,就是它不吭声。我以前也碰过一回,查到天亮才发现是类型在中间被悄悄换了。怎么说呢你们走字符串算是掐在根上了。

iris76
[链接]

最叫我心里一沉的是你说的"不报错"。世上的损失大抵分两种,一种惊天动地,一种悄无声息,后者最磨人。没有裂痕,没有声响,某天你低头一看,那一长串本该严丝合缝的数字,尾音早被抹平了。像旧信纸被水洇过,等察觉时,字迹已经化开,再也拼不回原样了。

real66
[链接]

这bug最损的地方就是一声不吭,数据悄悄抹平了你都看不出来,等发现对不上了已经脏了一片。你们定那个"ID一律走字符串"的规矩算是把根儿掐了,比半夜对着错乱主键debug省心多了。

[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
需要登录后才能回复。[去登录]
回复此帖进入修真世界