TShopping

 找回密碼
 註冊
搜索
查看: 1126|回復: 3

[教學] A MySQL 4.1 Story

[複製鏈接]
發表於 2008-12-27 01:05:54 | 顯示全部樓層 |閱讀模式
 
Push to Facebook Push to Plurk Push to Twitter 
下面要寫的是一篇非常無聊的東西,充斥了大量各式各樣的編碼、轉換、客戶端、服務器端、連接……呃,我自己都不願意去看它,但想一想,寫下來還是有點意義的,原因有四:
  • MySQL 4.1 對多語言的支持有了很大變化 (這導致了問題的出現);
  • 儘管大部分的地方 (包括個人使用和主機提供商),MySQL 3 仍然占主導地位;但 MySQL 4.1 是 MySQL 官方推薦的數據庫,已經有主機提供商開始提供並將會越來越多;
  • 許多 PHP 程序以 MySQL 作為默認的數據庫管理軟件,但它們一般不區分 MySQL 4.1 與 4.1 以下版本的區別,籠統地稱「MySQL 3.xx.xx 以上版本」就滿足安裝需求了;
  • 因為 utf8 在許多地方 (下邊會詳細描述具體是哪些地方) 作為默認的字符集,成功的蒙蔽了許多 PHP 程序的開發者和用戶,掩蓋了在中文等語言環境下會出現的問題;
簡單的說,MySQL 自身的變化和使用 MySQL 的 PHP 程序對此忽略,導致了問題的出現和複雜化,而由於大部分用戶使用的是英文,使這種問題不被重視。這裡提到的 PHP 程序,主要就 WordPress 而言。
MySQL 4.1 字符集支持的原理MySQL 4.1 對於字符集的指定可以細化到一台機器上安裝的 MySQL,其中的一個數據庫,其中的一張表,其中的一欄,應該用什麼字符集。但是,傳統的 Web 程序在創建數據庫和數據表時並沒有使用那麼複雜的配置,它們用的是默認的配置,那麼,默認的配置從何而來呢?
  • 編譯 MySQL 時,指定了一個默認的字符集,這個字符集是 utf8;
  • 安裝 MySQL 時,可以在配置文件 (my.ini) 中指定一個默認的的字符集,如果沒指定,這個值繼承自編譯時指定的;
  • 啟動 mysqld 時,可以在命令行參數中指定一個默認的的字符集,如果沒指定,這個值繼承自配置文件中的;
  • 此時
    1. character_set_server
    複製代碼
    被設定為這個默認的字符集;
  • 當創建一個新的數據庫時,除非明確指定,這個數據庫的字符集被缺省設定為
    1. character_set_server
    複製代碼
  • 當選定了一個數據庫時,
    1. character_set_database
    複製代碼
    被設定為這個數據庫默認的字符集;
  • 在這個數據庫裡創建一張表時,表默認的字符集被設定為
    1. character_set_database
    複製代碼
    ,也就是這個數據庫默認的字符集;
  • 當在表內設置一欄時,除非明確指定,否則此欄缺省的字符集就是表默認的字符集;
  • 這個字符集就是數據庫中實際存儲數據採用的字符集,mysqldump 出來的內容就是這個字符集下的;

簡單的總結一下,如果什麼地方都不修改,那麼所有的數據庫的所有表的所有欄位的都用 utf8 存儲,不過我們如果安裝 MySQL,一般都會選擇多語言支持,也就是說,安裝程序會自動在配置文件中把
  1. default_character_set
複製代碼
設置為 UTF-8,這保證了缺省情況下,所有的數據庫的所有表的所有欄位的都用 UTF-8 存儲。
當一個 PHP 程序與 MySQL 建立連接後,這個程序發送給 MySQL 的數據採用的是什麼字符集?MySQL 無從得知 (它最多只能猜測),所以 MySQL 4.1 要求客戶端必須指定這個字符集,也就是
  1. character_set_client
複製代碼
,MySQL 的怪異之處在於,得到的這個字符集並不立即轉換為存儲在數據庫中的那個字符集,而是先轉換為
  1. character_set_connection
複製代碼
變量指定的一個字符集;這個 connection 層究竟有什麼用我不大明白,但轉換為
  1. character_set_connection
複製代碼
的這個字符集之後,還要轉換為數據庫默認的字符集,也就是說要經過兩次轉換;當這個數據被輸出時,又要由數據庫默認的字符集轉換為
  1. character_set_results
複製代碼
指定的字符集。
一個典型的環境典型的環境以我自己的電腦上安裝的 MySQL 4.1 為例,我自己的電腦上安裝著 Apache 2,PHP 5 和 WordPress 1.5.1.3,MySQL 配置文件中指定了
  1. default_character_set
複製代碼
  1. utf8
複製代碼
。於是問題出現了:
  • WordPress 按照默認情況安裝,所以所有的表都用 UTF-8 存儲數據;
  • WordPress 默認採用的瀏覽字符集是 UTF-8 (Options->Reading 中設置),因此所有 WP 頁面的 meta 中會說明 charset 是 utf-8;
  • 所以瀏覽器會以 utf-8 方式顯示所有的 WP 頁面;這樣一來 Write 的所有 Post,和 Comment 都會以 UTF-8 格式從瀏覽器發送給 Apache,再由 Apache 交給 PHP;
  • 所以 WP 從所有的表單中得到的數據都是 utf-8 編碼的;WP 不加轉換的直接把這些數據發送給 MySQL;
  • MySQL 默認設置的
    1. character_set_client
    複製代碼
    1. character_set_connection
    複製代碼
    都是 utf8,此時怪異的事情發生了,實際上是 utf-8 格式的數據,被「當作 utf8」轉換成……居然還是轉換成 utf8,然後再由這個 utf8 轉換成 utf-8,這麼兩次轉換,有一部分 utf-8 的字符就丟失了,變成 ??,最後輸出的時候
    1. character_set_results
    複製代碼
    默認是 utf8,也就輸出為奇怪的東西了。
最神奇的還不是這個,如果 WordPress 中設置以 GB2312 格式閱讀,那麼 WP 發送給 MySQL 的 GB2312 編碼的數據,被「當作 utf8」轉換後,存進數據庫的是一種奇怪的格式 (真的是奇怪的格式,mysqldump 出來就能發現,無論當作 utf-8 還是當作 gb2312 來讀都是亂碼),但如果這種格式以 utf8 輸出出來,居然又能變回 GB2312!
這會導致什麼現象呢?WP 如果使用 MySQL 4.1 數據庫,把編碼改用 GB2312 就正常了,可惜,這種正常只是貌似正常。
如何解決問題如果你已經不耐煩了 (幾乎是肯定的),google 一下,會發現絕大部分的解答是,query 之前先執行一下:
  1. SET NAMES 'utf8'
複製代碼
,沒錯,這是解決方案,但本文的目的是說明,這為什麼是解決方案。
要保證結果正確,必須保證數據表採用的格式是正確的,也就是說,至少能夠存放所有的漢字,那麼我們只有兩種選擇,gbk 或者 utf-8,下面討論 utf-8 的情況。
因為配置文件設置的
  1. default_character_set
複製代碼
是 utf8,數據表默認採用的就是 utf-8 建立的。這也應該是所有採用 MySQL 4.1 的主機提供商應該採用的配置。所以我們要保證的只是客戶端與 MySQL 交互之間指定編碼的正確。
這只有兩種可能,客戶端以 gb2312 格式發送數據,或者以 utf-8 格式發送數據。
如果以 gb2312 格式發送:
  1. SET character_set_client='gb2312' SET character_set_connection='utf8' 或者 SET character_set_connection='gb2312'
複製代碼
都是可以的,都能夠保證數據在編碼轉換中不出現丟失,也就是保證存儲入數據庫的是正確的內容。
怎麼保證取出的是正確的內容呢?考慮到絕大部分客戶端 (包括 WP),發送數據的編碼也就是它所希望收到數據的編碼,所以:
  1. SET character_set_results='gb2312'
複製代碼
可以保證取出給瀏覽器顯示的格式就是 gb2312。
如果是第二種情況,客戶端以 utf-8 格式發送 (WP 的默認情況),可以採用下述配置:
  1. SET character_set_client='utf8' SET character_set_connection='utf8' SET character_set_results='utf8'
複製代碼
這個配置就等價於
  1. SET NAMES 'utf8'
複製代碼

WP 應該作什麼修改還是那句話,客戶端要發給數據庫什麼編碼的數據,數據庫是不可能確切知道的,只能讓客戶端自己說明白,所以,WP 是必須發送正確的
  1. SET...
複製代碼
給 MySQL 的。怎麼發送最合適呢?台灣的 pLog 同仁給出了一些建議
  • 首先,測試服務器是否 >= 4.1,編譯時是否加入了 UTF-8 支持;是則繼續
  • 然後測試數據庫以什麼格式存儲 (
    1. $dbEncoding
    複製代碼
    );
    1. SET NAMES $dbEncoding
    複製代碼
對於第二點,WP 的情況是不同的,按照上面的典型配置,只要用 WP,肯定數據庫是用 UTF-8 存儲的,所以要根據用戶設置的以 GB2312 還是 UTF-8 瀏覽來判斷 (
  1. bloginfo('charset')
複製代碼
),但這個值是要連接數據庫以後才能得到的,所以效率最高的方式是連接數據庫之後,根據這個配置設置一次
  1. SET NAMES
複製代碼
,而不必每次查詢之前都設置一遍。
我的修改方式是這樣的,在
  1. wp_includes/wp-db.php
複製代碼
中增加:
  1. function set_charset($charset) { // check mysql version first. $serverVersion = mysql_get_server_info($this->dbh); $version = explode('.', $serverVersion); if ($version[0] < 4) return; // check if utf8 support was compiled in $result = mysql_query("SHOW CHARACTER SET like 'utf8'", $this->dbh); if (mysql_num_rows($result) < = 0) return; if ($charset == 'utf-8' || $charset == 'UTF-8') $charset = 'utf8'; @mysql_query("SET NAMES '$charset'", $this->dbh); }
複製代碼
  1. wp-settings.php
複製代碼
  1. require (ABSPATH . WPINC . '/vars.php');
複製代碼
後增加:
  1. $wpdb->set_charset(get_bloginfo('charset'));
複製代碼

 

臉書網友討論
發表於 2013-11-9 00:59:43 | 顯示全部樓層
感謝版主  

版主招募中

發表於 2013-11-9 00:59:43 | 顯示全部樓層
ding   支持  


發表於 2013-11-9 00:59:43 | 顯示全部樓層
努力,努力,再努力!!!!!!!!!!!  


您需要登錄後才可以回帖 登錄 | 註冊 |

本版積分規則



Archiver|手機版|小黑屋|免責聲明|TShopping

GMT+8, 2016-12-12 06:01 , Processed in 0.055659 second(s), 19 queries .

本論壇言論純屬發表者個人意見,與 TShopping綜合論壇 立場無關 如有意見侵犯了您的權益 請寫信聯絡我們。

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回復 返回頂部 返回列表