Snowflake ID 跨前後端的整數精度問題
情境
最近在開發一款 Pos 系統的 Side Project, 由於是走微服務架構, 所以想採用 SnowflakeID 來確保全局唯一且趨勢遞增,避免自增 ID 對分庫分表的限制。
概念如下:
| 1 bit | 41 bits | 10 bits | 12 bits |
|---|---|---|---|
| 符號位 | 時間戳(ms) | 機器ID | 序列號 |
起始時間 2025-07-22 00:00:00 UTC = 1753056000000
snowflake.atacenter-id=1 # 資料中心 ID (1-2)
datacenter-id: 1 # 資料中心 ID (1-2 預設台北高雄各一台)
machine-id: 3 # 機器 ID (1-2 預設每個 DC 各一台 VM)
大家對實作若有興趣可以參考我沒勘誤的文件(汗…) Casha Pos 內的 Snowflake ID
結果在測試的時候踩了一個很基本的坑, 由於後端直接回傳 id 為 Long 到前端以 number 承接, 導致 13729683518521345 在瀏覽器變成 13729683518521344。
原因
JavaScript 的 Number 精度限制:
• JSON 在瀏覽器端預設會用原生 JSON.parse,把數字解析成 JavaScript 的 Number(IEEE-754 double)。
• Number 的「安全整數」範圍只有:-2^53 ~ 2^53(上限 9007199254740991)。
• 只要超過這個範圍,parse 時就會「四捨五入」到最近可表示的整數,導致尾數失真。
• 例:13729683518521345 > 9007199254740991,所以 parse 後會被四捨五入成 13729683518521344。
解法
簡單來說就是修改 Long -> String, 目前有兩個做法:
第一是直接在 Bff 的 Service 內置換 dto, 第二是在 Rs 上加入 @JsonSerialize(using = ToStringSerializer.class) 但建議還是乖乖換比較好。
結論
原先的專案多數用 Auto Increment, 用到不小心忽略這個細節, 果然還是要定期複習前端, 真是汗顏…