【转】Mysql中varchar存放中文与英文所占字节异同

本文详细解析了UTF-8、GBK及utf8mb4等字符编码的特点与应用场景,并深入探讨了MySQL中不同编码方式下varchar字段的最大存储限制。

本文转自Ruby china, 原文地址:https://ruby-china.org/topics/24920

 

一、关于UTF-8

UTF-8 Unicode Transformation Format-8bit。是用以解决国际上字符的一种多字节编码。

它对英文使用8位(即一个字节) ,中文使用24位(三个字节)来编码。

UTF-8包含全世界所有国家需要用到的字符,是国际编码,通用性强。

UTF-8编码的文字可以在各国支持UTF8字符集额的浏览器上显示。
如果是UTF8编码,则在外国人的英文IE也能显示中文,他们无需下载IE的中文语言支持包。

二、关于GBK

GBK 是国家标准GB2312基础上扩容后兼容GB2312的标准。

GBK的文字编码是用双字节来表示的,即不论中、英文字符均使用双字节来表示,为了区分中文,将其最高位都设定成1。

GBK包含全部中文字符,是国家编码,通用性比UTF8差,不过UTF8占用的数据库比GBK大。

三、关于utf8mb4

MySql 5.5 之前,UTF8 编码只支持1-3个字节,只支持BMP这部分的unicode编码区,BMP是从哪到哪?
戳这里 基本就是 0000 ~ FFFF 这一区。

从MySQL 5.5 开始,可支持4个字节UTF编码utf8mb4,一个字符最多能有4字节,所以能支持更多的字符集。


utf8mb4 is a superset of utf8

tf8mb4兼容utf8,且比utf8能表示更多的字符。

至于什么时候用,看你做的什么项目了。。。
在做移动应用时,会遇到IOS用户在文本的区域输入emoji表情,如果不做一定处理,就会导致插入数据库异常。

四、汉字长度与编码有关

MySql 5.0 以上的版本:

1、一个汉字占多少长度与编码有关:

  • UTF-8:一个汉字 = 3个字节,英文是一个字节
  • GBK: 一个汉字 = 2个字节,英文是一个字节

2、varchar(n) 表示n个字符,无论汉字和英文,MySql都能存入 n 个字符,仅实际字节长度有所区别。

3、MySQL检查长度,可用SQL语言


SELECT LENGTH(fieldname) FROM tablename 

五、实际测试

1、首先使用utf8 创建 str_test 表。

    CREATE TABLE `str_test` (
        `name_chn` varchar(20) NOT NULL,
        `name_en`  varchar(20) NOT NULL
    ) ENGINE=InnoDB AUTO_INCREMENT=62974 DEFAULT CHARSET=utf8

然后插入值

    mysql> insert into  str_test values ('我爱Ruby', 'I Love Ruby!');
    Query OK, 1 row affected (0.02 sec)

打开irb

    >> "我爱Ruby".size
    => 6
    >> "I Love Ruby!".size
    => 12
    >>

从MySQL中查询出来的结果,对比

    mysql> select * from str_test;
    +------------+--------------+
    | name_chn   | name_en      |
    +------------+--------------+
    | 我爱Ruby   | I Love Ruby! |
    +------------+--------------+
    1 row in set (0.02 sec)


    mysql> select length(name_chn) from str_test;
    +------------------+
    | length(name_chn) |
    +------------------+
    |               10 |
    +------------------+
    1 row in set (0.01 sec)

3[一个汉字三字节] * 2 + 1[一个英文一字节] * 4 = 10

    mysql> select length(name_en) from str_test;
    +-----------------+
    | length(name_en) |
    +-----------------+
    |              12 |
    +-----------------+
    1 row in set (0.00 sec)

10[一个英文一字节] * 1 + 2[空格一字节] * whitespace = 12

2、使用 GBK 做测试

创建表

        CREATE TABLE `str_test` (
        `name_chn` varchar(20) NOT NULL,
        `name_en`  varchar(20) NOT NULL
    ) ENGINE=InnoDB AUTO_INCREMENT=62974 DEFAULT CHARSET=gbk

插入数据,并且测试

    mysql> insert into  str_test values ('我爱Ruby', 'I Love Ruby!');
    Query OK, 1 row affected (0.00 sec)

    mysql> select * from str_test;
    +------------+--------------+
    | name_chn   | name_en      |
    +------------+--------------+
    | 我爱Ruby   | I Love Ruby! |
    +------------+--------------+
    1 row in set (0.01 sec)

GBK 中文是两个字节,英文是一个字节。

    mysql> select length(name_chn) from str_test;
    +------------------+
    | length(name_chn) |
    +------------------+
    |                8 |
    +------------------+
    1 row in set (0.00 sec)

2[中文两个字节] * 2 + 4[英文一个字节] * 1 = 8

    mysql> select length(name_en) from str_test;
    +-----------------+
    | length(name_en) |
    +-----------------+
    |              12 |
    +-----------------+
    1 row in set (0.00 sec)

10[英文一个字节] * 1 + 2[空格一个字节] * whitespace = 12

六、关于varchar 最多能存多少值
  • mysql的记录行长度是有限制的,不是无限长的,这个长度是64K,即65535个字节,对所有的表都是一样的。

  • MySQL对于变长类型的字段会有1-2个字节来保存字符长度。

  • 当字符数小于等于255时,MySQL只用1个字节来记录,因为2的8次方减1只能存到255。

  • 当字符数多余255时,就得用2个字节来存长度了。

  • utf-8状态下的varchar,最大只能到 (65535 - 2) / 3 = 21844 余 1。

  • gbk状态下的varchar, 最大只能到 (65535 - 2) / 2 = 32766 余 1

使用 utf-8 创建

        mysql>     CREATE TABLE `str_test` (
        ->         `id`  tinyint(1)  NOT NULL,
        ->         `name_chn` varchar(21845) NOT NULL
        ->     ) ENGINE=InnoDB AUTO_INCREMENT=62974 DEFAULT CHARSET=utf8
        -> ;
        ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs
        mysql>     CREATE TABLE `str_test` (
        ->         `id`  tinyint(1)  NOT NULL,
        ->         `name_chn` varchar(21844) NOT NULL
        ->     ) ENGINE=InnoDB AUTO_INCREMENT=62974 DEFAULT CHARSET=utf8
        ->
        ->
        -> ;
        Query OK, 0 rows affected (0.06 sec)

使用gbk创建

当存储长度为 32768 失败~

        mysql>     CREATE TABLE `str_test` (
        ->         `id`  tinyint(1)  NOT NULL,
        ->         `name_chn` varchar(32768) NOT NULL
        ->     ) ENGINE=InnoDB AUTO_INCREMENT=62974 DEFAULT CHARSET=gbk
        -> ;
    ERROR 1074 (42000): Column length too big for column 'name_chn' (max = 32767); use BLOB or TEXT instead

当存储长度为 32767 失败~

    mysql>     CREATE TABLE `str_test` (                                                                                                 ->         `id`  tinyint(1)  NOT NULL,
        ->         `name_chn` varchar(32767) NOT NULL
        ->     ) ENGINE=InnoDB AUTO_INCREMENT=62974 DEFAULT CHARSET=gbk
        -> ;
    ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs

当存储长度为 32766 成功~

    mysql>     CREATE TABLE `str_test` (
        ->         `id`  tinyint(1)  NOT NULL,
        ->         `name_chn` varchar(32766) NOT NULL
        ->     ) ENGINE=InnoDB AUTO_INCREMENT=62974 DEFAULT CHARSET=gbk
        -> ;
    Query OK, 0 rows affected (0.03 sec)

smallint 用两个字节存储,所以

2[smallint] + 32766 * 2[varchar存储长度] + 2[2个字节来存长度] > 65535

所以失败~

     mysql>     CREATE TABLE `str_test` (
         ->         `id`  smallint(1)  NOT NULL,
         ->         `name_chn` varchar(32766) NOT NULL
         ->     ) ENGINE=InnoDB AUTO_INCREMENT=62974 DEFAULT CHARSET=gbk
         -> ;
     ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs

#####七、数值类型所占的字节

类型所占字节
int4 字节
smallint2 字节
tinyint1 字节
decimal变长

官方关于decimal 的描述如下

Values for DECIMAL (and NUMERIC) columns are represented using a binary format that packs nine decimal (base 10) digits into four bytes.

Storage for the integer and fractional parts of each value are determined separately.

Each multiple of nine digits requires four bytes, and the “leftover” digits require some fraction of four bytes.

The storage required for excess digits is given by the following table.

翻译为中文

使用二进制格式将9个十进制(基于10)数压缩为4个字节来表示DECIMAL列值。

每个值的整数和分数部分的存储分别确定。

每个9位数的倍数需要4个字节,并且“剩余的”位需要4个字节的一部分。

下表给出了超出位数的存储需求:

Leftover DigitsNumber Of Bytes
00
11
21
32
42
53
63
74
84

那:decimal(10,2)占几个字节?

1、首先 10 指的是整数与小数部分的总长度, 2指的是小数部分的长度。那么整数部分就只有 10 - 2 = 8 位

2、因为整数与小数的存储市各自独立确定的,所以他们各自所占用空间的综合就是所占的总空间了。

3、对表可知,整数部分8位占了4个字节,小数部分2位占了1个字节,所以decimal(10,2)总共占了 4 + 1 = 5 个字节。

4、decimal(6,2) 整数部分(6 - 2 = 4) 位占2字节,小数部分2位占1字节,总共占3字节。

八、总结

varchar 字段是将实际内容单独存储在聚簇索引之外,内容开头用1到2个字节表示实际长度(长度超过255时需要2个字节),因此最大长度不能超过65535。

  • UTF-8:一个汉字 = 3个字节,英文是一个字节
  • GBK: 一个汉字 = 2个字节,英文是一个字节

utf-8状态下,汉字最多可以存 21844个字符串, 英文也为 21844个字符串。

gbk状态下,汉字最多可以存 32766个字符串,英文也为 32766个字符串。

转载于:https://www.cnblogs.com/technologylife/p/5962601.html

<think>首先,用户的问题是:“我想了解MySQLVARCHAR类型是基于字符长度还是字节长度进行存储 请问MySQL VARCHAR类型是按字符长度还是字节长度定义”。这是一个关于MySQL数据库VARCHAR类型定义的问题。 我需要参考提供的引用内容来回答这个问题。引用内容如下: - 引用[1]:在MySQL 5.X以上的版本中,VARCHAR(20)表示字符长度,可以存储20个英文字符或20个中文字符。在MySQL 4.X版本中,表示字节长度。 - 引用[2]:VARCHAR类型存储变长字段的字符类型,存储时需要前缀长度列表。如果字符串长度小于255字节,需要1字节空间;如果大于255字节,需要2字节空间。 - 引用[3]:在MySQL 5.0以上版本,VARCHAR的最大长度是65535字节,但受限于最大行长度。65535字节包括所有字段的长度、变长字段的长度标识和NULL标志位。 - 引用[4]:在UTF8MB4字符集下,一个中文汉字3个字节。例如,VARCHAR(60)存储“万科”,实际长度是6字节。 - 引用[5]:在Oracle中,可以用字符为单位定义VARCHAR2字段,但MySQL类似。 从这些引用中,我可以总结出: - 在MySQL中,VARCHAR类型定义的长度指的是字符长度,而不是字节长度。具体来说: - MySQL 5.X及以上版本:VARCHAR(n) 中的n表示最大字符数。 - MySQL 4.X版本:表示字节长度。 - 实际存储时,VARCHAR是变长的,存储的字节数取决于字符集和实际内容。例如,在UTF8MB4字符集下,一个中文字符用3个字节,所以VARCHAR(10)可以存储10个字符,但每个字符可能用多个字节。 用户的问题直接问“是按字符长度还是字节长度定义”,所以我需要明确回答:在MySQL中,VARCHAR类型定义的长度是基于字符长度的(在MySQL 5.X及以上版本)。 现在,回答结构需要清晰,帮助用户逐步解决问题。系统指令要求: - 回答结构清晰。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,如[^1]。 - 所有行内数学表达式用$...$,独立公式用$$...$$,但这个问题可能不需要数学表达式,除非涉及计算。 - 保证回答真实可靠。 所以,回答结构: 1. **直接回答问题**:明确说明VARCHAR类型定义的长度是字符长度(在MySQL 5.X及以上)。 2. **解释细节**:描述不同MySQL版本的区别,以及存储机制。 3. **引用支持**:在解释时,引用提供的引用内容。 4. **生成相关问题**:在最后,添加相关问题部分。 最后,添加相关问题。系统指令说“生成相关问题”,并给出示例格式。 相关问题应该基于用户的问题,比如: - VARCHAR的最大长度限制是什么? - 不同字符集如何影响VARCHAR存储? - 如何计算VARCHAR字段的实际存储大小? - MySQL 4.X和5.X的差异? 从引用中,我可以提取一些点。 完整回答结构: - 开头:直接回答。 - 主体:详细解释,分步说明。 - 结尾:相关问题。 添加引用标识:例如,在提到MySQL 5.X时,加[^1]。 现在,草拟回答: 回答: 在MySQL中,VARCHAR类型定义的长度是基于字符长度的,而不是字节长度。具体来说: - 在MySQL 5.X及以上版本中,VARCHAR(n) 中的n表示最大字符数。例如,VARCHAR(10)可以存储最多10个字符,无论这些字符是英文还是中文[^1]。 - 在MySQL 4.X版本中,VARCHAR(n) 表示字节长度,存储时会根据字符集计算字节数[^1]。 实际存储时,VARCHAR是变长字符串类型。存储的字节数取决于字符集和实际内容。例如: - 在UTF8MB4字符集下,一个中文字符通常用3个字节[^4]。 - 存储机制包括前缀长度标识:如果字符串长度小于255字节,需要1字节的前缀;如果大于255字节,需要2字节的前缀[^2]。 此外,VARCHAR的最大长度受限于最大行长度(65535字节),但实际可定义的长度会因字符集和行中其他字段而不同[^3]。 总之,在大多数现代MySQL版本(5.X及以上),VARCHAR(n) 定义的是字符长度,确保存储时能容纳n个字符。 相关问题: 1. 在MySQL中,如何计算VARCHAR字段的实际存储大小? 2. VARCHAR和CHAR类型在存储上有什么区别? 3. 不同字符集(如UTF8MB4 vs LATIN1)如何影响VARCHAR的存储空间? 4. 在MySQL 4.X版本中,VARCHAR的定义方式有什么不同? 确保引用正确: - 对于MySQL 5.X版本字符长度:[^1] - 对于存储机制:[^2]和[^3] - 对于字符集影响:[^4] 在回答中自然添加引用。</think>### MySQLVARCHAR类型定义的长度解析 在MySQL中,VARCHAR类型定义的长度**基于字符长度(character length)**,而非字节长度(byte length)。以下是详细解释,帮助您逐步理解: 1. **MySQL版本差异**: - **MySQL 5.X及以上版本**:VARCHAR(n) 中的 `n` 表示最大字符数(character count)。例如,`VARCHAR(10)` 可以存储最多10个英文字符或10个中文字符(无论字符用多少字节),定义时以字符为单位[^1]。 - **MySQL 4.X版本**:VARCHAR(n) 中的 `n` 表示字节长度(byte length),存储时会根据字符集动态计算字节数[^1]。但由于MySQL 4.X已过时,现代应用通常使用5.X及以上版本,因此字符长度是主流定义方式。 2. **存储机制**: - VARCHAR是变长字符串类型,实际存储的字节数取决于字符集(character set)和具体内容。例如: - 在UTF8MB4字符集下,一个英文字符用1字节,一个中文字符用3字节[^4]。 - 存储时需添加前缀长度标识:如果字符串长度小于255字节,前缀用1字节;如果大于255字节,前缀用2字节[^2]。 例如,定义 `VARCHAR(60)` 并存储 "万科"(2个中文字符),在UTF8MB4下实际字节长度为6字节(2字符 × 3字节/字符),但定义的长度60表示最多可容纳60个字符[^4]。 3. **最大长度限制**: - VARCHAR的最大理论长度为65535字节(包括所有字段长度、变长字段标识和NULL标志位),但实际可定义的最大字符数受字符集影响。例如: - 在LATIN1字符集(单字节字符)下,`VARCHAR(65535)` 可能有效。 - 在UTF8MB4字符集(多字节字符)下,最大字符数约为65535/3 ≈ 21845字符(需考虑行大小限制)[^3]。 实际使用中,需通过 `SHOW TABLE STATUS` 或计算行大小来验证。 4. **字节长度的关系**: - 虽然定义基于字符长度,但实际存储字节数需通过函数如 `LENGTH()`(字节长度)和 `CHAR_LENGTH()`(字符长度)计算。例如: ```sql SELECT name, CHAR_LENGTH(name) AS char_len, LENGTH(name) AS byte_len FROM users; ``` 在UTF8MB4下,若 `name VARCHAR(10)` 存储 "Hello"(5字符),则 `char_len=5`,`byte_len=5`;若存储 "你好"(2字符),则 `byte_len=6`[^4]。 **总结**:在MySQL 5.X及以上版本中,VARCHAR(n) 的 `n` 明确表示字符长度,确保用户定义时无需考虑字节换。存储引擎会根据字符集自动处理字节级细节,但需注意字符集选择对实际存储空间的影响(如UTF8MB4比LATIN1用更多字节)[^1][^2][^3][^4]。 ### 相关问题 1. 如何计算MySQLVARCHAR字段的实际存储大小(包括前缀和字符集影响)? 2. VARCHAR和CHAR类型在存储机制和性能上有何区别? 3. 不同字符集(如UTF8MB4、LATIN1)如何影响VARCHAR的最大可定义长度? 4. 在MySQL中,如何优化VARCHAR字段以减少存储空间用?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值