MySQL数据库中CHAR与VARCHAR之争

本文探讨了MySQL数据库中Char与Varchar两种字符型数据的区别,并提供了在不同场景下选择合适类型的方法。
【引言】在数据库中,字符型的数据是最多的,可以占到整个数据库的80%以上。为此正确处理字符型的数据,对于提高数据库的性能有很大的作用。在字符型数据中,用的最多的就是Char与Varchar两种类型。前面的是固定长度,而后面的是可变长度。现在我们需要考虑的是,在什么情况下使用Char字符型数据,什么情况下采用Varchar字符型数据。
  一、VARCHAR与CHAR字符型数据的差异
  在MySQL数据库中,用的最多的字符型数据类型就是Varchar和Char.。这两种数据类型虽然都是用来存放字符型数据,但是无论从结构还是从数据的保存方式来看,两者相差很大。而且其具体的实现方式,还依赖与存储引擎。我这里就以大家最常用的MYISAM存储引擎为例,谈谈这两种数据类型的差异。在后续建议中,也是针对这种存储类型而言的。

  这里首先需要明白的一点是,这两种数据类型,无论采用哪一种存储引起,系统存储数据的方式都是不同的。正是因为如此,我们才有必要研究两者的不同。然后在合适的情况下,采用恰当的方式。了解这一点之后,我们再来看后续的内容。

Varchar往往用来保存可变长度的字符串。简单的说,我们只是给其固定了一个最大值,然后系统会根据实际存储的数据量来分配合适的存储空间。为此相比CHAR字符数据而言,其能够比固定长度类型占用更少的存储空间。不过在实际工作中,由于某系特殊的原因,会在这里设置例外。如管理员可以根据需要指定ROW_FORMAT=FIXED选项。利用这个选项来创建MyISAM表的话,系统将会为每一行使用固定长度的空间。此时会造成存储空间的损耗。通常情况下,VARCHAR数据类型能够节约磁盘空间,为此往往认为其能够提升数据库的性能。不过这里需要注意的,这往往是一把双刃剑。其在提升性能的同时,往往也会产生一些副作用。如因为其长度是可变的,为此在数据进行更新时可能会导致一些额外的工作。如在更改前,其字符长度是10位(Varchar规定的最长字符数假设是50位),此时系统就只给其分配10个存储的位置(假设不考虑系统自身的开销)。更改后,其数据量达到了20位。由于没有超过最大50位的限制,为此数据库还是允许其存储的。只是其原先的存储位置已经无法满足其存储的需求。此时系统就需要进行额外的操作。如根据存储引擎不同,有的会采用拆分机制,而有的则会采用分页机制。

  CHAR数据类型与VARCHAR数据类型不同,其采用的是固定长度的存储方式。简单的说,就是系统总为其分配最大的存储空间。当数据保存时,即使其没有达到最大的长度,系统也会为其分配这么多的存储空间。显然,这种存储方式会造成磁盘空间的浪费。这里笔者需要提醒的一点是,当字符位数不足时,系统并不会采用空格来填充。相反,如果在保存CHAR值的时候,如果其后面有空值,系统还会自动过滤其空格。而在进行数据比较时,系统又会将空格填充到字符串的末尾。
  显然,VARCHAR与CHAR两种字符型数据类型相比,最大的差异就是前者是可变长度,而后者则是固定长度。在存储时,前者会根据实际存储的数据来分配最终的存储空间。而后者则不管实际存储数据的长度,都是根据CHAR规定的长度来分配存储空间。这是否意味着CHAR的数据类型劣于VARCHAR呢?其实不然。否则的话,就没有必要存在CHAR字符类型了。虽然VARCHAR数据类型可以节省存储空间,提高数据处理的效率。但是其可变长度带来的一些负面效应,有时候会抵消其带来的优势。为此在某些情况下,还是需要使用Char数据类型。
  二、项目建议

  根据上面的分析,我们知道VARCHAR数据类型是一把双刃剑,其在带来性能提升的同时,也可能会存在着一些额外的消耗。我们在评估到底是使用VARCHAR数据类型还是采用CHAR数据类型时,就需要进行均衡。在实际项目中,我们会考量如下情况。

一是根据字符的长度来判断。如某个字段,像人的名字,其最长的长度也是有限的。如我们给其分配18个字符长度即可。此时虽然每个人的名字长度有可能不同,但是即使为其分配了固定长度的字符类型,即18个字符长度,最后浪费的空间也不是很大。而如果采用NVARCHAR数据类型时,万一以后需要改名,而原先的存储空间不足用来容纳新的值,反而会造成一些额外的工作。在这种情况下,进行均衡时,会认为采用CHAR固定长度的数据类型更好。在实际项目中,如果某个字段的字符长度比较短此时一般是采用固定字符长度。

  二是考虑其长度的是否相近。如果某个字段其长度虽然比较长,但是其长度总是近似的,如一般在90个到100个字符之间,甚至是相同的长度。此时比较适合采用CHAR字符类型。比较典型的应用就是MD5哈希值。当利用MD5哈希值来存储用户密码时,就非常使用采用CHAR字符类型。因为其长度是相同的。另外,像用来存储用户的身份证号码等等,一般也建议使用CHAR类型的数据。
  另外请大家考虑一个问题,CHAR(1)与VARCHAR(1)两这个定义,会有什么区别呢?虽然这两个都只能够用来保存单个的字符,但是VARCHAR要比CHAR多占用一个存储位置。这主要是因为使用VARCHAR数据类型时,会多用1个字节用来存储长度信息。这个管理上的开销CHAR字符类型是没有的。
  三是从碎片角度进行考虑。使用CHAR字符型时,由于存储空间都是一次性分配的。为此某个字段的内容,其都是存储在一起的。单从这个角度来讲,其不存在碎片的困扰。而可变长度的字符数据类型,其存储的长度是可变的。当其更改前后数据长度不一致时,就不可避免的会出现碎片的问题。故使用可变长度的字符型数据时,数据库管理员要时不时的对碎片进行整理。如执行数据库导出导入作业,来消除碎片。
  四是即使使用Varchar数据类型,也不能够太过于慷慨。这是什么意思呢?如现在用户需要存储一个地址信息。根据评估,只要使用100个字符就可以了。但是有些数据库管理员会认为,反正Varchar数据类型是根据实际的需要来分配长度的。还不如给其大一点的呢。为此他们可能会为这个字段一次性分配200个字符的存储空间。这VARCHAR(100)与VARCHAR(200)真的相同吗?结果是否定的。虽然他们用来存储90个字符的数据,其存储空间相同。但是对于内存的消耗是不同的。对于VARCHAR数据类型来说,硬盘上的存储空间虽然都是根据实际字符长度
来分配存储空间的,但是对于内存来说,则不是。其时使用固定大小的内存块来保存值。简单的说,就是使用字符类型中定义的长度,即200个字符空间。显然,这对于排序或者临时表(这些内容都需要通过内存来实现)作业会产生比较大的不利影响。所以如果某些字段会涉及到文件排序或者基于磁盘的临时表时,分配VARCHAR数据类型时仍然不能够太过于慷慨。还是要评估实际需要的长度,然后选择一个最长的字段来设置字符长度。如果为了考虑冗余,可以留10%左右的字符长度。千万不能认为其为根据实际长度来分配存储空间,而随意的分配长度,或者说干脆使用最大的字符长度。

<think>我们正在讨论MySQLCHARVARCHAR的区别。之前已经给出过详细回答,现在需要根据用户的问题和提供的引用内容,重新整理一个清晰准确的回答。 用户的问题:MySQL数据库CHARVARCHAR的区别? 提供的引用内容: 引用[1]:主要概述了CHARVARCHAR是两种重要的字符串类型,有特定的用途和性能特点,并提到通过该文可以掌握它们的底层存储方式、性能对比和应用场景。 引用[2]:详细说明了存储上的不同:varchar可变长度,char固定长度;存储处理上,varchar末尾用1-2字节存储长度,char会删除末尾空格并用空格填充剩余空间;对于经常变更的数据,char不容易产生碎片;对于短列,char效率更高;最大容量:char固定255字符,varchar最大可达65532字节(但受行大小和字符集影响)。 引用[3]:给出了选择建议:长度小于4用char,大于20用varchar,4-20之间视情况定。大数情况用varchar更好,不知道用啥时就选varchar。 根据以上引用,结合我们之前的知识,我们可以将回答组织如下: 1. 存储方式: - CHAR:固定长度,定义长就占用长,存储时如果实际长度不足,会在末尾用空格填充到指定长度。检索时,会自动删除末尾的空格(除非使用PAD_CHAR_TO_FULL_LENGTH模式)。 - VARCHAR:可变长度,存储实际字符串再加上1到2个字节的长度标识(长度小于255时用1字节,否则用2字节)。存储时不会用空格填充,检索时也不会删除空格(除了原样存储和检索,但注意如果字符串末尾有空格,存储时会保留,检索时也会返回)。 2. 存储空间: - CHAR:占用空间固定,为定义的长度(单位为字符,但实际占用字节数取决于字符集)。例如,CHAR(10)在latin1字符集下固定占用10字节,在utf8mb4下固定占用40字节(因为每个字符最大4字节)。 - VARCHAR:占用空间为实际字符串长度(单位为字节)加上长度标识(1或2字节)。例如,存储字符串'abc',在latin1字符集下占用3+1=4字节,在utf8mb4下占用3*4+1=13字节(但实际存储可能更紧凑,因为utf8mb4中英文字符只占1字节)。 3. 最大长度: - CHAR:最大为255个字符(字符集无关)。 - VARCHAR:最大长度为65535字节(但实际单列可用为65532字节,因为需要1字节标识NULL和2字节存储长度,如果列定义为NOT NULL,则不需要NULL标识,那么可用65533字节?但通常我们考虑行限制,实际最大长度受行大小限制和字符集影响)。注意,在UTF8mb4字符集下,每个字符最占用4字节,所以最大字符数约为65533/4≈16383个字符。 4. 性能比较: - 对于短字符串(特别是长度固定的,如MD5、国家代码等),CHAR效率更高,因为不需要计算长度,且定长存储读取更快。 - 对于长字符串或长度变化大的字符串,VARCHAR更节省空间,因为CHAR会填充空格,而VARCHAR只占用实际需要的空间。 - 在频繁更新时,CHAR不容易产生碎片,因为长度固定;而VARCHAR如果更新后的长度变化较大,可能导致页分裂,产生碎片。 5. 选择建议: - 当存储的数据长度固定(比如身份证号、手机号码虽然长度可能不固定,但通常我们指的是绝对固定的情况,如UUID、MD5等)时,使用CHAR。 - 当存储的数据长度变化较大时(如用户昵称、地址等),使用VARCHAR。 - 对于非常短的列(比如只有几个字符),使用CHAR,因为VARCHAR需要额外的1-2字节存储长度,可能比CHAR占用更。 - 引用[3]建议:长度小于4的用CHAR,大于20的用VARCHAR,4-20之间的根据情况决定。如果不确定,优先选择VARCHAR(因为它在大数情况下更节省空间)。 6. 空格处理差异: - 在存储时,CHAR会删除末尾的空格(然后填充空格到指定长度,但实际存储的是填充后的空格,检索时又会去掉末尾的空格,所以实际上你存进去的末尾空格会被丢弃)。注意:如果使用PAD_CHAR_TO_FULL_LENGTH模式,检索时不会去掉空格,但默认行为是去掉。 - VARCHAR则原样存储字符串(包括末尾的空格),检索时也会原样返回(包括空格)。 注意:关于空格处理,我们可以用一个例子说明: CREATE TABLE test (a CHAR(5), b VARCHAR(5)); INSERT INTO test VALUES ('a ', 'a '); -- 注意,这里存的是'a'后面有两个空格 SELECT a, LENGTH(a), b, LENGTH(b) FROM test; 结果:a='a', LENGTH(a)=1;b='a ', LENGTH(b)=3。 这是因为在CHAR类型中,存储时两个空格被删除(然后填充了3个空格到5个字符),但检索时又会删除填充的空格,所以最终只看到'a'。而VARCHAR则原样存储和检索。 根据以上内容,我们可以组织一个结构清晰的回答,并添加相关问题。 注意:回答中引用内容的地方要添加引用标识。 </think>### MySQL中`CHAR``VARCHAR`的核心区别 #### 1. **存储机制** - **`CHAR`** 固定长度存储。定义长度$N$时,无论实际数据长度如何,始终占用$N$字符的空间。 - 存储时:用空格填充剩余空间 - 检索时:自动删除末尾空格 *例:`CHAR(5)`存储`"A"` ➔ 实际存储`"A "` ➔ 返回`"A"`*[^2] - **`VARCHAR`** 可变长度存储。仅占用"实际字符长度+长度标识"的空间。 - 存储时:保留原数据末尾空格 - 长度标识:1字节(长度≤255)或2字节(长度>255) *例:`VARCHAR(100)`存储`"Hello"` ➔ 占用$5+1=6$字节*[^2] #### 2. **空间占用对比** | 场景 | `CHAR(10)` | `VARCHAR(10)` | |--------------------|------------|---------------| | 存储`"OK"` | 10字节 | $2+1=3$字节 | | 存储`"MySQL"` | 10字节 | $5+1=6$字节 | | 存储10字符长数据 | 10字节 | $10+1=11$字节 | > ✅ **关键结论**: > - 短数据(≤4字符):`CHAR`更省空间(无长度标识开销) > - 长数据(>20字符):`VARCHAR`显著节省空间[^3] #### 3. **最大容量限制** | 类型 | 最大字符数 | 计算逻辑 | |-------------|-----------|------------------------------| | `CHAR` | 255 | 固定上限,无关字符集 | | `VARCHAR` | 可变 | $$ \frac{65535 - 1(\text{NULL标识}) - 2(\text{长度标识})}{\text{字符字节数}} $$ | *例:utf8mb4字符集(中文字符占4字节)下:* $$ \frac{65532}{4} \approx 16383 \text{字符} $$[^2] #### 4. **性能碎片** - **高频更新场景** `CHAR`优于`VARCHAR`:定长结构减少磁盘碎片[^2] *原因:`VARCHAR`长度变化可能导致行迁移* - **查询效率** `CHAR`略快:定长字段可直接计算偏移量 `VARCHAR`需解析长度标识 #### 5. **选择策略** | **场景** | **推荐类型** | **示例** | |-------------------------|--------------|---------------------------| | 绝对定长数据 | `CHAR` | UUID、MD5(32字符)、国家码 | | ≤4字符的短字符串 | `CHAR` | 性别(`M`/`F`)、状态码 | | 长度变化大的数据 | `VARCHAR` | 用户名、地址、评论 | | 不确定长度的通用字段 | `VARCHAR` | 产品描述、日志信息 | | 高频更新的长文本字段 | `CHAR` | 需权衡空间碎片成本 | > 💡 **最佳实践**: > - 优先`VARCHAR`(符合数场景) > - 仅严格定长或超短字段用`CHAR`[^3] #### 6. **空格处理实验** ```sql CREATE TABLE test ( col_char CHAR(5), col_varchar VARCHAR(5) ); INSERT INTO test VALUES ('A ', 'A '); -- 存入结尾含2空格 SELECT CONCAT('[', col_char, ']') AS char_result, -- 输出: [A] CONCAT('[', col_varchar, ']') AS varchar_result -- 输出: [A ] FROM test; ``` *结果证明:`CHAR`删除存储时的末尾空格,`VARCHAR`保留原空格[^2]* --- ### 相关问题 1. 如何在utf8mb4字符集下精确计算`VARCHAR`字段的最大允许长度? 2. 为什么频繁更新的`VARCHAR`字段可能导致性能下降?如何优化? 3. 存储手机号码时选择`CHAR(11)`还是`VARCHAR(15)`更合理? 4. `CHAR`字段自动删除末尾空格的行为会引发哪些潜在问题?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值