MySQL如何優化大分頁查詢?
很多應用往往只展示最新或最熱門的幾條記錄,但為了舊記錄仍然可訪問,所以就需要個分頁的導航欄。然而,如何通過MySQL更好的實現分頁,始終是比較令人頭疼的問題。雖然沒有拿來就能用的解決辦法,但了解數據庫的底層或多或少有助于優化分頁查詢。
我們先從一個常用但性能很差的查詢來看一看。
SELECT *
FROM city
ORDER BY id DESC
LIMIT 0, 15
這個查詢耗時0.00sec。So,這個查詢有什么問題呢?實際上,這個查詢語句和參數都沒有問題,因為它用到了下面表的主鍵,而且只讀取15條記錄。
CREATE TABLE city (
id int(10) unsigned NOT NULL AUTO_INCREMENT,
city varchar(128) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB;
真正的問題在于offset(分頁偏移量)很大的時候,像下面這樣:
SELECT *
FROM city
ORDER BY id DESC
LIMIT 100000, 15;
上面的查詢在有2M行記錄時需要0.22sec,通過EXPLAIN查看SQL的執行計劃可以發現該SQL檢索了100015行,但最后只需要15行。大的分頁偏移量會增加使用的數據,MySQL會將大量最終不會使用的數據加載到內存中。就算我們假設大部分網站的用戶只訪問前幾頁數據,但少量的大的分頁偏移量的請求也會對整個系統造成危害。Facebook意識到了這一點,但Facebook并沒有為了每秒可以處理更多的請求而去優化數據庫,而是將重心放在將請求響應時間的方差變小。
對于分頁請求,還有一個信息也很重要,就是總共的記錄數。我們可以通過下面的查詢很容易的獲取總的記錄數。
SELECT COUNT(*)
FROM city;
然而,上面的SQL在采用InnoDB為存儲引擎時需要耗費9.28sec。一個不正確的優化是采用 SQL_CALC_FOUND_ROWS,SQL_CALC_FOUND_ROWS 可以在能夠在分頁查詢時事先準備好符合條件的記錄數,隨后只要執行一句 select FOUND_ROWS(); 就能獲得總記錄數。但是在大多數情況下,查詢語句簡短并不意味著性能的提高。不幸的是,這種分頁查詢方式在許多主流框架中都有用到,下面看看這個語句的查詢性能。
SELECT SQL_CALC_FOUND_ROWS *
FROM city
ORDER BY id DESC
LIMIT 100000, 15;
這個語句耗時20.02sec,是上一個的兩倍。事實證明使用 SQL_CALC_FOUND_ROWS 做分頁是很糟糕的想法。
下面來看看到底如何優化。文章分為兩部分,第一部分是如何獲取記錄的總數目,第二部分是獲取真正的記錄。
高效的計算行數
如果采用的引擎是MyISAM,可以直接執行COUNT(*)去獲取行數即可。相似的,在堆表中也會將行數存儲到表的元信息中。但如果引擎是InnoDB情況就會復雜一些,因為InnoDB不保存表的具體行數。
我們可以將行數緩存起來,然后可以通過一個守護進程定期更新或者用戶的某些操作導致緩存失效時,執行下面的語句:
SELECT COUNT(*)
FROM city
USE INDEX(PRIMARY);
獲取記錄
下面進入這篇文章最重要的部分,獲取分頁要展示的記錄。上面已經說過了,大的偏移量會影響性能,所以我們要重寫查詢語句。為了演示,我們創建一個新的表“news”,按照時事性排序(最新發布的在最前面),實現一個高性能的分頁。為了簡單,我們就假設最新發布的新聞的Id也是最大的。
CREATE TABLE news(
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(128) NOT NULL
) ENGINE=InnoDB;
一個比較高效的方式是基于用戶展示的最后一個新聞Id。查詢下一頁的語句如下,需要傳入當前頁面展示的最后一個Id。
SELECT *
FROM news WHERE id < $last_id
ORDER BY id DESC
LIMIT $perpage
查詢上一頁的語句類似,只不過需要傳入當前頁的第一個Id,并且要逆序。
SELECT *
FROM news WHERE id > $last_id
ORDER BY id ASC
LIMIT $perpage
上面的查詢方式適合實現簡易的分頁,即不顯示具體的頁數導航,只顯示“上一頁”和“下一頁”,例如博客中頁腳顯示“上一頁”,“下一頁”的按鈕。但如果要實現真正的頁面導航還是很難的,下面看看另一種方式。
SELECT id
FROM (
SELECT id, ((@cnt:= @cnt + 1) + $perpage - 1) % $perpage cnt
FROM news
JOIN (SELECT @cnt:= 0)T
WHERE id < $last_id
ORDER BY id DESC
LIMIT $perpage * $buttons
)C
WHERE cnt = 0;
通過上面的語句可以為每一個分頁的按鈕計算出一個offset對應的id。這種方法還有一個好處。假設,網站上正在發布一片新的文章,那么所有文章的位置都會往后移一位,所以如果用戶在發布文章時換頁,那么他會看見一篇文章兩次。如果固定了每個按鈕的offset Id,這個問題就迎刃而解了。Mark Callaghan發表過一篇類似的博客,利用了組合索引和兩個位置變量,但是基本思想是一致的。
如果表中的記錄很少被刪除、修改,還可以將記錄對應的頁碼存儲到表中,并在該列上創建合適的索引。采用這種方式,當新增一個記錄的時候,需要執行下面的查詢重新生成對應的頁號。
SET p:= 0;
UPDATE news SET page=CEIL((p:= p + 1) / $perpage) ORDER BY id DESC;
當然,也可以新增一個專用于分頁的表,可以用個后臺程序來維護。
UPDATE pagination T
JOIN (
SELECT id, CEIL((p:= p + 1) / $perpage) page
FROM news
ORDER BY id
)C
ON C.id = T.id
SET T.page = C.page;
現在想獲取任意一頁的元素就很簡單了:
SELECT *
FROM news A
JOIN pagination B ON A.id=B.ID
WHERE page=$offset;
還有另外一種與上種方法比較相似的方法來做分頁,這種方式比較試用于數據集相對小,并且沒有可用的索引的情況下—比如處理搜索結果時。在一個普通的服務器上執行下面的查詢,當有2M條記錄時,要耗費2sec左右。這種方式比較簡單,創建一個用來存儲所有Id的臨時表即可(這也是最耗費性能的地方)。
CREATE TEMPORARY TABLE _tmp (KEY SORT(random))
SELECT id, FLOOR(RAND() * 0x8000000) random
FROM city;
ALTER TABLE _tmp ADD OFFSET INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, DROP INDEX SORT,ORDER BY random;
接下來就可以向下面一樣執行分頁查詢了。
SELECT *
FROM _tmp
WHERE OFFSET >= $offset
ORDER BY OFFSET
LIMIT $perpage;
簡單來說,對于分頁的優化就是。。。避免數據量大時掃描過多的記錄。
1.對查詢進行優化,應盡量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。
2.應盡量避免在 where 子句中對字段進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如:select id from t where num is null可以在num上設置默認值0,確保表中num列沒有null值,然后這樣查詢:select id from t where num=0
3.應盡量避免在 where 子句中使用!=或<>操作符,否則引擎將放棄使用索引而進行全表掃描。
4.應盡量避免在 where 子句中使用or 來連接條件,否則將導致引擎放棄使用索引而進行全表掃描,如:select id from t where num=10 or num=20可以這樣查詢:select id from t where num=10 union all select id from t where num=20
5.in 和 not in 也要慎用,否則會導致全表掃描,如:select id from t where num in(1,2,3) 對于連續的數值,能用 between 就不要用 in 了:select id from t where num between 1 and 3
6.下面的查詢也將導致全表掃描:select id from t where name like ‘%李%'若要提高效率,可以考慮全文檢索。
7. 如果在 where 子句中使用參數,也會導致全表掃描。因為SQL只有在運行時才會解析局部變量,但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然 而,如果在編譯時建立訪問計劃,變量的值還是未知的,因而無法作為索引選擇的輸入項。如下面語句將進行全表掃描:select id from t where?num=@num可以改為強制查詢使用索引:select id from t with(index(索引名)) where?num=@num
8.應盡量避免在 where 子句中對字段進行表達式操作,這將導致引擎放棄使用索引而進行全表掃描。如:select id from t where num/2=100應改為:select id from t where num=100*2
9.應盡量避免在where子句中對字段進行函數操作,這將導致引擎放棄使用索引而進行全表掃描。如:select id from t where substring(name,1,3)='abc' ,name以abc開頭的id應改為:
select id from t where name like ‘abc%'
10.不要在 where 子句中的“=”左邊進行函數、算術運算或其他表達式運算,否則系統將可能無法正確使用索引。
11.在使用索引字段作為條件時,如果該索引是復合索引,那么必須使用到該索引中的第一個字段作為條件時才能保證系統使用該索引,否則該索引將不會被使用,并且應盡可能的讓字段順序與索引順序相一致。
12.不要寫一些沒有意義的查詢,如需要生成一個空表結構:select col1,col2 into #t from t where 1=0
這類代碼不會返回任何結果集,但是會消耗系統資源的,應改成這樣:?
create table #t(…)
13.很多時候用 exists 代替 in 是一個好的選擇:select num from a where num in(select num from b)
用下面的語句替換:?
select num from a where exists(select 1 from b where num=a.num)
14.并不是所有索引對查詢都有效,SQL是根據表中數據來進行查詢優化的,當索引列有大量數據重復時,SQL查詢可能不會去利用索引,如一表中有字段sex,male、female幾乎各一半,那么即使在sex上建了索引也對查詢效率起不了作用。
15. 索引并不是越多越好,索引固然可 以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率,因為 insert 或 update 時有可能會重建索引,所以怎樣建索引需要慎重考慮,視具體情況而定。一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有 必要。
16. 應盡可能的避免更新 clustered 索引數據列,因為 clustered 索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將導致整個表記錄的順序的調整,會耗費相當大的資源。若應用系統需要頻繁更新 clustered 索引數據列,那么需要考慮是否應將該索引建為 clustered 索引。
17.盡量使用數字型字段
找對象,什么叫做合適?
很多時候,我們在遇到心儀的人時,不知道該不該跟他在一起,是否能夠托付終身,今天我就來教大家判斷下找對象要找什么樣的才合適。
找對象一定要找價值觀相似,最好一致的,否則你們會有不斷的爭吵。在當今社會,門當戶對跟家庭的出身已經不是最主要的因素了,只要看得是你在過去5到10年有沒有共同的經歷。
如果你的家庭條件很不錯,從小到大都是父母幫你安排好的,包過你的工作各方面,那么你最好不好找家庭貧窮的,一個人來到這個城市辛苦打拼到現在混的不錯的男人。雖然你們可以結婚,但你們的思想不在一個層次上,很多情況下你們都不會達成一個共識,會產生很多矛盾。
如果你的家庭條件不錯,但你比較獨立,學習工作什么都是靠自己,那這個這樣的青年完全沒問題的。出身不同,但經歷差不多,可以相互依靠,相互支持。
所以在這里,我要說的是現在的門當戶對已經不是之前的那個樣子了,更重要的是看你們的經歷,你們的價值觀。三觀吻合對于現在來說才是最重要的。
如果你找到了那個跟你價值觀一致的人,那么恭喜你,找到了自己要找的那個人。
我覺得有共同興趣愛好,共同話題,共同的家庭差距,然后雙方家庭生活方式都幾乎相同的
news是什么意思
the country or world or in a particular area of activity 新聞 例 Foreign News is on page 1 all aout the oms on the news 我從新聞廣播里聽到了關于炸彈的所有消息。 4 NUN
news是可數名詞還是不可數名詞
country or world or in a particular area of activity 語法信息 Foreign News is on Page 1 news 6點鐘的新聞 搭配模式 the N 4 NINNAMES 用于報紙名新聞 News is so
哪里有student cnn news視頻下載
httpforumputclucomforumdisplayphpfid27page1普特聽力論壇里面有 voa c cnn npr的新聞我讀書大學時都用這個網站
要可以下載student cnn news視頻的可以用的有最新視頻的網址。謝謝了
任務形閱讀hundreds of years ago news was
many factors does the passage talk aout A Two B Three C Five D Six 4 A Which of the following is Not True of this passage A News stories on the front page o
arried from p
Open your ooks and turn to page 20 為什么這里的ook要加s
如 The Chinese are industries and rave 中國人民是勤勞勇敢的。 4以s結尾仍為單數的名詞如 a mathspoliticsphysics等學科名詞為不可數名詞是單數。 news 是不
誰知道中央民大新聞系本科生都學什么課程
httpljccnpagenewsaspid126 新聞學專業 培養目標 本專業培養德、智、體、 冷霜 20061038056 影視評論 劉淑欣 20061038060 中國古代文學史四 傅承洲 2006
如題請盡量詳細解答。