MYSQL 第十章 MySQL事务和字符集

当多个用户访问同一数据时,一个用户在更改数据的过程中可能有其它用户同时发起更改请求,为保证数据的一致性状态,MySQL 引入了事务。

本章首先介绍了事务控制语句和隔离级别,然后介绍了字符集和校对规则的相关概念和操作。

MySQL 为了解决此类问题,提供了事务。事务可以将一系列的数据操作捆绑成一个整体进行统一管理,如果某一事务执行成功,则在该事务中进行的所有数据更改均会提交,成为数据库中的永久组成部分。如果事务执行时遇到错误,则就必须取消或回滚。取消或回滚后,数据将全部恢复到操作前的状态,所有数据的更改均被清除。

MySQL 通过事务保证了数据的一致性。上述提到的转账过程就是一个事务,它需要两条 UPDATE 语句来完成。这两条语句是一个整体,如果其中任何一个环节出现问题,则整个转账业务也应取消,两个账户中的余额应恢复为原来的数据,从而确保转账前和转账后的余额总和不变,即都是 1001 元。

数据库事务的概念和特性

数据库的事务(Transaction)是一种机制、一个操作序列,包含了一组数据库操作命令。事务把所有的命令作为一个整体一起向系统提交或撤销操作请求,即这一组数据库命令要么都执行,要么都不执行,因此事务是一个不可分割的工作逻辑单元。

在数据库系统上执行并发操作时,事务是作为最小的控制单元来使用的,特别适用于多用户同时操作的数据库系统。例如,航空公司的订票系统、银行、保险公司以及证券交易系统等。

事务具有 4 个特性,即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),这 4 个特性通常简称为 ACID。

1. 原子性

事务是一个完整的操作。事务的各元素是不可分的(原子的)。事务中的所有元素必须作为一个整体提交或回滚。如果事务中的任何元素失败,则整个事务将失败。

以银行转账事务为例,如果该事务提交了,则这两个账户的数据将会更新。如果由于某种原因,事务在成功更新这两个账户之前终止了,则不会更新这两个账户的余额,并且会撤销对任何账户余额的修改,事务不能部分提交。

2. 一致性

当事务完成时,数据必须处于一致状态。也就是说,在事务开始之前,数据库中存储的数据处于一致状态。在正在进行的事务中. 数据可能处于不一致的状态,如数据可能有部分被修改。然而,当事务成功完成时,数据必须再次回到已知的一致状态。通过事务对数据所做的修改不能损坏数据,或者说事务不能使数据存储处于不稳定的状态。

以银行转账事务事务为例。在事务开始之前,所有账户余额的总额处于一致状态。在事务进行的过程中,一个账户余额减少了,而另一个账户余额尚未修改。因此,所有账户余额的总额处于不一致状态。事务完成以后,账户余额的总额再次恢复到一致状态。

3. 隔离性

对数据进行修改的所有并发事务是彼此隔离的,这表明事务必须是独立的,它不应以任何方式依赖于或影响其他事务。修改数据的事务可以在另一个使用相同数据的事务开始之前访问这些数据,或者在另一个使用相同数据的事务结束之后访问这些数据。

另外,当事务修改数据时,如果任何其他进程正在同时使用相同的数据,则直到该事务成功提交之后,对数据的修改才能生效。张三和李四之间的转账与王五和赵二之间的转账,永远是相互独立的。

4. 持久性

事务的持久性指不管系统是否发生了故障,事务处理的结果都是永久的。

一个事务成功完成之后,它对数据库所作的改变是永久性的,即使系统出现故障也是如此。也就是说,一旦事务被提交,事务对数据所做的任何变动都会被永久地保留在数据库中。

事务的 ACID 原则保证了一个事务或者成功提交,或者失败回滚,二者必居其一。因此,它对事务的修改具有可恢复性。即当事务失败时,它对数据的修改都会恢复到该事务执行前的状态。

MySQL执行事务的语法和流程

MySQL 提供了多种存储引擎来支持事务。支持事务的存储引擎有 InnoDB 和 BDB,其中,InnoDB 存储引擎事务主要通过 UNDO 日志和 REDO 日志实现,MyISAM 存储引擎不支持事务。

拓展:任何一种数据库,都会拥有各种各样的日志,用来记录数据库的运行情况、日常操作、错误信息等,MySQL 也不例外。例如,当用户 root 登录到 MySQL 服务器,就会在日志文件里记录该用户的登录时间、执行操作等。

为了维护 MySQL 服务器,经常需要在 MySQL 数据库中进行日志操作:

  • UNDO 日志:复制事务执行前的数据,用于在事务发生异常时回滚数据。
  • REDO 日志:记录在事务执行中,每条对数据进行更新的操作,当事务提交时,该内容将被刷新到磁盘。


默认设置下,每条 SQL 语句就是一个事务,即执行 SQL 语句后自动提交。为了达到将几个操作做为一个整体的目的,需要使用 BEGIN 或 START TRANSACTION 开启一个事务,或者禁止当前会话的自动提交。

关于事务的自动提交可阅读学习《MySQL设置事务自动提交》一节。

执行事务的语法和流程

SQL 使用下列语句来管理事务。

1) 开始事务

BEGIN;

START TRANSACTION;

这个语句显式地标记一个事务的起始点。

2) 提交事务

MySQL 使用下面的语句来提交事务:

COMMIT;

COMMIT 表示提交事务,即提交事务的所有操作,具体地说,就是将事务中所有对数据库的更新都写到磁盘上的物理数据库中,事务正常结束。

提交事务,意味着将事务开始以来所执行的所有数据都修改成为数据库的永久部分,因此也标志着一个事务的结束。一旦执行了该命令,将不能回滚事务。只有在所有修改都准备好提交给数据库时,才执行这一操作。

3) 回滚(撤销)事务

MySQL 使用以下语句回滚事务:

ROLLBACK;

ROLLBACK 表示撤销事务,即在事务运行的过程中发生了某种故障,事务不能继续执行,系统将事务中对数据库的所有已完成的操作全部撤销,回滚到事务开始时的状态。这里的操作指对数据库的更新操作。

当事务执行过程中遇到错误时,使用 ROLLBACK 语句使事务回滚到起点或指定的保持点处。同时,系统将清除自事务起点或到某个保存点所做的所有的数据修改,并且释放由事务控制的资源。因此,这条语句也标志着事务的结束。

总结

BEGIN 或 START TRANSACTION 语句后面的 SQL 语句对数据库数据的更新操作都将记录在事务日志中,直至遇到 ROLLBACK 语句或 COMMIT 语句。如果事务中某一操作失败且执行了 ROLLBACK 语句,那么在开启事务语句之后所有更新的数据都能回滚到事务开始前的状态。如果事务中的所有操作都全部正确完成,并且使用了 COMMIT 语句向数据库提交更新数据,则此时的数据又处在新的一致状态。

实例演示

mysql> create table message (
    -> name varchar(25) not null,
    -> money int(10) not null);
Query OK, 0 rows affected (0.09 sec)

mysql> insert into message values('wang',1000),('li',1);
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> select * from message;
+------+-------+
| name | money |
+------+-------+
| wang |  1000 |
| li   |     1 |
+------+-------+
2 rows in set (0.00 sec)

mysql> 
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update message set money=money-500 where name='wang';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from message;    #在另外一个窗口视图中的还是 1000, 1 没有 commit 就不更新,我这是同一个视图
+------+-------+
| name | money |
+------+-------+
| wang |   500 |
| li   |     1 |
+------+-------+
2 rows in set (0.00 sec)

mysql> update message set money=money+500 where name='li';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from message;
+------+-------+
| name | money |
+------+-------+
| wang |   500 |
| li   |   501 |
+------+-------+
2 rows in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.01 sec)

mysql> 
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update message set money=money-500 where name='wang';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0


mysql> select * from message;
+------+-------+
| name | money |
+------+-------+
| wang |     0 |
| li   |   501 |
+------+-------+
2 rows in set (0.00 sec)

mysql> rollback;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from message;
+------+-------+
| name | money |
+------+-------+
| wang |   500 |
| li   |   501 |
+------+-------+
2 rows in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> commit;

MySQL 事务是一项非常消耗资源的功能,大家在使用过程中要注意以下几点。

1) 事务尽可能简短

事务的开启到结束会在数据库管理系统中保留大量资源,以保证事务的原子性、一致性、隔离性和持久性。如果在多用户系统中,较大的事务将会占用系统的大量资源,使得系统不堪重负,会影响软件的运行性能,甚至导致系统崩溃。

2) 事务中访问的数据量尽量最少

当并发执行事务处理时,事务操作的数据量越少,事务之间对相同数据的操作就越少。

3) 查询数据时尽量不要使用事务

对数据进行浏览查询操作并不会更新数据库的数据,因此应尽量不使用事务查询数据,避免占用过量的系统资源。

4) 在事务处理过程中尽量不要出现等待用户输入的操作

在处理事务的过程中,如果需要等待用户输入数据,那么事务会长时间地占用资源,有可能造成系统阻塞。

MySQL设置事务自动提交(开启和关闭)

mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set (0.02 sec)

mysql> 

MySQL事务隔离级别详解(附带实例)

在《数据库事务》一节中介绍了 MySQL 事务的四大特性,其中事务的隔离性就是指当多个事务同时运行时,各事务之间相互隔离,不可互相干扰。

如果事务没有隔离性,就容易出现脏读、不可重复读和幻读等情况。

1) 脏读

脏读是指一个事务正在访问数据,并且对数据进行了修改,但是这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。

2) 不可重复读

不可重复读是指在一个事务内,多次读取同一个数据。

在这个事务还没有结束时,另外一个事务也访问了该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。

3) 幻读

幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。

为了解决以上这些问题,标准 SQL 定义了 4 类事务隔离级别,用来指定事务中的哪些数据改变是可见的,哪些数据改变是不可见的。

MySQL 包括的事务隔离级别如下:

  • 读未提交(READ UNCOMITTED)
  • 读提交(READ COMMITTED)
  • 可重复读(REPEATABLE READ)
  • 串行化(SERIALIZABLE)


MySQL 事务隔离级别可能产生的问题如下表所示:

隔离级别脏读不可重复读幻读
READ UNCOMITTED
READ COMMITTED ×
REPEATABLE READ × ×
SERIALIZABLE × × ×


MySQL 的事务的隔离级别由低到高分别为 READ UNCOMITTED、READ COMMITTED、REPEATABLE READ、SERIALIZABLE。低级别的隔离级别可以支持更高的并发处理,同时占用的系统资源更少。

下面根据实例来一一阐述它们的概念和联系。

1. 读未提交(READ UNCOMITTED,RU)

顾名思义,读未提交就是可以读到未提交的内容。

如果一个事务读取到了另一个未提交事务修改过的数据,那么这种隔离级别就称之为读未提交。

在该隔离级别下,所有事务都可以看到其它未提交事务的执行结果。因为它的性能与其他隔离级别相比没有高多少,所以一般情况下,该隔离级别在实际应用中很少使用。

例 1 主要演示了在读未提交隔离级别中产生的脏读现象。

在 A 窗口中修改事务隔离级别,因为 A 窗口和 B 窗口的事务隔离级别需要保持一致,所以我们使用 SET GLOBAL TRANSACTION 修改全局变量。SQL 语句如下:

mysql> set global transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)

mysql> 
mysql> show global variables like '%isolation%';
+-----------------------+------------------+
| Variable_name         | Value            |
+-----------------------+------------------+
| transaction_isolation | READ-UNCOMMITTED |
| tx_isolation          | READ-UNCOMMITTED |
+-----------------------+------------------+
2 rows in set (0.01 sec)

mysql> 
mysql> use bank;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> select * from message;
+-------+-------+
| name  | money |
+-------+-------+
| wang  |   500 |
| li    |   501 |
| zhang |   300 |
+-------+-------+
3 rows in set (0.00 sec)

mysql> insert into message values('liu',300);
Query OK, 1 row affected (0.00 sec)

mysql> select * from message;
+-------+-------+
| name  | money |
+-------+-------+
| wang  |   500 |
| li    |   501 |
| zhang |   300 |
| liu   |   300 |
+-------+-------+
4 rows in set (0.00 sec)

mysql> rollback;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from message;
+-------+-------+
| name  | money |
+-------+-------+
| wang  |   500 |
| li    |   501 |
| zhang |   300 |
+-------+-------+
3 rows in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> 

2. 读提交(READ COMMITTED,RC)

顾名思义,读提交就是只能读到已经提交了的内容。

如果一个事务只能读取到另一个已提交事务修改过的数据,并且其它事务每对该数据进行一次修改并提交后,该事务都能查询得到最新值,那么这种隔离级别就称之为读提交。

该隔离级别满足了隔离的简单定义:一个事务从开始到提交前所做的任何改变都是不可见的,事务只能读取到已经提交的事务所做的改变。

这是大多数数据库系统的默认事务隔离级别(例如 Oracle、SQL Server),但不是 MySQL 默认的。

mysql> set global transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)

当 MySQL 的事务隔离级别为 READ COMMITTED 时,首先分别在 A 窗口和 B 窗口中开启事务,在 B 窗口中的事务更新并提交后,A 窗口中的事务读取到了更新后的数据。在该过程中,A 窗口中的事务必须要等待 B 窗口中的事务提交后才能读取到更新后的数据,这样就解决了脏读问题。而处于 A 窗口中的事务出现了不同的查询结果,即不可重复读现象。

3. 可重复读(REPEATABLE READ,RR)

顾名思义,可重复读是专门针对不可重复读这种情况而制定的隔离级别,可以有效的避免不可重复读。

在一些场景中,一个事务只能读取到另一个已提交事务修改过的数据,但是第一次读过某条记录后,即使其它事务修改了该记录的值并且提交,之后该事务再读该条记录时,读到的仍是第一次读到的值,而不是每次都读到不同的数据。那么这种隔离级别就称之为可重复读。

可重复读是 MySQL 的默认事务隔离级别,它能确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。在该隔离级别下,如果有事务正在读取数据,就不允许有其它事务进行修改操作,这样就解决了可重复读问题。

mysql> set global transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)

是事务中不会查询到其他事务已经修改提交的数据

4. 串行化(SERIALIZABLE)

如果一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来。那么这种隔离级别就称之为串行化。

SERIALIZABLE 是最高的事务隔离级别,主要通过强制事务排序来解决幻读问题。简单来说,就是在每个读取的数据行上加上共享锁实现,这样就避免了脏读、不可重复读和幻读等问题。但是该事务隔离级别执行效率低下,且性能开销也最大,所以一般情况下不推荐使用。

查看事务隔离级别
在 MySQL 中,可以通过show variables like '%tx_isolation%'或select @@tx_isolation;语句来查看当前事务隔离级别。

查看当前事务隔离级别的 SQL 语句和运行结果如下:
mysql> show variables like '%tx_isolation%';
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| tx_isolation  | REPEATABLE-READ |
+---------------+-----------------+
1 row in set, 1 warning (0.17 sec)
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set, 1 warning (0.00 sec)
结果显示,目前 MySQL 的事务隔离级别是 REPEATABLE-READ。

另外,还可以使用下列语句分别查询全局和会话的事务隔离级别:
SELECT @@global.tx_isolation;
SELECT @@session.tx_isolation;
提示:在MySQL 8.0.3 中,tx_isolation 变量被 transaction_isolation 变量替换了。在 MySQL 8.0.3 版本中查询事务隔离级别,只要把上述查询语句中的 tx_isolation 变量替换成 transaction_isolation 变量即可。

MySQL字符集和校对规则详解

在讲解字符集和校对规则之前,我们先来简单了解一下字符、字符集和字符编码。

字符(Character)是计算机中字母、数字、符号的统称,一个字符可以是一个中文汉字、一个英文字母、一个阿拉伯数字、一个标点符号等。

计算机是以二进制的形式来存储数据的。平时我们在显示器上看到的数字、英文、标点符号、汉字等字符都是二进制数转换之后的结果。

字符集(Character set)定义了字符和二进制的对应关系,为字符分配了唯一的编号。常见的字符集有 ASCII、GBK、IOS-8859-1 等。

字符编码(Character encoding)也可以称为字集码,规定了如何将字符的编号存储到计算机中。

大部分字符集都只对应一种字符编码,例如:ASCII、IOS-8859-1、GB2312、GBK,都是既表示了字符集又表示了对应的字符编码。所以一般情况下,可以将两者视为同义词。Unicode 字符集除外,Unicode 有三种编码方案,即 UTF-8、UTF-16 和 UTF-32。最为常用的是 UTF-8 编码。

校对规则(Collation)也可以称为排序规则,是指在同一个字符集内字符之间的比较规则。字符集和校对规则是一对多的关系,每个字符集都有一个默认的校对规则。字符集和校对规则相辅相成,相互依赖关联。

简单来说,字符集用来定义 MySQL 存储字符串的方式,校对规则用来定义 MySQL 比较字符串的方式。

有些数据库并没有清晰的区分开字符集和校对规则。例如,在 SQL Server 中创建数据库时,选择字符集就相当于选定了字符集和校对规则。

而在 MySQL 中,字符集和校对规则是区分开的,必须设置字符集和校对规则。一般情况下,没有特殊需求,只设置其一即可。只设置字符集时,MySQL 会将校对规则设置为字符集中对应的默认校对规则。

可以通过SHOW VARIABLES LIKE 'character%';命令查看当前 MySQL 使用的字符集,命令和运行结果如下:

mysql> SHOW VARIABLES LIKE 'character%';
+--------------------------+---------------------------------------------------------+
| Variable_name            | Value                                                   |
+--------------------------+---------------------------------------------------------+
| character_set_client     | gbk                                                     |
| character_set_connection | gbk                                                     |
| character_set_database   | latin1                                                  |
| character_set_filesystem | binary                                                  |
| character_set_results    | gbk                                                     |
| character_set_server     | latin1                                                  |
| character_set_system     | utf8                                                    |
| character_sets_dir       | C:Program FilesMySQLMySQL Server 5.7sharecharsets |
+--------------------------+---------------------------------------------------------+
8 rows in set, 1 warning (0.01 sec)

上述运行结果说明如下表所示:

名称说明
character_set_client MySQL 客户端使用的字符集
character_set_connection 连接数据库时使用的字符集
character_set_database 创建数据库使用的字符集
character_set_filesystem MySQL 服务器文件系统使用的字符集,默认值为 binary,不做任何转换
character_set_results 数据库给客户端返回数据时使用的字符集
character_set_server MySQL 服务器使用的字符集,建议由系统自己管理,不要人为定义
character_set_system 数据库系统使用的字符集,默认值为 utf8,不需要设置
character_sets_dir 字符集的安装目录

乱码时,不需要关心 character_set_filesystem、character_set_system 和 character_sets_dir 这 3 个系统变量,它们不会影响乱码 。

可以通过SHOW VARIABLES LIKE 'collation\_%';命令查看当前 MySQL 使用的校对规则,命令和运行结果如下:

mysql> SHOW VARIABLES LIKE 'collation\_%';
+----------------------+-------------------+
| Variable_name        | Value             |
+----------------------+-------------------+
| collation_connection | gbk_chinese_ci    |
| collation_database   | latin1_swedish_ci |
| collation_server     | latin1_swedish_ci |
+----------------------+-------------------+
3 rows in set, 1 warning (0.01 sec)

对上述运行结果说明如下:

  • collation_connection:连接数据库时使用的校对规则
  • collation_database:创建数据库时使用的校对规则
  • collation_server:MySQL 服务器使用的校对规则


校对规则命令约定如下:

  • 以校对规则所对应的字符集名开头
  • 以国家名居中(或以 general 居中)
  • 以 ci、cs 或 bin 结尾,ci 表示大小写不敏感,cs 表示大小写敏感,bin 表示按二进制编码值比较。

MySQL字符集的转换过程

MySQL 中字符集的转换过程如下:

1)在命令提示符窗口(cmd 命令行)中执行 MySQL 命令或 sql 语句时,这些命令或语句从“命令提示符窗口字符集”转换为“character_set_client”定义的字符集。

2)使用命令提示符窗口成功连接 MySQL 服务器后,就建立了一条“数据通信链路”,MySQL 命令或 sql 语句沿着“数据链路”传向 MySQL 服务器,由 character_set_client 定义的字符集转换为 character_set_connection 定义的字符集。

3)MySQL 服务实例收到数据通信链路中的 MySQL 命令或 sql 语句后,将 MySQL 命令或 sql 语句从 character_set_connection 定义的字符集转换为 character_set_server 定义的字符集。

4)若 MySQL 命令或 sql 语句针对于某个数据库进行操作,此时将 MySQL 命令或 sql 语句从 character_set_server 定义的字符集转换为 character_set_database 定义的字符集。

5)MySQL 命令或 sql 语句执行结束后,将执行结果设置为 character_set_results 定义的字符集。

6)执行结果沿着打开的数据通信链路原路返回,将执行结果从 character_set_results 定义的字符集转换为 character_set_client 定义的字符集,最终转换为命令提示符窗口字符集,显示到命令提示符窗口中。

MySQL字符集的转换过程

MySQL查看字符集和校对规则

mysql> show character set;
+----------+---------------------------------+---------------------+--------+
| Charset  | Description                     | Default collation   | Maxlen |
+----------+---------------------------------+---------------------+--------+
| big5     | Big5 Traditional Chinese        | big5_chinese_ci     |      2 |
| 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 |
| latin1   | cp1252 West European            | latin1_swedish_ci   |      1 |
| latin2   | ISO 8859-2 Central European     | latin2_general_ci   |      1 |
| swe7     | 7bit Swedish                    | swe7_swedish_ci     |      1 |
| ascii    | US ASCII                        | ascii_general_ci    |      1 |
| ujis     | EUC-JP Japanese                 | ujis_japanese_ci    |      3 |
| sjis     | Shift-JIS Japanese              | sjis_japanese_ci    |      2 |
| hebrew   | ISO 8859-8 Hebrew               | hebrew_general_ci   |      1 |
| tis620   | TIS620 Thai                     | tis620_thai_ci      |      1 |
| euckr    | EUC-KR Korean                   | euckr_korean_ci     |      2 |
| koi8u    | KOI8-U Ukrainian                | koi8u_general_ci    |      1 |
| gb2312   | GB2312 Simplified Chinese       | gb2312_chinese_ci   |      2 |
| greek    | ISO 8859-7 Greek                | greek_general_ci    |      1 |
| cp1250   | Windows Central European        | cp1250_general_ci   |      1 |
| gbk      | GBK Simplified Chinese          | gbk_chinese_ci      |      2 |
| latin5   | ISO 8859-9 Turkish              | latin5_turkish_ci   |      1 |
| armscii8 | ARMSCII-8 Armenian              | armscii8_general_ci |      1 |
| utf8     | UTF-8 Unicode                   | utf8_general_ci     |      3 |
| ucs2     | UCS-2 Unicode                   | ucs2_general_ci     |      2 |
| cp866    | DOS Russian                     | cp866_general_ci    |      1 |
| keybcs2  | DOS Kamenicky Czech-Slovak      | keybcs2_general_ci  |      1 |
| macce    | Mac Central European            | macce_general_ci    |      1 |
| macroman | Mac West European               | macroman_general_ci |      1 |
| cp852    | DOS Central European            | cp852_general_ci    |      1 |
| latin7   | ISO 8859-13 Baltic              | latin7_general_ci   |      1 |
| utf8mb4  | UTF-8 Unicode                   | utf8mb4_general_ci  |      4 |
| cp1251   | Windows Cyrillic                | cp1251_general_ci   |      1 |
| utf16    | UTF-16 Unicode                  | utf16_general_ci    |      4 |
| utf16le  | UTF-16LE Unicode                | utf16le_general_ci  |      4 |
| cp1256   | Windows Arabic                  | cp1256_general_ci   |      1 |
| cp1257   | Windows Baltic                  | cp1257_general_ci   |      1 |
| utf32    | UTF-32 Unicode                  | utf32_general_ci    |      4 |
| binary   | Binary pseudo charset           | binary              |      1 |
| geostd8  | GEOSTD8 Georgian                | geostd8_general_ci  |      1 |
| cp932    | SJIS for Windows Japanese       | cp932_japanese_ci   |      2 |
| eucjpms  | UJIS for Windows Japanese       | eucjpms_japanese_ci |      3 |
| gb18030  | China National Standard GB18030 | gb18030_chinese_ci  |      4 |
+----------+---------------------------------+---------------------+--------+
41 rows in set (0.04 sec)

mysql> 

其中:

  • 第一列(Charset)为字符集名称;
  • 第二列(Description)为字符集描述;
  • 第三列(Default collation)为字符集的默认校对规则;
  • 第四列(Maxlen)表示字符集中一个字符占用的最大字节数。


常用的字符集如下:

  • latin1 支持西欧字符、希腊字符等。
  • gbk 支持中文简体字符。
  • big5 支持中文繁体字符。
  • utf8 几乎支持所有国家的字符。


也可以通过查询 information_schema.character_set 表中的记录,来查看 MySQL 支持的字符集。SQL 语句和执行过程如下:

mysql> SELECT * FROM information_schema.character_sets;
+--------------------+----------------------+---------------------------------+--------+
| CHARACTER_SET_NAME | DEFAULT_COLLATE_NAME | DESCRIPTION                     | MAXLEN |
+--------------------+----------------------+---------------------------------+--------+
| big5               | big5_chinese_ci      | Big5 Traditional Chinese        |      2 |
| dec8               | dec8_swedish_ci      | DEC West European               |      1 |
| cp850              | cp850_general_ci     | DOS West European               |      1 |
| hp8                | hp8_english_ci       | HP West European                |      1 |
......


可以使用 SHOW COLLATION LIKE '***';命令来查看相关字符集的校对规则。

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)

上面运行结果为 GBK 字符集所对应的校对规则,其中 gbk_chinese_ci 是默认的校对规则,对大小写不敏感。而 gbk_bin 按照二进制编码的值进行比较,对大小写敏感。

也可以通过查询 information_schema.COLLATIONS 表中的记录,来查看 MySQL 中可用的校对规则。SQL 语句和执行过程如下:

mysql> SELECT * FROM information_schema.COLLATIONS;
+--------------------------+--------------------+-----+------------+-------------+---------+
| COLLATION_NAME           | CHARACTER_SET_NAME | ID  | IS_DEFAULT | IS_COMPILED | SORTLEN |
+--------------------------+--------------------+-----+------------+-------------+---------+
| big5_chinese_ci          | big5               |   1 | Yes        | Yes         |       1 |
| big5_bin                 | big5               |  84 |            | Yes         |       1 |
| dec8_swedish_ci          | dec8               |   3 | Yes        | Yes         |       1 |
| dec8_bin                 | dec8               |  69 |            | Yes         |       1 |
| cp850_general_ci         | cp850              |   4 | Yes        | Yes         |       1 |
| cp850_bin                | cp850              |  80 |            | Yes         |       1 |
......

MySQL设置默认字符集和校对规则

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

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

服务器字符集和校对规则

修改服务器默认字符集和校对规则的方法如下。

1)可以在 my.ini 配置文件中设置服务器字符集和校对规则,添加内容如下:

[mysqld]
character-set-server=字符集名称

2)连接 MySQL 服务器时指定字符集:

mysql --default-character-set=字符集名称 -h 主机IP地址 -u 用户名 -p 密码


如果没有指定服务器字符集,MySQL 会默认使用 latin1 作为服务器字符集。如果只指定了字符集,没有指定校对规则,MySQL 会使用该字符集对应的默认校对规则。如果要使用字符集的非默认校对规则,需要在指定字符集的同时指定校对规则。

可以用 SHOW VARIABLES LIKE 'character_set_server' 和 SHOW VARIABLES LIKE 'collation_server' 命令查询当前服务器的字符集和校对规则。

mysql> SHOW VARIABLES LIKE 'character_set_server';
+----------------------+--------+
| Variable_name        | Value  |
+----------------------+--------+
| character_set_server | gbk    |
+----------------------+--------+
1 row in set, 1 warning (0.01 sec)

mysql> SHOW VARIABLES LIKE 'collation_server';
+------------------+-------------------+
| Variable_name    | Value             |
+------------------+-------------------+
| collation_server | gbk_chinese_ci    |
+------------------+-------------------+
1 row in set, 1 warning (0.01 sec)

数据库字符集和校对规则

数据库的字符集和校对规则在创建数据库时指定,也可以在创建完数据库后通过 ALTER DATABASE 命令进行修改,具体操作可阅读学习《MySQL修改数据库》一节。

需要注意的是,如果数据库里已经存在数据,修改字符集后,已有的数据不会按照新的字符集重新存放,所以不能通过修改数据库的字符集来修改数据的内容。在《MySQL修改字符集步骤详解》一节我们介绍了如何修改已存在数据字符集的方法。

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

  • 如果指定了字符集和校对规则,则使用指定的字符集和校对规则;
  • 如果指定了字符集没有指定校对规则,则使用指定字符集的默认校对规则;
  • 如果指定了校对规则但未指定字符集,则字符集使用与该校对规则关联的字符集;
  • 如果没有指定字符集和校对规则,则使用服务器字符集和校对规则作为数据库的字符集和校对规则。

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

可以使用 SHOW VARIABLES LIKE 'character_set_database' 和 SHOW VARIABLES LIKE 'collation_database' 命令查看当前数据库的字符集和校对规则。

mysql> SHOW VARIABLES LIKE 'character_set_database';
+------------------------+--------+
| Variable_name          | Value  |
+------------------------+--------+
| character_set_database | latin1 |
+------------------------+--------+
1 row in set, 1 warning (0.00 sec)

mysql> SHOW VARIABLES LIKE 'collation_database';
+--------------------+-------------------+
| Variable_name      | Value             |
+--------------------+-------------------+
| collation_database | latin1_swedish_ci |
+--------------------+-------------------+
1 row in set, 1 warning (0.00 sec)

表字符集和校对规则

表的字符集和校对规则在创建表的时候指定,也可以在创建完表后通过 ALTER TABLE 命令进行修改,具体操作可阅读学习《MySQL修改数据表》一节。

同样,如果表中已有记录,修改字符集后,原有的记录不会按照新的字符集重新存放。表的字段仍然使用原来的字符集。

设置表的字符集规则和设置数据库字符集的规则基本类似:

  • 如果指定了字符集和校对规则,使用指定的字符集和校对规则;
  • 如果指定了字符集没有指定校对规则,使用指定字符集的默认校对规则;
  • 如果指定了校对规则但未指定字符集,则字符集使用与该校对规则关联的字符集;
  • 如果没有指定字符集和校对规则,使用数据库字符集和校对规则作为表的字符集和校对规则。

为了避免受到默认值的影响,推荐在创建表的时候指定字符集和校对规则。

可以使用 SHOW CREATE TABLE 命令查看当前表的字符集和校对规则,SQL 语句和运行结果如下:

mysql> SHOW CREATE TABLE tb_students_info G
*************************** 1. row ***************************
       Table: tb_students_info
Create Table: CREATE TABLE `tb_students_info` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `sex` char(1) DEFAULT NULL,
  `height` float DEFAULT NULL,
  `course_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

列字符集和校对规则

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

列字符集和校对规则的定义可以在创建表时指定,或者在修改表时调整。语法格式如下:

ALTER TABLE 表名 MODIFY 列名  数据类型 CHARACTER SET 字符集名;

例 1

修改 tb_students_info 表中 name 列的字符集,并查看。SQL 语句和运行结果如下:

mysql> ALTER TABLE tb_students_info MODIFY name VARCHAR(10) CHARACTER SET gbk;
Query OK, 11 rows affected (0.11 sec)
Records: 11  Duplicates: 0  Warnings: 0

mysql>  SHOW CREATE TABLE tb_students_info G
*************************** 1. row ***************************
       Table: tb_students_info
Create Table: CREATE TABLE `tb_students_info` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) CHARACTER SET gbk DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `sex` char(1) DEFAULT NULL,
  `height` float DEFAULT NULL,
  `course_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

结果显示,name 列字符集修改成功。

如果在创建列的时候没有特别指定字符集和校对规则,默认使用表的字符集和校对规则。

连接字符集和校对规则

上面所讲的 4 种设置方式,确定的都是数据保存的字符集和校对规则。实际应用中,还需要设置客户端和服务器之间交互的字符集和校对规则。

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

设置客户端和服务器连接的字符集和校对规则有以下几种方法:

1)在 my.ini 配置文件中,设置以下语句:

[mysql]
default-character-set=gbk

这样服务器启动后,所有连接默认使用 GBK 字符集进行连接。

2)可以通过以下命令来设置连接的字符集和校对规则,这个命令可以同时修改以上 3 个参数(character_set_client、character_set_connection 和 character_set_results)的值。

SET NAMES gbk;

使用这个方法可以“临时一次性地”修改客户端和服务器连接时的字符集为 gbk。

3)MySQL 还提供了下列 MySQL 命令“临时地”修改 MySQL“当前会话的”字符集和校对规则。

set character_set_client = gbk;
set character_set_connection = gbk;
set character_set_database = gbk;
set character_set_results = gbk;
set character_set_server = gbk;
set collation_connection = gbk_chinese_ci;
set collation_database = gbk_chinese_ci;
set collation_server = gbk_chinese_ci;

MySQL修改字符集步骤详解

在实际应用中,如果一开始没有正确的设置字符集,在运行一段时间以后,才发现当前字符集不能满足要求,需要进行调整,但又不想丢弃这段时间的数据,这个时候就需要修改字符集。

在《MySQL设置默认字符集和校对规则》一节我们讲到,ALTER DATABASE 或 ALTER TABLE 命令对已经存在的数据没有作用,只对新创建的表或记录生效。如果想修改已存在数据的字符集,需要先将数据导出,经过适当的调整后,再重新导入。

例 1

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

1)创建 testset 数据库,设置其字符集为 gb2312,并添加数据。

mysql> CREATE TABLE test.testset(
    -> id INT(11) DEFAULT NULL,
    -> name VARCHAR(25) DEFAULT NULL
    -> )CHARSET=gb2312;
Query OK, 0 rows affected (0.10 sec)

mysql> INSERT INTO test.testset VALUES (1,'C语言');
Query OK, 1 row affected (0.01 sec)

mysql> INSERT INTO test.testset VALUES (2,'Java语言');
Query OK, 1 row affected (0.01 sec)

mysql> INSERT INTO test.testset VALUES (3,'Python语言');
Query OK, 1 row affected (0.01 sec)

2)导出 testset 表结构,命令如下:

mysqldump -uroot -p --default-character-set=gbk -d test testset> D: estset.sql

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

3)打开 testset.sql 文件,修改表结构定义中的字符集为新的字符集,如下图所示。

修改表结构的字符集


4)确保表中的记录不再更新,导出所有记录。

mysqldump -uroot -p --quick --no-create-info --extended-insert --default-character-set=gb2312 test testset> D: estdata.sql

  • --quick:该选项用于存储记录多的表。它强制 mysqldump 从服务器一次一行地查询表中的行,而不是查询所有行,并在输出前将它缓存到内存中。
  • --extended-insert:使用 INSERT 插入多行数据语法。可以使文件更小,导入文件时加速插入。
  • --no-create-info:不导出表的 CREATE TABLE 语句。
  • --default-character-set=gb2312:按照原有的字符集导出所有数据。这样导出的文件中,所有中文都是可见的,不会保存成乱码。


5)打开 testdata.sql,将 SET NAMES gb2312 修改成 SET NAMES gbk,如下图所示。


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

CREATE DATABASE test2 DEFAULT CHARSET gbk;


7)创建表,执行 testset.sql。

mysql -uroot -p test2 < D: estset.sql


8)导入数据,执行 testdata.sql。

mysql -uroot -p test2 < D: estdata.sql


9)查看 testset 表结构是否修改了字符集,以及表内数据是否丢失或乱码,SQL 语句和运行结果如下:

mysql> SHOW CREATE TABLE test2.testset G
*************************** 1. row ***************************
       Table: testset
Create Table: CREATE TABLE `testset` (
  `id` int(11) DEFAULT NULL,
  `name` varchar(25) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=gbk
1 row in set (0.00 sec)

mysql> SELECT * FROM test2.testset;
+------+------------+
| id   | name       |
+------+------------+
|    1 | C语言      |
|    2 | Java语言   |
|    3 | Python语言 |
+------+------------+
3 rows in set (0.00 sec)

注意:选择目标字符集的时候,要注意最好的是原字符集的超集,或者确定比原字符集的字库更大,否则如果目标字符集的字库小于原字符集的字库,那么目标字符集中不支持的字符导入后会变成乱码,丢失一部分数据。

MySQL字符集的选择

由于数据库中存储的数据大部分都是各种文字,所以字符集对数据库的存储、处理性能,以及日后系统的移植、推广都会有影响。对数据库来说,字符集非常重要。不论是在 MySQL 数据库还是其它数据库,都存在字符集的选择问题。

如果在创建数据库时没有正确选择字符集,在后期就可能需要更换字符集,而更换字符集是代价比较高的操作,也存在一定的风险。所以推荐在应用开始阶段,就按照实际需求,正确的选择合适的字符集,避免后期不必要的调整。

在《MySQL查看字符集和校对规则》一节中,我们了解到目前 MySQL 5.7 支持几十种字符集,包括 UCS-2、UTF-16、UTF-16LE、UTF-32、 UTF-8 和 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)如果所有客户端程序都支持相同的字符集,则应该优先选择该字符集作为数据库字符集。这样可以避免因字符集转换带来的性能开销和数据损失。

6)在多种字符集都能够满足应用的前提下,应尽量使用小的字符集。因为更小的字符集意味着能够节省空间、减少网络传输字节数,同时由于存储空间的较小间接的提高了系统的性能。

拓展

有很多字符集都可以保存汉字,比如 UTF-8、GB2312、GBK、Latin1 等等。但是常用的是 GB2312 和 GBK。因为 GB2312 字库比 GBK 字库小,有些偏僻字(例如:洺)不能保存,因此在选择字符集的时候一定要权衡这些偏僻字出现的几率,一般情况下,最好选用 GBK。

原文地址:https://www.cnblogs.com/zy09/p/13183608.html