MySQL的SQL_CALC_FOUND_ROWS真的很慢么?
分頁程序一般由兩條SQL組成:
SELECT ... FROM ... WHERE LIMIT ...
如果使用SQL_CALC_FOUND_ROWS的話,一條SQL就可以了:
在得到數據后,通過FOUND_ROWS()可以得到不帶LIMIT的結果數:
看上去,似乎SQL_CALC_FOUND_ROWS應該快于COUNT(*),但實際情況并不是這樣簡單,請看:
To SQL_CALC_FOUND_ROWS or not to SQL_CALC_FOUND_ROWS?
用數據說話,證明了COUNT(*)相對SQL_CALC_FOUND_ROWS來說更快。不過我覺得這個結論也不全面,某些情況下,SQL_CALC_FOUND_ROWS更有優勢,看我的實驗:
表結構如下:
`a` int(10) unsigned NOT NULL AUTO_INCREMENT,
`b` int(10) unsigned NOT NULL,
`c` varchar(100) NOT NULL,
PRIMARY KEY (`a`),
KEY `bar` (`b`,`a`)
) ENGINE=MyISAM;
導入一些測試數據:
mysql_query("INSERT INTO foo SET b=ROUND(RAND()*10), c=MD5({$i})");
}
先測試COUNT(*)方式:
for ($i = 0; $i < 1000; $i++) {
mysql_query("SELECT SQL_NO_CACHE COUNT(*) FROM foo WHERE b = 1");
mysql_query("SELECT SQL_NO_CACHE a FROM foo WHERE b = 1 LIMIT 100, 10");
}
$end = microtime(true);
echo $end - $start;
結果輸出(數據大小視測試機性能而定):0.75777006149292
再測試SQL_CALC_FOUND_ROWS方式:
for ($i = 0; $i < 1000; $i++) {
mysql_query("SELECT SQL_NO_CACHE SQL_CALC_FOUND_ROWS a FROM foo WHERE b = 1 LIMIT 100, 10");
mysql_query("SELECT FOUND_ROWS()");
}
$end = microtime(true);
echo $end - $start;
結果輸出(數據大小視測試機性能而定):0.6681969165802
有數據有真相,那為什么我的實驗結論和MySQL Performance Blog的結論相悖呢?這是因為在MySQL Performance Blog的實驗里,COUNT(*)查詢是執行的的Covering Index,而SQL_CALC_FOUND_ROWS是執行的表查詢;而在我的實驗里,因為我定義了適當的索引,COUNT(*)和SQL_CALC_FOUND_ROWS都是執行的Covering Index,所以結論出現了差異。
既然使用了Covering Index,就意味著不能再使用SELECT *的形式了,只能使用類似SELECT id這樣的形式了,用的列在索引里都能查到,如此說來,我們需要的實際數據從哪來呢?這個很簡單,有了主鍵之后,實際數據可以通過Key/Value形式的緩存獲得,這樣的架構很常見。
結論:SQL_CALC_FOUND_ROWS如果執行的是Covering Index的話,是很快的!換個角度看,如果COUNT(*)和SQL_CALC_FOUND_ROWS都只能通過表查詢來檢索,那么分頁時,SQL_CALC_FOUND_ROWS同樣會快于COUNT(*),讀者可自行測試。