■河南 劉進京
某商品銷售網站出現了查詢緩慢的問題,當執行商品信息查詢時,需要等待十幾秒才出現回應信息,這給用戶正常訪問帶來了不便。
該網站采用了Python Flask 開發,后臺使用的是MySQL 數據庫,所有的商品信息都存儲在MySQL 數據庫中,用戶或應用可以通過MySQL接口,根據提供的商品信息,來查詢對應的數據。但是過于緩慢的查詢速度,讓網站無法正常為外界提供服務。
登錄到Web 服務器上,對系統狀態進行檢測,發現當執行任意命令后,都會稍微停頓一下才看到回應信息,可以明顯察覺系統響應速度變慢。
對于該情況,首先查看系統資源使用情況,檢測其是否存在異常狀態。執行“top”命令,在返回信息中可以看到,兩個CPU 的iowait 值都比較高,尤其對于CPI0 來說,其iowait 的值已經超過70%。但是對于各個進程進行查看,其CPU 占用率并不高。
根據以上分析,CPU 的使用情況還算比較正常,接下來需要排查I/O 使用情況,檢測問題是都磁盤的I/O 瓶頸有關。執行“iostat”命令,根據返回信息,發現磁盤每秒的讀取速率較高,I/O 使用率為98%,幾乎處于完全飽和的狀態。這表明磁盤的讀取出現了瓶頸。
接著需要找到究竟是什么進程造成了這么高的磁盤讀取情況。執行“pidstat-d 1”命令,顯示進程的I/O使用情況。
在返回信息中看到,對于PID 為28716 的進程來說,在其“kB_rd/s”列中顯示較高的磁盤讀取值,這說明該進程正在進行大量的讀操作,其讀取的速率和上述執行“iostat”命令檢測到的數值基本一致,該PID 對應的是“mysqld”進程。這說明“mysqld”進程引發了I/O 瓶頸問題。
但是,該進程為什么要去讀取大量的磁盤數據呢,這很有可能和MySQL 的慢查詢有關。對于慢查詢來說,其造成的故障現象基本上都會CPU 使用率過高的問題,一般并不會引發I/O 瓶頸。
要想對MySQL 讀取數據情況進行分析,需要使用到Strace 命令。我們知道,Strace 是一個可用于診斷、調試和教學的Linux 用戶空間跟蹤器。可以利用該命令來監控用戶空間進程和內核的交互,比如系統調用、信號傳遞、進程狀態變更等。因為MySQL 是一個支持多線程的數據庫工具,因此需要對其使用到的線程進行全面分析。
執 行“strace -f -p 28716”命令,在返回信息中顯示和MySQL 相關的所有線程的數據讀取信息,可以看到PID 為21970 的線程正在讀取大量的磁盤數據,其讀取的文件描述符編號為91,接著需要查找與該編號對應的文件。
執行“lsof -p 21970”命令,奇怪的是并沒有返回任何信息,說明沒有找到對應的文件。執行“echo $?”命令,得到的返回值為“1”,這說明上述命令執行失敗,因為只有返回“0”,才說明沒有問題。這表明MySQL 使用了很多線程,僅僅使用其中一個線程號進行查詢是沒有辦法找到目標文件的。
執行“lsof -p 28716”命令,使用MySQL 的進程的PID 進行查看,在返回信息中顯示MySQL 打開了很多的文件,根據上述文件描述符的編號,發現與其對應的文件為“/var/lib/data/products.myd”,注意MySQL是以讀寫方式訪問該文件的。
根據這些信息,說明使用了MyISAM 引擎來存儲數據。即MySQL 在讀取“DATA”數據庫中的“products”數據表。進入該目錄,執行“ls”命令,可以顯示相關的表文件,索引文件,元數據文件和原信息文件等。
進入MySQL 控制臺,執行“show global variables like "%datadir% ";”命令,可以看到MySQL 當前正在使用的數據庫路徑,這和上述查詢到的信息一致。執行“show full processlist;”命令,在返回信息中顯示當前的用戶名、主機名、使用的數據庫名稱、執行的命令類型,執行的時間、狀態、執行的語句等信息,例如在“Command”列中顯示“query”,表示執行的額是查詢操作,在“Info”列中顯示具體的查詢語句。
對于MySQL 的慢查詢來說,如果沒有設置好索引的話,很容易出現查詢緩慢的問題。執行“use data;”,“explain xxx;”命令,其中的“xxx”為在上述命令中找到的查詢命令。
在返回信息中的“select_type”列中顯示查詢的類型,這里顯示為“simple”,表示查詢中沒有包括“union”查詢或者子查詢,在“table”列中顯示數據表的名稱,在“type”列中顯示查詢類型,在“possible_keys”列中顯示可能引用的索引,這里為“NULL”。在“key”列中顯示確切使用的索引,這里為“NULL”。在“rows”列中顯示掃描的行數。
根據以上分析,這里并沒有使用索引,當掃描的行數很多時,自然會出現反應遲緩的問題。
看來,只要為其配置好索引,問題就可以解決了。執 行“CREATE INDEX products_index ON products(produceID);”命令,為該表添加索引,但是出現“EEEOR 1170”的錯誤信息,這說明必須為該字段設置一個合適的前綴長度。
執行“CREATE INDEX products_index ONproducts(product ID(64));”命令,使用一個合適的的數值來充當前綴,順利為該表創建索引。這樣,再執行查詢時,可以看到速度大大提高了。
根據以上分析,不難發現對于MySQL慢查詢來說,如果沒有設置索引的話,很容易出現查詢緩慢的問題。其實,對于MySQL 的MyISAM 引擎來說,其主要依靠系統緩存來加速磁盤的I/O 訪問,如果系統中存在其他的進程,必然會占用一定的系統緩存,就會造成MyISAM引擎引擎難以充分利用系統緩存,自然會造成運行緩慢等問題。
所以,不要將MySQL 等程序的的性能優化完全建立在系統緩存之上,最好能夠在應用程序內部分配內存,組建完全自主控制的緩存管理機制。