暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

开发篇-MySQL字符集

DBA天团 2021-02-05
674

从本质上来说,计算机只能识别二进制代码,因此,不论是计算机程序还是其处理的数据,最终都必须转换成二进制码,计算机才能认识。为了使计算机不仅能做科学计算,也能处理文字信息,人们想出了给每个文字符号编码以便于计算机识别处理的办法,这就是计算机字符集的由来。本章将详细介绍字符集的发展历程以及MySQL中字符集的使用。


9.1  字符集概述

简单地说字符集就是一套文字符号及其编码、比较规则的集合。20世纪60年代初期,美国标准化组织ANSI发布了第一个计算机字符集─ASCII(American Standard Code for Information Interchange),后来进一步变成了国际标准ISO-646。这个字符集采用7位编码,定义了包括大小写英文字母、阿拉伯数字和标点符号,以及33个控制符号等。虽然现在看来,这个美式的字符集很简单,包括的符号也很少,但直到今天它依然是计算机世界里奠基性的标准,其后制定的各种字符集基本都兼容ASCII字符集。

        自ASCII之后,为了处理不同的文字,各大计算机公司、各国政府、标准化组织等先后发明了几百种字符集,如大家熟悉的ISO-8859系列、GB 2312-80、GBK、BIG 5等。这些五花八门的字符集,从收录的字符到编码规则各不相同,给计算机软件开发和移植带来了很大困难。一个软件要在使用不同文字的国家或地区发布,必须进行本地化开发!基于这个原因,统一字符编码,成了20世纪80年代计算机业的迫切需要和普遍共识。


9.2  Unicode简述

为了统一字符编码,国际标准化组织ISO(International Organization for Standardization)的一些成员国于1984年发起制定新的国际字符集标准,以容纳全世界各种语言文字和符号。这个标准最后叫做“Universal Multiple-Octet Coded Character Set”,简称UCS,标准编号则定为ISO-10646。ISO-10646标准采用4字节(32bit)编码,因此简称UCS-4。具体编码规则是:将代码空间划分为组(group)、面(plane)、行(row)和格(ceil);第1个字节代表组(group),第2个字节代表面(plane),第3个字节代表行(row),第4个字节代表格(ceil),并规定字符编码的第32位必须为0,且每个面(plane)的最后两个码位FFFEh和FFFFh保留不用;因此,ISO-1064共有128个群组(0~0x7F),每个群组有256个字面(00~0xFF),每个字面有256行(00~0xFF),每行包括256格(0~0xFF),共有256 * 128 = 32,768 个字面,每个字面有256×256-2=65,534个码位,合计65,534×32,768=2,147,418,112个码位。

ISO-10646发布以后,遭到了部分美国计算机公司的反对。1988年Xerox公司提议制定新的以16位编码的统一字符集Unicode,并联合Apple、IBM、DEC、Sun、Microsoft、Novell等公司成立Unicode协会(The Unicode Consortium),并成立Unicode技术委员会(Unicode Technical Committee),专门负责Unicode文字的搜集、整理和编码,并于1991年推出了Unicode 1.0。

都是为了解决字符编码统一问题,ISO和Unicode协会却推出了两个不同的编码标准,这显然是不利的。后来,大家都认识到了这一点,经过双方谈判,1991年10月达成协议,ISO将Unicode编码并入ISO-10646的0组0字面,叫作基本多语言文字面(Basic Multi-lingual Plane, BMP),共有65,534个码位,并根据不同用途分为若干区域。除BMP外的32,767个字面又分为辅助字面(supplementary planes)和专用字面(private use planes)两部分,辅助字面用以收录ISO-10646后续搜集的各国文字,专用字面供使用者自定义收录ISO-10646未收录的文字符号。其实,大部分用户只使用BMP字面就足够了,早期的ISO-10646-1标准也只要求实现BMP字面,这样只需要2字节来编码就足够了,Unicode也正是这么做的,这叫作ISO-10646编码的基本面形式,简称为UCS-2编码,UCS-2编码转换成UCS-4编码也很容易,只要在前面加两个取值为0的字节即可。

ISO-10646的编码空间足以容纳人类从古至今使用过的所有文字和符号,但其实许多文字符号都已经很少使用了,超过99%的在用文字符号都编入了BMP,因此,绝大部分情况下,Unicode的双字节编码方式都能满足需求,而这种双字节编码方式比起ISO-10646的4字节原始编码来说,在节省内存和处理时间上都具有优势,这也是Unicode编码方式更流行的原因。但如果万一要使用ISO-10646 BMP字面以外的文字怎么办呢?Unicode提出了名为UTF-16或代理法(surrogates)的解决方案,UTF是UCS/Unicode Transformation Format 的缩写。UTF-16的解决办法是:对BMP字面的编码保持二字节不变,对其他字面的文字按一定规则将其32位编码转换为两个16位的Unicode编码,其两个字节的取值范围分别限定为0xD800~0xDBFF和0xDC00~0xDFFF,因此,UTF-16共有(4×256)×(4×256)=1,048,576个码位。

虽然UTF-16解决了ISO-10646除BMP外第1~15字面的编码问题,但当时的计算机和网络世界还是ASCII的天下,只能处理单字节数据流,UTF-16在离开Unicode环境后,在传输和处理中都存在问题。于是Unicode又提出了名为UTF-8的解决方案,UTF-8按一定规则将一个ISO-10646或Unicode字元码转换成1~4个字节的编码,其中将ASCII码(0~0x7F)转换成单字节编码,也就是严格兼容ASCII字符集;UTF-8的2字节编码,用以转换ISO-10646标准 0x0080~0x07FF的UCS-4原始码;UTF-8的3字节编码,用以转换ISO-10646标准 0x0800~0xFFFF的UCS-4原始码;UTF-8的4字节编码,用以转换ISO-10646标准 0x00010000~0001FFFF的UCS-4原始码。

上述各种编码方式,看起来有点让人迷惑。其实,ISO-10646只是给每一个文字符号分配了一个4字节无符号整数编号(UCS-4),并未规定在计算机中如何去表示这个无符号整数编号。UTF-16和UTF-8就是其两种变通表示方式。

ISO-10646与Unicode统一以后,两个组织虽然都继续发布各自的标准,但二者之间是一致的。由于Unicode最早投入应用,其编码方式更加普及,因此,许多人都知道Unicode,但对ISO-10646却了解不多。但由于二者是一致的,因此,区分ISO-10646和Unicode的意义也就不大了。现在,大家说Unicode和ISO-10646,一般指的是同一个东西,只是Unicode更直接、更普及罢了。二者不同版本的对应关系如下。

    Unicode 2.0等同于ISO/IEC 10646-1:1993。

    Unicode 3.0等同于ISO/IEC 10646-1:2000。

    Unicode 4.0等同于ISO/IEC 10646:2003。

最后要说的是,UTF-16和UTF-32因字节序的不同,又有了UTF-16BE(Big Endian)、UTF-16LE(Little Endian)和UTF-32BE(Big Endian)、UTF-32LE(Little Endian)等,在此不做进一步介绍。


9.3  汉字及一些常见字符集

在计算机发展的不同阶段,我国也参照当时的国际标准和实际需要,制定了一些汉字字符集编码标准,主要内容如下所示。

    GB 2312-80:全称《信息交换用汉字编码字符集 基本集》,于1980年发布。根据ISO/IEC 2022提供的字符编码扩充规范,形成双字节编码的字符集。收录了6,763个常用汉字和682个非汉字图形符号。

    GB 13000:全称《信息技术 通用多八位编码字符集(UCS) 第一部分:体系结构与基本多文种平面》,于1993年发布。根据ISO/IEC 10646-1:1993,在CJK(中、日、韩简称)统一汉字区和CJK统一汉字扩充区A,除收录GB 2312-80外,还收录了第1、3、5、7辅助集的全部汉字,共27,484个,以及一些偏旁部首等。但GB 13000推出后,几乎没有得到业界的支持,也就成了一个形式上的标准。

    GBK:全称《汉字内码扩展规范》1.0版,发布于1995年。GBK在GB 2312内码系统的基础上进行了扩充,收录了GB 13000.1-1993的全部20902个CJK统一汉字,包括GB 2312的全部6,763个汉字。此外,它增补编码了52个汉字,13个汉字结构符(在ISO/IEC 10646.1: 2000中称为表意文字描述符)和一些常用部首与汉字部件。在GBK的内码系统中,GB 2312汉字所在码位保持不变,这样,保证了GBK对GB 2312的完全兼容。同时,GBK内码与GB 13000.1代码一一对应,为GBK向GB 13000.1的转换提供了解决办法。有意思的是GBK并不是一个强制性的国家标准,只是一个行业指导规范,并没有强制力,但由于得到了Microsoft Windows 95的支持而大为流行。

    GB 18030:全称《信息技术信息交换用汉字编码字符集、基本集的扩充》,发布于2000年。根据ISO/IEC 10646-1:2000,收录了ISO/IEC 10646.1: 2000全部27,484个CJK统一汉字,13个表意文字描述符、部分汉字部首和部件、欧元符号等。GB 18030采用2字节或4字节编码,其二字节编码部分与GBK保持一致,因此,GB 18030是GBK的超集,也完全与GB 13000向上兼容,制定GB 18030也是为了解决GBK强制力不够的问题。

以上简要介绍了几种汉字字符集,下面将一些常用字符集的特点归纳如表9-1所示。

表9-1                                   常用字符集比较

字  符  集

是 否 定 长

编 码 方 式

其 他 说 明

ACSII

单字节7位编码

最早的奠基性字符集

ISO-8859-1/latin1

单字节8位编码

西欧字符集,经常被一些程序员用来转码

GB 2312-80

双字节编码

早期标准,不推荐再使用

GBK

双字节编码

虽然不是国标,但支持的系统不少

GB 18030

2字节或4字节编码

开始有一些支持,但数据库支持的还少见

UTF-32

4字节编码

UCS-4原始编码,目前很少采用

UCS-2

2字节编码

Windows 2000内部用UCS-2

UTF-16

2字节或4字节编码

Java和Windows XP/NT等内部使用UTF-16

UTF-8

1~4字节编码

互联网和UNIX/Linux广泛支持的Unicode字符集;MySQLServer也使用UTF-8

9.4  怎样选择合适的字符集

对数据库来说,字符集更加重要,因为数据库存储的数据大部分都是各种文字,字符集对数据库的存储、处理性能,以及日后系统的移植、推广都会有影响。

MySQL 5.6目前支持几十种字符集,包括ucs2、utf16、utf16le、utf32、utf8和utf8mb4等Unicode字符集。面对众多的字符集,我们该如何选择呢?

虽然没有一定之规,但在选择数据库字符集时,可以根据应用的需求,结合上面介绍的一些字符集的特点来权衡,主要考虑因素包括:

(1)满足应用支持语言的需求,如果应用要处理各种各样的文字,或者将发布到使用不同语言的国家或地区,就应该选择Unicode字符集。对MySQL来说,目前就是UTF-8。

(2)如果应用中涉及已有数据的导入,就要充分考虑数据库字符集对已有数据的兼容性。假如已有数据是GBK文字,如果选择GB 2312-80为数据库字符集,就很可能出现某些文字无法正确导入的问题。

(3)如果数据库只需要支持一般中文,数据量很大,性能要求也很高,那就应该选择双字节定长编码的中文字符集,比如GBK。因为,相对于UTF-8而言,GBK比较“小”,每个汉字只占2个字节,而UTF-8汉字编码需要3个字节,这样可以减少磁盘I/O、数据库cache,以及网络传输的时间,从而提高性能。相反,如果应用主要处理英文字符,仅有少量汉字数据,那么选择UTF-8更好,因为GBK、UCS-2、UTF-16的西文字符编码都是2个字节,会造成很大不必要的开销。

(4)如果数据库需要做大量的字符运算,如比较、排序等,选择定长字符集可能更好,因为定长字符集的处理速度要比变长字符集的处理速度快。

(5)如果所有客户端程序都支持相同的字符集,应该优先选择该字符集作为数据库字符集。这样可以避免因字符集转换带来的性能开销和数据损失。

9.5  MySQL支持的字符集简介

MySQL服务器可以支持多种字符集,在同一台服务器、同一个数据库甚至同一个表的不同字段都可以指定使用不同的字符集,相比Oracle等其他数据库管理系统,在同一个数据库只能使用相同的字符集,MySQL明显存在更大的灵活性。

查看所有可用的字符集的命令是show character set:


mysql> show character set;

+----------+-----------------------------+---------------------+--------+

| Charset  | Description                     | Default collation    | Maxlen |

+----------+-----------------------------+---------------------+--------+

| dec8      | DEC West European             | dec8_swedish_ci      | 1       |

| cp850    | DOS West European             | cp850_general_ci     | 1       |

| hp8      | HP West European              | hp8_english_ci       | 1       |

| koi8r    | KOI8-R Relcom Russian        | koi8r_general_ci     | 1       |

……

或者查看information_schema.character_set,可以显示所有的字符集和该字符集默认的校对规则。

mysql> desc information_schema.character_sets;

+----------------------+-------------+------+-----+---------+-------+

| Field                   | Type          | Null  | Key | Default | Extra |

+----------------------+-------------+------+-----+---------+-------+

| CHARACTER_SET_NAME    | varchar(64) | NO    |      |           |         |

| DEFAULT_COLLATE_NAME | varchar(64) | NO    |      |          |         |

| DESCRIPTION            | varchar(60) | NO    |      |          |         |

| MAXLEN                  | bigint(3)    | NO    |      | 0        |         |

+----------------------+-------------+------+-----+---------+-------+

4 rows in set (0.00 sec)


MySQL的字符集包括字符集(CHARACTER)和校对规则(COLLATION)两个概念。字符集是用来定义MySQL存储字符串的方式,校对规则则是定义了比较字符串的方式。字符集和校对规则是一对多的关系,MySQL支持30多种字符集的70多种校对规则。

每个字符集至少对应一个校对规则。可以用“SHOW COLLATION LIKE '***';”命令或者查看information_schema.COLLATIONS。查看相关字符集的校对规则。

mysql> SHOW COLLATION LIKE 'gbk%';

+----------------+---------+----+---------+----------+---------+

| Collation       | Charset | Id | Default | Compiled | Sortlen |

+----------------+---------+----+---------+----------+---------+

| gbk_chinese_ci | gbk       | 28 | Yes      | Yes       | 1         |

| gbk_bin          | gbk      | 87 |           | Yes       | 1         |

+----------------+---------+----+---------+----------+---------+

2 rows in set (0.00 sec)

校对规则命名约定:它们以其相关的字符集名开始,通常包括一个语言名,并且以_ci(大小写不敏感)、_cs(大小写敏感)或_bin(二元,即比较是基于字符编码的值而与language无关)结束。

例如,上面例子中GBK的校对规则,其中gbk_chinese_ci是默认的校对规则,大小写不敏感的,gbk_bin按照编码的值进行比较,是大小写敏感的。

下面的这个例子中,如果指定'A'和'a'按照gbk_chinese_ci校对规则进行比较,则认为两个字符是相同的,如果按照gbk_bin校对规则进行比较,则认为两个字符是不同的。我们事先需要确认应用的需求,是需要按照什么样的排序方式,是否需要区分大小写,以确定校对规则的选择。


mysql> select case when 'A' COLLATE gbk_chinese_ci = 'a' collate gbk_chinese_ci  then 1 else 0 end;

+-----------------------------------------------------------------------+

| case when 'A' COLLATE gbk_chinese_ci = 'a' collate gbk_chinese_ci  then 1
 else 0 end |

+-----------------------------------------------------------------------+

| 1                                                                                   |

+-----------------------------------------------------------------------+

1 row in set (0.00 sec)


mysql>  select case when 'A' COLLATE gbk_bin = 'a' collate gbk_bin  then 1 else 0 end;

+-----------------------------------------------------------------------+

| case when 'A' COLLATE gbk_bin = 'a' collate gbk_bin  then 1 else 0 end |

+-----------------------------------------------------------------------+

| 0                                                                                   |

+-----------------------------------------------------------------------+

1 row in set (0.00 sec)



9.6  MySQL字符集的设置

MySQL的字符集和校对规则有4个级别的默认设置:服务器级、数据库级、表级和字段级。它们分别在不同的地方设置,作用也不相同。

9.6.1  服务器字符集和校对规则

服务器字符集和校对,在MySQL服务启动的时候确定。

可以在my.cnf中设置:

 [mysqld]

character-set-server=gbk

或者在启动选项中指定:

mysqld --character-set-server=gbk


或者在编译的时候指定:

shell> cmake . -DDEFAULT_CHARSET=gbk


如果没有特别的指定服务器字符集,默认使用latin1作为服务器字符集。上面3种设置的方式都只指定了字符集,没有指定校对规则,这样是使用该字符集默认的校对规则,如果要使用该字符集的非默认校对规则,则需要在指定字符集的同时指定校对规则。

可以用“show variables like 'character_set_server';”命令查询当前服务器的字符集和校对规则。

mysql> show variables like 'character_set_server';

+----------------------+-------+

| Variable_name          | Value |

+----------------------+-------+

| character_set_server | gbk    |

+----------------------+-------+

1 row in set (0.00 sec)


mysql> show variables like 'collation_server';

+------------------+----------------+

| Variable_name     | Value            |

+------------------+----------------+

| collation_server | gbk_chinese_ci |

+------------------+----------------+

1 row in set (0.00 sec)


9.6.2  数据库字符集和校对规则

数据库的字符集和校对规则在创建数据库的时候指定,也可以在创建完数据库后通过“alter database”命令进行修改。需要注意的是,如果数据库里已经存在数据,因为修改字符集并不能将已有的数据按照新的字符集进行存放,所以不能通过修改数据库的字符集直接修改数据的内容,在9.7小节中通过一个具体的例子介绍了字符集的修改方法。

设置数据库字符集的规则是:

          如果指定了字符集和校对规则,则使用指定的字符集和校对规则;

      如果指定了字符集没有指定校对规则,则使用指定字符集的默认校对规则;

      如果指定了校对规则但未指定字符集,则字符集使用与该校对规则关联的字符集;

    如果没有指定字符集和校对规则,则使用服务器字符集和校对规则作为数据库的字符集和校对规则。

推荐在创建数据库的时候明确指定字符集和校对规则,避免受到默认值的影响。

要显示当前数据库的字符集和校对规则,可以使用“show variables like ' character_set_ database '”和“show variables like ' collation_database '”命令查看:


mysql> show variables like 'character_set_database';

+------------------------+-------+

| Variable_name            | Value |

+------------------------+-------+

| character_set_database | utf8  |

+------------------------+-------+

1 row in set (0.00 sec)


mysql> show variables like 'collation_database';

+--------------------+-----------------+

| Variable_name       | Value             |

+--------------------+-----------------+

| collation_database | utf8_general_ci |

+--------------------+-----------------+

1 row in set (0.00 sec)

9.6.3  表字符集和校对规则

表的字符集和校对规则在创建表的时候指定,可以通过alter table命令进行修改,同样,如果表中已有记录,修改字符集对原有的记录并没有影响,不会按照新的字符集进行存放。表的字段仍然使用原来的字符集。

设置表的字符集的规则和上面基本类似:

          如果指定了字符集和校对规则,使用指定的字符集和校对规则;

      如果指定了字符集没有指定校对规则,使用指定字符集的默认校对规则;

      如果指定了校对规则但未指定字符集,则字符集使用与该校对规则关联的字符集;

    如果没有指定字符集和校对规则,使用数据库字符集和校对规则作为表的字符集和校对规则。

推荐在创建表的时候明确指定字符集和校对规则,避免受到默认值的影响。要显示表的字符集和校对规则,可以使用show create table命令查看:


mysql>  show create table z1 \G;

*************************** 1. row ***************************

       Table: z1

Create Table: CREATE TABLE 'z1' (

  'id' int(11) default NULL

) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin

1 row in set (0.00 sec)

9.6.4  列字符集和校对规则

MySQL可以定义列级别的字符集和校对规则,主要是针对相同的表不同字段需要使用不同的字符集的情况,应该说一般遇到这种情况的几率比较小,这只是MySQL提供给我们一个灵活设置的手段。

列字符集和校对规则的定义可以在创建表时指定,或者在修改表时调整,如果在创建表的时候没有特别指定字符集和校对规则,则默认使用表的字符集和校对规则。

9.6.5  连接字符集和校对规则

上面4种设置方式,确定的是数据保存的字符集和校对规则,对于实际的应用访问来说,还存在客户端和服务器之间交互的字符集和校对规则的设置。

对于客户端和服务器的交互操作,MylSQL提供了3个不同的参数:character_set_client、character_set_connection和character_set_results,分别代表客户端、连接和返回结果的字符集,通常情况下,这3个字符集应该是相同的,才可以确保用户写入的数据可以正确地读出,特别是对于中文字符,不同的写入字符集和返回结果字符集将导致写入的记录不能正确读出。

通常情况下,不会单个地设置这3个参数,可以通过以下命令:

SET NAMES ***;

来设置连接的字符集和校对规则,这个命令可以同时修改这3个参数的值。使用这个方法修改连接的字符集和校对规则,需要应用每次连接数据库后都执行这个命令。

另外一个更简便的办法,是在my.cnf中设置以下语句:

[mysql]

default-character-set=gbk

这样服务器启动后,所有连接默认就是使用GBK字符集进行连接的,而不需要在程序中再执行set names命令。

另外,字符串常量的字符集也是由character_set_connection参数来指定的。

可以通过“[_charset_name]'string' [COLLATE collation_name]”命令强制字符串的字符集和校对规则。例如:

select _gbk '字符集';

select _latin1 '字符集';

通常情况下,基本不需要用户强制指定字符串字符集。


9.7  字符集的修改步骤

如果在应用开始阶段没有正确的设置字符集,在运行一段时间以后才发现存在不能满足要求需要调整,又不想丢弃这段时间的数据,那么就需要进行字符集的修改。字符集的修改不能直接通过“alter database character set ***”或者“alter table tablename character set ***”命令进行,这两个命令都没有更新已有记录的字符集,而只是对新创建的表或者记录生效。已有记录的字符集调整,需要先将数据导出,经过适当的调整重新导入后才可完成。

以下模拟的是将latin1字符集的数据库修改成GBK字符集的数据库的过程。

(1)导出表结构:

mysqldump -uroot -p --default-character-set=gbk -d databasename> createtab.sql

其中--default-character-set=gbk表示设置以什么字符集连接,-d表示只导出表结构,不导出数据。

(2)手工修改createtab.sql中表结构定义中的字符集为新的字符集。

(3)确保记录不再更新,导出所有记录。

mysqldump -uroot -p --quick --no-create-info --extended-insert --default- character-set=latin1  databasename> data.sql

  --quick:该选项用于转储大的表。它强制mysqldump从服务器一次一行地检索表中的行而不是检索所有行,并在输出前将它缓存到内存中。

    --extended-insert:使用包括几个VALUES列表的多行INSERT语法。这样使转储文件更小,重载文件时可以加速插入。

    --no-create-info:不写重新创建每个转储表的CREATE TABLE语句。

    --default-character-set=latin1:按照原有的字符集导出所有数据,这样导出的文件中,所有中文都是可见的,不会保存成乱码。

(4)打开data.sql,将SET NAMES latin1修改成SET NAMES gbk。

(5)使用新的字符集创建新的数据库。

create database databasename default charset gbk;

(6)创建表,执行createtab.sql。

mysql -uroot -p databasename < createtab.sql

(7)导入数据,执行data.sql。

mysql -uroot -p databasename < data.sql
注意:选择目标字符集的时候,要注意最好是源字符集的超集,或者确定比源字符集的字库更大,否则如果目标字符集的字库小于源字符集的字库,那么目标字符集中不支持的字符倒入后会变成乱码,丢失一部分数据。例如,GBK字符集的字库大于GB 2312字符集,那么GBK字符集的数据,如果导入GB 2312数据库中,就会丢失GB 2312中不支持的那部分汉字的数据。

9.8  小结

这一章主要介绍了MySQL中字符集和校对规则的概念、设置方法,以及推荐读者使用的字符集。最后,举例介绍了字符集修改的步骤和修改过程中遇到过的问题,希望会对读者有所帮助。


最后修改时间:2021-02-05 14:29:19
文章转载自DBA天团,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论