天天看點

深入了解mysql資料傳輸編碼原理

一、基本概念(這裡引用http://www.laruence.com/2008/01/05/12.html)

1、 給定一系列字元,對每個字元賦予一個數值,用數值來代表對應的字元,這一數值就是字元的編碼(Encoding)。例如,我們給字元’A'賦予數值0,給字元’B'賦予數值1,則0就是字元’A'的編碼;

2、 給定一系列字元并賦予對應的編碼後,所有這些字元和編碼對組成的集合就是字元集(Character Set)。例如,給定字元清單為{‘A’,'B’}時,{‘A’=>0, ‘B’=>1}就是一個字元集;

3、字元序(Collation)是指在同一字元集内字元之間的比較規則;

4、确定字元序後,才能在一個字元集上定義什麼是等價的字元,以及字元之間的大小關系;

5、每個字元序唯一對應一種字元集,但一個字元集可以對應多種字元序,其中有一個是預設字元序(Default Collation);

6、MySQL中的字元序名稱遵從命名慣例:以字元序對應的字元集名稱開頭;以_ci(表示大小寫不敏感)、_cs(表示大小寫敏感)或_bin(表示按編碼值比較)結尾。例如:在字元序“utf8_general_ci”下,字元“a”和“A”是等價的;

二、名詞解釋

1、character_set_client:用戶端資料解析、編碼的字元集。

2、character_set_connection:連接配接層字元集。

3、character_set_server:伺服器内部操作字元集。

4、character_set_results:查詢結果字元集。

5、character_set_database:目前資料庫的字元集。

6、character_set_system:系統源資料(字段名等)字元集。

注:

1、還有以collation_開頭的同上面對應的變量,用來描述字元序。

2、服務端編碼、解析時,是按照前一環節的編碼進行解析的,按照各自的字元集進行編碼的。

3、character_set_server是mysql資料庫記憶體的操作字元集。如果建立資料庫時,沒有指定資料庫的字元集,則使用character_set_server的字元集作為預設字元集;如果建立表時,沒有指定表的字元集,則使用character_set_database的字元集作為預設字元集;如果在建立字段時,沒有指定字段的字元集,則使用表的字元集作為預設字元集。

4、set names gbk;等同于同時設定character_set_client,character_set_connection,character_set_results這三個字元集。

三、資料傳輸過程中字元集編碼、解析

1.用戶端以及編碼

深入了解mysql資料傳輸編碼原理

        我們使用jdbc操作資料的程式、navicate操作工具、作業系統操作資料庫這些都認為是用戶端。用戶端navicate的編碼為utf8,windows預設的編碼為gbk。一般情況下,utf8編碼的中文占三個位元組,gbk占用兩個位元組(一個位元組是8位二進制,也就是兩個十六進制)。

Navicate操作(utf8)

mysql> show variables like '%char%';

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

| Variable_name            | Value                      |

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

| character_set_client     | utf8                       |

| character_set_connection | utf8                       |

| character_set_database   | utf8                       |

| character_set_filesystem | binary                     |

| character_set_results    | utf8                       |

| character_set_server     | utf8                       |

| character_set_system     | utf8                       |

| character_sets_dir       | /usr/share/mysql/charsets/ |

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

8 rows in set

 

mysql> select hex('我很帥');

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

| hex('我很帥')      |

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

| E68891E5BE88E5B885 |

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

1 row in set      
Windows上操作(gbk)

mysql> show variables like '%char%';

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

| Variable_name            | Value                      |

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

| character_set_client     | gbk                        |

| character_set_connection | gbk                        |

| character_set_database   | utf8                       |

| character_set_filesystem | binary                     |

| character_set_results    | gbk                        |

| character_set_server     | utf8                       |

| character_set_system     | utf8                       |

| character_sets_dir       | /usr/share/mysql/charsets/ |

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

8 rows in set

 

mysql> select hex('我很帥');

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

| hex('我很帥')      |

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

| CED2BADCCAA7       |

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

1 row in set      

2.解析過程

a.sql語句通過用戶端編碼發送到mysql伺服器上;

b.character_set_client對接收到的資料進行解碼,這裡解碼按照character_set_client編碼進行解碼,最後按照自身字元集進行編碼。

c.character_set_connection收到來自client的編碼,這裡進行字元集轉換。注意這裡s.decode(character_set_client).encode(character_set_connection)。

d.character_set_server這裡是伺服器内部使用的字元集,如果單獨給字段添加字元集,這裡取的是字段字元集。這裡收到connection的編碼,進行字元集轉換。e.decode(character_set_connection).encode(character_set_server)。

3.查詢過程

a.mysql伺服器轉換為character_set_results發送到用戶端,其實這裡你隻要知道最後從伺服器出來的時候是按照character_set_results編碼的。

b.發送到用戶端之後,按照用戶端編碼進行解碼。是以如果character_set_results和用戶端編碼不一緻,會導緻查詢亂碼。

ps:這裡我建立一個gbk表,裡面插入有資料(自己構造,帶有中文)。

Navicate操作(utf8)

mysql> select @@character_set_results;

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

| @@character_set_results |

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

| utf8                    |

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

1 row in set

 

mysql> select name_man from wsyy_marry where id = 1;

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

| name_man |

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

| 赫立廣   |

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

1 row in set

 

mysql> set @@session.character_set_results = 28;

Query OK, 0 rows affected

 

mysql> select @@character_set_results;

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

| @@character_set_results |

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

| gbk                     |

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

1 row in set

 

mysql> select name_man from wsyy_marry where id = 1;

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

| name_man |

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

| ������   |

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

1 row in set      
Windows操作(gbk)

mysql> select @@character_set_results;

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

| @@character_set_results |

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

| gbk                     |

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

1 row in set

 

mysql> select name_man from wsyy_marry where id = 1;

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

| name_man |

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

| 赫立廣   |

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

1 row in set

 

mysql> set @@session.character_set_results = 33;

Query OK, 0 rows affected

 

mysql> select @@character_set_results;

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

| @@character_set_results |

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

| utf8                    |

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

1 row in set

 

mysql> select name_man from test.wsyy_marry where id = 1;

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

| name_man |

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

| 璧珛骞�   |

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

1 row in set      

四、總結

1、字元集設定33,代表utf8;28代表gbk字元集設定33;

2、字元集出現亂碼的地方最大可能在兩個地方,character_set_client和character_set_results。如果這兩個地方的編碼個用戶端編碼不一緻會亂碼。告訴你,有可能存都存不進去。

3、後面也有可能出現編碼問題,如果中文字元串,latin1解碼不了中文,則會出現亂碼。也就是說進行編碼轉換的時候可能出現不相容的情況,latin1編碼的都能被utf8相容,反之就可能出現”??”這樣的情況。

4、看下來之後老老實實不要亂設定character_set_client這些值。如果能保持所有的都是utf8,那肯定沒問題。

五、疑問

用戶端編碼 client connection server 結果
utf8 gbk gbk gbk/utf8 插入失敗
utf8 gbk utf8 gbk/utf8 插入亂碼
utf8 utf8 gbk gbk/utf8 正常插入
utf8 urf8 utf8 gbk/utf8 正常插入

我做了如下統計,用戶端編碼和character_set_client編碼不一緻有可能出現插入亂碼,也有可能出現資料插都插不進去。我也不知道為啥會不能插入資料庫。下面這兩種情況很大都是可能是亂碼導緻的報錯。

1、Incorrect string value: '\xB6' for column 'NAME_MAN' at row 1。

2、SQLException errorcom.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long for column 'NAME_MAN' at row 1。

如果對這裡面介紹有異議或者能有更全面的了解可以在下面留言,大家共同學習。

六、參考資料

1.http://www.jianshu.com/p/96ee5b2adef3

2.http://blog.csdn.net/kxcfzyk/article/details/37723367

3.http://www.laruence.com/2008/01/05/12.html

轉載于:https://www.cnblogs.com/jave1ove/p/7454966.html