咱們本身鼓搗mysql時,總免不了會遇到這個問題:插入中文字符出現亂碼,雖然這是運維先給配好的環境,可是在本身機子上玩的時候咧,總得知道個一二吧,否則之後如何優雅的吹牛B。html
若是你也遇到了這個問題,咱先不談緣由,在PC自帶的cmd中(或者是mysql安裝版安裝後的Command Line客戶端,又或者是工做用的SecureCRT)試試效果。進入mysql環境,從頭開始操做。假設你的客戶端編碼是gbk或者utf8(這麼說太不嚴謹了,怎麼能假設呢,可是通常來講假如安裝後沒動過,cmd是gbk編碼,mysql安裝後的Command Line客戶端沒裝不記得,CRT看看Session Options裏面的編碼設置,通常也會設置成utf8),執行一些語句:java
1. 設置編碼客戶端、鏈接、返回結果的字符集,先設置成latin1,mysql
2. 而後執行下面的看下各個字符是否是這樣的,sql
若是你的character_set_client、character_set_connection、character_set_results不是latin1,能夠這樣執行,把他們單個分別設置成latin1,好比設character_set_client,其餘兩個同樣,確保這三個均是latin1(第一步的sql語句實際作的就是這件事),數據庫
3. 單首創建一個數據庫db_latin1,固然是很簡單的了,測試嘛,建立時就設置數據庫的編碼的爲latin1,服務器
4. 在它下面建立一張表tab_latin1,字符集也設置成latin1,這裏不設置字符也行,數據庫級已經設置了,這裏只建立一個name字段,運維
5. 插入一些中文字符到表中,先說明,本機的cmd編碼是gbk,查看方法是右鍵屬性->選項,看下當前代碼頁便可知道,編輯器
6. 查看下結果工具
看吧,正常顯示中文了~~~學習
OK,都到這兒了你就不想知道「爲何我那樣設置就是不行」麼,固然得往下看看是不。上圖:
咱們知道mysql是客戶端-服務器軟件,每次操做都是客戶端向服務端發送請求,而後可能會返回一些結果,這之間插入的字符通過了一系列轉換。首先供咱們編輯的客戶端自己就有一種編碼,好比PC端的命令行默認是gbk,PC自帶notepad新建文本文件默認是ANSI,經常使用的文本編輯器如notepad++,咱們可能會設置默認編碼爲utf8,就是說在編輯器上編輯,你所看到的自己就是一種編碼了。
1. 在客戶端編輯後,首先轉化爲client對應的字符集,即上面打印出的character_set_client變量指示的字符集;
2. 向數據庫服務發送請求,發送過程當中,轉化爲connection對應鏈接字符集,即character_set_connection變量對應字符集;
3. 存儲到數據庫中,轉化爲數據庫存儲的字符集,多是server級別(character_set_server)、database級別(character_set_database)或者表級別和列級別(這裏還要細說下);
4. 數據庫收到請求,執行查詢獲得結果,再次轉化爲results對應字符集,即character_set_results變量所指,該結果返回到客戶端上;
5. 結果來了,是按照results字符集編碼的,那咱們讓這個結果顯示的客戶端工具它支持什麼樣的編碼也很重要,這決定了它如何去解碼結果。假如這個結果是utf8編碼,返回給某客戶端了,但這個客戶端只有ANSI編碼,那固然不能顯示正常,好比它返回到SecureCRT,結果顯示不正常,可是CRT支持多種編碼,咱們手動將它調成utf8編碼,那它就又顯示正常了,因此嚴格來講這一步算不上,只是跟客戶端條件有關,畢竟當咱們知道後將客戶端調整成正常的編碼或者原本就支持轉換results的編碼後,這一步就不存在了。
在上面的第3步中,從鏈接字符集編碼轉化爲數據庫存儲使用的編碼時,要分幾種狀況,通常咱們在裝mysql時,特別是32位安裝版本時,中間有一個選擇編碼的步驟,大多會選擇utf8編碼,這時系統就可能會把一系列的字符集變量均設置成了utf8,好比character_set_server、character_set_connection、character_set_database等等。也就是說這個character_set_server變量在你啓動mysql服務的事先就被設置好了,咱們能夠稱它爲服務器級編碼,那咱們在建表前,先得建立數據庫,在建立數據庫時,咱們知道能夠顯式指定編碼的,好比最開頭時我建立時顯式指定採用latin1字符集,也能夠不指定,若是不指定的話,它將採用服務器級的字符集,即character_set_server,同理在建立表時,也可不指定編碼,不指定的話,採用數據庫級編碼,級character_set_database,更加同理在建立表中列字段時也可指定編碼,不指定編碼的話將採用表級別字符集,所以有這麼一個繼承關係在這:
character_set_server => character_set_database => character set in table(無此變量) => character set column(無此變量)
mysql建立表能夠細化到這四個層次,不是每一層都必須指定,默認使用上一級的字符集(字符校對規則也是這樣的,collation,稍後說明)。
那麼有沒有可能character_set_server沒有指定呢,若是任何地方都沒指定,特別是非安裝版中,若是忘了,mysql在編譯時默認採用latin1,爲了應對這種狀況,特別是非安裝版本中在配置mysql時,常常須要手動配置mysql配置文件mysql.ini,其中就有大概這麼一項:
在配置文件中默認採用的字符集,所以若是指定了character_set_server默認就會採用它,這樣其餘層次都不指定的話依次繼承。
其餘的,character_set_filesystem:把操做系統上的字符轉換成此字符集,即把character_set_client轉換成character_set_filesystem,默認爲binary則不轉換,character_set_system:此變量老是utf8,爲存儲系統元字符的字符集,如表名、列名、用戶名等,character_set_dir:很明顯是指示一個目錄的變量,打開這個目錄,裏邊存放的是mysql的各類用於編碼字符集的xml格式文件。以上三個值在解決亂碼問題時基本可忽視。
好,轉換流程和各變量的含義清楚了,就要搞清楚哪些字符集編碼之間能夠轉換,能轉換可能也是在必定編碼範圍內的字符能轉換,不至於出現亂碼甚至損壞。損壞了就再也沒法正確顯示了,哪怕設置是正確的,還原是還原不回來的。固然關於字符之間的轉化狀況不少,字符集有那麼多種,隨便兩個之間均可以轉換一下試試,不能一一列舉,能夠參考這篇文章:http://www.imcjd.com/?p=1324,它針對常常用到的字符轉換做了一些轉換比較和測試。
其中,能夠了解到,徹底匹配的轉換是確定沒有問題的,好比,gbk->gbk,utf8->utf8,latin1->latin1;轉換爲單字節編碼的latin1也沒問題,好比gbk->latin一、utf8->latin1;單字節編碼(latin1)轉爲其餘在某些編碼某些範圍內可能會出現轉換不全,好比latin1->gbk(很特殊的中文),或者編碼長度改變,好比latin1->utf8,變爲二、3等字節數。
下面引用另外一篇文章(http://hi.baidu.com/cuttinger/item/f4e79726a60ab450c28d59da)中的一段。
【Latin1是一種很常見的字符集,這種字符集是單字節編碼,向下兼容ASCII,其編碼範圍是0x00-0xFF,0x00-0x7F之間徹底和ASCII一致,0x80-0x9F之間是控制字符,0xA0-0xFF之間是文字符號。很明顯,Latin1覆蓋了全部的單字節,所以,能夠將任意字符串保存在latin1字符集中,而不用擔憂有內容不符合latin1的編碼規範而被拋棄。——gbk和utf8是多字節編碼,沒有這種特性。
mysql使用者常常利用Latin1的這種全覆蓋特性,將其它類型的字符串,gbk,utf8,big5等,保存在latin1列中。保存的過程當中沒有數據丟失,只要原樣取出來,便又是合法的gbk/utf8/big字符串。若是將gbk字符串保存在utf8列中,則gbk字符串中那些不符合utf8編碼格式的內容,會被拋棄,保存的內容沒法原樣取出,數據實際上遭到了破壞。
綜上,若是咱們看到一個字段的字符集是latin1的,那麼,他保存的多是任何編碼的字符串;而一個字段的字符集是utf8或者gbk的,那麼他保存的就應該是utf8或gbk的——除非數據庫的使用者用錯了。】
我沒有深刻學習過utf八、gbk編碼的細節,很可能說的不許確,只知道簡單的ASCII編碼(-_-),可是能夠了解個全局狀況。從上面來看,latin1的單字節編碼方式頗有用,其餘的編碼能夠轉換爲它再轉回去而不至於丟失內容。所謂單字節編碼就是挨着一個個來,我理解是,好比聖誕節到了,你要送妹子一箱蘋果,爲製造浪漫,商鋪提供兩種包裝方式,一是按個數來,即單個蘋果包裝進一個盒子,來一個包裝一個,這樣,妹子在拆完全部的盒子後完完整整的能夠還原爲一個個完整的和一箱無缺無損的蘋果,二是按重量來,每一個盒子限重2兩、3兩、6兩,這樣在包裝時,若恰好重3兩的固然能夠完整的放進一個盒子,可是若不夠或者多了,勉不了要切開蘋果,或者再往盒子中添加其餘的部分蘋果,這樣的話,妹子再不管怎樣拆開盒子,都會獲得一箱殘缺不堪的蘋果了,由於你在按照這種包裝方式進行時,已經破壞了單個蘋果的完整性,如今還原不回來了~咱們的字符集編碼轉換就是在作這種從新包裝的工做,latin1剛好就像單個蘋果包裝,而utf8就像第二種方式。
而剛纔說的徹底匹配的狀況是,你去買一箱蘋果,箱子裏邊的全部蘋果重量已經剛好要麼是2兩,要麼是3兩或6兩的,這樣再按重量包裝時固然就剛好分配了,獲得的仍然是完整的蘋果。
因此說白了,幾種可行的方式是:
1. 全部變量均設置成latin1(set names latin1;),這樣,即使咱們所使用的編輯客戶端編碼多樣(gbk或utf8),最終能夠獲得正確結果;
2. 全部的設置成gbk或者gb2312(國標編碼,只用於簡體中文),採用徹底匹配;
最後,關於字符集校對規則,只瞭解一點。在咱們設置mysql字符集時,mysql會自動給一個對應的校對規則,好比設置charset爲utf8,默認的collation就是utf8_general_ci,gb2312字符集對應gb2312_chinese_ci,mysql命令查看全部校對規則是show collation,查看某一對應字符集的校對規就是show collation like 'utf8%'了。
字符集校對是一種對使用當前字符集時採用的排序、對比方式,即使同一種字符集,在不一樣的地區也是不一樣的對比方式,因此纔有校對這麼一說,好比utf8_general_ci,這個ci就是case insensitive,即大小寫不敏感,採用它校對時,查詢某字段值匹配時,大小寫的記錄都會出現,固然還有其餘的規則,utf8打印出來一大坨,不細研究了~
參考文章:
http://www.blogjava.net/wldandan/archive/2007/09/04/142669.html
http://hi.baidu.com/cuttinger/item/f4e79726a60ab450c28d59da
http://www.imcjd.com/?p=1324
http://www.360doc.com/content/11/0303/01/2588264_97631236.shtml
http://blog.csdn.net/my_mao/article/details/30498313
----------------------------------------------------------------------------------------------------
偶然看到鳥哥整理的一篇http://www.laruence.com/2008/01/05/12.html,基本概念解釋得言簡意賅,畢竟大神,參考性強,能驗證的部分仍是手動驗證爲好。