【MySQL(九)】字符集

編碼:字符 -> 二進制;

解碼:二進制 -> 字符;

爲什麼會出現亂碼?因爲編碼和解碼的規則不同。本質上都是同樣的一串二進制流,按照不同的規則解讀的結果當然是不同的。類比一下我們的時間戳轉時間的場景,時間戳就好比是二進制,時區就好比是不同的字符集,同一個時間戳用不同的時區轉換,得到的結果當然是不同的。所以,我們只要保證編碼和解碼用同一套字符集就不會出現亂碼了。

mysql裏支持的字符集

這裏主要指存儲數據的字符集。可以使用如下命令查看:

show charset

另外我們可以設置不同級別的字符集,服務器、數據庫、表、列四個級別。

存儲不同字符集的數據,消耗的空間是不同的。比如ascii字符集,就是固定一個字節。但是對於utf8字符集,需要1-3個字節,這是變長字符集,所以空間是不固定的。

mysql中utf8其實指的是utf8mb3字符集,是mysql裏的一種原始utf8字符集的變種,原始的utf8字符集需要1-4字節,而utf8mb3只需要1-3字節。原始的utf8字符集在mysql裏叫utf8mb4。

字符集轉換

在客戶端發起一條sql語句,到接受服務端返回,這個過程中的字符集是如何轉換的?

1.客戶端使用mysql client發起sql語句查詢,此時在客戶端機器上,使用操作系統的字符集對命令進行了編碼,轉成了二進制流發送到網絡中;

2.mysql服務端接受二進制流,使用character_set_client指定的字符集解碼二進制流;

3.將字符流使用character_set_connection指定的字符集轉換;

4.將字符集與列數據作比較;

5.將結果使用character_set_results指定的字符集編碼,返回給客戶端;

這裏有個問題,爲什麼需要轉成character_set_connection?
https://stackoverflow.com/questions/16082480/what-is-the-purpose-of-character-set-connection
這裏有一段解釋。意思是在做字符常量比較時,需要使用character_set_connection對常量進行轉換。啥意思?

比如select * from text where name = '哈哈'這條語句。服務端首先會用character_set_client解碼,然後處理語句。裏面的where查詢中的name列,會按照該列對應的字符集轉碼,但是對於常量‘哈哈’怎麼處理?顯然如果不處理,那麼會按照character_set_client處理,但這可能有問題啊,可能會導致參與比較的雙方使用的字符集都不一樣,這還怎麼比?所以就需要再按照character_set_connection來轉換一次常量,所以character_set_connection貌似應該與比較的列名的字符集一致。

這裏有三種字符集需要設置?其實可以只用如下命令:

set names xxx

便可以同時設定如上三個字符集。

 

幾個例子:

默認都是utf8編碼。

create table text (
    name varchar(10)
) engine=Innodb default charset=utf8;

mysql> insert into text values('我');
Query OK, 1 row affected (0.01 sec)

建表並插入一條數據;

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

mysql> select * from text;
+------+
| name |
+------+
| ��     |
+------+
1 row in set (0.00 sec)

這裏設置了character_set_results爲不同的編碼,導致查詢結果亂碼;

 

mysql> set character_set_connection = ascii;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from text where name = '我';
Empty set, 1 warning (0.00 sec)

這是設置了character_set_connection,導致無法查到數據;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章