10 從MySQL得到最大的性能
優化是一項復雜的任務,因為它最終需要對整個系統的理解。當用你的系統/應用的小知識做一些局部優化是可能的時候,你越想讓你的系統更優化,你必須知道它也越多。
因此,本章將試圖解釋並給出優化MySQL的不同方法的一些例子。但是記住總是有某些(逐漸變難)是系統更快的方法留著去做。
10.1 優化概述
為瞭使一個系統更快的最重要部分當然是基本設計。你也需要知道你的系統將做這樣的事情,那就是你的瓶頸。
最常見的瓶頸是:
磁盤尋道。磁盤花時間找到一個數據,用在1999年的現代磁盤其平均時間通常小於10ms,因此理論上我們能大約一秒尋道 1000 次。這個時間用新磁盤提高很慢並且很難對一個表優化。優化它的方法是將數據散佈在多個磁盤上。
當磁盤在我們需要讀數據的正確位置時,磁盤讀/寫。用1999年的現代,一個磁盤傳輸類似10-20Mb/s。這必尋道更容易優化,因為你能從多個磁盤並行地讀。
CPU周期。當我們讀數據進內存時,(或如果它已經在那裡)我們需要處理它以達到我們的結果。當我們有相對內存較小的表時,這是最常見的限制因素,但是用小表速度通常不是問題。
內存帶寬。當CPU需要超出適合cpu緩存的數據時,緩存帶寬就成為內存的一個瓶頸。這是對大多數系統的一個不常見的瓶頸但是你應該知道它。
10.2 系統/編譯時和啟動參數的調節
我們以系統級的東西開始,因為這些決策的某一些很早就做好瞭。在其他情況下,快速瀏覽這部分可能就夠瞭,因為它對大收獲並不重要,但是有一個關於在這個層次上收獲有多大的感覺總是好的。
使用的缺省OS確實重要!為瞭最大程度地使用多CPU,應該使用Solaris(因為線程工作得確實不錯)或Linux(因為2.2本的核心又確實不錯的SMP支持)。而且在32位的機器上,Linux缺省有2G的文件大小限制。當新的文件系統被釋出時( XFS ),希望這不久被修正。
因為我們沒在很多平臺上運行生產MySQL,我們忠告你在可能選擇它前,測試你打算運行的平臺。
其他建議:
如果你有足夠的RAM,你能刪除所有交換設備。一些操作系統在某些情況下將使用一個SWAP設備,即使你有空閑的內存。
使用–skip-locking的MySQL選項避免外部鎖定。註意這將不影響MySQL功能,隻要它僅運行在一個服務器上。隻要在你運行myisamchk以前,記得要停掉服務器(或鎖定相關部分)。在一些系統上這個開關是強制的,因為外部鎖定不是在任何情況下都工作。當用MIT-pthreads編譯時,–skip-locking選項缺省為打開(on),因為flock()沒在所有的平臺上被MIT-pthreads充分支持。唯一的情況是如果你對同一數據運行MySQL服務器(不是客戶),你不能使用–skip-locking之時,否則對沒有先清掉(flushing)或先鎖定mysqld服務器的表上運行myisamchk。你仍然能使用LOCK TABLES/ UNLOCK TABLES,即使你正在使用–skip-locking。
10.2.1 編譯和鏈接怎樣影響MySQL的速度
大多數下列測試在Linux上並用MySQL基準進行的,但是它們應該對其他操作系統和工作負載給出一些指示。
當你用-static鏈接時,你得到最快的可執行文件。使用Unix套接字而非TCP/IP連接一個數據庫也可給出好一些的性能。
在Linux上,當用pgcc和-O6編譯時,你將得到最快的代碼。為瞭用這些選項編譯“sql_yacc.cc”,你需要大約200M內存,因為gcc/pgcc需要很多內存使所有函數嵌入(inline)。在配置MySQL時,你也應該設定CXX=gcc以避免包括libstdc++庫(它不需要)。
隻通過使用一個較好的編譯器或較好的編譯器選項,在應用中你能得到一個10-30%的加速。如果你自己編譯SQL服務器,這特別重要!
在Intel上,你應該例如使用pgcc或Cygnus CodeFusion編譯器得到最大速度。我們已經測試瞭新的 Fujitsu編譯器,但是它是還沒足夠不出錯來優化編譯MySQL。
這裡是我們做過的一些測量表:
如果你以-O6使用pgcc並且編譯任何東西,mysqld服務器是比用gcc快11%(用字符串99的版本)。
如果你動態地鏈接(沒有-static),結果慢瞭13%。註意你仍能使用一個動態連接的MySQL庫。隻有服務器對性能是關鍵的。
如果你使用TCP/IP而非Unix套接字,結果慢7.5%。
在一個Sun SPARCstation 10上,gcc2.7.3是比Sun Pro C++ 4.2快13%。
在Solaris 2.5.1上,在單個處理器上MIT-pthreads比帶原生線程的Solaris慢8-12%。以更多的負載/cpus,差別應該變得更大。
由TcX提供的MySQL-Linux的分發用pgcc編譯並靜態鏈接。
10.2.2 磁盤問題
正如前面所述,磁盤尋道是一個性能的大瓶頸。當數據開始增長以致緩存變得不可能時,這個問題變得越來越明顯。對大數據庫,在那你或多或少地要隨機存取數據,你可以依靠你將至少需要一次磁盤尋道來讀取並且幾次磁盤尋道寫入。為瞭使這個問題最小化,使用有低尋道時間的磁盤。
為瞭增加可用磁盤軸的數量(並且從而減少尋道開銷),符號聯接文件到不同磁盤或分割磁盤是可能的。
使用符號連接
這意味著你將索引/數據文件符號從正常的數據目錄鏈接到其他磁盤(那也可以被分割的)。這使得尋道和讀取時間更好(如果磁盤不用於其他事情)。見10.2.2.1 使用數據庫和表的符號鏈接。
分割
分割意味著你有許多磁盤並把第一塊放在第一個磁盤上,在第二塊放在第二個磁盤上,並且第 n塊在第(n mod number_of_disks)磁盤上,等等。這意味著,如果你的正常數據大小於分割大小(或完美地排列過),你將得到較好一些的性能。註意,分割是否很依賴於OS和分割大小。因此用不同的分割大小測試你的應用程序。見10.8 使用你自己的基準。註意對分割的速度差異很依賴於參數,取決於你如何分割參數和磁盤數量,你可以得出以數量級的不同。註意你必須選擇為隨機或順序存取優化。
為瞭可靠,你可能想要使用襲擊RAID 0+1(分割+鏡像),但是在這種情況下,你將需要2*N個驅動器來保存N個驅動器的數據。如果你有錢,這可能是最好的選擇!然而你也可能必須投資一些卷管理軟件投資以高效地處理它。
一個好選擇是讓稍重要的數據(它能再生)上存在RAID 0磁盤上,而將確實重要的數據(像主機信息和日志文件)存在一個RAID 0+1或RAID N磁盤上。如果因為更新奇偶位你有許多寫入,RAID N可能是一個問題。
你也可以對數據庫使用的文件系統設置參數。一個容易的改變是以noatime選項掛裝文件系統。這是它跳過更新在inode中的最後訪問時間,而且這將避免一些磁盤尋道。
10.2.2.1 為數據庫和表使用符號鏈接
你可以從數據庫目錄移動表和數據庫到別處,並且用鏈接到新地點的符號代替它們。你可能想要這樣做,例如,轉移一個數據庫到有更多空閑空間的一個文件系統。
如果MySQL註意到一個表是一個符號鏈接,它將解析符號鏈接並且使用其實際指向的表,它可工作在支持realpath()調用的所有系統上(至少Linux和Solaris支持realpath())!在不支持realpath()的系統上,你應該不同時通過真實路徑和符號鏈接訪問表!如果你這樣做,表在任何更新後將不一致。
MySQL缺省不支持數據庫鏈接。隻要你不在數據庫之間做一個符號鏈接,一切將工作正常。假定你在MySQL數據目錄下有一個數據庫db1,並且做瞭一個符號鏈接db2指向db1:
shell> cd /path/to/datadir
shell> ln -s db1 db2
現在,對在db1中的任一表tbl_a,在db2種也好象有一個表tbl_a。如果一個線程更新db1.tbl_a並且另一個線程更新db2.tbl_a,將有問題。
如果你確實需要這樣,你必須改變下列在“mysys/mf_format.c”中的代碼:
if (!lstat(to,&stat_buff)) /* Check if its a symbolic link */
if (S_ISLNK(stat_buff.st_mode) && realpath(to,buff))
把代碼改變為這樣:
if (realpath(to,buff))
10.2.3 調節服務器參數
你能用這個命令得到mysqld服務器缺省緩沖區大小:
shell> mysqld –help
這個命令生成一張所有mysqld選項和可配置變量的表。輸出包括缺省值並且看上去象這樣一些東西:
Possible variables for option –set-variable (-O) are:
back_log current value: 5
connect_timeout current value: 5
delayed_insert_timeout current value: 300
delayed_insert_limit current value: 100
delayed_queue_size current value: 1000
flush_time current value: 0
interactive_timeout current value: 28800
join_buffer_size current value: 131072
key_buffer_size current value: 1048540
lower_case_table_names current value: 0
long_query_time current value: 10
max_allowed_packet current value: 1048576
max_connections current value: 100
max_connect_errors current value: 10
max_delayed_threads current value: 20
max_heap_table_size current value: 16777216
max_join_size current value: 4294967295
max_sort_length current value: 1024
max_tmp_tables current value: 32
max_write_lock_count current value: 4294967295
net_buffer_length current value: 16384
query_buffer_size current value: 0
record_buffer current value: 131072
sort_buffer current value: 2097116
table_cache current value: 64
thread_concurrency current value: 10
tmp_table_size current value: 1048576
thread_stack current value: 131072
wait_timeout current value: 28800
如果有一個mysqld服務器正在運行,通過執行這個命令,你可以看到它實際上使用的變量的值:
shell> mysqladmin variables
每個選項在下面描述。對於緩沖區大小、長度和棧大小的值以字節給出,你能用於個後綴“K”或“M” 指出以K字節或兆字節顯示值。例如,16M指出16兆字節。後綴字母的大小寫沒有關系;16M和16m是相同的