這是MySQL(和其他數據庫系統)的常見問題。使用LIMIT + OFFSET(這就是LIMIT x,y所隱含的使用方法),它起初效果很好,但隨着獲取行數的增長而呈指數級下降。 添加索引絕對是第一步,因爲您應該始終根據索引查詢數據,以避免全表掃描。
只有在價格上有索引是不夠的,因爲您有其他WHERE屬性。基本上,這是MySQL正在做的事情: 假設$limit = 25
和$startPoint = 0
,MySQL將從頭開始讀取表,並在找到25個匹配的行並停止後返回它們。我們假設它在第一次迭代中讀取500行。下一次迭代,因爲它沒有car + color + price
上的索引,它不知道如何直接跳轉到第25個匹配行(表中的第500行),因此它將從頭再次開始讀取,跳過前25個匹配行並返回25個匹配的行。我們假設這個迭代還需要讀取500個額外的行。
現在你看看發生了什麼問題。對於每一次迭代,MySQL都必須從頭開始讀取所有行,以指數方式增加返回行所需的時間。在我的示例中,要獲取100(25 * 4迭代)行,MySQL將不得不讀取500 + 1000 + 1500 + 2000 = 5000行,而您只能讀取500 * 4 = 2,000行。要獲取1000(25 * 40迭代)行,MySQL將不得不讀取500 + 1000 + 1500 + ... 20000 = 410,000行!這比您預期的500 * 40 = 20000行要多得多。
要優化您的查詢,請首先選擇您需要的數據(沒有SELECT *
)。然後訣竅是記住最後提取的ID。
$lastFetchedId = 0;
do {
$sql = mysql_query("SELECT * FROM data WHERE id > $lastFetchedId AND (car = '$cars' AND color = '$color' AND price BETWEEN '".$min."' AND '".$max."')
ORDER BY price LIMIT {$limit}");
$hasFoundRows = false;
while ($row = mysql_fetch_assoc($sql)) {
$hasFoundRows = true;
$lastFetchedId = $row['id'];
// do something with the row
}
} while ($hasFoundRows === false);
只有當您在WHERE子句中使用的所有列都有索引時,纔有MySQL處理排序的效果。這樣考慮一下:如果數據沒有被排序,MySQL如何知道哪些行將匹配以及匹配行的位置。爲了能夠對結果進行排序並僅返回一個子集,MySQL需要構建一個所有實際匹配行的排序列表。這意味着要遍歷整個表首先獲取所有匹配的行,然後對它們進行排序,最後只返回其中的一小部分。
希望幫助你更好地理解什麼,你可以做的更好在這裏:)
我犯了一個單獨的指數價格,它只是增加的負載 – Matt