MySQL 中 DECIMAL、FLOAT 和 DOUBLE 有什么区别?金额字段应该怎么选?
简化版
DECIMAL 是定点数,按十进制精确存储,适合金额、费率、结算等需要精确计算的场景。FLOAT 和 DOUBLE 是浮点数,使用二进制近似表示,适合科学计算、测量值、坐标等允许误差的场景。金额字段通常选 DECIMAL,或者用整数分、厘等最小单位存储,避免浮点误差。
详细版
浮点数的问题不是 MySQL 独有,而是二进制浮点表示天然无法精确表示很多十进制小数。比如 0.1、0.2 在二进制中可能是无限循环,参与计算后会出现误差。
DECIMAL(M, D) 中,M 表示总位数,D 表示小数位数。它更适合业务精确值,但存储和计算成本通常高于浮点。实际设计金额时还要关注币种、最大金额、小数位、四舍五入规则和溢出风险。
面试回答不能只说“金额用 DECIMAL”,还要解释为什么,以及整数最小单位方案的取舍。
完整版教学
一、定点数和浮点数
定点数强调十进制精确。
浮点数强调表示范围和计算效率。
业务金额通常要求精确到分或厘。
科学测量通常允许一定误差。
所以选择字段类型要看业务容错。
二、DECIMAL 的含义
DECIMAL(10, 2) 表示最多 10 位数字,其中 2 位小数。
它能表示从整数位到小数位的固定精度。
如果金额最大可能很大,要提前规划总位数。
如果小数位不足,写入时可能发生四舍五入或报错,取决于 SQL 模式。
三、FLOAT 和 DOUBLE 的特点
FLOAT 通常是单精度浮点。
DOUBLE 通常是双精度浮点。
它们都可能存在二进制表示误差。
比较浮点值时不适合直接用等号判断精确相等。
聚合计算后误差可能放大。
四、类型对比
| 类型 | 特点 | 适合场景 |
|---|---|---|
DECIMAL | 十进制精确,定点 | 金额、税率、结算 |
FLOAT | 范围较大,有近似误差 | 测量值、非关键指标 |
DOUBLE | 精度高于 FLOAT,但仍近似 | 科学计算、坐标 |
| 整数最小单位 | 用分、厘等整数存 | 高频计算、金额精度固定 |
五、金额字段设计
CREATE TABLE payment_order (
id BIGINT PRIMARY KEY,
amount DECIMAL(18, 2) NOT NULL,
currency CHAR(3) NOT NULL
);
如果业务涉及多币种,不同币种的小数位可能不同。
如果涉及积分、虚拟币或高精度资产,要单独确认精度。
六、整数最小单位方案
有些系统会用 amount_cent BIGINT 存储分。
好处是计算快、比较简单、避免小数精度问题。
缺点是展示时需要转换,且多币种小数位规则要额外管理。
如果精度不是固定 2 位,整数方案也要定义 scale。
金额字段不是单纯类型选择,还包括单位、币种、舍入规则和审计要求。
七、误区和追问
- 误区:DOUBLE 精度高,所以适合金额。 DOUBLE 仍是近似浮点,不适合精确金额。
- 误区:DECIMAL 写得越大越好。 过大精度会增加存储和计算成本,也可能掩盖设计不清。
- 误区:金额只有人民币两位小数。 多币种、虚拟资产和计费系统可能需要不同精度。
- 追问:为什么 0.1 + 0.2 会有误差? 二进制浮点无法精确表示很多十进制小数。
- 追问:DECIMAL(10,2) 最大能存多少? 总共 10 位,其中 2 位小数,整数部分最多 8 位。
- 追问:金额用 BIGINT 存分可以吗? 可以,但要统一单位、展示转换和多币种精度规则。
八、面试收束
回答金额类型时,推荐 DECIMAL 或整数最小单位。
同时解释浮点误差、精度规划、币种和舍入规则,这样才像真实项目设计。