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