源本科技 | 码上会

PostgreSQL CHAR

2026/03/17
2
0

学习目标

  • 理解 PostgreSQL 中 CHARCHARACTER 类型的定义及其与 VARCHARTEXT 的本质区别

  • 掌握 CHAR 类型在存储机制上的特点,特别是空格填充行为及其对查询的影响

  • 学会在实际数据库设计中判断何时使用 CHAR,何时应避免使用,以优化性能和维护性

  • 熟悉针对 CHAR 类型字段的常见陷阱及最佳实践方案


什么是 CHAR 类型

在 PostgreSQL 中,CHAR(或全称 CHARACTER)是一种固定长度的字符数据类型。当你定义一个列类型为 CHAR(n) 时,你指定了该字段必须存储恰好 n 个字符。如果存入的字符串长度小于 n,PostgreSQL 会自动在字符串末尾填充空格,直到长度达到 n。如果存入的字符串长度超过 n,数据库将抛出错误,除非显式进行截断处理。这与许多其他数据库系统中的行为类似,但在 PostgreSQL 的具体实现中,有一些独特的细节需要注意,尤其是关于存储空间和比较行为的处理。

-- 定义一个固定长度为 10 的字符列
CREATE TABLE example_table (
    fixed_code CHAR(10)
);

在此示例中,无论实际存入的是 "A" 还是 "ABCDEFGHIJ",该字段在磁盘上占用的空间(在不考虑压缩的情况下)都是固定的 10 个字符长度。


CHAR 与 VARCHAR、TEXT

在 PostgreSQL 的生态系统中,开发者经常面临 CHARVARCHARTEXT 的选择。理解它们的差异对于数据库设计至关重要。

特性

CHAR(n)

VARCHAR(n)

TEXT

长度特性

固定长度

可变长度(有上限)

可变长度(无上限)

空格填充

自动填充空格至指定长度

不填充空格

不填充空格

存储开销

始终占用 n 字节 + 头部信息

实际长度 + 头部信息

实际长度 + 头部信息

性能表现

在极短且长度一致的数据上略快

现代版本中与 CHAR 差异极小

与 VARCHAR 无本质区别

适用场景

国家代码、状态标志、哈希值

用户名、邮箱、地址

文章内容、日志、描述

差异

  1. 存储机制
    CHAR 类型的主要特征是“空间换时间”的旧式思维残留。它强制分配固定空间。而 VARCHARTEXT 只存储实际数据长度加上一个很短的长度前缀。在现代 PostgreSQL 版本中,由于 TOAST(The Oversized-Attribute Storage Technique)机制的存在,VARCHARTEXT 在处理长文本时具有极大的灵活性,且性能损耗几乎可以忽略不计。

  2. 空格敏感性
    这是 CHAR 最容易引发 Bug 的地方。

    • 当你向 CHAR(5) 插入 'ABC' 时,实际存储的是 'ABC '(后跟两个空格)。

    • 在进行相等性比较时,PostgreSQL 遵循 SQL 标准,通常会忽略尾随空格,即 'ABC' = 'ABC ' 返回真。

    • 但是,在使用 LIKE 操作符或某些字符串函数(如 length())时,尾随空格会被计算在内,这可能导致意想不到的结果。

  3. 国内开发习惯建议
    在国内互联网开发场景中,除非是存储定长的加密哈希值(如 MD5 固定 32 位)或国际标准代码(如 ISO 国家代码),否则强烈建议优先使用 TEXTVARCHARCHAR 带来的空格填充问题往往弊大于利,增加了应用层处理字符串的复杂度。


存储机制与空格陷阱

深入理解 CHAR 的空格填充行为是避免数据错误的关键。让我们通过具体的示例来观察这一机制。

示例演示

假设我们创建一个包含 CHAR(5) 字段的表:

CREATE TABLE char_demo (
    id SERIAL PRIMARY KEY,
    code CHAR(5)
);

-- 插入不同长度的数据
INSERT INTO char_demo (code) VALUES ('A');
INSERT INTO char_demo (code) VALUES ('ABCD');
INSERT INTO char_demo (code) VALUES ('ABCDE');

当我们查询这些数据时,肉眼可能无法直接看到差异,但通过长度函数可以清晰发现:

SELECT code, length(code) as actual_length FROM char_demo;

预期输出结果:

code

actual_length

A

5

ABCD

5

ABCDE

5

可以看到,即使输入的是 'A',数据库内部存储的也是 'A '

潜在风险

这种自动填充行为在以下场景会导致问题:

  1. 前端展示异常:如果直接将 CHAR 字段内容输出到前端页面,用户可能会看到多余的空白,影响 UI 美观。

  2. 字符串拼接错误:在进行字符串拼接时,中间会混入不可见的空格。

  3. 外部系统集成:当数据导出到不支持自动修剪空格的其他系统时,会导致校验失败。

解决方案

在应用层或查询层处理 CHAR 数据时,务必使用 RTRIM 函数去除尾随空格:

-- 推荐做法:查询时去除空格
SELECT RTRIM(code) as clean_code FROM char_demo;

或者在插入数据前确保数据格式规范,但在 PostgreSQL 中,更推荐直接从 schema 设计层面避免使用 CHAR,转而使用 TEXT


最佳实践

很多传统观点认为 CHARVARCHAR 快,因为它是定长的,不需要计算长度。但在 PostgreSQL 中,这一结论需要重新审视。

性能真相

  • 现代优化器:PostgreSQL 的查询优化器非常智能。对于 VARCHARTEXT,获取长度的操作开销极低。

  • 缓存效率:虽然 CHAR 是定长的,理论上有利于内存对齐,但如果定义的長度远大于实际数据(例如定义 CHAR(255) 却只存 3 个字符),会造成大量的磁盘空间和内存缓存浪费,反而降低整体 I/O 效率。

  • 索引大小CHAR 字段的索引会包含填充的空格,导致索引体积虚大,降低索引扫描效率。

最佳实践指南

  1. 默认选择 TEXT
    在 PostgreSQL 中,TEXT 类型没有长度限制,性能与 VARCHAR(n) 几乎完全相同,且避免了长度约束带来的维护麻烦(如修改字段长度需锁表)。国内大厂如阿里、腾讯的数据库规范中,通常推荐直接使用 TEXT

  2. 仅在特定场景使用 CHAR
    仅当数据严格固定长度长度较短时使用 CHAR

    • ✅ 适合:性别标识(虽通常用 tinyint 或 enum,但若用字符则固定 1 位)、ISO 国家代码(2 位)、MD5 哈希值(32 位)、UUID(若不使用专用 UUID 类型)。

    • ❌ 不适合:姓名、地址、电话号码(长度多变)、描述信息。

  3. 注意排序规则
    在某些排序规则(Collation)下,尾随空格可能会影响排序结果。确保测试环境与应用环境的排序规则一致。


总结

  • 定义特性CHAR(n) 是固定长度类型,不足部分自动用空格填充,超长则报错。

  • 核心差异:与 VARCHARTEXT 相比,CHAR 的主要区别在于空格填充机制,而非显著的性能优势。

  • 主要陷阱:尾随空格会导致 length() 函数结果偏差、LIKE 匹配失败以及前端显示异常。

  • 处理建议:查询时常用 RTRIM() 清理数据;在设计新表时,除非有强理由(如定长哈希),否则优先选用 TEXT 类型。

  • 本地化建议:符合国内开发习惯的做法是减少 CHAR 的使用,利用 PostgreSQL 对 TEXT 的良好支持来简化开发流程。