MySQL的優(yōu)化1
發(fā)布時間:2008-08-13 閱讀數(shù): 次 來源:網(wǎng)樂原科技
一、我們可以且應(yīng)該優(yōu)化什么?
硬件
操作系統(tǒng)/軟件庫
SQL服務(wù)器(設(shè)置和查詢)
應(yīng)用編程接口(API)
應(yīng)用程序
--------------------------------------------------------------------------------
二、優(yōu)化硬件
如果你需要龐大的數(shù)據(jù)庫表(>2G),你應(yīng)該考慮使用64位的硬件結(jié)構(gòu),像Alpha、Sparc或即將推出的IA64。因為MySQL內(nèi)部使用大量64位的整數(shù),64位的CPU將提供更好的性能。
對大數(shù)據(jù)庫,優(yōu)化的次序一般是RAM、快速硬盤、CPU能力。
更多的內(nèi)存通過將最常用的鍵碼頁面存放在內(nèi)存中可以加速鍵碼的更新。
如果不使用事務(wù)安全(transaction-safe)的表或有大表并且想避免長文件檢查,一臺UPS就能夠在電源故障時讓系統(tǒng)安全關(guān)閉。
對于數(shù)據(jù)庫存放在一個專用服務(wù)器的系統(tǒng),應(yīng)該考慮1G的以太網(wǎng)。延遲與吞吐量同樣重要。
--------------------------------------------------------------------------------
三、優(yōu)化磁盤
為系統(tǒng)、程序和臨時文件配備一個專用磁盤,如果確是進行很多修改工作,將更新日志和事務(wù)日志放在專用磁盤上。
低尋道時間對數(shù)據(jù)庫磁盤非常重要。對與大表,你可以估計你將需要log(行數(shù))/log(索引塊長度/3*2/(鍵碼長度 + 數(shù)據(jù)指針長度))+1次尋到才能找到一行。對于有500000行的表,索引Mediun int類型的列,需要log(500000) / log(1024/3*2/(3 + 2))+1=4次尋道。上述索引需要500000*7*3/2=5.2M的空間。實際上,大多數(shù)塊將被緩存,所以大概只需要1-2次尋道。
然而對于寫入(如上),你將需要4次尋道請求來找到在哪里存放新鍵碼,而且一般要2次尋道來更新索引并寫入一行。
對于非常大的數(shù)據(jù)庫,你的應(yīng)用將受到磁盤尋道速度的限制,隨著數(shù)據(jù)量的增加呈N log N數(shù)據(jù)級遞增。
將數(shù)據(jù)庫和表分在不同的磁盤上。在MySQL中,你可以為此而使用符號鏈接。
條列磁盤(RAID 0)將提高讀和寫的吞吐量。
帶鏡像的條列(RAID 0+1)將更安全并提高讀取的吞吐量。寫入的吞吐量將有所降低。
不要對臨時文件或可以很容易地重建的數(shù)據(jù)所在的磁盤使用鏡像或RAID(除了RAID 0)。
在Linux上,在引導(dǎo)時對磁盤使用命令hdparm -m16 -d1以啟用同時讀寫多個扇區(qū)和DMA功能。這可以將響應(yīng)時間提高5~50%。
在Linux上,用async (默認)和noatime掛載磁盤(mount)。
對于某些特定應(yīng)用,可以對某些特定表使用內(nèi)存磁盤,但通常不需要。
--------------------------------------------------------------------------------
四、優(yōu)化操作系統(tǒng)
不要交換區(qū)。如果內(nèi)存不足,增加更多的內(nèi)存或配置你的系統(tǒng)使用較少內(nèi)存。
不要使用NFS磁盤(會有NFS鎖定的問題)。
增加系統(tǒng)和MySQL服務(wù)器的打開文件數(shù)量。(在safe_mysqld腳本中加入ulimit -n #)。
增加系統(tǒng)的進程和線程數(shù)量。
如果你有相對較少的大表,告訴文件系統(tǒng)不要將文件打碎在不同的磁道上(Solaris)。
使用支持大文件的文件系統(tǒng)(Solaris)。
選擇使用哪種文件系統(tǒng)。在Linux上的Reiserfs對于打開、讀寫都非???。文件檢查只需幾秒種。
--------------------------------------------------------------------------------
五、選擇應(yīng)用編程接口
PERL
可在不同的操作系統(tǒng)和數(shù)據(jù)庫之間移植。
適宜快速原型。
應(yīng)該使用DBI/DBD接口。
PHP
比PERL易學(xué)。
使用比PERL少的資源。
通過升級到PHP4可以獲得更快的速度。
C
MySQL的原生接口。
較快并賦予更多的控制。
低層,所以必須付出更多。
C++
較高層次,給你更多的時間來編寫應(yīng)用。
仍在開發(fā)中
ODBC
運行在Windows和Unix上。
幾乎可在不同的SQL服務(wù)器間移植。
較慢。MyODBC只是簡單的直通驅(qū)動程序,比用原生接口慢19%。
有很多方法做同樣的事。很難像很多ODBC驅(qū)動程序那樣運行,在不同的領(lǐng)域還有不同的錯誤。
問題成堆。Microsoft偶爾還會改變接口。
不明朗的未來。(Microsoft更推崇OLE而非ODBC)
ODBC
運行在Windows和Unix上。
幾乎可在不同的SQL服務(wù)器間移植。
較慢。MyODBC只是簡單的直通驅(qū)動程序,比用原生接口慢19%。
有很多方法做同樣的事。很難像很多ODBC驅(qū)動程序那樣運行,在不同的領(lǐng)域還有不同的錯誤。
問題成堆。Microsoft偶爾還會改變接口。
不明朗的未來。(Microsoft更推崇OLE而非ODBC)
JDBC
理論上可在不同的操作系統(tǒng)何時據(jù)庫間移植。
可以運行在web客戶端。
Python和其他
可能不錯,可我們不用它們。
--------------------------------------------------------------------------------
六、優(yōu)化應(yīng)用
應(yīng)該集中精力解決問題。
在編寫應(yīng)用時,應(yīng)該決定什么是最重要的:
速度
操作系統(tǒng)間的可移植性
SQL服務(wù)器間的可移植性
使用持續(xù)的連接。.
緩存應(yīng)用中的數(shù)據(jù)以減少SQL服務(wù)器的負載。
不要查詢應(yīng)用中不需要的列。
不要使用SELECT * FROM table_name...
測試應(yīng)用的所有部分,但將大部分精力放在在可能最壞的合理的負載下的測試整體應(yīng)用。通過以一種模塊化的方式進行,你應(yīng)該能用一個快速“啞模塊”替代找到的瓶頸,然后很容易地標出下一個瓶頸。
如果在一個批處理中進行大量修改,使用LOCK TABLES。例如將多個UPDATES或DELETES集中在一起。
--------------------------------------------------------------------------------
七、應(yīng)該使用可移植的應(yīng)用
Perl DBI/DBD
ODBC
JDBC
Python(或其他有普遍SQL接口的語言)
你應(yīng)該只使用存在于所有目的SQL服務(wù)器中或可以很容易地用其他構(gòu)造模擬的SQL構(gòu)造。www.mysql.com上的Crash-me頁可以幫助你。
為操作系統(tǒng)/SQL服務(wù)器編寫包裝程序來提供缺少的功能。
--------------------------------------------------------------------------------
八、如果你需要更快的速度,你應(yīng)該:
找出瓶頸(CPU、磁盤、內(nèi)存、SQL服務(wù)器、操作系統(tǒng)、API或應(yīng)用)并集中全力解決。
使用給予你更快速度/靈活性的擴展。
逐漸了解SQL服務(wù)器以便能為你的問題使用可能最快的SQL構(gòu)造并避免瓶頸。
優(yōu)化表布局和查詢。
使用復(fù)制以獲得更快的選擇(select)速度。
如果你有一個慢速的網(wǎng)絡(luò)連接數(shù)據(jù)庫,使用壓縮客戶/服務(wù)器協(xié)議。
不要害怕時應(yīng)用的第一個版本不能完美地移植,在你解決問題時,你總是可以在以后優(yōu)化它。
--------------------------------------------------------------------------------
九、優(yōu)化MySQL
挑選編譯器和編譯選項。
位你的系統(tǒng)尋找最好的啟動選項。
通讀MySQL參考手冊并閱讀Paul DuBios的《MySQL》一書。(已有中文版-譯注)
多用EXPLAIN SELECT、SHOW VARIABLES、SHOW STATUS和SHOW PROCESSLIST。
了解查詢優(yōu)化器的工作原理。
優(yōu)化表的格式。
維護你的表(myisamchk、CHECK TABLE、 OPTIMIZE TABLE)
使用MySQL的擴展功能以讓一切快速完成。
如果你注意到了你將在很多場合需要某些函數(shù),編寫MySQL UDF函數(shù)。
不要使用表級或列級的GRANT,除非你確實需要。
購買MySQL技術(shù)支持以幫助你解決問題:)
--------------------------------------------------------------------------------
十、編譯和安裝MySQL
通過位你的系統(tǒng)挑選可能最好的編譯器,你通??梢垣@得10-30%的性能提高。
在Linux/Intel平臺上,用pgcc(gcc的奔騰芯片優(yōu)化版)編譯MySQL。然而,二進制代碼將只能運行在Intel奔騰CPU上。
對于一種特定的平臺,使用MySQL參考手冊上推薦的優(yōu)化選項。
一般地,對特定CPU的原生編譯器(如Sparc的Sun Workshop)應(yīng)該比gcc提供更好的性能,但不總是這樣。
用你將使用的字符集編譯MySQL。
靜態(tài)編譯生成mysqld的執(zhí)行文件(用--with-mysqld-ldflags=all-static)并用strip sql/mysqld整理最終的執(zhí)行文件。
注意,既然MySQL不使用C++擴展,不帶擴展支持編譯MySQL將贏得巨大的性能提高。
如果操作系統(tǒng)支持原生線程,使用原生線程(而不用mit-pthreads)。
用MySQL基準測試來測試最終的二進制代碼。
--------------------------------------------------------------------------------
十一、維護
如果可能,偶爾運行一下OPTIMIZE table,這對大量更新的變長行非常重要。
偶爾用myisamchk -a更新一下表中的鍵碼分布統(tǒng)計。記住在做之前關(guān)掉MySQL。
如果有碎片文件,可能值得將所有文件復(fù)制到另一個磁盤上,清除原來的磁盤并拷回文件。
如果遇到問題,用myisamchk或CHECK table檢查表。
用mysqladmin -i10 precesslist extended-status監(jiān)控MySQL的狀態(tài)。
用MySQL GUI客戶程序,你可以在不同的窗口內(nèi)監(jiān)控進程列表和狀態(tài)。
使用mysqladmin debug獲得有關(guān)鎖定和性能的信息。
--------------------------------------------------------------------------------
十二、優(yōu)化SQL
揚SQL之長,其它事情交由應(yīng)用去做。使用SQL服務(wù)器來做:
找出基于WHERE子句的行。
JOIN表
GROUP BY
ORDER BY
DISTINCT
不要使用SQL來做:
檢驗數(shù)據(jù)(如日期)
成為一只計算器
技巧:
明智地使用鍵碼。
鍵碼適合搜索,但不適合索引列的插入/更新。
保持數(shù)據(jù)為數(shù)據(jù)庫第三范式,但不要擔(dān)心冗余信息或這如果你需要更快的速度,創(chuàng)建總結(jié)表。
在大表上不做GROUP BY,相反創(chuàng)建大表的總結(jié)表并查詢它。
UPDATE table set count=count+1 where key_column=constant非???。
對于大表,或許最好偶爾生成總結(jié)表而不是一直保持總結(jié)表。
充分利用INSERT的默認值。
--------------------------------------------------------------------------------
十三、不同SQL服務(wù)器的速度差別(以秒計)
通過鍵碼讀取2000000行: NT Linux
mysql 367 249
mysql_odbc 464
db2_odbc 1206
informix_odbc 121126
ms-sql_odbc 1634
oracle_odbc 20800
solid_odbc 877
sybase_odbc 17614
插入350768行: NT Linux
mysql 381 206
mysql_odbc 619
db2_odbc 3460
informix_odbc 2692
ms-sql_odbc 4012
oracle_odbc 11291
solid_odbc 1801
sybase_odbc 4802
在上述測試中,MySQL配置8M高速緩存運行,其他數(shù)據(jù)庫以默認安裝運行。
--------------------------------------------------------------------------------
十四、重要的MySQL啟動選項
back_log 如果需要大量新連接,修改它。
thread_cache_size 如果需要大量新連接,修改它。
key_buffer_size 索引頁池,可以設(shè)成很大。
bdb_cache_size BDB表使用的記錄和鍵嗎高速緩存。
table_cache 如果有很多的表和并發(fā)連接,修改它。
delay_key_write 如果需要緩存所有鍵碼寫入,設(shè)置它。
log_slow_queries 找出需花大量時間的查詢。
max_heap_table_size 用于GROUP BY
sort_buffer 用于ORDER BY和GROUP BY
myisam_sort_buffer_size 用于REPAIR TABLE
join_buffer_size 在進行無鍵嗎的聯(lián)結(jié)時使用。
--------------------------------------------------------------------------------
十五、優(yōu)化表
MySQL擁有一套豐富的類型。你應(yīng)該對每一列嘗試使用最有效的類型。
ANALYSE過程可以幫助你找到表的最優(yōu)類型:SELECT * FROM table_name PROCEDURE ANALYSE()。
對于不保存NULL值的列使用NOT NULL,這對你想索引的列尤其重要。
將ISAM類型的表改為MyISAM。
如果可能,用固定的表格式創(chuàng)建表。
不要索引你不想用的東西。
利用MySQL能按一個索引的前綴進行查詢的事實。如果你有索引INDEX(a,b),你不需要在a上的索引。
不在長CHAR/VARCHAR列上創(chuàng)建索引,而只索引列的一個前綴以節(jié)省存儲空間。CREATE TABLE table_name (hostname CHAR(255) not null, index(hostname(10)))
對每個表使用最有效的表格式。
在不同表中保存相同信息的列應(yīng)該有同樣的定義并具有相同的列名。
--------------------------------------------------------------------------------
十六、MySQL如何次存儲數(shù)據(jù)
數(shù)據(jù)庫以目錄存儲。
表以文件存儲。
列以變長或定長格式存儲在文件中。對BDB表,數(shù)據(jù)以頁面形式存儲。
支持基于內(nèi)存的表。
數(shù)據(jù)庫和表可在不同的磁盤上用符號連接起來。
在Windows上,MySQL支持用.sym文件內(nèi)部符號連接數(shù)據(jù)庫。
--------------------------------------------------------------------------------
十七、MySQL表類型
HEAP表:固定行長的表,只存儲在內(nèi)存中并用HASH索引進行索引。
ISAM表:MySQL 3.22中的早期B-tree表格式。
MyIASM:IASM表的新版本,有如下擴展:
二進制層次的可移植性。
NULL列索引。
對變長行比ISAM表有更少的碎片。
支持大文件。
更好的索引壓縮。
更好的鍵嗎統(tǒng)計分布。
更好和更快的auto_increment處理。
來自Sleepcat的Berkeley DB(BDB)表:事務(wù)安全(有BEGIN WORK/COMMIT|ROLLBACK)。
--------------------------------------------------------------------------------
十八、MySQL行類型(專指IASM/MyIASM表)
如果所有列是定長格式(沒有VARCHAR、BLOB或TEXT),MySQL將以定長表格式創(chuàng)建表,否則表以動態(tài)長度格式創(chuàng)建。
定長格式比動態(tài)長度格式快很多并更安全。
動態(tài)長度行格式一般占用較少的存儲空間,但如果表頻繁更新,會產(chǎn)生碎片。
在某些情況下,不值得將所有VARCHAR、BLOB和TEXT列轉(zhuǎn)移到另一個表中,只是獲得主表上的更快速度。
利用myiasmchk(對ISAM,pack_iasm),可以創(chuàng)建只讀壓縮表,這使磁盤使用率最小,但使用慢速磁盤時,這非常不錯。壓縮表充分地利用將不再更新的日志表
--------------------------------------------------------------------------------
十九、MySQL高速緩存(所有線程共享,一次性分配)
鍵碼緩存:key_buffer_size,默認8M。
表緩存:table_cache,默認64。
線程緩存:thread_cache_size,默認0。
主機名緩存:可在編譯時修改,默認128。
內(nèi)存映射表:目前僅用于壓縮表。
注意:MySQL沒有行高速緩存,而讓操作系統(tǒng)處理。
--------------------------------------------------------------------------------
二十、MySQL緩存區(qū)變量(非共享,按需分配)
sort_buffer:ORDER BY/GROUP BY
record_buffer:掃描表。
join_buffer_size:無鍵聯(lián)結(jié)
myisam_sort_buffer_size:REPAIR TABLE
net_buffer_length:對于讀SQL語句并緩存結(jié)果。
tmp_table_size:臨時結(jié)果的HEAP表大小。
--------------------------------------------------------------------------------
二十一、MySQL表高速緩存工作原理
每個MyISAM表的打開實例(instance)使用一個索引文件和一個數(shù)據(jù)文件。如果表被兩個線程使用或在同一條查詢中使用兩次,MyIASM將共享索引文件而是打開數(shù)據(jù)文件的另一個實例。
如果所有在高速緩存中的表都在使用,緩存將臨時增加到比表緩存尺寸大些。如果是這樣,下一個被釋放的表將被關(guān)閉。
你可以通過檢查mysqld的Opened_tables變量以檢查表緩存是否太小。如果該值太高,你應(yīng)該增大表高速緩存。
--------------------------------------------------------------------------------
二十二、MySQL擴展/優(yōu)化-提供更快的速度
使用優(yōu)化的表類型(HEAP、MyIASM或BDB表)。
對數(shù)據(jù)使用優(yōu)化的列。
如果可能使用定長行。
使用不同的鎖定類型(SELECT HIGH_PRIORITY,INSERT LOW_PRIORITY)
Auto_increment
REPLACE (REPLACE INTO table_name valueS (...))
INSERT DELAYED
LOAD DATA INFILE / LOAD_FILE()
使用多行INSERT一次插入多行。
SELECT INTO OUTFILE
LEFT JOIN, STRAIGHT JOIN
LEFT JOIN ,結(jié)合IS NULL
ORDER BY可在某些情況下使用鍵碼。
如果只查詢在一個索引中的列,將只使用索引樹解決查詢。
聯(lián)結(jié)一般比子查詢快(對大多數(shù)SQL服務(wù)器亦如此)。
LIMIT
SELECT * from table1 WHERE a > 10 LIMIT 10,20
DELETE * from table1 WHERE a > 10 LIMIT 10
foo IN (常數(shù)列表) 高度優(yōu)化。
GET_LOCK()/RELEASE_LOCK()
LOCK TABLES
INSERT和SELECT可同時運行。
UDF函數(shù)可裝載進一個正在運行的服務(wù)器。
壓縮只讀表。
CREATE TEMPORARY TABLE
CREATE TABLE .. SELECT
帶RAID選項的MyIASM表將文件分割成很多文件以突破某些文件系統(tǒng)的2G限制。
Delay_keys
復(fù)制功能
--------------------------------------------------------------------------------
二十二、MySQL何時使用索引
對一個鍵碼使用>, >=, =, <, <=, IF NULL和BETWEEN
SELECT * FROM table_name WHERE key_part1=1 and key_part2 > 5;
SELECT * FROM table_name WHERE key_part1 IS NULL;
當(dāng)使用不以通配符開始的LIKE
SELECT * FROM table_name WHERE key_part1 LIKE 'jani%'
在進行聯(lián)結(jié)時從另一個表中提取行時
SELECT * from t1,t2 where t1.col=t2.key_part
找出指定索引的MAX()或MIN()值
SELECT MIN(key_part2),MAX(key_part2) FROM table_name where key_part1=10
一個鍵碼的前綴使用ORDER BY或GROUP BY
SELECT * FROM foo ORDER BY key_part1,key_part2,key_part3
在所有用在查詢中的列是鍵碼的一部分時間
SELECT key_part3 FROM table_name WHERE key_part1=1
--------------------------------------------------------------------------------
二十三、MySQL何時不使用索引
如果MySQL能估計出它將可能比掃描整張表還要快時,則不使用索引。例如如果key_part1均勻分布在1和100之間,下列查詢中使用索引就不是很好:
SELECT * FROM table_name where key_part1 > 1 and key_part1 < 90
如果使用HEAP表且不用=搜索所有鍵碼部分。
在HEAP表上使用ORDER BY。
如果不是用鍵碼第一部分
SELECT * FROM table_name WHERE key_part2=1
如果使用以一個通配符開始的LIKE
SELECT * FROM table_name WHERE key_part1 LIKE '%jani%'
搜索一個索引而在另一個索引上做ORDER BY
SELECT * from table_name WHERE key_part1 = # ORDER BY key2
--------------------------------------------------------------------------------
二十四、學(xué)會使用EXPLAIN
對于每一條你認為太慢的查詢使用EXPLAIN!
mysql> explain select t3.DateOfAction, t1.TransactionID
-> from t1 join t2 join t3
-> where t2.ID = t1.TransactionID and t3.ID = t2.GroupID
-> order by t3.DateOfAction, t1.TransactionID;
+-------+--------+---------------+---------+---------+------------------+------+---------------------------------+
| table | type | possible_keys | key | key_len | ref | rows | Extra |
+-------+--------+---------------+---------+---------+------------------+------+---------------------------------+
| t1 | ALL | NULL | NULL | NULL | NULL | 11 | Using temporary; Using filesort |
| t2 | ref | ID | ID | 4 | t1.TransactionID | 13 | |
| t3 | eq_ref | PRIMARY | PRIMARY | 4 | t2.GroupID | 1 | |
+-------+--------+---------------+---------+---------+------------------+------+---------------------------------+
ALL和范圍類型提示一個潛在的問題。
--------------------------------------------------------------------------------
二十五、學(xué)會使用SHOW PROCESSLIST
使用SHOW processlist來發(fā)現(xiàn)正在做什么:
+----+-------+-----------+----+---------+------+--------------+-------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-------+-----------+----+---------+------+--------------+-------------------------------------+
| 6 | monty | localhost | bp | Query | 15 | Sending data | select * from station,station as s1 |
| 8 | monty | localhost | | Query | 0 | | show processlist |
+----+-------+-----------+----+---------+------+--------------+-------------------------------------+
在mysql或mysqladmin中用KILL來殺死溜掉的線程。