电商金额字段必须用decimal而非float/double,因后者二进制近似存储导致0.1+0.2≠0.3等底层失真,引发对账失败、where匹配失效及小数截断;decimal(10,2)是电商通用硬性标准。

电商项目中金额字段必须用 DECIMAL,不是“建议”,而是避免财务对账失败、订单金额错乱、促销计算偏差的硬性技术底线。
为什么FLOAT/DOUBLE一存就错?
浮点类型本质是二进制近似——0.1 在二进制里是无限循环小数,存进去就失真。这不是显示问题,是底层存储和运算逻辑不同:
-
FLOAT走 CPU 浮点单元,DECIMAL由 MySQL 用定点算法逐位计算 -
SELECT 0.1 + 0.2返回0.30000000000000004;而DECIMAL(10,2)确实返回0.30 - WHERE 条件匹配会失效:插入
99.99后查WHERE amount = 99.99可能查不到,因为实际存的是99.98999786376953
DECIMAL(M,D) 不写参数等于自毁
DECIMAL 不显式指定精度时,默认是 DECIMAL(10,0),小数位为 0——这意味着:
-
INSERT INTO orders(amount) VALUES(99.99)会被截断成99,直接丢掉两位小数 - 电商常用
DECIMAL(10,2)(最大99999999.99),高精度如汇率可用DECIMAL(12,4) - M 最大 65、D 最大 30,但别盲目设大:
DECIMAL(65,30)占约 33 字节,DECIMAL(10,2)仅 5 字节
BIGINT 存“分”看似省事,其实埋雷
用 BIGINT 存“分”单位(如 12345.67 元 → 1234567)确实无精度丢失,但代价明显:
- 所有金额操作都需应用层做单位换算:入库前 ×100、展示前 ÷100,漏一处就出错
- 数据库层无法直接约束小数位数,比如误存
123456789(即 1234567.89 元)没人拦得住 - 聚合计算(如 SUM)结果仍是“分”,报表或下游系统容易忘记除以 100,导致金额放大百倍
- 跨服务协作时,字段语义模糊:“这个
amount是元还是分?”靠文档?靠约定?靠人肉 review?
上线后改 DECIMAL 精度要锁表重写
一旦上线,修改 DECIMAL 的 M 或 D 值不是 ALTER COLUMN 那么简单:
-
ALTER TABLE t MODIFY amount DECIMAL(12,2)会触发全表重建,大表可能锁表数小时 - 如果原值超新精度(如原存
999999999.99,想缩成DECIMAL(10,2)),MySQL 直接报错Out of range value - 所以建表第一版就必须定死:电商用
DECIMAL(10,2),金融账户余额用DECIMAL(15,2),一步到位
最易被忽略的一点:精度错误在单条记录里看不出问题,只有当订单累计、分润结算、财务月结时,误差才会指数级放大——等发现时,往往已无法回溯原始数据。











