0

0

MySQL中varchar与char的区别及其底层存储原理探析

betcha

betcha

发布时间:2025-09-08 12:25:01

|

872人浏览过

|

来源于php中文网

原创

答案:CHAR是固定长度类型,适合存储长度固定的值,如哈希值、编码等,存取效率高且不易产生碎片;VARCHAR是可变长度类型,仅占用实际数据所需空间,适合存储长度不一的文本,节省存储但可能因更新引发行迁移。选择取决于数据长度是否固定、更新频率、索引需求及字符集影响,核心是在空间利用率与性能间权衡。

mysql中varchar与char的区别及其底层存储原理探析

MySQL中,

VARCHAR
CHAR
最核心的区别在于它们的存储方式:
CHAR
是固定长度的,无论你存入的数据有多长,它都会占用声明时指定的最大长度空间;而
VARCHAR
是可变长度的,它只占用实际存储数据所需的空间,外加一个或两个字节来记录数据的实际长度。这种差异直接影响着存储效率、查询性能乃至数据处理的细微行为。简单来说,
CHAR
以空间换时间,
VARCHAR
则致力于节省空间,但可能引入一点点额外的处理开销。

CHAR
VARCHAR
的选择,说到底,就是对存储空间和性能开衡量的艺术。我个人在设计数据库时,对这两个数据类型一直有着一番思考。

先说说

CHAR
吧。当你定义一个
CHAR(10)
的字段时,无论你存入“hello”还是“world_test”,它在磁盘上都会占用10个字节。如果数据不足10个字节,MySQL会在右侧用空格填充;读取时,通常又会把这些填充的空格去除(这在不同SQL模式下行为可能略有差异,但多数情况下我们感知不到)。这种固定长度的特性,让MySQL在处理
CHAR
类型的数据时效率很高,因为它知道每条记录的这个字段在哪里结束,无需额外计算。对于那些长度总是固定不变的数据,比如MD5哈希值(
CHAR(32)
)、SHA1值(
CHAR(40)
)、国家代码(
CHAR(2)
)或者一些内部的状态码,
CHAR
无疑是更优的选择。它能提供更快的存取速度,并且在内存中分配也是固定大小,减少了变长数据可能带来的内存碎片问题。

VARCHAR
,它就显得“灵活”多了。定义一个
VARCHAR(255)
的字段,如果你存入“hello”,它实际只占用5个字节的数据空间,外加1个字节来记录这个“5”的长度信息。如果数据长度超过255个字节(当然,这取决于字符集,UTF8MB4下255个字符可能远超255字节),就需要2个字节来记录长度了。这种按需分配的存储方式,在处理像姓名、地址、文章标题这类长度不定的文本数据时,能显著节省存储空间。想象一下,如果一个字段可能存入短短几个字的标题,也可能存入几十个字的描述,用
CHAR
就会造成大量的空间浪费。
VARCHAR
的这种节省空间特性,对于大型数据库来说,意味着更少的磁盘I/O,因为更多的数据可以被塞进一个数据页,从而提高整体的查询效率。但它也有其代价:每次读写都需要额外处理长度字节,而且当
VARCHAR
字段的数据长度发生变化时,尤其是在更新操作导致数据变长时,可能会引发行迁移(row migration)或页分裂(page split),这会增加I/O开销,影响性能。

从底层存储原理来看,InnoDB存储引擎在处理这两种类型时,也有一些值得注意的地方。对于

CHAR
类型,由于其固定长度,InnoDB在数据页上会为该字段预留固定的空间。这使得数据的物理存储非常规整,查找起来也更直接。对于
VARCHAR
,InnoDB的
COMPACT
DYNAMIC
等行格式对其处理方式有所不同。
DYNAMIC
COMPRESSED
行格式在处理大
VARCHAR
字段时,会将部分数据存储在溢出页(off-page storage)中,而不是直接存储在数据行内,这有助于保持数据行较小,减少行迁移的发生,但也会增加访问这些溢出数据的开销。字符集的影响也至关重要,比如
UTF8MB4
字符集下,一个字符可能占用1到4个字节。所以,
VARCHAR(100)
UTF8MB4
下,其最大实际存储字节数可能是400字节,加上长度字节,实际占用的空间会更多。

MySQL中,选择CHAR还是VARCHAR对性能有什么影响?

在我看来,

CHAR
VARCHAR
对性能的影响,是一个典型的“看场景”问题,没有绝对的优劣。

首先,对于

CHAR
类型,由于其固定长度的特性,在数据存取时,MySQL无需额外计算或解析长度信息,可以直接定位到字段的起始和结束位置。这种确定性使得
CHAR
在某些情况下表现出更好的性能,尤其是在查询、排序和索引操作中。比如,如果你有一个
CHAR(32)
的字段作为主键或频繁查询的索引列,由于索引的键值长度固定,索引树的遍历会更加高效,内存分配也更稳定。此外,
CHAR
字段在更新时,即使数据长度发生变化(比如从“a”更新为“b”),也不会导致存储空间的实际变化,因此不会引发行迁移或页分裂,这对于高并发的更新操作来说,是一个潜在的优势。

然而,

CHAR
的缺点在于可能造成的空间浪费。如果你的数据长度远小于声明的长度,那么这些被填充的空格就白白占用了磁盘空间,也增加了I/O的负担,因为需要读取更多无用的数据。在数据量庞大的表中,这种空间浪费积累起来可能非常可观,反而会降低整体性能,因为更少的数据能被缓存到内存中,导致更多的磁盘I/O。

再看

VARCHAR
类型,它的优势在于节省存储空间。对于长度变化大的数据,
VARCHAR
只存储实际数据,这能显著减少数据文件的体积。文件小了,意味着在同样的数据页中能存放更多行数据,从而减少磁盘I/O操作,提高缓存命中率。这对于读取操作来说,往往能带来整体性能的提升。

VARCHAR
性能劣势也显而易见。每次读取或写入时,MySQL都需要额外处理1到2个字节的长度信息。虽然这开销很小,但在海量数据和高并发场景下,累积起来也不容忽视。更重要的是,当
VARCHAR
字段的数据更新后,如果新数据的长度超过了旧数据的长度,并且在当前数据页上没有足够的连续空间来容纳,那么MySQL就可能需要进行行迁移。行迁移意味着将整行数据移动到新的数据页,并在原位置留下一个指向新位置的“指针”。频繁的行迁移会导致数据碎片化,降低数据访问效率,因为查询一行数据可能需要访问多个数据页。这在更新频繁、且数据长度波动大的场景下,对性能的影响是比较明显的。索引方面,
VARCHAR
索引的键值长度不固定,理论上会比
CHAR
索引略慢,但在实际应用中,这种差异往往微乎其微,远不如存储空间和行迁移的影响来得大。

所以,在选择时,我通常会权衡:数据长度是否固定?是否会频繁更新?数据量有多大?如果数据长度固定且不长,

CHAR
可能更优;如果数据长度可变且可能较长,
VARCHAR
几乎是必然的选择,但要警惕更新带来的行迁移问题。

VARCHAR的最大长度限制是65535,这具体指什么?

关于

VARCHAR
的65535字节限制,这是一个经常被误解的地方。很多人以为这意味着一个
VARCHAR
字段可以存储65535个字符,或者说它能单独占用65535字节。但实际上,这个限制指的是一张表的所有
VARCHAR
TEXT
BLOB
等可变长字段,在单行数据中,它们所能占用的最大总存储字节数
(不包括那些真正存储在溢出页中的大对象数据)。换句话说,65535字节是MySQL一个数据行能够存储的最大物理长度,而不是单个
VARCHAR
字段的字符数上限。

闪念贝壳
闪念贝壳

闪念贝壳是一款AI 驱动的智能语音笔记,随时随地用语音记录你的每一个想法。

下载

这个限制是针对整个数据行的,并且它还包括了

VARCHAR
字段本身用于存储长度信息的1或2个字节。具体来说:

  1. 行总长度限制: MySQL的每个数据行(不包括
    BLOB
    TEXT
    类型存储在溢出页的部分)不能超过65535字节。这意味着你表中的所有列(
    CHAR
    INT
    DATE
    等固定长度的,以及
    VARCHAR
    VARBINARY
    等可变长度的)加起来的总字节数不能超过这个限制。
  2. 字符集影响:
    VARCHAR
    的长度是按字符计算的,但其占用的字节数取决于所使用的字符集。例如,如果你的字段是
    VARCHAR(255)
    并使用了
    UTF8MB4
    字符集,那么一个字符最多可能占用4个字节。所以,
    VARCHAR(255)
    实际上可能占用
    255 * 4 = 1020
    字节,再加上1或2字节的长度前缀。显然,你不可能在一个
    UTF8MB4
    字符集的
    VARCHAR
    字段中定义
    VARCHAR(65535)
    ,因为
    65535 * 4
    远远超出了65535字节的行限制。实际上,对于
    UTF8MB4
    ,单个
    VARCHAR
    字段能存储的最大字符数大约是16383个字符(
    65535 / 4 ≈ 16383
    ),再减去长度字节。
  3. 长度前缀: 如果
    VARCHAR
    字段的声明长度小于或等于255字节,MySQL会用1个字节来存储其实际长度。如果声明长度大于255字节(且小于65535字节),则需要2个字节来存储实际长度。这个长度字节也计入65535字节的总限制。
  4. 溢出页存储(Off-Page Storage): 对于InnoDB存储引擎,当一个
    VARCHAR
    字段非常大,以至于它会导致数据行超过65535字节的限制时,或者当它本身就超过了数据页的某个阈值时(比如半页大小),InnoDB会选择将这个大字段的部分或全部数据存储到溢出页(off-page storage)中。在这种情况下,数据行中只会存储一个指向溢出页的20字节指针,而不是实际的数据。这意味着,虽然单个
    VARCHAR
    字段的字符数可以非常大(理论上可以达到
    TEXT
    类型的限制),但行内存储的只是一个指针,从而满足了65535字节的行内限制。但这仅适用于
    DYNAMIC
    COMPRESSED
    行格式。

所以,当你在设计表结构时,看到

VARCHAR
的65535字节限制,更应该把它理解为所有可变长字段的总和,以及它们在行内存储的上限,并结合字符集来计算实际的字节占用。如果你确实需要存储超过这个限制的单个大文本,
TEXT
BLOB
类型才是正确的选择,因为它们天生就是为溢出页存储设计的。

在实际开发中,如何根据业务场景合理选择CHAR和VARCHAR?

在实际的数据库设计中,选择

CHAR
还是
VARCHAR
,我通常会遵循几个原则,结合具体的业务场景来做决策。这不仅仅是技术上的选择,更是对未来数据增长、系统性能以及维护成本的一种预判。

1. 优先考虑数据长度的确定性:

  • 如果数据长度总是固定不变的:毫无疑问,选择

    CHAR
    。这是
    CHAR
    最理想的应用场景。例如:

    • MD5哈希值
      CHAR(32)
    • UUID(不带分隔符)
      CHAR(32)
    • 国家代码(如ISO 3166-1 alpha-2):
      CHAR(2)
    • 邮政编码(如果你的业务场景中邮编长度是固定的):
      CHAR(6)
      CHAR(7)
    • 性别
      CHAR(1)
      ('M'或'F')。
    • 固定长度的内部编码或状态码。 使用
      CHAR
      可以确保存储空间的统一,提高存取效率,并且在索引上表现更优。
  • 如果数据长度可变,且变化范围较大:这时,

    VARCHAR
    几乎是唯一的选择。例如:

    • 用户姓名
      VARCHAR(100)
    • 地址
      VARCHAR(255)
    • 文章标题或描述
      VARCHAR(255)
      甚至更大。
    • 评论内容
      VARCHAR(500)
      TEXT
      VARCHAR
      能有效节省存储空间,避免大量无效空格的存储,从而减少磁盘I/O。

2. 关注字段的更新频率和数据增长模式:

  • 如果字段内容很少更新,或者更新后长度基本不变
    VARCHAR
    是一个很好的选择。即使数据长度有微小变化,也不会频繁触发行迁移。
  • 如果字段内容会频繁更新,并且更新后长度可能大幅度增长:这时需要特别小心
    VARCHAR
    。频繁的长度增长可能导致行迁移和数据碎片化,严重影响性能。在这种极端情况下,有时我会考虑预留比实际数据稍大的
    VARCHAR
    长度,以减少更新时的行迁移,或者干脆考虑使用
    TEXT
    类型,让MySQL自动处理溢出存储。

3. 考虑索引和查询效率:

  • 作为索引列时
    CHAR
    索引由于键值长度固定,理论上性能会略优于
    VARCHAR
    索引。但在大多数现代系统中,这种差异微乎其微。更重要的是索引列的选择性长度本身。如果
    VARCHAR
    字段的实际数据长度很短,且选择性高,那么它作为索引列也是非常高效的。
  • 前缀索引:对于很长的
    VARCHAR
    字段,为了节省索引空间和提高效率,我们通常会创建前缀索引,例如
    INDEX(col_name(10))
    。这在
    CHAR
    上就不太需要,因为其本身就是固定长度。

4. 字符集的影响:

  • 始终要记住字符集对字节数的影响。
    VARCHAR(N)
    中的
    N
    是字符数,不是字节数。
    UTF8MB4
    字符集下,一个字符最多占4字节。所以,定义
    VARCHAR(255)
    时,要清楚它在最坏情况下可能占用
    255 * 4 = 1020
    字节,加上长度字节,远超255字节。这会影响到整个行的长度限制。

5. 避免过度优化或过度保守:

  • 不要为了节省几个字节而牺牲可读性和可维护性。例如,一个明显是变长的数据,没必要强行用
    CHAR
    ,然后通过应用程序去补齐或截断。
  • 也不要过度保守,给
    VARCHAR
    设置一个远超实际需求的巨大长度
    。虽然
    VARCHAR
    只存储实际数据,但声明的长度过大,会影响内存分配(在某些操作中,MySQL可能会根据声明长度分配内存),并且在某些情况下,过大的声明长度可能会阻止MySQL进行某些优化。例如,
    VARCHAR(255)
    只需要1个字节存储长度,而
    VARCHAR(256)
    就需要2个字节。

总结一下,我个人的经验是:对于固定长度、长度较短且频繁用作索引或查询条件的字段,倾向于使用

CHAR
。对于绝大多数文本数据,特别是长度不确定、变化范围大的字段,果断选择
VARCHAR
。对于那些特别长、且不常作为查询条件的文本,如文章正文、大段描述,则会考虑使用
TEXT
类型。核心思想是根据数据本身的特性和业务需求,做出最平衡的选择。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

1135

2023.10.12

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

340

2023.10.27

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

381

2024.02.23

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2214

2024.03.06

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

380

2024.03.06

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

1703

2024.04.07

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

586

2024.04.29

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

440

2024.04.29

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

49

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
MySQL 教程
MySQL 教程

共48课时 | 2.6万人学习

MySQL 初学入门(mosh老师)
MySQL 初学入门(mosh老师)

共3课时 | 0.3万人学习

简单聊聊mysql8与网络通信
简单聊聊mysql8与网络通信

共1课时 | 850人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号