驚天動(dòng)地私服與MySQL數(shù)據(jù)庫服務(wù)器的優(yōu)化
普通MySQL運(yùn)行,數(shù)據(jù)量和訪問量不大的話,是足夠快的,但是當(dāng)數(shù)據(jù)量和訪問量劇增的時(shí)候,那么就會(huì)明顯發(fā)現(xiàn)MySQL很慢,甚至down掉,那么就要考慮優(yōu)化我們的MySQL了。
優(yōu)化無非是從三個(gè)角度入手:
第一個(gè)是從硬件,增加硬件,增加服務(wù)器
第二個(gè)就是對(duì)我們的MySQL服務(wù)器進(jìn)行優(yōu)化,增加緩存大小,開多端口,讀寫分開
第三個(gè)就是我們的應(yīng)用優(yōu)化,建立索引,優(yōu)化SQL查詢語句,建立緩存等等
我就簡(jiǎn)單的說說SQL查詢語句的優(yōu)化。因?yàn)槿绻覀僕eb服務(wù)器比數(shù)據(jù)庫服務(wù)器多或者性能優(yōu)良的話,我們完全可以把數(shù)據(jù)庫的壓力轉(zhuǎn)嫁到Web服務(wù)器上,因?yàn)槿绻麊闻_(tái)MySQL,或者 Master/Slave 架構(gòu)的數(shù)據(jù)庫服務(wù)器都負(fù)擔(dān)比較重,那么就可以考慮把MySQL的運(yùn)算放到Web服務(wù)器上去進(jìn)行。當(dāng)然了,如果你Web服務(wù)器比數(shù)據(jù)庫服務(wù)器差,那就把壓力放在數(shù)據(jù)庫服務(wù)器上吧。
如果是把MySQL服務(wù)器的壓力放在Web服務(wù)器上,那么很多運(yùn)算就需要我們的程序去執(zhí)行,比如Web程序中全部交給PHP腳本去處理數(shù)據(jù)。單臺(tái)MySQL服務(wù)器,查詢、更新、插入、刪除都在一臺(tái)服務(wù)器上的話,訪問量一大,你會(huì)明顯發(fā)現(xiàn)鎖表現(xiàn)象,當(dāng)對(duì)一個(gè)表進(jìn)行更新刪除操作的時(shí)候,就會(huì)拒絕其他操作,這樣就會(huì)導(dǎo)致鎖表,解決這個(gè)問題最簡(jiǎn)單直接的辦法就是拿兩臺(tái)MySQL服務(wù)器,一臺(tái)負(fù)責(zé)查詢(select)操作,另外一臺(tái)負(fù)責(zé)更改(update/delete/insert),然后進(jìn)行同步,這樣能夠避免鎖表,如果服務(wù)器更多,那么就更好處理了,可以采用分布式數(shù)據(jù)庫架構(gòu)和數(shù)據(jù)的散列存儲(chǔ),驚天動(dòng)地私服下面我們會(huì)簡(jiǎn)單說一下。
一、SQL的優(yōu)化和注意事項(xiàng)
現(xiàn)在我們假設(shè)我們只有一臺(tái)MySQL服務(wù)器,所有的select/update/insert/delete操作都是在這上面進(jìn)行的,我們同時(shí)有三臺(tái)Web服務(wù)器,通過DNS輪巡來訪問,那么我們?nèi)绾芜M(jìn)行我們應(yīng)用程序和SQL的優(yōu)化。
1. Where條件
在查詢中,WHERE條件也是一個(gè)比較重要的因素,盡量少并且是合理的where條件是很重要的,在寫每一個(gè)where條件的時(shí)候都要仔細(xì)考慮,盡量在多個(gè)條件的時(shí)候,把會(huì)提取盡量少數(shù)據(jù)量的條件放在前面,這樣就會(huì)減少后一個(gè)where條件的查詢時(shí)間。
有時(shí)候一些where條件會(huì)導(dǎo)致索引無效,當(dāng)使用了Mysql函數(shù)的時(shí)候,索引將無效,比如:select * from tbl1 where left(name, 4) = 'hylr',那么這時(shí)候索引無效,還有就是使用LIKE進(jìn)行搜索匹配的時(shí)候,這樣的語句索引是無效的:select * from tbl1 where name like '%xxx%',但是這樣索引是有效的:select * from tbl1 where name like 'xxx%',所以謹(jǐn)慎的寫你的SQL是很重要的。
2. 關(guān)聯(lián)查詢和子查詢
數(shù)據(jù)庫一個(gè)很重要的特點(diǎn)是關(guān)聯(lián)查詢,LEFT JOIN 和全關(guān)聯(lián),特別是多個(gè)表進(jìn)行關(guān)聯(lián),因?yàn)槊總€(gè)關(guān)聯(lián)表查詢的時(shí)候,進(jìn)行掃描的時(shí)候都是一個(gè)笛卡爾乘積的數(shù)量級(jí),掃描數(shù)量很大,如果確實(shí)是需要進(jìn)行天龍八部私服關(guān)聯(lián)操作,請(qǐng)給where或者on的條件進(jìn)行索引。
關(guān)聯(lián)操作也是可能交給應(yīng)用去操作的,看數(shù)據(jù)量的大小,如果數(shù)據(jù)量不是非常大,比如10萬條以下,那么就可以交給程序去處理(totododo提出筆誤,特此修正),程序分別提取左右兩個(gè)表的數(shù)據(jù),然后進(jìn)行循環(huán)的掃描處理,返回結(jié)果,這個(gè)過程同樣非常耗費(fèi)Web服務(wù)器的資源,那么就需要取決于你愿意把壓力放在Web服務(wù)器上或者數(shù)據(jù)庫服務(wù)器上了。
子查詢是在mysql5中支持的功能,比如:select * from tbl1 where id in(select id from tbl1),那樣效率是非常非常低,要盡量避免使用子查詢,要是我,絕對(duì)不用真封神私服,呵呵。
3. 一些耗費(fèi)時(shí)間和資源的操作
SQL語句中一些浪費(fèi)的操作,比如 DISTINCT、COUNT、GROUP BY、各種MySQL函數(shù)。這些操作都是比較耗資源的,我想應(yīng)用最多的是count字句吧,如果使用count,盡量不要count(*),最好count一個(gè)字段,比如count(id),或者count(1),(據(jù)totododo測(cè)試效率其實(shí)是一樣的),同樣能夠起到統(tǒng)計(jì)的作用。如果不是十分必要,盡量不要使用distinct操作,就是提取唯一值,你完全可以把這個(gè)操作交給腳本程序去執(zhí)行提取唯一值,減少M(fèi)ySQL的負(fù)擔(dān)。group by 操作也是,確實(shí)需要分組的話,請(qǐng)謹(jǐn)慎的操作,如果是小批量的數(shù)據(jù),可以考慮交給腳本程序去做。 #p#page_title#e#
至于MySQL的函數(shù),估計(jì)很多常用,比如有人喜歡把截取字符串也交給MySQL去操作,或者時(shí)間轉(zhuǎn)換操作,使用比較多的函數(shù)像 SUBSTR(), CONCAT(), DATE_FORMAT(), TO_DAYS(), MAX(), MIN(), MD5() 等等,這些操作完全可以交給腳本程序去做,減輕MySQL的負(fù)擔(dān)。
4. 合理的建立索引
索引的提升速度的一個(gè)非常重要的手段,索引在對(duì)一些經(jīng)常進(jìn)行select操作,并且值比較唯一的字段是相當(dāng)有效的,比如主鍵的id字段,唯一的名字name字段等等。
但是索引對(duì)于唯一值比較少的字段,比如性別gender字段,寥寥無幾的類別字段等,意義不大,因?yàn)樾詣e是50%的幾率,索引幾乎沒有意義。對(duì)于update/delete/insert非常頻繁的表,建立索引要慎重考慮,因?yàn)檫@些頻繁的操作同樣對(duì)于索引的維護(hù)工作量也是很大的,最后反而得不償失,這個(gè)需要自己仔細(xì)考慮。索引同樣不是越多越好,適當(dāng)?shù)乃饕龝?huì)起到很關(guān)鍵的作用,不適當(dāng)?shù)乃饕炊鴾p低效率維護(hù),增加維護(hù)機(jī)戰(zhàn)私服索引的負(fù)擔(dān)。
5. 監(jiān)控sql執(zhí)行效率
在select語句前面使用EXPLAIN字句能夠查看當(dāng)前這個(gè)select字句的執(zhí)行情況,包括使用了什么操作、返回多少幾率、對(duì)索引的使用情況如何等等,能夠有效分析SQL語句的執(zhí)行效率和合理程度。
另外使用MySQL中本身的慢查詢?nèi)罩荆簊low-log,同樣能夠記錄查詢中花費(fèi)時(shí)間比較多的SQL語句,好對(duì)相應(yīng)的語句進(jìn)行優(yōu)化和改寫。
另外在MySQL終端下,使用show processlist命令能夠有效的查看當(dāng)前MySQL在進(jìn)行的線程,包括線程的狀態(tài),是否鎖表等等,可以實(shí)時(shí)的查看SQL執(zhí)行情況,同時(shí)對(duì)一些鎖表操作進(jìn)行優(yōu)化。
二、數(shù)據(jù)庫服務(wù)器的架構(gòu)和分布想法
對(duì)于服務(wù)器的架構(gòu)設(shè)計(jì),這個(gè)其實(shí)是比較重要的,一個(gè)合理的設(shè)計(jì),能夠讓應(yīng)用更好的運(yùn)行。當(dāng)然,架構(gòu)的設(shè)計(jì),取決于你的應(yīng)用和你硬件的實(shí)際情況。我就簡(jiǎn)單的說說幾種不同的數(shù)據(jù)庫架構(gòu)設(shè)計(jì)方式,權(quán)當(dāng)是一個(gè)個(gè)人的想法,希望能夠有幫助。
1. 單臺(tái)服務(wù)器開多進(jìn)程和端口
單臺(tái)MySQL服務(wù)器,如果使用長(zhǎng)鏈接等等都無法解決負(fù)載太大,連接太多的問題,不凡考慮采用一臺(tái)MySQL上使用多個(gè)端口開啟多個(gè)MySQL守護(hù)進(jìn)程的方法來緩解壓力。當(dāng)然,前提是你的應(yīng)用必須支持多端口,并且你的cpu和內(nèi)存足夠運(yùn)行多個(gè)守護(hù)進(jìn)程。
優(yōu)點(diǎn) 是能夠很好的緩解暫時(shí)服務(wù)器的壓力,把不同的操作放在不同的端口,或者把不同的項(xiàng)目模塊放在不同的端口去操作,良好的分擔(dān)單個(gè)守護(hù)進(jìn)程的壓力。
缺點(diǎn) 是數(shù)據(jù)可能會(huì)產(chǎn)生紊亂,同時(shí)可能會(huì)導(dǎo)致很多未知的莫名風(fēng)云私服錯(cuò)誤。呵呵
2. 使用Master/Slave的服務(wù)器結(jié)構(gòu)
Mysql本身具有同步功能,完全可以利用這個(gè)功能。構(gòu)建 Master/Slave 的主從服務(wù)器結(jié)構(gòu),最少只需要兩臺(tái)MySQL服務(wù)器,我們可以把 Master 服務(wù)器用戶更新操作,包括 update/delete/insert,把Slave服務(wù)器用于查詢操作,包括 select 操作,然后兩機(jī)進(jìn)行同步。
優(yōu)點(diǎn) 是合理的把更新和查詢的壓力分擔(dān),并且能夠避免鎖表的問題。
缺點(diǎn) 是更新部實(shí)時(shí),如果網(wǎng)絡(luò)繁忙,可能會(huì)存在延遲的問題,并且任何一臺(tái)服務(wù)器down掉了都很麻煩。
3. 使用分布式的散列存儲(chǔ)
這種結(jié)構(gòu)適合大數(shù)據(jù)量,并且負(fù)載比較大,然后服務(wù)器比較充足的情況。分布式存儲(chǔ)結(jié)構(gòu),簡(jiǎn)單的可以是多臺(tái)服務(wù)器,每臺(tái)服務(wù)器功能是類似的,但是存儲(chǔ)的數(shù)據(jù)不一樣,比如做一個(gè)用戶系統(tǒng),那么把用戶ID在1-10萬以內(nèi)的存儲(chǔ)在A服務(wù)器,用戶ID在10-20萬存儲(chǔ)在B服務(wù)器,20-3-萬存儲(chǔ)在C服務(wù)器,以此類推。如果每個(gè)用戶訪問的服務(wù)器不足,可以構(gòu)建組服務(wù)器,就是每組用戶擁有多臺(tái)服務(wù)器,比如可以在某用戶組建立兩臺(tái)MySQL服務(wù)器,一臺(tái)Master,一臺(tái)Slave,同樣分離他們的更新和查詢操作,或者可以設(shè)計(jì)成雙向同步。同時(shí),你的應(yīng)用程序必須支持跨數(shù)據(jù)庫和跨服務(wù)器的操作能力。
優(yōu)點(diǎn) 是服務(wù)器的負(fù)載合理的被平攤,每臺(tái)服務(wù)器都是負(fù)責(zé)一部分用戶,如果一臺(tái)服務(wù)器down掉了,不會(huì)影響其他用戶ID的用戶正常訪問。同時(shí)添加節(jié)點(diǎn)比較容易,如果又增加了10萬用戶,那么又可以增加一個(gè)節(jié)點(diǎn)服務(wù)器,升級(jí)很方便。 #p#page_title#e#
缺點(diǎn) 是任何一臺(tái)數(shù)據(jù)庫服務(wù)器down掉或者數(shù)據(jù)丟失,那么這部分服務(wù)器的用戶將很郁悶,數(shù)據(jù)都沒了,當(dāng)然,這個(gè)需要良好的備份機(jī)制。
現(xiàn)在大概列出如下望各位補(bǔ)充)
1.數(shù)據(jù)庫的設(shè)計(jì)
盡量把數(shù)據(jù)庫設(shè)計(jì)的更小的占磁盤空間.
1).盡可能使用更小的整數(shù)類型.(mediumint就比int更合適).
2).盡可能的定義字段為not null,除非這個(gè)字段需要null.(這個(gè)規(guī)則只適合字段為KEY的情形)
3).如果沒有用到變長(zhǎng)字段的話比如varchar,那就采用固定大小的紀(jì)錄格式比如char.(CHAR 總是比VARCHR快)
4).表的主索引應(yīng)該盡可能的短.這樣的話每條紀(jì)錄都有名字標(biāo)志且更高效.
5).只創(chuàng)建確實(shí)需要的索引。索引有利于檢索記錄,但是不利于快速保存記錄。如果總是要在表的組合字段上做搜索,那么就在這些字段上創(chuàng)建索引。索引的第一部分必須是最常使用的字段.如果總是需要用到很多字段,首先就應(yīng)該多復(fù)制這些字段,使索引更好的壓縮。
(這條只適合MYISAM引擎的表,對(duì)于INNODB則在保存記錄的時(shí)候關(guān)系不大,因?yàn)镮NNODB是以事務(wù)為基礎(chǔ)的,如果想快速保存記錄的話,特別是大批量的導(dǎo)入記錄的時(shí)候)
6).所有數(shù)據(jù)都得在保存到數(shù)據(jù)庫前進(jìn)行處理。
7).所有字段都得有默認(rèn)值。
8).在某些情況下,把一個(gè)頻繁掃描的表分成兩個(gè)速度會(huì)快好多。在對(duì)動(dòng)態(tài)格式表掃描以取得相關(guān)記錄時(shí),它可能使用更小的靜態(tài)格式表的情況下更是如此。
(具體的表現(xiàn)為:MYISAM表的MERGE類型,以及MYISAM和INNODB通用的分區(qū),詳情見手冊(cè))
9).不會(huì)用到外鍵約束的地方盡量不要使用外鍵。
2.系統(tǒng)的用途
1).及時(shí)的關(guān)閉對(duì)MYSQL的連接。
2).explain 復(fù)雜的SQL語句。(這樣能確定你的SELECT 語句怎么優(yōu)化最佳)
3).如果兩個(gè)關(guān)聯(lián)表要做比較話,做比較的字段必須類型和長(zhǎng)度都一致.(在數(shù)據(jù)龐大的時(shí)候建立INDEX)
4).LIMIT語句盡量要跟order by或者 distinct.這樣可以避免做一次full table scan.
5).如果想要清空表的所有紀(jì)錄,建議用truncate table tablename而不是delete from tablename.
不過有一個(gè)問題,truncate 不會(huì)在事務(wù)處理中回滾。因?yàn)樗{(diào)用create table 真封神私服 語句。
(Truncate Table 語句先刪除表然后再重建,這個(gè)是屬于文件級(jí)別的,所以自然快N多)
實(shí)測(cè)例子:
song2為INNODB表。
mysql> select count(1) from song2;
+----------+
| count(1) |
+----------+
| 500000 |
+----------+
1 row in set (0.91 sec)
mysql> delete from song2;
Query OK, 500000 rows affected (15.70 sec)
mysql> truncate table song2;
Query OK, 502238 rows affected (0.17 sec)
6).能使用STORE PROCEDURE 或者 USER FUNCTION的時(shí)候.(ROUTINE總是減少了服務(wù)器端的開銷)
7).在一條insert語句中采用多重紀(jì)錄插入奇跡世界私服格式.而且使用load data infile來導(dǎo)入大量數(shù)據(jù),這比單純的indert快好多.(在MYSQL中具體表現(xiàn)為:INSERT INTO TABLEQ VALUES (),(),...();)
(還有就是在MYISAM表中插入大量記錄的時(shí)候先禁用到KEYS后面再建立KEYS,具體表現(xiàn)語句:
ALTER TABLE TABLE1 DISABLE KEYS;ALTER TABLE TABLE1 ENABLE KEYS;
而對(duì)于INNNODB 表在插入前先 set autocommit=0;完了后:set autocommit=1;這樣效率比較高。)
8).經(jīng)常OPTIMIZE TABLE 來整理碎片.
9).還有就是date 類型的數(shù)據(jù)如果頻繁要做比較的話盡量保存在unsigned int 類型比較快。
3.系統(tǒng)的瓶頸
1).磁盤搜索.
并行搜索,把數(shù)據(jù)分開存放到多個(gè)磁盤中,這樣能加快搜索時(shí)間.
2).磁盤讀寫(IO)
可以從多個(gè)媒介中并行的讀取數(shù)據(jù)。
3).CPU周期
數(shù)據(jù)存放在主內(nèi)存中.這樣就得增加CPU的個(gè)數(shù)來處理這些數(shù)據(jù)。
4).內(nèi)存帶寬
當(dāng)CPU要將更多的數(shù)據(jù)存放到CPU的緩存中來的話,內(nèi)存的帶寬就成了瓶頸