當你執行管理員優化的時候,應該緊記以下規則:
· 訪問內存中的數據快於訪問磁盤上的數據。
· 盡量把數據保存在內存中可以減少磁盤操作。
· 保留索引中的信息比保留數據記錄的內容更重要。
我們在後面將討論如何應用這些規則。
增加服務器緩存的大小。服務器擁有很多參數(系統變量),你可以改變這些參數來影響服務器的操作。其中的幾個參數直接地影響查詢處理的速度。你可以改變的最重要的參數是數據表緩存的大小和存儲引擎用於緩沖索引操作信息的緩存大小。如果你擁有可用的內存,就把它分配給服務器的緩存,以允許信息存儲在內存中並減少磁盤操作。這會有很好的效果,因為訪問內存中的信息比從磁盤讀取信息的速度快得多。
· 當服務器打開表文件的時候,它試圖保持這些文件的打開狀態,以減少打開文件操作的數量。為了實現這樣的功能,它在表緩存中維護打開文件的信息。table_cache系統變量控制著這個緩存的大小。如果服務器訪問了大量的表,表緩存就會被填滿,並且服務器會關閉那些有一段時間沒有使用的表,為打開新表留出空間。你可以通過檢查Opened_tables狀態指示器來訪問表緩存的效果:
SHOW STATUS LIKE ’Opened_tables’;
Opened_tables顯示了某個數據表必須打開的次數(因為它還沒有打開)。這個值也顯示為MySQLadmin狀態命令的輸出信息中的Opens值。如果這個數字是穩定的或緩慢增長,那麼它的設置可能是正確的。如果這個數字增長得很快,就意味著這個緩存太小了,必須經常關閉數據表來為打開其它的數據表留出空間。如果你擁有文件描述信息,增加表緩存大小將減少數據表打開操作的數量。
· MyISAM存儲引擎使用鍵緩沖來保持與索引相關的操作的索引信息塊。它的大小是由key_buffer_size系統變量控制的。這個值越大,MySQL就一次性在內存中保持更多的索引信息塊,可以增加在內存中(而不用從磁盤上讀取新的信息塊)找到鍵值的可能性。鍵緩存的默認大小是8MB。如果你擁有很多的內存,這是一個很保守的值,你可以直接增加它的大小,並且會看到基於索引的檢索、索引的建立和修改操作的性能有很大改善。
在MySQL 4.1以上版本中,你可以為MyISAM數據表建立附加的鍵緩存,並指定某些表使用它們。這樣可以幫助提高這些數據表上的查詢處理速度。
· InnoDB和BDB引擎擁有自己的用於緩沖數據和索引值的緩存。它們的大小是由innodb_buffer_pool_size和bdb_cache_size變量控制的。InnoDB引擎還維護了一個日志緩沖。innodb_log_buffer_size變量可以控制它的大小。
· 另一個專用的緩存是查詢緩存,我們在"使用查詢緩存"部分中解釋。
當你改變這些參數值的時候,應該遵循下面一些原則:
· 每次只改變一個參數。如果你一次改變多個相互獨立的變量,那麼就很難評估每種改變的效果了。
· 逐漸地增加系統變量值。根據理論,數量越多,性能越好,但是如果你使某個變量變得太大了,有可能造成系統資源匮乏,導致逆向效果,降低速度。
· 不要在運行業務MySQL數據庫的服務器上做調整參數的實驗,最好建立一個獨立的測試服務器。
· 為了大致了解哪種參數變量可能適合自己的系統,你可以查看MySQL發布文檔中包含的my-small.cnf、my-medium.cnf、my-large.cnf和my-huge.cnf選項文件(在Unix系統上,你可以在源發布文件的支持文件目錄和二進制發布文件的共享目錄總找到這些文件。在Windows上,它們位於基本的安裝目錄中,其擴展名可能是.ini)。這些文件可能讓你知道最好改變服務器上的那些參數以適應不同的使用層次,並且為這些參數提供了一些典型值。
用於提高服務器的操作性能的其它一些策略還包括:
禁止不需要的存儲引擎。服務器不會為禁止的引擎分配任何內存,因此我們可以利用這一點。如果從源文件建立MySQL,那麼在配置的時候,大多數存儲引擎就可以被排除在服務器之外。對於那些包含在服務器中的引擎來說,使用適當的啟動選項可以在運行時禁止其中的大多數。
保持授權表許可的簡單性。盡管服務器在內存中緩存了授權表內容,但是如果你在tables_priv或columns_priv表中有一些數據行的話,服務器就必須為每個查詢語句檢查表層次和列層次的權限。如果這些表是空的,那麼服務器就能優化自己的權限檢查過程,略過這些層次。
如果你從源文件建立MySQL,那麼就把它配置為使用靜態類庫,而不要使用共享類庫。使用共享類庫的動態二進制文件節約磁盤空間,然而靜態二進制文件速度更快。但是,如果你使用了用戶自定義函數(UDF)機制,那麼有些系統要求使用動態鏈接。在這類系統上,靜態二進制文件不能工作。
使用MyISAM鍵緩存
當MySQL執行某個利用了MyISAM數據表索引的語句的時候,它會使用鍵緩存來保持索引值。這種緩存減少了磁盤I/O:如果在緩存中找到了某個數據表需要的鍵值,就不需要再次從磁盤中讀取。不幸的是,這種鍵緩存是有限的,並且在默認情況下,它是所有的MyISAM數據表共享使用的。如果在鍵緩存中沒有找到鍵值並且鍵緩存是滿的,爭用將會導致:必須丟棄緩存中的某些值,為新值留出空間。如果下次需要那些已經被丟棄的值,就必須再次從磁盤上讀取。
如果你很倚重MyISAM數據表,那麼把它的鍵保存在內存中效果會很好,但是緩存中的爭用卻會導致相反的效果。從同一張表或不同的表讀取數據都可能引起爭用。你可以通過把鍵緩存設置成足以保存某個特定數據表的全部索引,從而避免同一張數據表的爭用,但是其它數據表的鍵仍然需要爭用緩存空間。
MySQL 4.1以上版本為這個問題提供了一種解決方案:它支持我們建立多個鍵緩存,並允許我們把某張數據表的索引指定並且預先裝入某個緩存。如果你的數據表使用得很頻繁,並且你有足夠的內存,能夠把它的索引載入緩存中,那麼這種操作就是有用的。這種能力允許你同時避免同一張表和不同的表的爭用:建立一個足夠大的緩存,讓它保存數據表的全部索引,並且指定該緩存專門用於那張數據表。在鍵被載入緩存之後,不在需要磁盤I/O操作。同時,鍵值永遠不會被丟棄,對數據表的鍵的查看操作可以在內存中完成。
下面的例子顯示了如何為sampdb數據庫的member數據表建立一個鍵緩存,該緩存的名稱是member_cache,大小為1MB。執行這些指令的時候,你必須有超級(SUPER)權限。
1.建立一個足夠容納數據表索引的獨立的緩存:
MySQL> SET GLOBAL member_cache.key_buffer_size = 1024*1024;
2.給數據表指定鍵緩存:
MySQL> CACHE INDEX member IN member_cache;
+---------------+--------------------+----------+----------+
| Table | Op | Msg_type | Msg_text |
+---------------+--------------------+----------+----------+
| sampdb.member | assign_to_keycache | status | OK |
+---------------+--------------------+----------+----------+
3.把數據表索引預先讀入它的鍵緩存中:
MySQL> LOAD INDEX INTO CACHE member;
+---------------+--------------+----------+----------+
| Table | Op | Msg_type | Msg_text |
+---------------+--------------+----------+----------+
| sampdb.member | preload_keys | status | OK
|+---------------+--------------+----------+----------+
如果你希望把其它的數據表載入同一個緩存中,或者為其它的數據表建立鍵緩存,上面的操作就足夠了。
使用查詢緩存
MySQL服務器可以使用查詢緩存來提高那些重復執行的SELECT語句的處理速度。它對性能的提高通常都是驚人的。查詢緩存的工作方式如下所示:
· 第一次執行某條SELECT語句的時候,服務器記住該查詢的文本內容和它返回的結果。
· 服務器下一次碰到這個語句的時候,它不會再次執行該語句。作為代替,它直接從查詢緩存中的得到結果並把結果返回給客戶端。
· 查詢緩存是基於服務器所接收到的查詢字符串的文本內容的。如果某些查詢的文本完全相同,那些它就認為這些查詢是相同的。如果某些查詢的字符不同,或者來自那些使用了不同的字符集或通訊協議的客戶端,那麼它會認為這些查詢是不同的。同樣,如果某些查詢采用其它的功能相當、但是實際上沒有指向相同的數據表(例如引用了不同的數據庫中的同名數據表),那麼它們也是不同的。
· 當數據表被更新了之後,涉及到該數據表的任何緩存查詢都變成無效的,並且會被丟棄。這可以防止服務器返回過期的結果。
在默認情況下,MySQL對查詢緩存的支持是內建的。如果你不希望使用這種緩存,並且想避免它所導致的性能開銷,可以使用--without-query-cache選項來運行配置腳本建立服務器。
如果需要檢測某個服務器是否支持查詢緩存,可以檢查它的have_query_cache系統變量:
MySQL> SHOW VARIABLES LIKE ’have_query_cache’;
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| have_query_cache | YES |
+------------------+-------+
對於那些支持查詢緩存的服務器來說,緩存的操作是基於三個系統變量值的:
· query_cache_type決定查詢緩存的操作模式。下表顯示了可以使用的模式值:
模式 含義
0 不要緩存查詢結果或檢索緩存的結果。
1 緩存查詢,除非它們以SELECT SQL_NO_CACHE開頭。
2 根據需要只緩存那些以SELECT SQL_CACHE開頭的查詢。
· query_cache_size決定分配給緩存的內存數量,單位是字節。
· query_cache_limit設置被緩存的最大結果集大小;比這個值大的查詢結果不會被緩存。
例如,為了激活查詢緩存並為它分配16MB內存,在配置文件中使用下面的設置:
[MySQLd]
query_cache_type=1
query_cache_size=16M
即使query_cache_type的值設置為零,query_cache_size指定內存數量也會被分配。為了避免浪費內存,只有在希望激活緩存的時候才把大小設置成大於零。同時,即使query_cache_type不為零,查詢緩存的大小設置為零也會禁用緩存。
使用了查詢緩存的獨立客戶端會在服務器的默認緩存模式狀態下操作。客戶端可以使用下面的語句改變自己的查詢的默認緩存模式:
SET query_cache_type = val;
其中的val可以是0、1或2,它的意義與設置服務器啟動時的query_cache_type變量的意義是相同的。在SET語句中,OFF、ON和DEMAND這些符號值與0、1和2對應。
客戶端還可以通過在SELECT關鍵字後天添加調節符來控制個別查詢的緩存操作。如果緩存模式是ON或DEMAND,那麼SELECT SQL_CACHE語句會讓查詢結果被緩存。SELECT SQL_NO_CACHE語句會使查詢結果不被緩存。
如果某些查詢從經常改變的數據表中檢索信息,那麼抑止對這些查詢的緩存操作是有用的。在這種情況下,緩存未必有多大用處。假設你把Web服務器請求的日志存儲在MySQL數據表中,同時周期性地運行該數據表上的一組統計查詢。對於很繁忙的Web服務器來說,會頻繁地出現新行插入該數據表的操作,因此該數據表的任何緩存了的查詢結果很快就變成無效的了。其含義是,盡管你周期性地提交統計查詢,但是查詢緩存可能對這些查詢沒有什麼價值。在這種情況下,最好使用SQL_NO_CACHE調節符告訴服務器不要緩存這些查詢的結果。
硬件問題
本文前面的部分中討論的幫助你提高服務器性能的技術是沒有考慮硬件配置的。你當然可以通過使用更好的硬件來讓服務器運行地更快。但是並非所有的與硬件相關的改變都有相同的價值。當我們評估哪些硬件提高了性能的時候,最重要的原則與調整服務器參數的原則是相同的:盡可能地把最多的信息放在最快的存儲中,並讓這些信息盡可能地保持在該存儲中。
你可以改變幾種硬件配置來提升服務器的性能:
在計算機上安裝更多的內存。這可以讓你把服務器的緩存和緩沖區大小值配置成更大的,從而使數據保存在內存中的時間更長,從磁盤上讀取信息的需要更少。
重新配置系統,如果你擁有足夠的內存,能夠在內存文件系統中執行全部的交換操作,那麼就刪除所有的磁盤交換設備。否則,即使你擁有足夠的用於交換操作的RAM,某些系統仍然會跟磁盤進行交換操作。
增加更快的磁盤以改善I/O等待時間。在這種情況下,尋道時間是有代表性的主要的性能決定因素。橫向移動磁頭的速度比較慢,在磁頭定位以後,從磁道上讀取信息塊的速度相對較快。但是,如果需要選擇是添加更多的內存還是更快的磁盤,那麼最好選擇添加更多的內存。內存總是比磁盤快,而且添加內存可以讓你使用更大的緩存,從而減少磁盤活動。
通過在物理設備上劃分磁盤活動來獲取並行操作的優勢。如果你可以在多個物理設備上劃分讀操作和寫操作,那麼其速度就會比從同一個設備讀寫要快一些。例如,如果你把數據庫存儲在一個設備上,把日志存儲在另一個設備上,那麼同時向兩個設備寫入信息的速度就比數據庫和日志共享同一個設備的速度要快。請注意,使用同一個物理設備上的不同分區不算是並行操作。這是沒有好處的,因為它們仍然需要爭用相同的物理資源(磁頭)。
在把數據重新部署到另外一個設備之前,你要確保自己知道系統的負載狀況。如果在某個特定的物理設備上正在運行一些重要的業務,那麼把數據庫放在該設備上有可能使性能更差。例如,如果你正在處理大量的Web業務,同時把數據庫移動到Web服務器文檔目錄所在的設備上,就可能感覺不到任何優勢。
使用RAID設備可以讓你獲取並行操作的優勢。
使用多處理器硬件。對於類似MySQL服務器的多線程應用程序來說,多處理器硬件可以同時執行多個線程。