IT技术博客大学习 共学习 共进步

mysql连接通道中的字符集和校验规则

ilonng 2009-10-12 10:02:15 浏览 2,942 次

     这里首先需要解释的是,我想应该就是连接通道的含义了。那什么是连接通道呢?
    所谓连接通道,就是客户端和服务器端保持连接的一个通道,它是逻辑上的一个概念。客户端通过连接通道发送sql语句到服务器端,服务端执行,将结果再通过连接通道返回至客户端。the connection is the pass when you connect to the server.

     这个过程中,有几个临界点(逻辑上概念),是我们需要注意的,mysql也就在这几个临界点上做了文章。
    1、当语句离开客户端的时候:
    从客户端出来的,包括sql语句本身(这里里面就包含字符串和关键字等了),以及character_set_client系统变量。为什么要包含这个变量呢?这个变量的作用说明2点,也是它的作用:一是表示该语句中的字符集是使用character_set_client指定的字符集编码的,二是通过此系统变量来告诉服务器所发送来的语句中的字符集编码。
    2、当服务器端接受到客户端的语句的时候:
    mysql会使用character_set_connection/collation_connection指定的字符集以及校验规则,将客户端的字符串,做一个从character_set_client到character_set_connection的转换。
    3、当服务器处理好结果以后,在把结果传给客户端前:
    mysql会先将结果转换成character_set_results指定的字符集,然后传回给客户端。

     当字符串在mysql服务器的时候,最终以什么格式存储到mysql数据库中,这个是受到具体的数据表级别、列级别字符集设置的控制了。

     从上面的介绍中,我们就知道和连接通道相关几个参数了,他们分别是character_set_client/connection/results,可以如下查看:
mysql> show variables like \'char%\';
+--------------------------+-------------------+
| Variable_name            | Value             |
+--------------------------+-------------------+
| character_set_client     | latin1            |
| character_set_connection | latin1            |
| character_set_database   | gbk               |
| character_set_results    | latin1            |
| .........................| ......            |
+--------------------------+-------------------+
8 rows in set (0.00 sec)

    mysql> show variables like \'colla%\';
+----------------------+-------------------+
| Variable_name        | Value             |
+----------------------+-------------------+
| collation_connection | latin1_swedish_ci |
| collation_database   | gbk_bin           |
| collation_server     | latin1_swedish_ci |
+----------------------+-------------------+
3 rows in set (0.00 sec)

     下面我们来做个实验,来证明一下这个结果:
    首先保证character_set_connection与character_set_results以及底层存储字符集的一致性,看看character_set_client的效果。
mysql> show variables like \'char%\';
+--------------------------+------------------+
| Variable_name            | Value            |
+--------------------------+------------------+
| character_set_client     | latin1           |
| character_set_connection | gbk              |
| character_set_database   | gbk              |
| character_set_results    | gbk              |
| .........................| ......           |
+--------------------------+------------------+
8 rows in set (0.00 sec)

    mysql> create table t (a varchar(10));  --  这里没有指定字符集,就默认使用了database的字符集gbk了
Query OK, 0 rows affected (0.08 sec)

    mysql> insert into t values(\'中国\');
Query OK, 1 row affected (0.00 sec)

    mysql> select * from t;
+-------+
| a     |
+-------+
| ???ú |
+-------+
1 row in set (0.00 sec)

     由此可以看到由于latin1与gbk对汉字的编码方式不一样(或者说latin1根本就不能正确编码汉字),在这个collection过程中,从character_set_client到character_set_connection转换时,就把你输入的好好的汉字转换成乱码了。那么,如果让过程不发生转换呢?

    mysql> set character_set_client=gbk;
Query OK, 0 rows affected (0.00 sec)

    mysql> insert into t values(\'人民\');
Query OK, 1 row affected (0.02 sec)

    mysql> select * from t;
+-------+
| a     |
+-------+
| ???ú |
| 人民  |
+-------+
2 rows in set (0.00 sec)

     可见,这里是能正确存储和显示的,很简单,因为任何转换都没有发生,当然就不会出现乱码了。

     接着做实验,我们让character_set_connection发生变化:
mysql> set character_set_connection=latin1;
Query OK, 0 rows affected (0.00 sec)

    mysql> insert into t values (\'共和国\');
Query OK, 1 row affected (0.00 sec)

    mysql> select * from t;
+-------+
| a     |
+-------+
| ???ú |
| 人民  |
| ???   |
+-------+
3 rows in set (0.00 sec)

     可见,这里character_set_client=gbk,character_set_connection=latin1,character_set_database=gbk。首先,client到connection过程中,汉字被转换成乱码(这个过程可能就会丢失信息);然后存储数据的时候,又从connection到存储的字符集(gbk)发生一次转换,乱码被转换成“更”乱码。这里如果connection与client的字符集有种包容性关系的话,如character_set_client=gbk, character_set_connection=utf8,character_set_results=gbk,底层存储也是gbk编码,由于utf8“兼容”所有的字符集,故在转换过程中不会发生信息丢失,查询的时候也不会是乱码,如下:

    mysql> show variables like \'char%\';
+--------------------------+-----------------+
| Variable_name            | Value           |
+--------------------------+-----------------+
| character_set_client     | gbk             |
| character_set_connection | utf8            |
| character_set_database   | gbk             |
| character_set_results    | gbk             |
| .........................| ......          |
+--------------------------+-----------------+
8 rows in set (0.00 sec)

    mysql> create table t (a varchar(10)) charset=gbk;
Query OK, 0 rows affected (0.05 sec)

    mysql> insert into t values(\'中国\');
Query OK, 1 row affected (0.00 sec)

    mysql> select * from t;
+------+
| a    |
+------+
| 中国 |
+------+
1 rows in set (0.00 sec)

     我来解释一下这个过程,假设“中国”的gbk编码是1234,而utf8的编码是4321,utf8的编码为1234的假设是“淘宝”。client的“中国”的编码1234进入connection,仍然是1234(但实际上它的含义已经发生变化为“淘宝”),存储的时候connection的1234编码到表存储也是1234,刚好正确表达了“中国”的意思,查询返回时,由于results也是gbk,所以不发生转换,正确显示。很显然,如果results又是一种与gbk不兼容的字符集如latin1,查询又会出问题,如下:

    mysql> set character_set_results=latin1;
Query OK, 0 rows affected (0.00 sec)

    mysql> select * from t;
+------+
| a    |
+------+
| ??   |
+------+
1 rows in set (0.00 sec)

     当我们改变底层存储的字符集的时候,会怎样?请看如下实验:
mysql> show variables like \'char%\';
+--------------------------+---------------+
| Variable_name            | Value         |
+--------------------------+---------------+
| character_set_client     | gbk           |
| character_set_connection | gbk           |
| character_set_database   | gbk           |
| character_set_results    | gbk           |
| .........................| ......        |
+--------------------------+---------------+
8 rows in set (0.01 sec)

    mysql> create table t (a varchar(10)) charset=latin1;
Query OK, 0 rows affected (0.05 sec)

    mysql> insert into t values(\'淘宝\');
Query OK, 1 row affected, 1 warning (0.02 sec)

    mysql> select * from t;
+------+
| a    |
+------+
| ??   |
+------+
1 row in set (0.00 sec)
   
   很显然,由于latin1字符集无法存储汉字,故出现乱码。

     下面,我们只改变results字符集,看看效果如何:
mysql> show variables like \'char%\';
+--------------------------+----------------+
| Variable_name            | Value          |
+--------------------------+----------------+
| character_set_client     | gbk            |
| character_set_connection | gbk            |
| character_set_database   | gbk            |
| character_set_results    | latin1         |
| .........................| ......         |
+--------------------------+----------------+
8 rows in set (0.00 sec)

    mysql> create table t (a varchar(10)) charset=gbk;
Query OK, 0 rows affected (0.08 sec)

    mysql> insert into t values(\'淘宝\');
Query OK, 1 row affected (0.00 sec)

    mysql> select * from t;
+------+
| a    |
+------+
| ??   |
+------+
1 row in set (0.00 sec)

     很显然,底层为gbk编码的字符串转换成latin1的字符集的字符,变成乱码,传给客户端,再次转换成gbk字符集,变成“更”乱码,故显示乱码。

     下面,我再介绍几个“简约”命令。
    SET NAMES \'x\',(SET NAMES \'charset_name\' COLLATE \'collation_name\')相当于:
SET character_set_client = x;
SET character_set_results = x;
SET character_set_connection = x;

    mysql> show variables like \'char%\';
+--------------------------+----------------+
| Variable_name            | Value          |
+--------------------------+----------------+
| character_set_client     | gbk            |
| character_set_connection | gbk            |
| character_set_results    | latin1         |
| .........................| ......         |
+--------------------------+----------------+
8 rows in set (0.00 sec)

    mysql> set names \'ascii\';
Query OK, 0 rows affected (0.00 sec)

    mysql> show variables like \'char%\';
+--------------------------+----------------+
| Variable_name            | Value          |
+--------------------------+----------------+
| character_set_client     | ascii          |
| character_set_connection | ascii          |
| character_set_results    | ascii          |
| .........................| ......         |
+--------------------------+----------------+

     SET CHARACTER SET \'x\',相当于:
SET character_set_client = x;
SET character_set_results = x;
SET collation_connection = @@collation_database;
    这里collation_connection = @@collation_database的意思就是把collation_connection的设置的collation_database的值,由于collation肯定能确定character set,故其又相当于多做了个设置:把character_set_connetion设置成character_set_database的值。请看如下实验:

    mysql> show variables like \'char%\';
+--------------------------+-------------+
| Variable_name            | Value       |
+--------------------------+-------------+
| character_set_client     | latin1      |
| character_set_connection | latin1      |
| character_set_database   | gbk         |
| character_set_results    | latin1      |
| .......................  | ......      |
+--------------------------+-------------+
8 rows in set (0.00 sec)

    mysql> show variables like \'collat%\';
+----------------------+-------------------+
| Variable_name        | Value             |
+----------------------+-------------------+
| collation_connection | latin1_swedish_ci |
| collation_database   | gbk_bin           |
| collation_server     | latin1_swedish_ci |
+----------------------+-------------------+
3 rows in set (0.00 sec)

    mysql> set character set \'ascii\';
Query OK, 0 rows affected (0.00 sec)

    mysql> show variables like \'char%\';
+--------------------------+-------------+
| Variable_name            | Value       |
+--------------------------+-------------+
| character_set_client     | ascii       |
| character_set_connection | gbk         |
| character_set_database   | gbk         |
| character_set_results    | ascii       |
| .......................  | ......      |
+--------------------------+-------------+
8 rows in set (0.00 sec)

    mysql> show variables like \'collat%\';
+----------------------+-------------------+
| Variable_name        | Value             |
+----------------------+-------------------+
| collation_connection | gbk_bin           |
| collation_database   | gbk_bin           |
| collation_server     | latin1_swedish_ci |
+----------------------+-------------------+
3 rows in set (0.00 sec)

     通过以上的介绍,所以为了防止出现乱码,我们可以把character_set_client,character_set_connection,character_set_database,character_set_results设置成同样的值,把collation_connection和collation_database也设置成同样的值,这样就“一劳永逸”了。程序连接mysql的时候,一般都会显示设置character_set_client的值,如java连接中,一般都有如下的一段代码来显示设置这个值:
jdbc:mysql://10.1.6.174:3306/notify?connectTimeout=1000&characterEncoding=utf8

建议继续学习

  1. ORACEL RAC 字符集 (阅读 5,362)
  2. linux下vim的编译以及终端乱码的最终解决方案 (阅读 4,742)
  3. 区分一个包含汉字的字符串是 UTF-8 还是 GBK (阅读 4,041)
  4. oracle查看字符集 修改字符集 (阅读 3,844)
  5. 如何在MYSQL5.5只支出utf8环境下正常使用GBK网站 (阅读 3,704)
  6. java中文乱码解决之道(一)—–认识字符集 (阅读 3,643)
  7. mysql latin1转utf8 的两种方法 (阅读 3,344)
  8. mysql字符集和校验规则概念小介 (阅读 3,242)
  9. mysql字符集与校验规则的设置 (阅读 3,062)
  10. java中文乱码解决之道(二)—–字符编码详解:基础知识 + ASCII + GB** (阅读 2,883)