完全解決MySQL中的亂碼問題

標籤: 公衆號文章mysql


字符集轉換概述

咱們有必要說明一下,字符實際上是面向人類的一個概念,計算機可並不關心字符是什麼,它只關心這個字符對應的字節編碼是什麼。對於一個字節序列,計算機怎麼知道它是使用什麼字符集編碼的呢?計算機不知道,因此其實在計算機中表示一個字符串時,都須要附帶上它對應的字符集是什麼,就像這樣(以C++語言爲例):sql

class String {
    byte* content;
    CHARSET_INFO* charset;
}
複製代碼

比方說咱們如今有一個以utf8字符集編碼的漢字'我',那麼意味着計算機中不只僅要存儲'我'的utf8編碼0xE68891,還須要存儲它是使用什麼字符集編碼的信息,就像這樣:數組

{
    content: 0xE68891;
    charset: utf8;
}
複製代碼

計算機內部包含將一種字符集轉換成另外一種字符集的函數庫,也就是某個字符在某種字符集下的編碼能夠很順利的轉換爲另外一種字符集的編碼,咱們將這個過程稱之爲字符集轉換。比方說咱們能夠將上述採用utf8字符集編碼的字符'我',轉換成gbk字符集編碼的形式,就變成了這樣:bash

{
    content: 0xCED2;
    charset: gbk;
}
複製代碼

小貼士: 咱們上邊所說的'編碼'能夠看成動詞,也能夠看成名詞來理解。看成動詞的話意味着將一個字符映射到一個字節序列的過程,看成名詞的話意味着一個字符對應的字節序列。你們根據上下文理解'編碼'的含義。 服務器

MySQL客戶端和服務器是怎麼通訊的

MySQL客戶端發送給服務器的請求以及服務器發送給客戶端的響應其實都是聽從必定格式的,咱們把它們通訊過程當中事先規定好的數據格式稱之爲MySQL通訊協議,這個協議是公開的,咱們能夠簡單的使用wireshark等截包軟件十分方便的分析這個通訊協議。在瞭解了這個通訊協議以後,咱們甚至能夠動手製做本身的客戶端軟件。市面上的MySQL客戶端軟件多種多樣,咱們並不想各個都分析一下,如今只選取在MySQL安裝目錄的bin目錄下自帶的mysql程序(此處的mysql程序指的是名字叫作mysql的一個可執行文件),如圖所示: 函數

image_1e58vtqro1b2q1gjo13np7c55u1t.png-39.3kB
咱們在計算機的黑框框中執行該可執行文件,就至關於啓動了一個客戶端,就像這樣:
image_1e595lu7to64occj6710in9nf2a.png-32.7kB

小貼士: 咱們這裏的'黑框框'指的是Windows操做系統中的cmd.exe或者UNIX系統中的Shell。 學習

咱們一般是按照下述步驟使用MySQL的:ui

  1. 啓動客戶端並鏈接到服務器
  2. 客戶端發送請求。
  3. 服務器接收到請求
  4. 服務器處理請求
  5. 服務器處理請求完畢生成對該客戶端的響應
  6. 客戶端接收到響應

下邊咱們就詳細分析一下每一個步驟中都影響到了哪些字符集。編碼

啓動客戶端並鏈接到服務器過程

每一個MySQL客戶端都維護者一個客戶端默認字符集,這個默認字符集按照下邊的套路進行取值:spa

  • 自動檢測操做系統使用的字符集

    MySQL客戶端會在啓動時檢測操做系統當前使用的字符集,並按照必定規則映射成爲MySQL支持的一些字符集(一般是操做系統當前使用什麼字符集,就映射爲何字符集,有一些特殊狀況,比方說若是操做系統當前使用的是ascii字符集,會被映射爲latin1字符集)。

    • 當咱們使用UNIX操做系統時

      此時會調用操做系統提供的nl_langinfo(CODESET)函數來獲取操做系統當前正在使用的字符集,而這個函數的結果是依賴LC_ALLLC_CTYPELANG這三個環境變量的。其中LC_ALL的優先級比LC_CTYPE高,LC_CTYPE的優先級比LANG高。也就是說若是設置了LC_ALL,不論有沒有設置LC_CTYPE或者LANG,最終都以LC_ALL爲準;若是沒有設置LC_ALL,那麼就以LC_CTYPE爲準;若是既沒有設置LC_ALL也沒有設置LC_CTYPE,就以LANG爲準。比方說咱們將環境變量LC_ALL設置爲zh_CN.UTF-8,就像這樣:

      export LC_ALL=zh_CN.UTF-8
      複製代碼

      那麼咱們在黑框框裏啓動MySQL客戶端時,MySQL客戶端就會檢測到這個操做系統使用的是utf8字符集,並將客戶端默認字符集設置爲utf8

      固然,若是這三個環境變量都沒有設置,那麼nl_langinfo(CODESET)函數將返回操做系統默認的字符集,比方說在個人macOS 10.15.3操做系統中,該默認字符集爲:

      US-ASCII
      複製代碼

      此時MySQL客戶端的默認字符集將會被設置爲latin1

      另外,咱們這裏還須要強調一下,咱們使用的黑框框展現字符的時候有一個本身特有的字符集,好比在個人mac上使用iTerm2做爲黑框框,咱們能夠打開:Preferences->Profiles->Terminal選項卡,能夠看到iTerm2使用utf8來展現字符:

      image_1e5e4vl9i11t61kgu1q521gen16mg41.png-111.8kB
      咱們通常要把黑框框展現字符時採用的編碼和操做系統當前使用的編碼保持一致,若是不一致的話,咱們敲擊的字符可能都沒法顯示到屏幕上。比方說若是我此時把LC_ALL屬性設置成GBK,那麼咱們再向黑框框上輸入漢字的話,屏幕都不會顯示了,就像這樣(以下圖所示,我敲擊了漢字'我'的效果):
      image_1e5e55j3ti87124aur2f0p48u4e.png-10kB

    • 當咱們使用Windows操做系統時

      此時會調用操做系統提供的GetConsoleCP函數來獲取操做系統當前正在使用的字符集。在Windows裏,會把當前cmd.exe使用的字符集映射到一個數字,稱之爲代碼頁(英文名:code page),咱們能夠經過右鍵點擊cmd.exe標題欄,而後點擊屬性->選項,以下圖所示,當前代碼頁的值是936,表明當前cmd.exe使用gbk字符集:

      image_1e5cg35lc34v24e113t7ggrqi2n.png-91.9kB
      更簡便一點,咱們能夠運行chcp命令直接看到當前code page是什麼:
      image_1e5cg45umnfsj151ru01nuv1d1k3k.png-49.8kB
      這樣咱們在黑框框裏啓動MySQL客戶端時,MySQL客戶端就會檢測到這個操做系統使用的是gbk字符集,並將客戶端默認字符集設置爲gbk。咱們前邊提到的utf8字符集對應的代碼頁爲65001,若是當前代碼頁的值爲65001,以後再啓動MySQL客戶端,那麼客戶端的默認字符集就會變成utf8

  • 若是MySQL不支持自動檢測到的操做系統當前正在使用的字符集,或者在某些狀況下不容許自動檢測的話,MySQL會使用它本身的內建的默認字符集做爲客戶端默認字符集。這個內建的默認字符集在MySQL 5.7以及以前的版本中是latin1,在MySQL 8.0中修改成了utf8mb4

  • 使用了default-character-set啓動參數

    若是咱們在啓動MySQL客戶端是使用了default-character-set啓動參數,那麼客戶端的默認字符集將再也不檢測操做系統當前正在使用的字符集,而是直接使用啓動參數default-character-set所指定的值。比方說咱們使用以下命令來啓動客戶端:

    mysql --default-character-set=utf8
    複製代碼

    那麼不論咱們使用什麼操做系統,操做系統目前使用的字符集是什麼,咱們都將會以utf8做爲MySQL客戶端的默認字符集。

在確認了MySQL客戶端默認字符集以後,客戶端就會向服務器發起登錄請求,傳輸一些諸如用戶名、密碼等信息,在這個請求裏就會包含客戶端使用的默認字符集是什麼的信息,服務器收到後就明白了稍後客戶端即將發送過來的請求是採用什麼字符集編碼的,本身生成的響應應該以什麼字符集編碼了(劇透一下:其實服務器在明白了客戶端使用的默認字符集以後,就會將character_set_clientcharacter_set_connection以及character_set_result這幾個系統變量均設置爲該值)。

客戶端發送請求

登錄成功以後,咱們就可使用鍵盤在黑框框中鍵入咱們想要輸入的MySQL語句,輸入完了以後就能夠點擊回車鍵將該語句看成請求發送到服務器,但是客戶端發送的語句(本質是個字符串)究竟是採用什麼字符集編碼的呢?這其實涉及到應用程序和操做系統之間的交互,咱們的MySQL客戶端程序實際上是一個應用程序,它從黑框框中讀取數據實際上是要調用操做系統提供的讀取接口。在不一樣的操做系統中,調用的讀取接口實際上是不一樣的,咱們還得分狀況討論一下:

  • 對於UNIX操做系統來講

    在咱們使用某個輸入法軟件向黑框框中輸入字符時,該字符采用的編碼字符集實際上是操做系統當前使用的字符集。比方說當前LC_ALL環境變量的值爲zh_CN.UTF-8,那麼意味着黑框框中的字符實際上是使用utf8字符集進行編碼。稍後MySQL客戶端程序將調用操做系統提供的read函數從黑框框中讀取數據(其實就是所謂的從標準輸入流中讀取數據),所讀取的數據其實就是採用utf8字符集進行編碼的字節序列,稍後將該字節序列做爲請求內容發送到服務器。

    這樣其實會產生一個問題,若是客戶端的默認字符集和操做系統當前正在使用的字符集不一樣,那麼將產生比較尷尬的結果。比方說咱們在啓動客戶端是攜帶了--default-character-set=gbk的啓動參數,那麼客戶端的默認字符集將會被設置成gbk,而若是操做系統此時採用的字符集是utf8。比方說咱們的語句中包含漢字'我',那麼客戶端調用read函數讀到的字節序列實際上是0xE68891,從而將0xE68891發送到服務器,而服務器認爲客戶端發送過來的請求都是採用gbk進行編碼的,這樣就會產生問題(固然,這僅僅是發生亂碼問題的前奏,並不意味着產生亂碼,亂碼只有在最後一步,也就是客戶端應用程序將服務器返回的數據寫到黑框框裏時纔會發生)。

  • 對於Windows操做系統來講

    在Windows操做系統中,從黑框框中讀取數據調用的是Windows提供的ReadConsoleW函數。在該函數執行後,MySQL客戶端會獲得一個寬字符數組(其實就是一組16位的UNICODE),而後客戶端須要把該寬字符數組再次轉換成客戶端使用的默認字符集編碼的字節序列,而後纔將該字節序列做爲請求的內容發送到服務器。

    這樣在UNIX操做系統中可能產生的問題,在Windows系統中卻能夠避免。比方說咱們在啓動客戶端是攜帶了--default-character-set=gbk的啓動參數,那麼客戶端的默認字符集將會被設置成gbk,假如此時操做系統採用的字符集是utf8。比方說咱們的語句中包含漢字'我',那麼客戶端調用ReadConsoleW函數先讀到一個表明着字的寬字符數組,以後又將其轉換爲客戶端的默認字符集,也就是gbk字符集編碼的數據0xCED2,而後將0xCED2發送到服務器。此時服務器也認爲客戶端發送過來的請求就是採用gbk進行編碼的,這樣就徹底正確了~

服務器接收請求

服務器接收到到的請求本質上就是一個字節序列,服務器將其看做是採用系統變量character_set_client表明的字符集進行編碼的字節序列。character_set_client是一個SESSION級別的系統變量,也就是說每一個客戶端和服務器創建鏈接後,服務器都會爲該客戶端維護一個單獨的character_set_client變量,每一個客戶端在登陸服務器的時候都會將客戶端的默認字符集通知給服務器,而後服務器設置該客戶端專屬的character_set_client

咱們可使用SET命令單獨修改character_set_client對應的值,就像這樣:

SET character_set_client=gbk;
複製代碼

須要注意的是,character_set_client對應的字符集必定要包含請求中的字符,比方說咱們把character_set_client設置成ascii,而請求中發送了一個漢字'我',將會發生這樣的事情:

mysql> SET character_set_client=ascii;
Query OK, 0 rows affected (0.00 sec)

mysql> SHOW VARIABLES LIKE 'character%';
+--------------------------+------------------------------------------------------+
| Variable_name            | Value                                                |
+--------------------------+------------------------------------------------------+
| character_set_client     | ascii                                                |
| 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/local/Cellar/mysql/5.7.21/share/mysql/charsets/ |
+--------------------------+------------------------------------------------------+
8 rows in set (0.00 sec)

mysql> SELECT '我';
+-----+
| ??? |
+-----+
| ??? |
+-----+
1 row in set, 1 warning (0.00 sec)

mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
  Level: Warning
   Code: 1300
Message: Invalid ascii character string: '\xE6\x88\x91'
1 row in set (0.00 sec)
複製代碼

如圖所示,最後提示了'E六、8八、91'並非正確的ascii字符。

小貼士: 能夠將character_set_client設置爲latin1,看看還會不會報告WARNINGS,以及爲何~

服務器處理請求

服務器在處理請求時會將請求中的字符再次轉換爲一種特定的字符集,該字符集由系統變量character_set_connection表示,該系統變量也是SESSION級別的。每一個客戶端在登陸服務器的時候都會將客戶端的默認字符集通知給服務器,而後服務器設置該客戶端專屬的character_set_connection

不過咱們以後能夠經過SET命令單獨修改這個character_set_connection系統變量。比方說客戶端發送給服務器的請求中包含字節序列0xE68891,而後服務器針對該客戶端的系統變量character_set_clientutf8,那麼此時服務器就知道該字節序列實際上是表明漢字'我',若是此時服務器針對該客戶端的系統變量character_set_connection爲gbk,那麼在計算機內部還須要將該字符轉換爲採用gbk字符集編碼的形式,也就是0xCED2

有同窗可能會想這一步有點兒像脫了褲子放屁的意思,可是你們請考慮下邊這個查詢語句:

mysql> SELECT 'a' = 'A';
複製代碼

請問你們這個查詢語句的返回結果應該是TRUE仍是FALSE?其實結果是不肯定。這是由於咱們並不知道比較兩個字符串的大小到底比的是什麼!咱們應該從兩個方面考慮:

  • 考慮一:這些字符串是採用什麼字符集進行編碼的呢?

  • 考慮二:在咱們肯定了編碼這些字符串的字符集以後,也就意味着每一個字符串都會映射到一個字節序列,那麼咱們怎麼比較這些字節序列呢,是直接比較它們二進制的大小,仍是有別的什麼比較方式?比方說'a''A'在utf8字符集下的編碼分別爲0x610x41,那麼'a' = 'A'是應該直接比較0x610x41的大小呢,仍是將0x61減去32以後再比較大小呢?其實這兩種比較方式均可以,每一種比較方式咱們都稱做一種比較規則(英文名:collation)。

MySQL中支持若干種字符集,咱們可使用SHOW CHARSET命令查看,以下圖所示(太多了,只展現幾種,具體本身運行一下該命令):

mysql> SHOW CHARSET;
+----------+---------------------------------+---------------------+--------+
| Charset  | Description                     | Default collation   | Maxlen |
+----------+---------------------------------+---------------------+--------+
| big5     | Big5 Traditional Chinese        | big5_chinese_ci     |      2 |
| latin1   | cp1252 West European            | latin1_swedish_ci   |      1 |
| latin2   | ISO 8859-2 Central European     | latin2_general_ci   |      1 |
| ascii    | US ASCII                        | ascii_general_ci    |      1 |
| gb2312   | GB2312 Simplified Chinese       | gb2312_chinese_ci   |      2 |
| 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 |
| utf16le  | UTF-16LE Unicode                | utf16le_general_ci  |      4 |
| utf32    | UTF-32 Unicode                  | utf32_general_ci    |      4 |
| binary   | Binary pseudo charset           | binary              |      1 |
| gb18030  | China National Standard GB18030 | gb18030_chinese_ci  |      4 |
+----------+---------------------------------+---------------------+--------+
41 rows in set (0.04 sec)
複製代碼

其中每一種字符集又對應着若干種比較規則,咱們以utf8字符集爲例(太多了,也只展現幾個):

mysql> SHOW COLLATION WHERE Charset='utf8';
+--------------------------+---------+-----+---------+----------+---------+
| Collation                | Charset | Id  | Default | Compiled | Sortlen |
+--------------------------+---------+-----+---------+----------+---------+
| utf8_general_ci          | utf8    |  33 | Yes     | Yes      |       1 |
| utf8_bin                 | utf8    |  83 |         | Yes      |       1 |
| utf8_unicode_ci          | utf8    | 192 |         | Yes      |       8 |
| utf8_icelandic_ci        | utf8    | 193 |         | Yes      |       8 |
| utf8_latvian_ci          | utf8    | 194 |         | Yes      |       8 |
| utf8_romanian_ci         | utf8    | 195 |         | Yes      |       8 |
+--------------------------+---------+-----+---------+----------+---------+
27 rows in set (0.00 sec)
複製代碼

其中utf8_general_ci是utf8字符集默認的比較規則,在這種比較規則下是不區分大小寫的,不過utf8_bin這種比較規則就是區分大小寫的。

在咱們將請求中的字節序列轉換爲character_set_connection對應的字符集編碼的字節序列後,也要配套一個對應的比較規則,這個比較規則就由collation_connection系統變量來指定。咱們如今經過SET命令來修改一下collation_connection的值分別設置爲utf8utf8_general_ci,而後比較一下'a''A'

mysql> SET character_set_connection=utf8;
Query OK, 0 rows affected (0.00 sec)

mysql> SET collation_connection=utf8_general_ci;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT 'a' = 'A';
+-----------+
| 'a' = 'A' |
+-----------+
|         1 |
+-----------+
1 row in set (0.00 sec)
複製代碼

能夠看到在這種狀況下這兩個字符串就是相等的。

咱們如今經過SET命令來修改一下collation_connection的值分別設置爲utf8utf8_bin,而後比較一下'a''A'

mysql> SET character_set_connection=utf8;
Query OK, 0 rows affected (0.00 sec)

mysql> SET collation_connection=utf8_bin;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT 'a' = 'A';
+-----------+
| 'a' = 'A' |
+-----------+
|         0 |
+-----------+
1 row in set (0.00 sec)
複製代碼

能夠看到在這種狀況下這兩個字符串就是不相等的。

固然,若是咱們並不須要單獨指定將請求中的字符串採用何種字符集以及比較規則的話,並不用太關心character_set_connectioncollation_connection設置成啥,不過須要注意一點,就是character_set_connection對應的字符集必須包含請求中的字符。

服務器處理請求完畢生成對該客戶端的響應

爲了故事的順利發展,咱們先建立一個表:

CREATE TABLE t (
    c VARCHAR(100)
) ENGINE=INNODB CHARSET=utf8;
複製代碼

而後向這個表插入一條記錄:

INSERT INTO t VALUE('我');
複製代碼

如今這個表中的數據就以下所示:

mysql> SELECT * FROM t;
+------+
| c    |
+------+
| 我   |
+------+
1 row in set (0.00 sec)
複製代碼

咱們能夠看到該表中的字段實際上是使用utf8字符集編碼的,因此底層存放格式是:0xE68891,將它讀出後須要發送到客戶端,是否是直接將0xE68891發送到客戶端呢?這可不必定,這個取決於character_set_result系統變量的值,該系統變量也是一個SESSION級別的變量。服務器會將該響應轉換爲character_set_result系統變量對應的字符集編碼後的字節序列發送給客戶端。每一個客戶端在登陸服務器的時候都會將客戶端的默認字符集通知給服務器,而後服務器設置該客戶端專屬的character_set_result

咱們也可使用SET命令來設置character_set_result的值。不過也須要注意,character_set_result對應的字符集應該包含響應中的字符。

這裏再強調一遍,character_set_clientcharacter_set_connectioncharacter_set_result這三個系統變量是服務器的系統變量,每一個客戶端在與服務器創建鏈接後,服務器都會爲這個鏈接維護這三個變量,如圖所示(咱們假設鏈接1的這三個變量均爲utf8,鏈接1的這三個變量均爲gbk,鏈接1的這三個變量均爲ascii,):

image_1e54s4mr3c1112tmpkqcbt6hm.png-82.7kB
通常狀況下 character_set_clientcharacter_set_connectioncharacter_set_result這三個系統變量應該和客戶端的默認字符集相同, SET names命令能夠一次性修改這三個系統變量:

SET NAMES 'charset_name'
複製代碼

該語句和下邊三個語句等效:

SET character_set_client = charset_name;
SET character_set_results = charset_name;
SET character_set_connection = charset_name;
複製代碼

不過這裏須要你們特別注意,SET names語句並不會改變客戶端的默認字符集!

客戶端接收到響應

客戶端收到的響應其實仍然是一個字節序列。客戶端是如何將這個字節序列寫到黑框框中的呢,這又涉及到應用程序和操做系統之間的一次交互。

  • 對於UNIX操做系統來講,MySQL客戶端向黑框框中寫入數據使用的是操做系統提供的fputsputc或者fwrite函數,這些函數基本上至關於直接就把接收到的字節序列寫到了黑框框中(請注意咱們用詞:'基本上至關於',其實內部還會作一些工做,可是咱們這裏就不想再關注這些細節了)。此時若是該字節序列實際的字符集和黑框框展現字符所使用的字符集不一致的話,就會發生所謂的亂碼(你們注意,這個時候和操做系統當前使用的字符集沒啥關係)。

    比方說咱們在啓動MySQL客戶端的時候使用了--default-character-set=gbk的啓動參數,那麼服務器的character_set_result變量就是gbk。而後再執行SELECT * FROM t語句,那麼服務器就會將字符'我'的gbk編碼,也就是0xCDE2發送到客戶端,客戶端直接把這個字節序列寫到黑框框中,若是黑框框此時採用utf8字符集展現字符,那天然就會發生亂碼。

  • 對於Windows操做系統來講,MySQL客戶端向黑框框中寫入數據使用的是操做系統提供的WriteConsoleW函數,該函數接收一個寬字符數組,因此MySQL客戶端調用它的時候須要顯式地將它從服務器收到的字節序列按照客戶端默認的字符集轉換成一個寬字符數組。正由於這一步驟的存在,因此能夠避免上邊提到的一個問題。

    比方說咱們在啓動MySQL客戶端的時候使用了--default-character-set=gbk的啓動參數,那麼服務器的character_set_result變量就是gbk。而後再執行SELECT * FROM t語句,那麼服務器就會將字符'我'的gbk編碼,也就是0xCDE2發送到客戶端,客戶端將這個字節序列先從客戶端默認字符集,也就是gbk的編碼轉換成一個寬字符數組,而後再調用WriteConsoleW函數寫到黑框框,黑框框天然能夠把它顯示出來。

亂碼問題應該如何分析

好了,介紹了各個步驟中涉及到的各類字符集,你們估計也看的眼花繚亂了,下邊總結一下咱們遇到亂碼的時候應該如何分析,而不是鬍子眉毛一把抓,隨便百度一篇文章,而後修改某個參數,運氣好修改了以後改對了,運氣很差改了一天也改很差。知其然也要知其因此然,在學習了本篇文章後,你們必定要有節奏的去分析亂碼問題:

  • 我使用的是什麼操做系統

    • 對於UNIX系統用戶來講,要搞清楚我使用的黑框框究竟是使用什麼字符集展現字符,就像是iTerm2中的character encoding屬性:

      image_1e5e8phv1146qoe52401gf6dp95b.png-112.3kB

      一樣還要搞清楚操做系統當前使用什麼字符集,運行locale命令查看:

      王大爺喊你輸入呢,跟這兒>locale
      LANG=""
      LC_COLLATE="zh_CN.UTF-8"
      LC_CTYPE="zh_CN.UTF-8"
      LC_MESSAGES="zh_CN.UTF-8"
      LC_MONETARY="zh_CN.UTF-8"
      LC_NUMERIC="zh_CN.UTF-8"
      LC_TIME="zh_CN.UTF-8"
      LC_ALL="zh_CN.UTF-8"
      王大爺喊你輸入呢,跟這兒>
      複製代碼

      沒有什麼特別極端的特殊需求的話,必定要保證上述兩個字符集是相同的,不然可能連漢字都輸入不進去!

    • 對於Windows用戶來講

      搞清楚本身使用的黑框框的代碼頁是什麼,也就是操做系統當前使用的字符集是什麼。

  • 搞清楚客戶端的默認字符集是什麼

    啓動MySQL客戶端的時候有沒有攜帶--default-character-set參數,若是攜帶了,那麼客戶端默認字符集就以該參數指定的值爲準。不然分析本身操做系統當前使用的字符集是什麼。

  • 搞清楚客戶端發送請求時是以什麼字符集編碼請求的

    • 對於UNIX系統來講,咱們能夠認爲請求就是採用操做系統當前使用的字符集進行編碼的。

    • 對於Windows系統來講,咱們能夠認爲請求就是採用客戶端默認字符集進行編碼的。

  • 經過執行SHOW VARIABLES LIKE 'character%'命令搞清楚:

    • character_set_client:服務器是怎樣認爲客戶端發送過來的請求是採用何種字符集編碼的
    • character_set_connection:服務器在運行過程當中會採用何種字符集編碼請求中的字符
    • character_set_result:服務器會將響應使用何種字符集編碼後再發送給客戶端的
  • 客戶端收到響應以後:

    對於服務器發送過來的字節序列來講:

    • 在UNIX操做系統上,能夠認爲會把該字節序列直接寫到黑框框裏。此時應該搞清楚咱們的黑框框究竟是採用何種字符集展現數據。

    • 在Windows操做系統上,該字節序列會被認爲是由客戶端字符集編碼的數據,而後再轉換成寬字符數組寫入到黑框框中。

請認真分析上述的每個步驟,而後發出驚呼:小樣,不就是個亂碼嘛,還治不了個你!

想輕鬆的瞭解更多MySQL進階內容,請看小冊:《MySQL是怎樣運行的:從根兒上理解MySQL》的連接

題外話

本文首發於公衆號「咱們都是小青蛙」。

寫文章挺累的,有時候你以爲閱讀挺流暢的,那實際上是背後無數次修改的結果。若是你以爲不錯請幫忙轉發一下,萬分感謝~ 這裏是個人公衆號「咱們都是小青蛙」,裏邊有更多技術乾貨,時不時扯一下犢子,歡迎關注:

相關文章
相關標籤/搜索