PostgreSQL 类型选择速查:字节数、精度与五个常见选错
类型选错不会立刻报错,会在数据长大、精度丢失或索引变大之后才显现。下面把常用类型的实际存储和取舍列出来。
1. 常用类型的存储
| 类型 | 字节 | 备注 |
|---|---|---|
bool | 1 | |
int2 / int4 / int8 | 2 / 4 / 8 | 对应 smallint / integer / bigint |
date | 4 | |
timestamp / timestamptz | 8 / 8 | 两者等宽,差别在语义 |
uuid | 16 | |
interval | 16 | |
text / varchar / numeric / json / jsonb | 变长 | typlen = -1,按内容分配 |
注意最后一行:varchar(n) 与 text 的存储方式相同,长度限制只多一次检查,不会让存储更省。
2. 五个常见选错
整数用 int4 做主键。自增主键用 int4 到 21 亿就到顶,迁移时改类型要重写表。直接用 bigint(GENERATED ALWAYS AS IDENTITY),多出的 4 字节换掉一次停机。
金额用 float8。浮点无法精确表示 0.1,累计后会出现 0.30000000000000004 这类结果。金额用 numeric(12,2),代价是运算比整数慢,但正确。
varchar(255) 当万能字符串。它和 text 存储一样,255 只是约束。真正需要约束时用 CHECK (length(x) <= 255) 或 varchar(n) 都行,但不要以为它能省空间。反过来说,char(n) 会用空格补齐,几乎总是错误选择。
json 代替 jsonb。json 保留原始文本(含空白与键序),每次访问都要重新解析;jsonb 存解析后的二进制,可建 GIN 索引。除非要保留原始格式,否则用 jsonb。
时间用 timestamp 存 now()。会按会话时区转成本地墙上时间,丢掉时区信息。记录时刻用 timestamptz(见同目录另一篇)。
3. 三个需要权衡的
uuid 做主键。16 字节,比 bigint 大一倍,随机 UUID 还会让 B-tree 插入分散、索引膨胀。需要外部可见的 ID 时,可以考虑"内部 bigint 主键 + 外部 uuid 唯一列"。
numeric 做统计聚合。精确但有额外开销,海量聚合时可以先用 float8 近似、最终结果再转 numeric。精度要求由业务决定,不是由类型决定。
text 存大文本再全文检索。text 本身没问题,但要做全文检索得配 tsvector 列或表达式索引,否则 LIKE '%x%' 只能全表扫。
4. 查现有表的类型分布
SELECT table_name, column_name, data_type,
coalesce(character_maximum_length::text, numeric_precision::text, '-') AS size
FROM information_schema.columns
WHERE table_schema = 'public'
ORDER BY table_name, ordinal_position;
小结
四条默认选择能覆盖大多数场景:主键用 bigint 自增、金额用 numeric、字符串用 text、时刻用 timestamptz。
类型选择的收益往往不在单行,而在索引与 IO:int4 换成 int8 让索引变大,text 换成 varchar(20) 什么都不会变。改类型前先想清楚省的是什么。
数据来源与检索时间
检索时间 2026-11-10。字节数来自本机 18.6 的 pg_type 实查。
| 来源 | URL | 对应数据 |
|---|---|---|
| PostgreSQL 18 文档《8.1 数字类型》 | https://www.postgresql.org/docs/18/datatype-numeric.html | int2/int4/int8 宽度、numeric 的精确语义与性能取舍 |
| PostgreSQL 18 文档《8.3 字符类型》 | https://www.postgresql.org/docs/18/datatype-character.html | text 与 varchar(n) 存储一致、char(n) 空格补齐、长度限制不省空间 |
| PostgreSQL 18 文档《8.14 JSON 类型》 | https://www.postgresql.org/docs/18/datatype-json.html | json 保留原始文本、jsonb 解析后存储并可建索引 |
核验说明
- 字节数取自本机
pg_type(typlen列)实查,变长类型的-1也如实记录。 - 未做:没有对比
numeric与float8在大规模聚合上的耗时,也没有测随机 UUID 主键造成的索引膨胀幅度。这两条是常见经验,这里只说明机制,不给数字。