数据库必修课:MySQL金额字段用decimal还是bigint?

前言

在开发涉及金钱的系统时,比如电商、支付、财务等,我们几乎每天都要和"金额"打交道。

在对接一些银行系统时,金额通常使用的是整数,这一点让我产生了疑惑!

作为开发者,首先面临的一个问题就是:MySQL里应该用什么数据类型来保存金额? 常见的选择有两个:decimalbigint

今天我们就来掰扯掰扯,这两者到适用于什么场景?


一、先认识一下两位选手

1. DECIMAL:为精确小数而生

DECIMAL 是 MySQL 中专门用于存储精确小数的类型。你可以指定总位数和小数位数,比如 DECIMAL(10,2) 表示一共 10 位数字,其中小数部分占 2 位,整数部分 8 位,范围是 -99999999.99 到 99999999.99。
特点 :存储的是精确值,不会像 FLOAT/DOUBLE 那样出现浮点误差。非常适合金额这种需要精确计算的场景。

sql 复制代码
CREATE TABLE orders (
    id INT PRIMARY KEY,
    amount DECIMAL(10,2)   
);
-- 最多存 99999999.99 元
INSERT INTO orders VALUES (1, 12345.67);

2. BIGINT:用整数来"曲线救国"

BIGINT 是 64 位整数,范围 -2^63 到 2^63-1(约 -9.22×10^18 到 9.22×10^18)。

如果用它存金额,通常需要约定一个最小单位,比如"分"(1 元 = 100 分)。这样,金额就变成了整数存入数据库,显示时再除以相应的倍数。
特点:整数运算极快,且没有精度丢失(因为就是整数),但需要应用层做单位转换。

sql 复制代码
CREATE TABLE orders (
    id INT PRIMARY KEY,
    amount_in_cents BIGINT   
);
-- 以"分"为单位,12345.67 元存为 1234567
INSERT INTO orders VALUES (1, 1234567);  

二、正面 PK:DECIMAL vs BIGINT

我们从几个关键维度来对比一下,看看谁更适合存钱。

1. 精度

  • DECIMAL:天生就是精确小数,存多少就是多少,不会多一位也不会少一位。
  • BIGINT:只要单位约定好(比如分),也是精确的。但要注意,如果业务需要更小的单位(比如厘),就要调整单位倍数,整数照样精确。

结论:两者都能保证精度,平手。

2. 存储空间

  • DECIMAL :存储空间取决于定义的位数。MySQL 将每 9 位数字打包成 4 个字节(例如 DECIMAL(10,2) 整数部分 8 位 + 小数部分 2 位 ≈ 10 位,占用 4 字节(前 9 位)+ 1 字节(剩余 1 位)= 5 字节左右)。位数越多,占用越大。
    • DECIMAL(M,D)占用的字节数随 M(总位数)变化;当 M ≤ 18 时,DECIMAL 通常 ≤ 8 字节 ,当 M ≥ 19 时通常 > 8 字节
  • BIGINT:固定 8 字节,无论数值大小。

MySQL 中 DECIMAL 的存储规则(简化说明)

MySQL 把十进制数字按 每 9 位用 4 字节 打包,剩余不满 9 位的那部分按下表占用字节数:

剩余位数 r(1..9) → 占用字节

  • 1--2 位:1 字节
  • 3--4 位:2 字节
  • 5--6 位:3 字节
  • 7--9 位:4 字节

总字节数 = floor(M / 9) * 4 + leftover_bytes(r),其中 r = M % 9(若 r=0,则 leftover_bytes=0)。

举例说明

  • BIGINT = 8 字节(固定)。
  • DECIMAL(10,2):M=10 → groups=floor(10/9)=1 → leftover r=1 → bytes = 1*4 + 1 = 5 字节(比 BIGINT 小)。
  • DECIMAL(17,2):M=17 → groups=1, r=8 → leftover_bytes=4 → bytes = 4 + 4 = 8 字节(等于 BIGINT)。
  • DECIMAL(18,2):M=18 → groups=2, r=0 → bytes = 2*4 = 8 字节(等于 BIGINT)。
  • DECIMAL(19,2):M=19 → groups=2, r=1 → bytes = 8 + 1 = 9 字节(比 BIGINT 大)。
  • DECIMAL(20,2):M=20 → groups=2, r=2 → bytes = 8 + 1 = 9 字节(比 BIGINT 大)。

结论 :阈值大致在 M = 18/19 :当 M ≤ 18 时,DECIMAL 的存储通常不会超过 BIGINT 的 8 字节;当 M ≥ 19 时,DECIMAL 会占用更多空间。

3. 性能

  • DECIMAL:CPU 对小数运算需要额外处理,尤其是做 SUM、AVG 等聚合时,比整数运算稍慢。
  • BIGINT:整数运算是 CPU 的强项,速度飞快。在千万级数据量的报表统计中,差距会很明显。

结论BIGINT 性能更优,尤其适合需要大量计算的场景。

4. 运算方便性

  • DECIMAL:可以直接在 SQL 里做加减乘除,结果也是精确小数,非常直观。
sql 复制代码
-- 直接算税率
SELECT amount * 0.01 AS tax FROM orders;  
  • BIGINT :SQL 里做运算要注意单位。比如要算两个金额的平均值,结果仍是整数分,如果想得到元,还得手动除以 100,而且除法可能产生小数,需要配合 ROUND 等函数。
sql 复制代码
-- 这样会得到浮点数,可能不精确
SELECT (amount_in_cents * 0.01) FROM orders;  
-- 更好的做法:先转换为小数,或者用整数运算再处理单位

结论DECIMAL 在 SQL 中更方便直观;BIGINT 则需要在应用层多一道转换,稍显麻烦。

5. 可读性与维护性

  • DECIMAL:数据库里直接看到的就是"元.角分",DBA 或运维查询时一目了然。
  • BIGINT:看到的是一串整数,得知道单位才能看懂。如果不小心搞混了单位(比如把分当成元),数据就全错了。

结论DECIMAL 更直观,降低沟通成本。

6. 范围

  • DECIMAL :最大可以定义 DECIMAL(65,30),几乎无限大,适合超大金额。
  • **BIGINT`:最大约 9.22×10^18,如果以分为单位,大约可存 9.22×10^16 元,也就是 9 万万亿,远超任何实际业务需求。以厘为单位也够用。

结论:两者范围都足够,除非是天文数字,否则无压力。


三、结合实例看选择

场景 1:普通电商订单

订单金额一般在几元到几十万元之间,需要两位小数(分)。用 DECIMAL(10,2) 非常直观,开发人员写代码时直接映射到 Java 的 BigDecimal,不会出错。

SQL 统计也很方便:

sql 复制代码
SELECT SUM(amount) FROM orders WHERE create_date = '2023-01-01';

结果直接就是总金额(元),不需要额外处理。

此时推荐 DECIMAL

场景 2:金融级系统,需要高精度

比如股票交易,可能涉及到"厘"(0.001 元)甚至更小的单位。如果用 DECIMAL,可以定义 DECIMAL(20,4) 等。但如果数据量巨大(每天上亿笔交易),统计报表时 DECIMAL 的聚合可能成为瓶颈。

此时可以用 BIGINT 以"毫"或"厘"为单位存储,例如 1 元 = 1000 厘。这样所有运算都是整数,性能极高。

sql 复制代码
-- 以毫为单位存储 -- 12345.678 元
INSERT INTO trades (amount_in_mill) VALUES (12345678);  

-- 统计时,应用层拿到整数除以 1000 得到元

但注意,除法会产生小数,需要在应用层用 BigDecimal 处理,避免精度丢失。
BIGINT 更适合高性能、海量数据的场景。

场景 3:分库分表或分布式系统

当数据量极大,需要分库分表时,BIGINT 作为整数更容易做分片键,且跨库聚合时整数运算更快。

此时可以考虑 BIGINT


四、千万不要用 FLOAT 或 DOUBLE!

很多人新手会用 FLOATDOUBLE 存金额,结果踩坑。因为浮点数在计算机中无法精确表示十进制小数,比如 0.1 在二进制中是无限循环,存进去再取出来可能变成 0.099999994。

做金融计算时,分毫之差都会导致对账不平。所以 绝对不要用浮点数存金额


五、到底怎么选?一张表帮你决定

因素 DECIMAL BIGINT(以最小单位存)
精度 精确小数,无误差 精确整数,需约定单位
存储空间 数字较大时占用的空间大,否则反之 固定 8 字节
性能(运算) 稍慢 极快
SQL 开发便利性 直接,直观 需要单位转换,易出错
应用层处理 简单(如 Java 用 BigDecimal) 需要手动转换单位
推荐场景 大部分业务系统,对性能不极端敏感 高性能、海量数据、需要整数运算的场景

我的建议:

  • 80% 的情况用 DECIMAL :开发简单,维护容易,不容易出错。配合 BigDecimal 类,完美匹配。
  • 20% 的情况用 BIGINT:当你确定性能是关键瓶颈,或者系统已经足够成熟,有统一的单位约定和转换工具时,可以考虑。比如一些大型金融系统、广告计费系统等。

六、总结

金额存储没有绝对的"银弹",DECIMALBIGINT 都能胜任,关键看你的业务场景。

如果是普通项目,选 DECIMAL 省心;如果是追求极致性能的大型系统,选 BIGINT 并做好单位封装。

无论选哪种,记得永远不要用 FLOAT/DOUBLE,这是原则问题。

相关推荐
彭于晏Yan2 小时前
LangChain4j实战三:图像模型
java·spring boot·后端·langchain
SimonKing2 小时前
跨越数据孤岛!SpringBoot使用JDBC调用Calcite联邦查询实战
java·后端·程序员
Java编程爱好者2 小时前
金融级数据库架构实战:MySQL Router + MGR 深度指南
后端
Java编程爱好者2 小时前
Java后端开发面试题总结(全网最全、最细、附答案)
后端
diaya2 小时前
麒麟V10 x86系统安装mysql
数据库·mysql
LaughingZhu2 小时前
Product Hunt 每日热榜 | 2026-02-24
大数据·数据库·人工智能·经验分享·搜索引擎
Java水解2 小时前
Spring应用事件机制实践
后端·spring
feathered-feathered2 小时前
测试实战【用例设计】自己写的项目+功能测试(1)
java·服务器·后端·功能测试·jmeter·单元测试·压力测试
Sincerelyplz3 小时前
【WebSocket】消息丢失的补偿/补发机制
后端·websocket