1. 从一次“金额计算错误”说起:为什么类型选择如此重要
几年前,我接手过一个财务系统的优化项目。系统里有个报表,每天凌晨计算用户账户的日终余额,逻辑并不复杂,但偶尔会报出几分钱的差额,对不上总账。团队排查了很久,最后发现问题出在一张核心交易表的一个字段上。这个字段用来存储交易金额,开发同学当时可能觉得“金额嘛,肯定要精确”,于是大手一挥,全部定义成了 NUMERIC(20,6)。这个选择本身在精度上没问题,但在这个高频写入、需要实时汇总的交易表上,海量的 NUMERIC 类型计算导致了显著的性能开销,并且在某些复杂的多级汇总路径中,微小的性能延迟和并发问题,最终在特定时间窗口下放大成了肉眼可见的精度舍入争议。
这个坑让我印象深刻。在 PostgreSQL 里,面对整数或需要精确表示的数字时,我们常常会陷入两难:是用灵活的、绝对精确的 NUMERIC(包括它的同义词 DECIMAL),还是用更高效的整数类型(INT, BIGINT)?这绝不是拍脑袋就能决定的。选型错误,轻则让数据库“负重前行”,查询慢如蜗牛;重则像我们那样,引发数据一致性质疑,动摇业务根基。
简单来说,NUMERIC 是“精确型选手”,它能像保管保险箱里的金币一样,一分一毫都不差地存储你给它的数字,特别适合金融、科学计算等场景。而 INT/BIGINT 是“速度型选手”,它们是 CPU 的“母语”,计算起来飞快,但只能表示整数,且有固定的范围限制。本文,我们就来彻底掰扯清楚这两位选手的脾性,让你在下次建表时,能胸有成竹地做出最合适的选择。
2. 核心概念辨析:NUMERIC/DECIMAL 与 INT/BIGINT 到底是什么?
在深入对比前,我们必须把基本概念打牢。很多人刚开始会混淆,尤其是看到 DECIMAL 和 NUMERIC 时。
2.1 完全相同的双胞胎:NUMERIC 与 DECIMAL
首先,请你记住一个最重要的结论:在 PostgreSQL 中,NUMERIC 和 DECIMAL 是完全相同的数据类型,它们是一对同义词。 官方文档白纸黑字写着:“The types decimal and numeric are equivalent.” 所以,NUMERIC(10,2) 和 DECIMAL(10,2) 在 PostgreSQL 眼里没有任何区别,你可以根据个人习惯或团队规范任选其一。下文我将主要使用 NUMERIC 来指代它们。
NUMERIC 是一种任意精度的数值类型。它的核心特点是“精确存储”,你存入什么数字,它就会原封不动地记住,不会像浮点数那样因为二进制表示而产生微小的误差。它通过两个参数来定义:NUMERIC(precision, scale)。
- 精度(Precision):指数字总共的位数(不包括小数点)。例如
123.45的精度是5。 - 标度(Scale):指小数点后的位数。例如
123.45的标度是2。
你可以创建一个没有小数部分的 NUMERIC,例如 NUMERIC(10,0),这表示一个最多10位的精确整数。这也是我们将其与 BIGINT 对比的前提。
2.2 纯粹的整数战士:SMALLINT, INTEGER, BIGINT
这是 PostgreSQL 中的整数家族,它们存储没有小数部分的数字,采用二进制补码形式存储,是 CPU 原生支持的高效类型。
SMALLINT:2字节,范围 -32,768 到 32,767。除非存储空间极端紧张,否则现在用得较少。INTEGER(或INT):4字节,范围 -2,147,483,648 到 2,147,483,647。这是最常用、平衡性最好的整数类型。BIGINT:8字节,范围 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807(约 ±922亿亿)。
它们的特点是范围固定、存储空间固定、计算速度极快。
为了更直观地看清它们的底细,我整理了下面这个核心对比表:


2万+

被折叠的 条评论
为什么被折叠?



