开发金流或电商系统,设计数据库存储金额字段时,你脑中是否曾闪过“金额就用 FLOAT 或 DOUBLE 存吧”的念头?
如果是的话,快停下你的手!你的系统可能正在偷偷漏钱。
为什么金融系统绝对不能用浮点数?一点点看似微不足道的精度误差,在庞大的交易量与时间累积下,可能会酿成无法挽回的灾难。
那我们到底该用什么来存钱呢?
为什么 FLOAT 是财务系统的毒药?
在电脑的世界里,数字是用二进制表示的。FLOAT(浮点数)在表示某些十进制小数时,其实是一种“近似值”。这就像是你试图用一把粗糙的电锯去切精致的蛋糕,不管你怎么小心,边缘总会掉下一些屑屑。
最经典的例子就是:0.1 + 0.2 在电脑里往往不等于 0.3。如果你处理的是几百万笔交易,这些“0.00000000000000004”的微小误差累积起来,账面就永远对不齐了。记住:
在金钱面前,任何“近似值”都是灾难。
会计师的精致账本:DECIMAL 的优势
如果你想要一个“看得到、对得到”的精确方案,DECIMAL 是数据库原生支持的定点数,也是业界最标准的做法。
DECIMAL 就像是会计师手中的精致账本,它会精确地把整数和小数分开存储,确保 0.1 + 0.2 绝对等于 0.3。
业界黄金比例:DECIMAL(19, 4)
通常我们会建议使用 DECIMAL(19, 4):
- 19:代表总共可以存 19 位数字(精度)。
- 4:代表小数点后保留 4 位。
为什么要留 4 位小数?因为在计算利息、税率或汇率时,中间过程往往会产生超过 2 位的小数位,多留 2 位缓冲区可以增加运算的精确度,最后再依照业务需求四舍五入即可。
这样的容量,甚至足以让你买下好几个地球的 GDP 总和!
实际金融场景够不够用?
以 DECIMAL(19, 4) 为例:
- 整数位:15 位
- 最大金额:999,999,999,999,999
- 美元换算:约 999 兆美元
| 参考值 | 金额 |
|---|---|
| 美国 GDP | 约 27 兆美元 |
| 全球 GDP 总和 | 约 105 兆美元 |
| 全球财富总和 | 约 454 兆美元 |
DECIMAL(19, 4) 已经可以容纳远超全球财富总和的数字,对绝大多数金融系统来说完全足够。
各大数据库支持的最大 DECIMAL 的精度上限
| 数据库 | 最大 precision |
|---|---|
| MySQL / MariaDB | 65 |
| PostgreSQL | 131072(整数位)+ 16383(小数位) |
| SQL Server | 38 |
| Oracle | 38 |
游乐场的代币机器:BIGINT 最小单位法
如果你正在追求极致的性能,或是像 Stripe、支付宝这种具备超高并发需求的系统,那么 BIGINT(整数存储法) 可能是你的首选。
这种做法就像是游乐场的代币机:不管你投入多少钱,机器都会把它换算成“最小单位”来存储。例如:
- 100.50 美元 → 存储为
10050(美分) - 100 台币 → 存储为
100(元)
为什么选 BIGINT?
| 原因 | 说明 |
|---|---|
| 速度极快 | 整数加减是 CPU 的拿手好戏,运算性能通常比 DECIMAL 快上许多。 |
| 空间效率 | 固定占用 8 bytes,非常适合超大型数据库。 |
不过,缺点是可读性较差,你打开数据库看到 10050 时,必须在脑袋(或代码)里自动除以 100。
终极对决:该怎么选?
要决定用哪一个,我们可以从 “查询频率” 和 “系统规模” 来考量:
| 比较维度 | DECIMAL | BIGINT |
|---|---|---|
| 可读性 | 极佳(直接看数字) | 较差(需手动换算) |
| 运算速度 | 普通 | 极快 |
| 适用场景 | ERP、内部财务系统、一般电商 | 高频交易、超大型微服务、Stripe 风格 API |
务实建议
| 适用场景 | 建议字段 |
|---|---|
| 一般电商、公司内部报表系统,且会计人员需要直接下 SQL 来查账 | DECIMAL(19, 4) |
| 高频交易系统、或需要极致扩展性 | BIGINT |
总结
简单来说,不管你选哪一种,千万、绝对、永久禁止使用 FLOAT 存钱! 选择正确的字段类型,才能让你的系统在金钱运算上稳如泰山。