1. 程式人生 > >2021-2-18:請你說說MySQL的字符集與排序規則對開發有哪些影響?

2021-2-18:請你說說MySQL的字符集與排序規則對開發有哪些影響?

任何計算機儲存資料,都需要**字符集**,因為計算機儲存的資料其實都是二進位制編碼,將一個個字元,對映到對應的二進位制編碼的這個對映就是字元編碼(字符集)。這些字元如何排序呢?決定字元排序的規則就是**排序規則**。 # 檢視內建字符集與比較規則 通過`show charset;`命令,可以檢視所有的字符集。 以下僅展示了我們常用的字符集: ``` +----------+---------------------------------+---------------------+--------+ | Charset | Description | Default collation | Maxlen | +----------+---------------------------------+---------------------+--------+ | latin1 | cp1252 West European | latin1_swedish_ci | 1 | | ascii | US ASCII | ascii_general_ci | 1 | | gb2312 | GB2312 Simplified Chinese | gb2312_chinese_ci | 2 | | cp1250 | Windows Central European | cp1250_general_ci | 1 | | gbk | GBK Simplified Chinese | gbk_chinese_ci | 2 | | utf8 | UTF-8 Unicode | utf8_general_ci | 3 | | utf8mb4 | UTF-8 Unicode | utf8mb4_general_ci | 4 | | utf16 | UTF-16 Unicode | utf16_general_ci | 4 | | utf32 | UTF-32 Unicode | utf32_general_ci | 4 | +----------+---------------------------------+---------------------+--------+ ``` - `ascii`:共收錄128個字元,包括空格、標點符號、數字、大小寫字母和一些不可見字元。由於總共才128個字元,所以可以使用1個位元組來進行編碼 - `latin1`:共收錄256個字元,是在ASCII字符集的基礎上又擴充了128個西歐常用字元(包括德法兩國的字母),也可以使用1個位元組來進行編碼。 - `gb2312`: 收錄了漢字以及拉丁字母、希臘字母、日文平假名及片假名字母、俄語西裡爾字母。其中收錄漢字6763個,其他文字元號682個,**相容ASCII字符集**。這是一個變長字符集,如果該字元在`ascii`字符集中,則採用1位元組編碼,否則採用兩位元組。 - `gbk`: GBK是在`gb2312`基礎上擴容後的標準。收錄了所有的中文字元。同樣的,這是一個變長字符集,如果該字元在`ascii`字符集中,則採用1位元組編碼,否則採用兩位元組。 - `utf8`和`utf8mb4`: 收錄地球上能想到的所有字元,而且還在不斷擴充。這種字符集相容ASCII字符集,採用變長編碼方式,編碼一個字元需要使用1~4個位元組。`MySQL`為了節省空間,其中的`utf8`是標準 UTF8 閹割後的,只有1~3位元組編碼的字符集,基本包含了所有常用的字元。如果還要使用 enoji 表情,那麼需要使用`utf8mb4`,這個是完整的 UTF8 字符集。 - `utf16`: 不同於`utf8`,`utf16`用兩個位元組或者四個位元組編碼字元,可以理解為`utf8`的不節省空間的一種形式 - `utf32`: 固定用四個位元組編碼字元,可以理解為`utf8`的不節省空間的一種形式 通過檢視`information_schema.character_sets`表,也可以看到所有的字符集: ``` mysql> select * from information_schema.character_sets where character_set_name = "utf8"; +--------------------+----------------------+---------------+--------+ | CHARACTER_SET_NAME | DEFAULT_COLLATE_NAME | DESCRIPTION | MAXLEN | +--------------------+----------------------+---------------+--------+ | utf8 | utf8_general_ci | UTF-8 Unicode | 3 | +--------------------+----------------------+---------------+--------+ 1 row in set (0.06 sec) ``` 通過`show collation;`命令,可以檢視所有的字符集,我們這裡來檢視`utf8mb4`的排序規則: ``` mysql> show collation like 'utf8mb4%'; +------------------------+---------+-----+---------+----------+---------+ | Collation | Charset | Id | Default | Compiled | Sortlen | +------------------------+---------+-----+---------+----------+---------+ | utf8mb4_general_ci | utf8mb4 | 45 | Yes | Yes | 1 | | utf8mb4_bin | utf8mb4 | 46 | | Yes | 1 | | utf8mb4_unicode_ci | utf8mb4 | 224 | | Yes | 8 | | utf8mb4_icelandic_ci | utf8mb4 | 225 | | Yes | 8 | | utf8mb4_latvian_ci | utf8mb4 | 226 | | Yes | 8 | | utf8mb4_romanian_ci | utf8mb4 | 227 | | Yes | 8 | | utf8mb4_slovenian_ci | utf8mb4 | 228 | | Yes | 8 | | utf8mb4_polish_ci | utf8mb4 | 229 | | Yes | 8 | | utf8mb4_estonian_ci | utf8mb4 | 230 | | Yes | 8 | | utf8mb4_spanish_ci | utf8mb4 | 231 | | Yes | 8 | | utf8mb4_swedish_ci | utf8mb4 | 232 | | Yes | 8 | | utf8mb4_turkish_ci | utf8mb4 | 233 | | Yes | 8 | | utf8mb4_czech_ci | utf8mb4 | 234 | | Yes | 8 | | utf8mb4_danish_ci | utf8mb4 | 235 | | Yes | 8 | | utf8mb4_lithuanian_ci | utf8mb4 | 236 | | Yes | 8 | | utf8mb4_slovak_ci | utf8mb4 | 237 | | Yes | 8 | | utf8mb4_spanish2_ci | utf8mb4 | 238 | | Yes | 8 | | utf8mb4_roman_ci | utf8mb4 | 239 | | Yes | 8 | | utf8mb4_persian_ci | utf8mb4 | 240 | | Yes | 8 | | utf8mb4_esperanto_ci | utf8mb4 | 241 | | Yes | 8 | | utf8mb4_hungarian_ci | utf8mb4 | 242 | | Yes | 8 | | utf8mb4_sinhala_ci | utf8mb4 | 243 | | Yes | 8 | | utf8mb4_german2_ci | utf8mb4 | 244 | | Yes | 8 | | utf8mb4_croatian_ci | utf8mb4 | 245 | | Yes | 8 | | utf8mb4_unicode_520_ci | utf8mb4 | 246 | | Yes | 8 | | utf8mb4_vietnamese_ci | utf8mb4 | 247 | | Yes | 8 | +------------------------+---------+-----+---------+----------+---------+ 26 rows in set (0.13 sec) ``` 同樣的,通過查詢`information_schema.collations`也可以: ``` mysql> select * from information_schema.collations where character_set_name = "utf8mb4"; +------------------------+--------------------+-----+------------+-------------+---------+ | COLLATION_NAME | CHARACTER_SET_NAME | ID | IS_DEFAULT | IS_COMPILED | SORTLEN | +------------------------+--------------------+-----+------------+-------------+---------+ | utf8mb4_general_ci | utf8mb4 | 45 | Yes | Yes | 1 | | utf8mb4_bin | utf8mb4 | 46 | | Yes | 1 | | utf8mb4_unicode_ci | utf8mb4 | 224 | | Yes | 8 | | utf8mb4_icelandic_ci | utf8mb4 | 225 | | Yes | 8 | | utf8mb4_latvian_ci | utf8mb4 | 226 | | Yes | 8 | | utf8mb4_romanian_ci | utf8mb4 | 227 | | Yes | 8 | | utf8mb4_slovenian_ci | utf8mb4 | 228 | | Yes | 8 | | utf8mb4_polish_ci | utf8mb4 | 229 | | Yes | 8 | | utf8mb4_estonian_ci | utf8mb4 | 230 | | Yes | 8 | | utf8mb4_spanish_ci | utf8mb4 | 231 | | Yes | 8 | | utf8mb4_swedish_ci | utf8mb4 | 232 | | Yes | 8 | | utf8mb4_turkish_ci | utf8mb4 | 233 | | Yes | 8 | | utf8mb4_czech_ci | utf8mb4 | 234 | | Yes | 8 | | utf8mb4_danish_ci | utf8mb4 | 235 | | Yes | 8 | | utf8mb4_lithuanian_ci | utf8mb4 | 236 | | Yes | 8 | | utf8mb4_slovak_ci | utf8mb4 | 237 | | Yes | 8 | | utf8mb4_spanish2_ci | utf8mb4 | 238 | | Yes | 8 | | utf8mb4_roman_ci | utf8mb4 | 239 | | Yes | 8 | | utf8mb4_persian_ci | utf8mb4 | 240 | | Yes | 8 | | utf8mb4_esperanto_ci | utf8mb4 | 241 | | Yes | 8 | | utf8mb4_hungarian_ci | utf8mb4 | 242 | | Yes | 8 | | utf8mb4_sinhala_ci | utf8mb4 | 243 | | Yes | 8 | | utf8mb4_german2_ci | utf8mb4 | 244 | | Yes | 8 | | utf8mb4_croatian_ci | utf8mb4 | 245 | | Yes | 8 | | utf8mb4_unicode_520_ci | utf8mb4 | 246 | | Yes | 8 | | utf8mb4_vietnamese_ci | utf8mb4 | 247 | | Yes | 8 | +------------------------+--------------------+-----+------------+-------------+---------+ 26 rows in set (0.11 sec) ``` - 每個字符集都有一個預設的排序規則:IS_DEFAULT 為 Yes。 - 比較規則名稱以與其關聯的字符集的名稱開頭,可以用通過這個開頭查詢所有的字符集,也可以查詢`information_schema.collations`精確指定字符集 - 字符集後面跟著的是語言編碼,因為`utf8mb4`包含了所有字元,不同國家的文字語言排序肯定不一樣。 - 最後末尾的`ci`代表`case insensitive`,大小寫不敏感,所有可能的字尾如下所示: - ai: accent insensitive 不區分重音 - as: accent sensitive 區分重音 - ci: case insensitive 不區分大小寫 - cs: case sensitive 區分大小寫 - bin: binary 以二進位制方式比較 # 應用字符集與比較規則 字符集與比較規則配置有四個級別: - MySQL例項級別 - 庫級別 - 表級別 - 欄位級別 **指定的級別粒度越小,則以粒度越小的字符集還有比較規則優先**。例如指定MySQL例項級別字符集是`utf8mb4`,指定某個表字符集是`latin1`,那麼這個表的所有欄位如果不指定的話,編碼就是`latin1` 由於字符集和比較規則是互相有聯絡的,如果我們只修改了字符集,比較規則也會跟著變化,如果只修改了比較規則,字符集也會跟著變化,具體規則如下: - 只修改字符集,則比較規則將變為修改後的字符集預設的比較規則。 - 只修改比較規則,則字符集將變為修改後的比較規則對應的字符集。 ## 例項級別 通過兩個系統變數來指定例項級別的字符集與排序規則。 配置檔案: ``` [server] character_set_server=utf8mb4 collation_server=utf8mb4_general_ci ``` 啟動之後,可以檢視並修改這兩個變數。 ``` mysql> show variables like 'character_set_server'; +----------------------+---------+ | Variable_name | Value | +----------------------+---------+ | character_set_server | utf8mb4 | +----------------------+---------+ 1 row in set (0.06 sec) mysql> show variables like 'collation_server'; +------------------+--------------------+ | Variable_name | Value | +------------------+--------------------+ | collation_server | utf8mb4_general_ci | +------------------+--------------------+ 1 row in set (0.05 sec) mysql> set character_set_server = 'utf8mb4'; Query OK, 0 rows affected (0.00 sec) mysql> set collation_server = 'utf8mb4_general_ci'; Query OK, 0 rows affected (0.00 sec) ``` ## 庫級別 建立資料庫的時候,可以指定字符集還有排序規則。 ``` mysql> create database test_db character set utf8mb4 collate utf8mb4_general_ci; Query OK, 1 row affected (0.01 sec) ``` 不指定的話,就用例項級別的字符集還有排序規則。 檢視當前資料庫的字符集還有排序規則則是通過`use`命令指定資料庫之後,檢視`character_set_database`變數以及`collation_database `來實現: ``` mysql> show variables like 'character_set_database'; +------------------------+---------+ | Variable_name | Value | +------------------------+---------+ | character_set_database | utf8mb4 | +------------------------+---------+ 1 row in set (0.07 sec) mysql> show variables like 'collation_database'; +--------------------+--------------------+ | Variable_name | Value | +--------------------+--------------------+ | collation_database | utf8mb4_general_ci | +--------------------+--------------------+ 1 row in set (0.09 sec) ``` 就算設定這兩個變數,也是無效的: ``` mysql> set character_set_database = 'utf8'; Query OK, 0 rows affected (0.00 sec) mysql> show variables like 'character_set_database'; +------------------------+---------+ | Variable_name | Value | +------------------------+---------+ | character_set_database | utf8mb4 | +------------------------+---------+ 1 row in set (0.09 sec) ``` 修改資料庫的字符集還有排序規則的方式: ``` mysql> alter database test_db character set = 'utf8'; Query OK, 1 row affected (0.01 sec) mysql> show variables like 'character_set_database'; +------------------------+-------+ | Variable_name | Value | +------------------------+-------+ | character_set_database | utf8 | +------------------------+-------+ 1 row in set (0.08 sec) ``` 這個更新只會對新建的表如果沒指定字符集和排序規則的生效,並不會更新老表的字符集還有排序規則。 ## 表級別 可以在建立時指定字元集合排序規則,不指定的話,用資料庫的字符集還有排序規則,也可以修改字符集和排序規則。 ``` mysql> create table test (name varchar(32)) character set utf8mb4 collate utf8mb4_bin; Query OK, 0 rows affected (0.04 sec) mysql> show create table test; +-------+---------------------------------------------------------------------------------------------------------------------------------------+ | Table | Create Table | +-------+---------------------------------------------------------------------------------------------------------------------------------------+ | test | CREATE TABLE `test` ( `name` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin | +-------+---------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.09 sec) mysql> alter table test character set = 'utf8'; Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> show create table test; +-------+--------------------------------------------------------------------------------------------------------------------------------------+ | Table | Create Table | +-------+--------------------------------------------------------------------------------------------------------------------------------------+ | test | CREATE TABLE `test` ( `name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 | +-------+--------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.06 sec) ``` 可以看出,僅僅是表的字符集還有排序規則變了,對於已有欄位,並沒有改變編碼和排序規則。 ## 列級別 可以在建立表的時候,指定不同的列有不同的字符集和排序規則,也可以修改列的字符集和排序規則: ``` mysql> create table test (name varchar(32) character set utf8 collate utf8_bin) character set utf8mb4 collate utf8mb4_bin; Query OK, 0 rows affected (0.03 sec) mysql> show create table test; +-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+ | Table | Create Table | +-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+ | test | CREATE TABLE `test` ( `name` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin | +-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.09 sec) mysql> alter table test modify column name varchar(32) COLLATE latin1_bin; Query OK, 0 rows affected (0.09 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> show create table test; +-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ | Table | Create Table | +-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ | test | CREATE TABLE `test` ( `name` varchar(32) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin | +-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.09 sec) ``` # MySQL客戶端字元編碼問題 有時候,我們會遇到字元編碼不一致導致的程式問題。例如我們的 Java 程式,使用 jdbc 連結。讀取的資料,打印出來是亂碼。或者是,MySQL 無法識別我們客戶端發來的命令。這涉及到字元編碼問題。**我們需要保持 Java 程式的字元編碼與 JDBC 連結指定的字元編碼一致,這樣才不會有亂碼的問題。** **指定 Java 程式編碼**:通過啟動引數:`-Dfile.encoding=UTF-8` 設定預設的字元編碼(`java.nio.charset.Charset.defaultCharset();`)是`utf-8`(對應 MySQL 的`utf8`還有`utf8mb4`)。 **指定 JDBC 連結編碼:** ``` jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8 ``` mysql客戶端命令列指定字符集 ``` mysql -h 127.0.0.1 -P 3306 -u root --default-character-set=utf8mb4 -p ``` 之後檢視有關編碼的環境變數,都是和設定的這個字符集一樣。 ``` mysql> SHOW VARIABLES LIKE 'character_set_client'; +----------------------+---------+ | Variable_name | Value | +----------------------+---------+ | character_set_client | utf8mb4 | +----------------------+---------+ 1 row in set, 1 warning (0.00 sec) mysql> SHOW VARIABLES LIKE 'character_set_connection'; +--------------------------+---------+ | Variable_name | Value | +--------------------------+---------+ | character_set_connection | utf8mb4 | +--------------------------+---------+ 1 row in set, 1 warning (0.00 sec) mysql> SHOW VARIABLES LIKE 'character_set_results'; +-----------------------+---------+ | Variable_name | Value | +-----------------------+---------+ | character_set_results | utf8mb4 | +-----------------------+---------+ 1 row in set, 1 warning (0.00 sec) ``` 其中: - `character_set_client`: 伺服器解碼請求時使用的字符集 - `character_set_connection`:伺服器處理請求時將字符集轉換成這個字符集處理。操作具體列時,在轉換為具體列的編碼。 - `character_set_results`:伺服器向客戶端返回資料時使用的字符集 MySQL 設計這三個編碼的時候,出於以下考慮: - 一個 MySQL,可能有多種不同語言和作業系統或者國家的客戶端,所以通過設定`character_set_client`還有`character_set_results`進行相容。 - 由於操作具體列資料的時候需要編碼轉換,如果`character_set_connection`和欄位一致的話,就不用轉換了,所以設定`character_set_connection`可以讓 MySQL 用一種編碼理解命令統一處理,同時設定`character_set_connection`為最常用的可以減少轉換。 一般情況下,保持這三個一致就好。我們就設定好連線使用的字符集就行了。 > **微信搜尋“我的程式設計喵”關注公眾號,每日一刷,輕鬆提升技術,斬獲各種offer**: >![image](https://zhxhash-blog.oss-cn-beijing.aliyuncs.com/qr-c