隨筆-11  評(píng)論-0  文章-2  trackbacks-0

          1. 為查詢緩存優(yōu)化你的查詢

          大多數(shù)的MySQL服務(wù)器都開啟了查詢緩存。這是提高性最有效的方法之一,而且這是被MySQL的數(shù)據(jù)庫(kù)引擎處理的。當(dāng)有很多相同的查詢被執(zhí)行了多次的時(shí)候,這些查詢結(jié)果會(huì)被放到一個(gè)緩存中,這樣,后續(xù)的相同的查詢就不用操作表而直接訪問緩存結(jié)果了。

           

          這里最主要的問題是,對(duì)于程序員來(lái)說(shuō),這個(gè)事情是很容易被忽略的。因?yàn)椋覀兡承┎樵冋Z(yǔ)句會(huì)讓MySQL不使用緩存。請(qǐng)看下面的示例:

          1 // 查詢緩存不開啟

          2 $r = mysql_query("SELECT username FROM user WHERE signup_date >= CURDATE()");

          3   

          4 // 開啟查詢緩存

          5 $today = date("Y-m-d");

          6 $r = mysql_query("SELECT username FROM user WHERE signup_date >= '$today'");

          上面兩條SQL語(yǔ)句的差別就是 CURDATE() MySQL的查詢緩存對(duì)這個(gè)函數(shù)不起作用。所以,像 NOW() RAND() 或是其它的諸如此類的SQL函數(shù)都不會(huì)開啟查詢緩存,因?yàn)檫@些函數(shù)的返回是會(huì)不定的易變的。所以,你所需要的就是用一個(gè)變量來(lái)代替MySQL的函數(shù),從而開啟緩存。

          2. 當(dāng)只要一行數(shù)據(jù)時(shí)使用 LIMIT 1

          當(dāng)你查詢表的有些時(shí)候,你已經(jīng)知道結(jié)果只會(huì)有一條結(jié)果,但因?yàn)槟憧赡苄枰?/span>fetch游標(biāo),或是你也許會(huì)去檢查返回的記錄數(shù)。

           

          在這種情況下,加上 LIMIT 1 可以增加性能。這樣一樣,MySQL數(shù)據(jù)庫(kù)引擎會(huì)在找到一條數(shù)據(jù)后停止搜索,而不是繼續(xù)往后查少下一條符合記錄的數(shù)據(jù)。

           

          下面的示例,只是為了找一下是否有“中國(guó)”的用戶,很明顯,后面的會(huì)比前面的更有效率。(請(qǐng)注意,第一條中是Select *,第二條是Select 1

          01 // 沒有效率的:

          02 $r = mysql_query("SELECT * FROM user WHERE country = 'China'");

          03 if (mysql_num_rows($r) > 0) {

          04     // ...

          05 }

          06   

          07 // 有效率的:

          08 $r = mysql_query("SELECT 1 FROM user WHERE country = 'China' LIMIT 1");

          09 if (mysql_num_rows($r) > 0) {

          10     // ...

          11 }

          3. 千萬(wàn)不要 ORDER BY RAND()

          想打亂返回的數(shù)據(jù)行?隨機(jī)挑一個(gè)數(shù)據(jù)?真不知道誰(shuí)發(fā)明了這種用法,但很多新手很喜歡這樣用。但你確不了解這樣做有多么可怕的性能問題。

           

          如果你真的想把返回的數(shù)據(jù)行打亂了,你有N種方法可以達(dá)到這個(gè)目的。這樣使用只讓你的數(shù)據(jù)庫(kù)的性能呈指數(shù)級(jí)的下降。這里的問題是:MySQL會(huì)不得不去執(zhí)行RAND()函數(shù)(很耗CPU時(shí)間),而且這是為了每一行記錄去記行,然后再對(duì)其排序。就算是你用了Limit 1也無(wú)濟(jì)于事(因?yàn)橐判颍?/span>

           

          下面的示例是隨機(jī)挑一條記錄

          1 // 千萬(wàn)不要這樣做:

          2 $r = mysql_query("SELECT username FROM user ORDER BY RAND() LIMIT 1");

          3   

          4 // 這要會(huì)更好:

          5 $r = mysql_query("SELECT count(*) FROM user");

          6 $d = mysql_fetch_row($r);

          7 $rand = mt_rand(0,$d[0] - 1);

          8   

          9 $r = mysql_query("SELECT username FROM user LIMIT $rand, 1");

          4. 避免 SELECT *

          從數(shù)據(jù)庫(kù)里讀出越多的數(shù)據(jù),那么查詢就會(huì)變得越慢。并且,如果你的數(shù)據(jù)庫(kù)服務(wù)器和WEB服務(wù)器是兩臺(tái)獨(dú)立的服務(wù)器的話,這還會(huì)增加網(wǎng)絡(luò)傳輸?shù)呢?fù)載。

           

          所以,你應(yīng)該養(yǎng)成一個(gè)需要什么就取什么的好的習(xí)慣。

          1 // 不推薦

          2 $r = mysql_query("SELECT * FROM user WHERE user_id = 1");

          3 $d = mysql_fetch_assoc($r);

          4 echo "Welcome {$d['username']}";

          5   

          6 // 推薦

          7 $r = mysql_query("SELECT username FROM user WHERE user_id = 1");

          8 $d = mysql_fetch_assoc($r);

          9 echo "Welcome {$d['username']}";

          5. 永遠(yuǎn)為每張表設(shè)置一個(gè)ID

          我們應(yīng)該為數(shù)據(jù)庫(kù)里的每張表都設(shè)置一個(gè)ID做為其主鍵,而且最好的是一個(gè)INT型的(推薦使用UNSIGNED),并設(shè)置上自動(dòng)增加的AUTO_INCREMENT標(biāo)志。

           

          就算是你 users 表有一個(gè)主鍵叫 email”的字段,你也別讓它成為主鍵。使用 VARCHAR 類型來(lái)當(dāng)主鍵會(huì)使用得性能下降。另外,在你的程序中,你應(yīng)該使用表的ID來(lái)構(gòu)造你的數(shù)據(jù)結(jié)構(gòu)。

           

          而且,在MySQL數(shù)據(jù)引擎下,還有一些操作需要使用主鍵,在這些情況下,主鍵的性能和設(shè)置變得非常重要,比如,集群,分區(qū)……

           

          在這里,只有一個(gè)情況是例外,那就是“關(guān)聯(lián)表”的“外鍵”,也就是說(shuō),這個(gè)表的主鍵,通過若干個(gè)別的表的主鍵構(gòu)成。我們把這個(gè)情況叫做“外鍵”。比如:有一個(gè)“學(xué)生表”有學(xué)生的ID,有一個(gè)“課程表”有課程ID,那么,“成績(jī)表”就是“關(guān)聯(lián)表”了,其關(guān)聯(lián)了學(xué)生表和課程表,在成績(jī)表中,學(xué)生ID和課程ID叫“外鍵”其共同組成主鍵。

           

          6. 使用 ENUM 而不是 VARCHAR

          ENUM 類型是非常快和緊湊的。在實(shí)際上,其保存的是 TINYINT,但其外表上顯示為字符串。這樣一來(lái),用這個(gè)字段來(lái)做一些選項(xiàng)列表變得相當(dāng)?shù)耐昝馈?/span>

           

          如果你有一個(gè)字段,比如“性別”,“國(guó)家”,“民族”,“狀態(tài)”或“部門”,你知道這些字段的取值是有限而且固定的,那么,你應(yīng)該使用 ENUM 而不是 VARCHAR

           

          MySQL也有一個(gè)“建議”(見第十條)告訴你怎么去重新組織你的表結(jié)構(gòu)。當(dāng)你有一個(gè) VARCHAR 字段時(shí),這個(gè)建議會(huì)告訴你把其改成 ENUM 類型。使用 PROCEDURE ANALYSE() 你可以得到相關(guān)的建議。

          7. 盡可能的使用 NOT NULL

          除非你有一個(gè)很特別的原因去使用 NULL 值,你應(yīng)該總是讓你的字段保持 NOT NULL。這看起來(lái)好像有點(diǎn)爭(zhēng)議,請(qǐng)往下看。

          首先,問問你自己“Empty”和“NULL”有多大的區(qū)別(如果是INT,那就是0和NULL)?如果你覺得它們之間沒有什么區(qū)別,那么你就不要使用NULL。(你知道嗎?在 Oracle 里,NULL 和 Empty 的字符串是一樣的!)

          不要以為 NULL 不需要空間,其需要額外的空間,并且,在你進(jìn)行比較的時(shí)候,你的程序會(huì)更復(fù)雜。當(dāng)然,這里并不是說(shuō)你就不能使用NULL了,現(xiàn)實(shí)情況是很復(fù)雜的,依然會(huì)有些情況下,你需要使用NULL值。

          8. 把IP地址存成 UNSIGNED INT

          很多程序員都會(huì)創(chuàng)建一個(gè) VARCHAR(15) 字段來(lái)存放字符串形式的IP而不是整形的IP。如果你用整形來(lái)存放,只需要4個(gè)字節(jié),并且你可以有定長(zhǎng)的字段。而且,這會(huì)為你帶來(lái)查詢上的優(yōu)勢(shì),尤其是當(dāng)你需要使用這樣的WHERE條件:IP between ip1 and ip2。

          我們必需要使用UNSIGNED INT,因?yàn)?IP地址會(huì)使用整個(gè)32位的無(wú)符號(hào)整形。

          而你的查詢,你可以使用 INET_ATON() 來(lái)把一個(gè)字符串IP轉(zhuǎn)成一個(gè)整形,并使用 INET_NTOA() 把一個(gè)整形轉(zhuǎn)成一個(gè)字符串IP

          9. 拆分大的 DELETE 或 INSERT 語(yǔ)句

          如果你需要在一個(gè)在線的網(wǎng)站上去執(zhí)行一個(gè)大的 DELETE INSERT 查詢,你需要非常小心,要避免你的操作讓你的整個(gè)網(wǎng)站停止相應(yīng)。因?yàn)檫@兩個(gè)操作是會(huì)鎖表的,表一鎖住了,別的操作都進(jìn)不來(lái)了。

          如果你把你的表鎖上一段時(shí)間,比如30秒鐘,那么對(duì)于一個(gè)有很高訪問量的站點(diǎn)來(lái)說(shuō),這30秒所積累的訪問進(jìn)程/線程,數(shù)據(jù)庫(kù)鏈接,打開的文件數(shù),可能不僅僅會(huì)讓你泊WEB服務(wù)Crash,還可能會(huì)讓你的整臺(tái)服務(wù)器馬上宕機(jī)。

           

          所以,如果你有一個(gè)大的處理,你定你一定把其拆分,使用 LIMIT 條件是一個(gè)好的方法。下面是一個(gè)示例:

          01 while (1) {

          02     //每次只做1000

          03     mysql_query("DELETE FROM logs WHERE log_date <= '2009-11-01' LIMIT 1000");

          04     if (mysql_affected_rows() == 0) {

          05         // 沒得可刪了,退出!

          06         break;

          07     }

          08     // 每次都要休息一會(huì)兒

          09     usleep(50000);

          10 }

          10. 越小的列會(huì)越快

          對(duì)于大多數(shù)的數(shù)據(jù)庫(kù)引擎來(lái)說(shuō),硬盤操作可能是最重大的瓶頸。所以,把你的數(shù)據(jù)變得緊湊會(huì)對(duì)這種情況非常有幫助,因?yàn)檫@減少了對(duì)硬盤的訪問。

          如果一個(gè)表只會(huì)有幾列罷了(比如說(shuō)字典表,配置表),那么,我們就沒有理由使用 INT 來(lái)做主鍵,使用 MEDIUMINT, SMALLINT 或是更小的 TINYINT 會(huì)更經(jīng)濟(jì)一些。

           

          posted on 2012-03-19 14:50 閱讀(506) 評(píng)論(0)  編輯  收藏 所屬分類: MySQL

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 临汾市| 诸城市| 栾川县| 黎平县| 玛沁县| 平和县| 财经| 上蔡县| 岚皋县| 长治市| 瑞安市| 谷城县| 宜春市| 四平市| 磴口县| 涞水县| 建水县| 高要市| 灵璧县| 砀山县| 邯郸县| 刚察县| 东方市| 高安市| 沙湾县| 醴陵市| 塔河县| 华宁县| 阜新市| 壤塘县| 屯留县| 上饶县| 六枝特区| 泗阳县| 定结县| 应用必备| 昌邑市| 太湖县| 扬中市| 达拉特旗| 增城市|