大數(shù)據(jù)量時(shí)Mysql的優(yōu)化要點(diǎn)

如今隨著互聯(lián)網(wǎng)的發(fā)展茉继,數(shù)據(jù)的量級(jí)也是撐指數(shù)的增長(zhǎng)枫振,從GB到TB到PB柑营。對(duì)數(shù)據(jù)的各種操作也是愈加的困難,傳統(tǒng)的關(guān)系性數(shù)據(jù)庫已經(jīng)無法滿足快速查詢與插入數(shù)據(jù)的需求泊愧。這個(gè)時(shí)候NoSQL的出現(xiàn)暫時(shí)解決了這一危機(jī)伊磺。它通過降低數(shù)據(jù)的安全性,減少對(duì)事務(wù)的支持删咱,減少對(duì)復(fù)雜查詢的支持屑埋,來獲取性能上的提升。但是痰滋,在有些場(chǎng)合NoSQL一些折衷是無法滿足使用場(chǎng)景的摘能,就比如有些使用場(chǎng)景是絕對(duì)要有事務(wù)與安全指標(biāo)的。這個(gè)時(shí)候NoSQL肯定是無法滿足的敲街,所以還是需要使用關(guān)系性數(shù)據(jù)庫团搞。
雖然關(guān)系型數(shù)據(jù)庫在海量數(shù)據(jù)中遜色于NoSQL數(shù)據(jù)庫,但是如果你操作正確多艇,它的性能還是會(huì)滿足你的需求的逻恐。針對(duì)數(shù)據(jù)的不同操作,其優(yōu)化方向也是不盡相同峻黍。對(duì)于數(shù)據(jù)移植钠绍,查詢和插入等操作撬即,可以從不同的方向去考慮整袁。而在優(yōu)化的時(shí)候還需要考慮其他相關(guān)操作是否會(huì)產(chǎn)生影響卫旱。就比如你可以通過創(chuàng)建索引提高查詢性能,但是這會(huì)導(dǎo)致插入數(shù)據(jù)的時(shí)候因?yàn)橐⒏滤饕龑?dǎo)致插入性能降低骨饿,你是否可以接受這一降低那轻局。所以,對(duì)數(shù)據(jù)庫的優(yōu)化是要考慮多個(gè)方向样刷,尋找一個(gè)折衷的最佳方案。
一:查詢優(yōu)化

1:創(chuàng)建索引览爵。
最簡(jiǎn)單也是最常用的優(yōu)化就是查詢置鼻。因?yàn)閷?duì)于CRUD操作,read操作是占據(jù)了絕大部分的比例蜓竹,所以read的性能基本上決定了應(yīng)用的性能箕母。對(duì)于查詢性能最常用的就是創(chuàng)建索引储藐。經(jīng)過測(cè)試,2000萬條記錄嘶是,每條記錄200字節(jié)兩列varchar類型的钙勃。當(dāng)不使用索引的時(shí)候查詢一條記錄需要一分鐘,而當(dāng)創(chuàng)建了索引的時(shí)候查詢時(shí)間可以忽略聂喇。但是辖源,當(dāng)你在已有數(shù)據(jù)上添加索引的時(shí)候,則需要耗費(fèi)非常大的時(shí)間希太。我插入2000萬條記錄之后克饶,再創(chuàng)建索引大約話費(fèi)了幾十分鐘的樣子。
創(chuàng)建索引的弊端和場(chǎng)合誊辉。雖然創(chuàng)建索引可以很大程度上優(yōu)化查詢的速度矾湃,但是弊端也是很明顯的。一個(gè)是在插入數(shù)據(jù)的時(shí)候堕澄,創(chuàng)建索引也需要消耗部分的時(shí)間邀跃,這就使得插入性能在一定程度上降低;另一個(gè)很明顯的是數(shù)據(jù)文件變的更大蛙紫。在列上創(chuàng)建索引的時(shí)候拍屑,每條索引的長(zhǎng)度是和你創(chuàng)建列的時(shí)候制定的長(zhǎng)度相同的。比如你創(chuàng)建varchar(100)惊来,當(dāng)你在該列上創(chuàng)建索引丽涩,那么索引的長(zhǎng)度則是102字節(jié),因?yàn)殚L(zhǎng)度超過64字節(jié)則會(huì)額外增加2字節(jié)記錄索引的長(zhǎng)度裁蚁。


從上圖可以看到我在YCSB_KEY這一列(長(zhǎng)度100)上創(chuàng)建了一個(gè)名字為index_ycsb_key的索引矢渊,每條索引長(zhǎng)度都為102,想象一下當(dāng)數(shù)據(jù)變的巨大無比的時(shí)候枉证,索引的大小也是不可以小覷的矮男。而且從這也可以看出,索引的長(zhǎng)度和列類型的長(zhǎng)度還不同室谚,比如varchar它是變長(zhǎng)的字符類型(請(qǐng)看MySQL數(shù)據(jù)類型分析)毡鉴,實(shí)際存儲(chǔ)長(zhǎng)度是是實(shí)際字符的大小,但是索引卻是你聲明的長(zhǎng)度的大小秒赤。你創(chuàng)建列的時(shí)候聲明100字節(jié)猪瞬,那么索引長(zhǎng)度就是這個(gè)字節(jié)再加上2,它不管你實(shí)際存儲(chǔ)是多大入篮。
除了創(chuàng)建索引需要消耗時(shí)間陈瘦,索引文件體積會(huì)變的越來越大之外,創(chuàng)建索引也需要看的你存儲(chǔ)數(shù)據(jù)的特征潮售。當(dāng)你存儲(chǔ)數(shù)據(jù)很大一部分都是重復(fù)記錄痊项,那這個(gè)時(shí)候創(chuàng)建索引是百害而無一利锅风。請(qǐng)先查看MySQL索引介紹。所以鞍泉,當(dāng)很多數(shù)據(jù)重復(fù)的時(shí)候皱埠,索引帶來的查詢提升的效果是可以直接忽略的,但是這個(gè)時(shí)候你還要承受插入數(shù)據(jù)的時(shí)候創(chuàng)建索引帶來的性能消耗咖驮。
2:緩存的配置边器。
在MySQL中有多種多樣的緩存,有的緩存負(fù)責(zé)緩存查詢語句游沿,也有的負(fù)責(zé)緩存查詢數(shù)據(jù)饰抒。這些緩存內(nèi)容客戶端無法操作,是由server端來維護(hù)的诀黍。它會(huì)隨著你查詢與修改等相應(yīng)不同操作進(jìn)行不斷更新袋坑。通過其配置文件我們可以看到在MySQL中的緩存:

在這里主要分析query cache,它是主要用來緩存查詢數(shù)據(jù)眯勾。當(dāng)你想使用該cache枣宫,必須把query_cache_size大小設(shè)置為非0。當(dāng)設(shè)置大小為非0的時(shí)候吃环,server會(huì)就會(huì)緩存每次查詢返回的結(jié)果也颤,到下次相同查詢server就直接從緩存獲取數(shù)據(jù),而不是再執(zhí)行查詢郁轻。能緩存的數(shù)據(jù)量就和你的size大小設(shè)置有關(guān)翅娶,所以當(dāng)你設(shè)置的足夠大,數(shù)據(jù)可以完全緩存到內(nèi)存好唯,速度就會(huì)非常之快竭沫。
但是,query cache也有它的弊端骑篙。當(dāng)你對(duì)數(shù)據(jù)表做任何的更新操作(update/insert/delete)等操作蜕提,server為了保證緩存與數(shù)據(jù)庫的一致性,會(huì)強(qiáng)制刷新緩存數(shù)據(jù)靶端,導(dǎo)致緩存數(shù)據(jù)全部失效谎势。所以,當(dāng)一個(gè)表格的更新數(shù)據(jù)表操作非常多的話杨名,query cache是不會(huì)起到查詢提升的性能脏榆,還會(huì)影響其他操作的性能。
3:slow_query_log分析台谍。
其實(shí)對(duì)于查詢性能提升姐霍,最重要也是最根本的手段也是slow_query的設(shè)置。

當(dāng)你設(shè)置slow_query_log為on的時(shí)候,server端會(huì)對(duì)每次的查詢進(jìn)行記錄镊折,當(dāng)超過你設(shè)置的慢查詢時(shí)間 (long_query_time)的時(shí)候就把該條查詢記錄到日志。而你對(duì)性能進(jìn)行優(yōu)化的時(shí)候介衔,就可以分析慢查詢?nèi)罩竞夼撸瑢?duì)慢查詢的查詢語句進(jìn)行有目的的優(yōu)化⊙卓В可以通過創(chuàng)建各種索引赃泡,可以通過分表等操作。那為什么要分庫分表那乘盼,當(dāng)不分庫分表的時(shí)候那個(gè)地方是限制性能的地方啊升熊。下面我們就簡(jiǎn)單介紹。
4:分庫分表
分庫分表應(yīng)該算是查詢優(yōu)化的殺手锏了绸栅。上述各種措施在數(shù)據(jù)量達(dá)到一定等級(jí)之后级野,能起到優(yōu)化的作用已經(jīng)不明顯了。這個(gè)時(shí)候就必須對(duì)數(shù)據(jù)量進(jìn)行分流粹胯。分流一般有分庫與分表兩種措施蓖柔。而分表又有垂直切分與水平切分兩種方式。下面我們就針對(duì)每一種方式簡(jiǎn)單介紹风纠。
對(duì)于mysql况鸣,其數(shù)據(jù)文件是以文件形式存儲(chǔ)在磁盤上的。當(dāng)一個(gè)數(shù)據(jù)文件過大的時(shí)候竹观,操作系統(tǒng)對(duì)大文件的操作就會(huì)比較麻煩與耗時(shí)镐捧,而且有的操作系統(tǒng)就不支持大文件,所以這個(gè)時(shí)候就必須分表了臭增。另外對(duì)于mysql常用的存儲(chǔ)引擎是Innodb懂酱,它的底層數(shù)據(jù)結(jié)構(gòu)是B+樹。當(dāng)其數(shù)據(jù)文件過大的時(shí)候速址,B+樹就會(huì)從層次和節(jié)點(diǎn)上比較多玩焰,當(dāng)查詢一個(gè)節(jié)點(diǎn)的時(shí)候可能會(huì)查詢很多層次,而這必定會(huì)導(dǎo)致多次IO操作進(jìn)行裝載進(jìn)內(nèi)存芍锚,肯定會(huì)耗時(shí)的昔园。除此之外還有Innodb對(duì)于B+樹的鎖機(jī)制。對(duì)每個(gè)節(jié)點(diǎn)進(jìn)行加鎖并炮,那么當(dāng)更改表結(jié)構(gòu)的時(shí)候默刚,這時(shí)候就會(huì)樹進(jìn)行加鎖,當(dāng)表文件大的時(shí)候逃魄,這可以認(rèn)為是不可實(shí)現(xiàn)的荤西。
所以綜上我們就必須進(jìn)行分表與分庫的操作。
5:子查詢優(yōu)化
在查詢中經(jīng)常會(huì)用到子查詢,在子查詢的時(shí)候一般使用in或者exist關(guān)鍵詞邪锌。針對(duì)in和exist在查詢的時(shí)候當(dāng)數(shù)據(jù)量大到一定程度以后勉躺,查詢執(zhí)行時(shí)間就差別比較大。但是觅丰,為了避免此類情況出現(xiàn)饵溅,最好的方式是使用join查詢。因?yàn)樵诮^大多數(shù)情況下妇萄,服務(wù)器對(duì)join的查詢優(yōu)化要遠(yuǎn)遠(yuǎn)高于子查詢優(yōu)化蜕企。在比較高的版本5.6,mysql查詢會(huì)自動(dòng)把in查詢優(yōu)化成joint查詢冠句,就不會(huì)出現(xiàn)子查詢比較慢的問題轻掩。有時(shí)候也可以采用distinct關(guān)鍵詞來限制子查詢的數(shù)量,但是需要注意的是distinct很多時(shí)候會(huì)轉(zhuǎn)化為group by懦底,這個(gè)時(shí)候就會(huì)出現(xiàn)一個(gè) 臨時(shí)表唇牧,就會(huì)出現(xiàn)copy數(shù)據(jù)到臨時(shí)表的時(shí)延。
更多的子查詢優(yōu)化 請(qǐng)點(diǎn)擊基茵。

二:數(shù)據(jù)轉(zhuǎn)移
當(dāng)數(shù)據(jù)量達(dá)到一定等級(jí)之后奋构,那么移庫將是一個(gè)非常慎重又危險(xiǎn)的工作。在移庫中保證前后數(shù)據(jù)的一致性拱层,各種突發(fā)情況的處理弥臼,移庫過程中數(shù)據(jù)的變遷,每一個(gè)都是一個(gè)非常困難的問題根灯。
2.1:插入數(shù)據(jù)
當(dāng)進(jìn)行數(shù)據(jù)遷移的時(shí)候径缅,肯定會(huì)存在大數(shù)據(jù)的重新導(dǎo)入,你可以選擇直接load文件烙肺,有的時(shí)候可能就需要代碼插入了纳猪。這個(gè)時(shí)候就需要對(duì)插入語句進(jìn)行一定的優(yōu)化了。這個(gè)時(shí)候可以使用INSERT DELAYED語句桃笙,該語句是當(dāng)你發(fā)出插入請(qǐng)求的時(shí)候氏堤,不是馬上就插入到數(shù)據(jù)庫而是放在緩存里面,等待時(shí)機(jī)成熟之后再進(jìn)行插入搏明。
1鼠锈、對(duì)查詢進(jìn)行優(yōu)化、應(yīng)盡量避免全表掃描星著、首先應(yīng)考慮在 where 及 order by 涉及的列上建立索引购笆。

2、應(yīng)盡量避免在 where 子句中對(duì)字段進(jìn)行 null 值判斷虚循、否則將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描同欠、如:

select id from t where num is null;
--可以在num上設(shè)置默認(rèn)值0样傍、確保表中num列沒有null值、然后這樣查詢:

select id from t where num=0;
3铺遂、應(yīng)盡量避免在 where 子句中使用!=或<>操作符衫哥、否則將引擎放棄使用索引而進(jìn)行全表掃描。

4娃循、應(yīng)盡量避免在 where 子句中使用 or 來連接條件炕檩、否則將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描、如:

select id from t where num=10 or num=20
--可以這樣查詢:

select id from t where num=10
union all
select id from t where num=20;
5捌斧、in 和 not in 也要慎用、否則會(huì)導(dǎo)致全表掃描泉沾、如:

select id from t where num in(1,2,3);
對(duì)于連續(xù)的數(shù)值捞蚂、能用 between 就不要用 in 了:

select id from t where num between 1 and 3;
6、下面的查詢也將導(dǎo)致全表掃描:

select id from t where name like '%abc%';
--若要提高效率跷究、可以考慮全文檢索姓迅。

7、如果在 where 子句中使用參數(shù)俊马、也會(huì)導(dǎo)致全表掃描丁存。因?yàn)镾QL只有在運(yùn)行時(shí)才會(huì)解析局部變量、但優(yōu)化程序不能將訪問計(jì)劃的選擇推遲到運(yùn)行時(shí)柴我;它必須在編譯時(shí)進(jìn)行選擇解寝。然而、如果在編譯時(shí)建立訪問計(jì)劃艘儒、變量的值還是未知的聋伦、因而無法作為索引選擇的輸入項(xiàng)。如下面語句將進(jìn)行全表掃描:

select id from t where num= @num ;
--可以改為強(qiáng)制查詢使用索引:

select id from t with(index(索引名)) where num= @num ;
8界睁、應(yīng)盡量避免在 where 子句中對(duì)字段進(jìn)行表達(dá)式操作觉增、這將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描。如:

select id from t where num/2=100;
--應(yīng)改為:

select id from t where num=100*2;
9翻斟、應(yīng)盡量避免在where子句中對(duì)字段進(jìn)行函數(shù)操作、這將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描访惜。如:

select id from t where substring(name,1,3)='abc';
--name以abc開頭的id

select id from t where datediff(day,createdate,'2005-11-30')=0;
--‘2005-11-30’生成的id
--應(yīng)改為:

select id from t where name like 'abc%';
select id from t where createdate>='2005-11-30' and createdate<'2005-12-1';
10嘹履、不要在 where 子句中的“=”左邊進(jìn)行函數(shù)、算術(shù)運(yùn)算或其他表達(dá)式運(yùn)算疾牲、否則系統(tǒng)將可能無法正確使用索引植捎。

11、在使用索引字段作為條件時(shí)阳柔、如果該索引是復(fù)合索引焰枢、那么必須使用到該索引中的第一個(gè)字段作為條件時(shí)才能保證系統(tǒng)使用該索引、否則該索引將不會(huì)被使用、并且應(yīng)盡可能的讓字段順序與索引順序相一致济锄。

12暑椰、不要寫一些沒有意義的查詢、如需要生成一個(gè)空表結(jié)構(gòu):

select col1,col2 into #t from t where 1=0;
--這類代碼不會(huì)返回任何結(jié)果集荐绝、但是會(huì)消耗系統(tǒng)資源的一汽、應(yīng)改成這樣:

create table #t(...);
13、很多時(shí)候用 exists 代替 in 是一個(gè)好的選擇:

select num from a where num in(select num from b);
--用下面的語句替換:

select num from a where exists(select 1 from b where num=a.num);
14低滩、并不是所有索引對(duì)查詢都有效召夹、SQL是根據(jù)表中數(shù)據(jù)來進(jìn)行查詢優(yōu)化的、當(dāng)索引列有大量數(shù)據(jù)重復(fù)時(shí)恕沫、SQL查詢可能不會(huì)去利用索引监憎、如一表中有字段sex、male婶溯、female幾乎各一半鲸阔、那么即使在sex上建了索引也對(duì)查詢效率起不了作用。

15迄委、索引并不是越多越好褐筛、索引固然可以提高相應(yīng)的 select 的效率、但同時(shí)也降低了 insert 及 update 的效率叙身、因?yàn)?insert 或 update 時(shí)有可能會(huì)重建索引渔扎、所以怎樣建索引需要慎重考慮、視具體情況而定曲梗。一個(gè)表的索引數(shù)最好不要超過6個(gè)赞警、若太多則應(yīng)考慮一些不常使用到的列上建的索引是否有必要。

16虏两、應(yīng)盡可能的避免更新 clustered 索引數(shù)據(jù)列愧旦、因?yàn)?clustered 索引數(shù)據(jù)列的順序就是表記錄的物理存儲(chǔ)順序、一旦該列值改變將導(dǎo)致整個(gè)表記錄的順序的調(diào)整定罢、會(huì)耗費(fèi)相當(dāng)大的資源。若應(yīng)用系統(tǒng)需要頻繁更新 clustered 索引數(shù)據(jù)列惠况、那么需要考慮是否應(yīng)將該索引建為 clustered 索引翎苫。

17呐粘、盡量使用數(shù)字型字段五芝、若只含數(shù)值信息的字段盡量不要設(shè)計(jì)為字符型蓄坏、這會(huì)降低查詢和連接的性能渔彰、并會(huì)增加存儲(chǔ)開銷再沧。這是因?yàn)橐嬖谔幚聿樵兒瓦B接時(shí)會(huì)逐個(gè)比較字符串中每一個(gè)字符顷扩、而對(duì)于數(shù)字型而言只需要比較一次就夠了汹胃。

18贱勃、盡可能的使用 varchar/nvarchar 代替 char/nchar 戚绕、因?yàn)槭紫茸冮L(zhǎng)字段存儲(chǔ)空間小球切、可以節(jié)省存儲(chǔ)空間鸵钝、其次對(duì)于查詢來說痕届、在一個(gè)相對(duì)較小的字段內(nèi)搜索效率顯然要高些。

19、任何地方都不要使用 select * from t 、用具體的字段列表代替“*”决乎、不要返回用不到的任何字段送膳。

20、盡量使用表變量來代替臨時(shí)表丑蛤。如果表變量包含大量數(shù)據(jù)肠缨、請(qǐng)注意索引非常有限(只有主鍵索引)。

21盏阶、避免頻繁創(chuàng)建和刪除臨時(shí)表、以減少系統(tǒng)表資源的消耗闻书。

22名斟、臨時(shí)表并不是不可使用、適當(dāng)?shù)厥褂盟鼈兛梢允鼓承├谈行敲肌⒗缗檠巍?dāng)需要重復(fù)引用大型表或常用表中的某個(gè)數(shù)據(jù)集時(shí)。但是坑律、對(duì)于一次性事件岩梳、最好使用導(dǎo)出表。

23晃择、在新建臨時(shí)表時(shí)冀值、如果一次性插入數(shù)據(jù)量很大、那么可以使用 select into 代替 create table宫屠、避免造成大量 log 列疗、以提高速度;如果數(shù)據(jù)量不大浪蹂、為了緩和系統(tǒng)表的資源抵栈、應(yīng)先create table告材、然后insert。

24古劲、如果使用到了臨時(shí)表斥赋、在存儲(chǔ)過程的最后務(wù)必將所有的臨時(shí)表顯式刪除、先 truncate table 产艾、然后 drop table 疤剑、這樣可以避免系統(tǒng)表的較長(zhǎng)時(shí)間鎖定。

25胰舆、盡量避免使用游標(biāo)骚露、因?yàn)橛螛?biāo)的效率較差、如果游標(biāo)操作的數(shù)據(jù)超過1萬行缚窿、那么就應(yīng)該考慮改寫棘幸。

26、使用基于游標(biāo)的方法或臨時(shí)表方法之前倦零、應(yīng)先尋找基于集的解決方案來解決問題误续、基于集的方法通常更有效。

27扫茅、與臨時(shí)表一樣蹋嵌、游標(biāo)并不是不可使用。對(duì)小型數(shù)據(jù)集使用 FAST_FORWARD 游標(biāo)通常要優(yōu)于其他逐行處理方法葫隙、尤其是在必須引用幾個(gè)表才能獲得所需的數(shù)據(jù)時(shí)栽烂。在結(jié)果集中包括“合計(jì)”的例程通常要比使用游標(biāo)執(zhí)行的速度快。如果開發(fā)時(shí)間允許恋脚、基于游標(biāo)的方法和基于集的方法都可以嘗試一下腺办、看哪一種方法的效果更好。

28糟描、在所有的存儲(chǔ)過程和觸發(fā)器的開始處設(shè)置 SET NOCOUNT ON 怀喉、在結(jié)束時(shí)設(shè)置 SET NOCOUNT OFF 。無需在執(zhí)行存儲(chǔ)過程和觸發(fā)器的每個(gè)語句后向客戶端發(fā)送 DONE_IN_PROC 消息船响。

29躬拢、盡量避免大事務(wù)操作、提高系統(tǒng)并發(fā)能力见间。

30聊闯、盡量避免向客戶端返回大數(shù)據(jù)量、若數(shù)據(jù)量過大米诉、應(yīng)該考慮相應(yīng)需求是否合理馅袁。

  1. 為查詢緩存優(yōu)化你的查詢
    大多數(shù)的MySQL服務(wù)器都開啟了查詢緩存。這是提高性最有效的方法之一荒辕,而且這是被MySQL的數(shù)據(jù)庫引擎處理的汗销。當(dāng)有很多相同的查詢被執(zhí)行了多 次的時(shí)候犹褒,這些查詢結(jié)果會(huì)被放到一個(gè)緩存中,這樣弛针,后續(xù)的相同的查詢就不用操作表而直接訪問緩存結(jié)果了叠骑。
    這里最主要的問題是,對(duì)于程序員來說削茁,這個(gè)事情是很容易被忽略的宙枷。因?yàn)椋覀兡承┎樵冋Z句會(huì)讓MySQL不使用緩存茧跋。請(qǐng)看下面的示例:
    1
    2
    3
    4
    5
    6

// 查詢緩存不開啟
$r = mysql_query( "SELECT username FROM user WHERE signup_date >= CURDATE()" );

// 開啟查詢緩存
$today = date ( "Y-m-d" );
$r = mysql_query( "SELECT username FROM user WHERE signup_date >= '$today'" );

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

  1. EXPLAIN 你的 SELECT 查詢
    使用 EXPLAIN 關(guān)鍵字可以讓你知道MySQL是如何處理你的SQL語句的讨阻。這可以幫你分析你的查詢語句或是表結(jié)構(gòu)的性能瓶頸芥永。
    EXPLAIN 的查詢結(jié)果還會(huì)告訴你你的索引主鍵被如何利用的,你的數(shù)據(jù)表是如何被搜索和排序的……等等钝吮,等等埋涧。
    挑一個(gè)你的SELECT語句(推薦挑選那個(gè)最復(fù)雜的,有多表聯(lián)接的)奇瘦,把關(guān)鍵字EXPLAIN加到前面飞袋。你可以使用phpmyadmin來做這個(gè) 事。然后链患,你會(huì)看到一張表格。下面的這個(gè)示例中瓶您,我們忘記加上了group_id索引麻捻,并且有表聯(lián)接:

    當(dāng)我們?yōu)?group_id 字段加上索引后:

    我們可以看到,前一個(gè)結(jié)果顯示搜索了 7883 行呀袱,而后一個(gè)只是搜索了兩個(gè)表的 9 和 16 行贸毕。查看rows列可以讓我們找到潛在的性能問題。
  2. 當(dāng)只要一行數(shù)據(jù)時(shí)使用 LIMIT 1
    當(dāng)你查詢表的有些時(shí)候夜赵,你已經(jīng)知道結(jié)果只會(huì)有一條結(jié)果明棍,但因?yàn)槟憧赡苄枰etch游標(biāo),或是你也許會(huì)去檢查返回的記錄數(shù)寇僧。
    在這種情況下摊腋,加上 LIMIT 1 可以增加性能沸版。這樣一樣,MySQL數(shù)據(jù)庫引擎會(huì)在找到一條數(shù)據(jù)后停止搜索兴蒸,而不是繼續(xù)往后查少下一條符合記錄的數(shù)據(jù)视粮。
    下面的示例,只是為了找一下是否有“中國”的用戶橙凳,很明顯蕾殴,后面的會(huì)比前面的更有效率。(請(qǐng)注意岛啸,第一條中是Select *钓觉,第二條是Select 1)
    // 沒有效率的:$r = mysql_query( "SELECT * FROM user WHERE country = 'China'" );if (mysql_num_rows( $r ) > 0) { // ...}// 有效率的:$r = mysql_query( "SELECT 1 FROM user WHERE country = 'China' LIMIT 1" );if (mysql_num_rows( $r ) > 0) { // ...}
  3. 為搜索字段建索引
    索引并不一定就是給主鍵或是唯一的字段。如果在你的表中坚踩,有某個(gè)字段你總要會(huì)經(jīng)常用來做搜索荡灾,那么,請(qǐng)為其建立索引吧堕虹。

    從上圖你可以看到那個(gè)搜索字串 “l(fā)ast_name LIKE ‘a(chǎn)%’”卧晓,一個(gè)是建了索引,一個(gè)是沒有索引赴捞,性能差了4倍左右逼裆。
    另外,你應(yīng)該也需要知道什么樣的搜索是不能使用正常的索引的赦政。例如胜宇,當(dāng)你需要在一篇大的文章中搜索一個(gè)詞時(shí),如: “WHERE post_content LIKE ‘%apple%’”恢着,索引可能是沒有意義的桐愉。你可能需要使用MySQL 全文索引 或是自己做一個(gè)索引(比如說:搜索關(guān)鍵詞或是Tag什么的)
  4. 在Join表的時(shí)候使用相當(dāng)類型的例,并將其索引
    如果你的應(yīng)用程序有很多 JOIN 查詢掰派,你應(yīng)該確認(rèn)兩個(gè)表中Join的字段是被建過索引的从诲。這樣,MySQL內(nèi)部會(huì)啟動(dòng)為你優(yōu)化Join的SQL語句的機(jī)制靡羡。
    而且系洛,這些被用來Join的字段,應(yīng)該是相同的類型的略步。例如:如果你要把 DECIMAL 字段和一個(gè) INT 字段Join在一起描扯,MySQL就無法使用它們的索引。對(duì)于那些STRING類型趟薄,還需要有相同的字符集才行绽诚。(兩個(gè)表的字符集有可能不一樣)
    1
    2
    3
    4
    5
    6

// 在state中查找company
$r = mysql_query("SELECT company_name FROM users
LEFT JOIN companies ON (users.state = companies.state)
WHERE users.id = $user_id ");

// 兩個(gè) state 字段應(yīng)該是被建過索引的,而且應(yīng)該是相當(dāng)?shù)念愋停嗤淖址?/p>

  1. 千萬不要 ORDER BY RAND()
    想打亂返回的數(shù)據(jù)行恩够?隨機(jī)挑一個(gè)數(shù)據(jù)卒落?真不知道誰發(fā)明了這種用法,但很多新手很喜歡這樣用玫鸟。但你確不了解這樣做有多么可怕的性能問題导绷。
    如果你真的想把返回的數(shù)據(jù)行打亂了,你有N種方法可以達(dá)到這個(gè)目的屎飘。這樣使用只讓你的數(shù)據(jù)庫的性能呈指數(shù)級(jí)的下降妥曲。這里的問題是:MySQL會(huì)不得 不去執(zhí)行RAND()函數(shù)(很耗CPU時(shí)間),而且這是為了每一行記錄去記行钦购,然后再對(duì)其排序檐盟。就算是你用了Limit 1也無濟(jì)于事(因?yàn)橐判颍?br> 下面的示例是隨機(jī)挑一條記錄
    1
    2
    3
    4
    5
    6
    7
    8
    9

// 千萬不要這樣做:
$r = mysql_query( "SELECT username FROM user ORDER BY RAND() LIMIT 1" );

// 這要會(huì)更好:
$r = mysql_query( "SELECT count(*) FROM user" );
$d = mysql_fetch_row( $r );
$rand = mt_rand(0, $d [0] - 1);

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

  1. 避免 SELECT *
    從數(shù)據(jù)庫里讀出越多的數(shù)據(jù),那么查詢就會(huì)變得越慢押桃。并且葵萎,如果你的數(shù)據(jù)庫服務(wù)器和WEB服務(wù)器是兩臺(tái)獨(dú)立的服務(wù)器的話,這還會(huì)增加網(wǎng)絡(luò)傳輸?shù)呢?fù)載唱凯。
    所以羡忘,你應(yīng)該養(yǎng)成一個(gè)需要什么就取什么的好的習(xí)慣。

// 不推薦
$r = mysql_query( "SELECT * FROM user WHERE user_id = 1" );
$d = mysql_fetch_assoc( $r );
echo "Welcome {$d['username']}" ;

// 推薦
$r = mysql_query( "SELECT username FROM user WHERE user_id = 1" );
$d = mysql_fetch_assoc( $r );
echo "Welcome {$d['username']}" ;

  1. 永遠(yuǎn)為每張表設(shè)置一個(gè)ID
    我們應(yīng)該為數(shù)據(jù)庫里的每張表都設(shè)置一個(gè)ID做為其主鍵磕昼,而且最好的是一個(gè)INT型的(推薦使用UNSIGNED)卷雕,并設(shè)置上自動(dòng)增加的 AUTO_INCREMENT標(biāo)志。
    就算是你 users 表有一個(gè)主鍵叫 “email”的字段票从,你也別讓它成為主鍵漫雕。使用 VARCHAR 類型來當(dāng)主鍵會(huì)使用得性能下降。另外峰鄙,在你的程序中浸间,你應(yīng)該使用表的ID來構(gòu)造你的數(shù)據(jù)結(jié)構(gòu)。
    而且吟榴,在MySQL數(shù)據(jù)引擎下魁蒜,還有一些操作需要使用主鍵,在這些情況下吩翻,主鍵的性能和設(shè)置變得非常重要兜看,比如,集群仿野,分區(qū)……
    在這里,只有一個(gè)情況是例外她君,那就是“關(guān)聯(lián)表”的“外鍵”脚作,也就是說,這個(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叫“外鍵”其共同組成主鍵。

  2. 使用 ENUM 而不是 VARCHAR
    ENUM 類型是非畴鼓埃快和緊湊的擎浴。在實(shí)際上,其保存的是 TINYINT毒涧,但其外表上顯示為字符串贮预。這樣一來,用這個(gè)字段來做一些選項(xiàng)列表變得相當(dāng)?shù)耐昝馈?br> 如果你有一個(gè)字段契讲,比如“性別”仿吞,“國家”,“民族”捡偏,“狀態(tài)”或“部門”唤冈,你知道這些字段的取值是有限而且固定的,那么霹琼,你應(yīng)該使用 ENUM 而不是 VARCHAR务傲。
    MySQL也有一個(gè)“建議”(見第十條)告訴你怎么去重新組織你的表結(jié)構(gòu)。當(dāng)你有一個(gè) VARCHAR 字段時(shí)枣申,這個(gè)建議會(huì)告訴你把其改成 ENUM 類型售葡。使用 PROCEDURE ANALYSE() 你可以得到相關(guān)的建議。

  3. 從 PROCEDURE ANALYSE() 取得建議
    PROCEDURE ANALYSE() 會(huì)讓 MySQL 幫你去分析你的字段和其實(shí)際的數(shù)據(jù)忠藤,并會(huì)給你一些有用的建議挟伙。只有表中有實(shí)際的數(shù)據(jù),這些建議才會(huì)變得有用模孩,因?yàn)橐鲆恍┐蟮臎Q定是需要有數(shù)據(jù)作為基礎(chǔ) 的尖阔。
    例如,如果你創(chuàng)建了一個(gè) INT 字段作為你的主鍵榨咐,然而并沒有太多的數(shù)據(jù)介却,那么,PROCEDURE ANALYSE()會(huì)建議你把這個(gè)字段的類型改成 MEDIUMINT 块茁〕菘溃或是你使用了一個(gè) VARCHAR 字段桂肌,因?yàn)閿?shù)據(jù)不多,你可能會(huì)得到一個(gè)讓你把它改成 ENUM 的建議永淌。這些建議崎场,都是可能因?yàn)閿?shù)據(jù)不夠多,所以決策做得就不夠準(zhǔn)遂蛀。
    在phpmyadmin里谭跨,你可以在查看表時(shí),點(diǎn)擊 “Propose table structure” 來查看這些建議


    一定要注意李滴,這些只是建議螃宙,只有當(dāng)你的表里的數(shù)據(jù)越來越多時(shí),這些建議才會(huì)變得準(zhǔn)確悬嗓。一定要記住污呼,你才是最終做決定的人。

  4. 盡可能的使用 NOT NULL
    除非你有一個(gè)很特別的原因去使用 NULL 值包竹,你應(yīng)該總是讓你的字段保持 NOT NULL燕酷。這看起來好像有點(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)然,這里并不是說你就不能使用NULL了墅垮,現(xiàn)實(shí)情況是很復(fù)雜的惕医,依然會(huì)有些情況下,你需要使用NULL值算色。
    下面摘自MySQL自己的文檔:
    “NULL columns require additional space in the row to record whether their values are NULL. For MyISAM tables, each NULL column takes one bit extra, rounded up to the nearest byte.”

  5. Prepared Statements
    Prepared Statements很像存儲(chǔ)過程抬伺,是一種運(yùn)行在后臺(tái)的SQL語句集合,我們可以從使用 prepared statements 獲得很多好處灾梦,無論是性能問題還是安全問題峡钓。
    Prepared Statements 可以檢查一些你綁定好的變量齐鲤,這樣可以保護(hù)你的程序不會(huì)受到“SQL注入式”攻擊。當(dāng)然椒楣,你也可以手動(dòng)地檢查你的這些變量,然而牡肉,手動(dòng)的檢查容易出問題捧灰, 而且很經(jīng)常會(huì)被程序員忘了。當(dāng)我們使用一些framework或是ORM的時(shí)候统锤,這樣的問題會(huì)好一些毛俏。
    在性能方面,當(dāng)一個(gè)相同的查詢被使用多次的時(shí)候饲窿,這會(huì)為你帶來可觀的性能優(yōu)勢(shì)煌寇。你可以給這些Prepared Statements定義一些參數(shù),而MySQL只會(huì)解析一次逾雄。
    雖然最新版本的MySQL在傳輸Prepared Statements是使用二進(jìn)制形勢(shì)阀溶,所以這會(huì)使得網(wǎng)絡(luò)傳輸非常有效率。
    當(dāng)然鸦泳,也有一些情況下银锻,我們需要避免使用Prepared Statements,因?yàn)槠洳恢С植樵兙彺孀鲇ァ5珦?jù)說版本5.1后支持了击纬。
    在PHP中要使用prepared statements,你可以查看其使用手冊(cè):mysqli 擴(kuò)展 或是使用數(shù)據(jù)庫抽象層钾麸,如: PDO .

// 創(chuàng)建 prepared statement
if ( $stmt = $mysqli ->prepare( "SELECT username FROM user WHERE state=?" )) {

 // 綁定參數(shù)
 $stmt ->bind_param( "s" , $state );

 // 執(zhí)行
 $stmt ->execute();

 // 綁定結(jié)果
 $stmt ->bind_result( $username );

 // 移動(dòng)游標(biāo)
 $stmt ->fetch();

 printf( "%s is from %s\n" , $username , $state );

 $stmt ->close();

}

  1. 無緩沖的查詢
    正常的情況下更振,當(dāng)你在當(dāng)你在你的腳本中執(zhí)行一個(gè)SQL語句的時(shí)候,你的程序會(huì)停在那里直到?jīng)]這個(gè)SQL語句返回饭尝,然后你的程序再往下繼續(xù)執(zhí)行肯腕。你可 以使用無緩沖查詢來改變這個(gè)行為。
    關(guān)于這個(gè)事情芋肠,在PHP的文檔中有一個(gè)非常不錯(cuò)的說明: mysql_unbuffered_query() 函數(shù):
    “mysql_unbuffered_query() sends the SQL query query to MySQL without automatically fetching and buffering the result rows as mysql_query() does. This saves a considerable amount of memory with SQL queries that produce large result sets, and you can start working on the result set immediately after the first row has been retrieved as you don’t have to wait until the complete SQL query has been performed.”

上面那句話翻譯過來是說乎芳,mysql_unbuffered_query() 發(fā)送一個(gè)SQL語句到MySQL而并不像mysql_query()一樣去自動(dòng)fethch和緩存結(jié)果。這會(huì)相當(dāng)節(jié)約很多可觀的內(nèi)存帖池,尤其是那些會(huì)產(chǎn)生大 量結(jié)果的查詢語句奈惑,并且,你不需要等到所有的結(jié)果都返回睡汹,只需要第一行數(shù)據(jù)返回的時(shí)候肴甸,你就可以開始馬上開始工作于查詢結(jié)果了。
然而囚巴,這會(huì)有一些限制原在。因?yàn)槟阋窗阉行卸甲x走友扰,或是你要在進(jìn)行下一次的查詢前調(diào)用 mysql_free_result()清除結(jié)果。而且庶柿, mysql_num_rows()mysql_data_seek() 將無法使用村怪。所以,是否使用無緩沖的查詢你需要仔細(xì)考慮浮庐。

  1. 把IP地址存成 UNSIGNED INT
    很多程序員都會(huì)創(chuàng)建一個(gè) VARCHAR(15) 字段來存放字符串形式的IP而不是整形的IP甚负。如果你用整形來存放,只需要4個(gè)字節(jié)审残,并且你可以有定長(zhǎng)的字段梭域。而且,這會(huì)為你帶來查詢上的優(yōu)勢(shì)搅轿,尤其是當(dāng) 你需要使用這樣的WHERE條件:IP between ip1 and ip2病涨。
    我們必需要使用UNSIGNED INT,因?yàn)?IP地址會(huì)使用整個(gè)32位的無符號(hào)整形璧坟。
    而你的查詢既穆,你可以使用 INET_ATON() 來把一個(gè)字符串IP轉(zhuǎn)成一個(gè)整形,并使用 INET_NTOA() 把一個(gè)整形轉(zhuǎn)成一個(gè)字符串IP雀鹃。在PHP中循衰,也有這樣的函數(shù) ip2long()long2ip()
    1

$r = "UPDATE users SET ip = INET_ATON('{$_SERVER['REMOTE_ADDR']}') WHERE user_id = $user_id" ;

  1. 固定長(zhǎng)度的表會(huì)更快
    如果表中的所有字段都是“固定長(zhǎng)度”的褐澎,整個(gè)表會(huì)被認(rèn)為是 “static” 或 “fixed-length” 会钝。 例如,表中沒有如下類型的字段: VARCHAR工三,TEXT迁酸,BLOB。只要你包括了其中一個(gè)這些字段俭正,那么這個(gè)表就不是“固定長(zhǎng)度靜態(tài)表”了奸鬓,這樣,MySQL 引擎會(huì)用另一種方法來處理掸读。
    固定長(zhǎng)度的表會(huì)提高性能串远,因?yàn)镸ySQL搜尋得會(huì)更快一些,因?yàn)檫@些固定的長(zhǎng)度是很容易計(jì)算下一個(gè)數(shù)據(jù)的偏移量的儿惫,所以讀取的自然也會(huì)很快澡罚。而如果 字段不是定長(zhǎng)的,那么肾请,每一次要找下一條的話留搔,需要程序找到主鍵。
    并且铛铁,固定長(zhǎng)度的表也更容易被緩存和重建隔显。不過却妨,唯一的副作用是,固定長(zhǎng)度的字段會(huì)浪費(fèi)一些空間括眠,因?yàn)槎ㄩL(zhǎng)的字段無論你用不用彪标,他都是要分配那么多 的空間。
    使用“垂直分割”技術(shù)(見下一條)掷豺,你可以分割你的表成為兩個(gè)一個(gè)是定長(zhǎng)的捐下,一個(gè)則是不定長(zhǎng)的。
  2. 垂直分割
    “垂直分割”是一種把數(shù)據(jù)庫中的表按列變成幾張表的方法萌业,這樣可以降低表的復(fù)雜度和字段的數(shù)目,從而達(dá)到優(yōu)化的目的奸柬。(以前生年,在銀行做過項(xiàng)目,見過 一張表有100多個(gè)字段廓奕,很恐怖)
    示例一 :在Users表中有一個(gè)字段是家庭地址抱婉,這個(gè)字段是可選字段,相比起桌粉,而且你在數(shù)據(jù)庫操作的時(shí)候除了個(gè) 人信息外蒸绩,你并不需要經(jīng)常讀取或是改寫這個(gè)字段。那么铃肯,為什么不把他放到另外一張表中呢患亿? 這樣會(huì)讓你的表有更好的性能,大家想想是不是押逼,大量的時(shí)候步藕,我對(duì)于用戶表來說,只有用戶ID挑格,用戶名咙冗,口令,用戶角色等會(huì)被經(jīng)常使用漂彤。小一點(diǎn)的表總是會(huì)有 好的性能雾消。
    示例二 : 你有一個(gè)叫 “l(fā)ast_login” 的字段,它會(huì)在每次用戶登錄時(shí)被更新挫望。但是立润,每次更新時(shí)會(huì)導(dǎo)致該表的查詢緩存被清空。所以媳板,你可以把這個(gè)字段放到另一個(gè)表中范删,這樣就不會(huì)影響你對(duì)用戶 ID,用戶名拷肌,用戶角色的不停地讀取了到旦,因?yàn)椴樵兙彺鏁?huì)幫你增加很多性能旨巷。
    另外,你需要注意的是添忘,這些被分出去的字段所形成的表采呐,你不會(huì)經(jīng)常性地去Join他們,不然的話搁骑,這樣的性能會(huì)比不分割時(shí)還要差斧吐,而且,會(huì)是極數(shù)級(jí) 的下降仲器。
  3. 拆分大的 DELETE 或 INSERT 語句
    如果你需要在一個(gè)在線的網(wǎng)站上去執(zhí)行一個(gè)大的 DELETE 或 INSERT 查詢煤率,你需要非常小心,要避免你的操作讓你的整個(gè)網(wǎng)站停止相應(yīng)乏冀。因?yàn)檫@兩個(gè)操作是會(huì)鎖表的蝶糯,表一鎖住了,別的操作都進(jìn)不來了辆沦。
    Apache 會(huì)有很多的子進(jìn)程或線程昼捍。所以,其工作起來相當(dāng)有效率肢扯,而我們的服務(wù)器也不希望有太多的子進(jìn)程妒茬,線程和數(shù)據(jù)庫鏈接,這是極大的占服務(wù)器資源的事情蔚晨,尤其是 內(nèi)存乍钻。
    如果你把你的表鎖上一段時(shí)間,比如30秒鐘铭腕,那么對(duì)于一個(gè)有很高訪問量的站點(diǎn)來說团赁,這30秒所積累的訪問進(jìn)程/線程,數(shù)據(jù)庫鏈接谨履,打開的文件數(shù)欢摄,可 能不僅僅會(huì)讓你泊WEB服務(wù)Crash,還可能會(huì)讓你的整臺(tái)服務(wù)器馬上掛了笋粟。
    所以怀挠,如果你有一個(gè)大的處理,你定你一定把其拆分害捕,使用 LIMIT 條件是一個(gè)好的方法绿淋。下面是一個(gè)示例:

while (1) {
//每次只做1000條
mysql_query( "DELETE FROM logs WHERE log_date <= '2009-11-01' LIMIT 1000" );
if (mysql_affected_rows() == 0) {
// 沒得可刪了,退出尝盼!
break ;
}
// 每次都要休息一會(huì)兒
usleep(50000);
}

  1. 越小的列會(huì)越快
    對(duì)于大多數(shù)的數(shù)據(jù)庫引擎來說吞滞,硬盤操作可能是最重大的瓶頸。所以,把你的數(shù)據(jù)變得緊湊會(huì)對(duì)這種情況非常有幫助裁赠,因?yàn)檫@減少了對(duì)硬盤的訪問殿漠。
    參看 MySQL 的文檔 Storage Requirements 查看所有的數(shù)據(jù)類型。
    如果一個(gè)表只會(huì)有幾列罷了(比如說字典表佩捞,配置表)绞幌,那么,我們就沒有理由使用 INT 來做主鍵一忱,使用 MEDIUMINT, SMALLINT 或是更小的 TINYINT 會(huì)更經(jīng)濟(jì)一些莲蜘。如果你不需要記錄時(shí)間,使用 DATE 要比 DATETIME 好得多帘营。
    當(dāng)然票渠,你也需要留夠足夠的擴(kuò)展空間,不然芬迄,你日后來干這個(gè)事问顷,你會(huì)死的很難看,參看Slashdot 的例子 (2009年11月06日)薯鼠,一個(gè)簡(jiǎn)單的ALTER TABLE語句花了3個(gè)多小時(shí),因?yàn)槔锩嬗幸磺Я偃f條數(shù)據(jù)械蹋。

  2. 選擇正確的存儲(chǔ)引擎
    在 MySQL 中有兩個(gè)存儲(chǔ)引擎 MyISAM 和 InnoDB出皇,每個(gè)引擎都有利有弊』└辏酷殼以前文章《MySQL: InnoDB 還是 MyISAM? 》討論和這個(gè)事情郊艘。
    MyISAM 適合于一些需要大量查詢的應(yīng)用,但其對(duì)于有大量寫操作并不是很好唯咬。甚至你只是需要update一個(gè)字段纱注,整個(gè)表都會(huì)被鎖起來,而別的進(jìn)程胆胰,就算是讀進(jìn)程都 無法操作直到讀操作完成狞贱。另外,MyISAM 對(duì)于 SELECT COUNT(*) 這類的計(jì)算是超快無比的蜀涨。
    InnoDB 的趨勢(shì)會(huì)是一個(gè)非常復(fù)雜的存儲(chǔ)引擎瞎嬉,對(duì)于一些小的應(yīng)用,它會(huì)比 MyISAM 還慢厚柳。他是它支持“行鎖” 氧枣,于是在寫操作比較多的時(shí)候,會(huì)更優(yōu)秀别垮。并且便监,他還支持更多的高級(jí)應(yīng)用,比如:事務(wù)碳想。
    下面是MySQL的手冊(cè)
    target=”_blank”MyISAM Storage Engine
    InnoDB Storage Engine

  3. 使用一個(gè)對(duì)象關(guān)系映射器(Object Relational Mapper)
    使用 ORM (Object Relational Mapper)烧董,你能夠獲得可靠的性能增漲毁靶。一個(gè)ORM可以做的所有事情,也能被手動(dòng)的編寫出來解藻。但是老充,這需要一個(gè)高級(jí)專家。
    ORM 的最重要的是“Lazy Loading”螟左,也就是說啡浊,只有在需要的去取值的時(shí)候才會(huì)去真正的去做。但你也需要小心這種機(jī)制的副作用胶背,因?yàn)檫@很有可能會(huì)因?yàn)橐?chuàng)建很多很多小的查 詢反而會(huì)降低性能巷嚣。
    ORM 還可以把你的SQL語句打包成一個(gè)事務(wù),這會(huì)比單獨(dú)執(zhí)行他們快得多得多钳吟。
    目前廷粒,個(gè)人最喜歡的PHP的ORM是:Doctrine

  4. 小心“永久鏈接”
    “永久鏈接”的目的是用來減少重新創(chuàng)建MySQL鏈接的次數(shù)红且。當(dāng)一個(gè)鏈接被創(chuàng)建了坝茎,它會(huì)永遠(yuǎn)處在連接的狀態(tài),就算是數(shù)據(jù)庫操作已經(jīng)結(jié)束了暇番。而且嗤放,自 從我們的Apache開始重用它的子進(jìn)程后——也就是說,下一次的HTTP請(qǐng)求會(huì)重用Apache的子進(jìn)程壁酬,并重用相同的 MySQL 鏈接次酌。
    PHP 手冊(cè):mysql_pconnect()

在理論上來說,這聽起來非常的不錯(cuò)舆乔。但是從個(gè)人經(jīng)驗(yàn)(也是大多數(shù)人的)上來說岳服,這個(gè)功能制造出來的麻煩事更多。因?yàn)橄A阒挥杏邢薜逆溄訑?shù)吊宋,內(nèi)存問 題,文件句柄數(shù)颜武,等等贫母。
而且,Apache 運(yùn)行在極端并行的環(huán)境中盒刚,會(huì)創(chuàng)建很多很多的了進(jìn)程腺劣。這就是為什么這種“永久鏈接”的機(jī)制工作地不好的原因。在你決定要使用“永久鏈接”之前因块,你需要好好地 考慮一下你的整個(gè)系統(tǒng)的架構(gòu)橘原。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子趾断,更是在濱河造成了極大的恐慌拒名,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芋酌,死亡現(xiàn)場(chǎng)離奇詭異增显,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)脐帝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門同云,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人堵腹,你說我怎么就攤上這事炸站。” “怎么了疚顷?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵旱易,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我腿堤,道長(zhǎng)阀坏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任笆檀,我火速辦了婚禮忌堂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘误债。我一直安慰自己浸船,他們只是感情好妄迁,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布寝蹈。 她就那樣靜靜地躺著,像睡著了一般登淘。 火紅的嫁衣襯著肌膚如雪箫老。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天黔州,我揣著相機(jī)與錄音耍鬓,去河邊找鬼。 笑死流妻,一個(gè)胖子當(dāng)著我的面吹牛牲蜀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播绅这,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼涣达,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起度苔,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤匆篓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后寇窑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鸦概,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年甩骏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了窗市。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡横漏,死狀恐怖谨设,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情缎浇,我是刑警寧澤扎拣,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站素跺,受9級(jí)特大地震影響二蓝,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜指厌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一刊愚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧踩验,春花似錦鸥诽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至袭异,卻和暖如春钠龙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背御铃。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國打工碴里, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人上真。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓咬腋,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親睡互。 傳聞我的和親對(duì)象是個(gè)殘疾皇子根竿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容