當表達到幾十萬條時, 使用limit語句分頁查詢將會出現問題.查詢比較靠前的數據還好,但要是比較靠後的數據就會出現變得非常慢.
需求: 有一個上百萬行的大表, 需要按照其時間字段順序分頁讀取, 該字段的順序與主鍵id順序並不一致. 而且該字段會有重複. 對於大表的分頁網上方法是按照主鍵id順序分頁, 所以可以利用到主鍵id的遞增特性達到效果: 如下面演示:
select * from table_name order by id asc limit 500000,10; //普通分頁語句
//轉換後的, 該語句查詢非常快
select * from table_name where id > (select id from table_name order by id asc limit 500000,10) limit 10
但是本需求中, 顯然無法利用這個特性, 因其有重複值.
解決方案:
- 首先肯定是先對時間字段(假設該字段名爲create_time)加上索引了, 加上索引後limit 0,10這種查詢靠前的語句就非常快, 但是limit 500000,10語句這種查詢靠後數據的查詢就慢得令人髮指, 一般需要好幾秒. explain查詢計劃可以看到limit 50000.10這個語句mysql要先掃描前500000行後才能讀到數據,所以這就非常慢了.
- 在步驟1上繼續優化.
//對於幾十萬表基本都能在1s內查出來 select * from table_name where id in (SELECT id from (select id from table_name LIMIT 500000,10) as tmp) // 1.其中使用(SELECT id from (select id from table_name LIMIT 500000,10) as tmp) 這種繁瑣的寫法是爲了適應mysql5.6不支持子查詢語句中有limit操作.當然你也可以將上述語句拆分成 兩條,先執行子查詢獲取id結果集, 然後在用id in id結果集語句獲取最終結果也可. // 2.該句子主要時間花費在(SELECT id from (select id from table_name LIMIT 500000,10) as tmp) 子查詢上, 而 id in的查詢是非常快的,自己測試只要零點零幾秒. // 3.最後要強調的是以上例子寫法比較簡化,查詢只取出自己需要的列,不要列就不要取出,因爲取出不需要的列 時,傳送數據的時間會加大以及其他的一些開銷,尤其是無用的列很多很大時. 所以select *寫法要根據你 的業務需要調整,有時候這個方面的耗時還是很大的,一定引起重視.
錦上添花的解決方案(利用mysql本身的查詢緩存, 可只針對某些語句緩存)
使用條件: (首先要是已經正在使用mysql查詢緩存,那麼這部分就可不看了)如果你要優化的這個查詢語句基本上是不變的, 例如你應用的一些公共數據(如首頁), 那麼這些只需要查詢一遍緩存起來,後續相同的查詢就可以直接使用,這樣速度是非常快的. 正好mysql是提供了查詢緩存這個功能, 什麼?你只要緩存一些特定的語句, 不想因優化一條語句而整個應用的所有語句都用上查詢緩存, 要知道查詢也是有開銷的, 對於一些語句是沒有幫助甚至會使其變慢. 湊巧, mqsql也支持只對指定的語句進行緩存其他語句不受影響.
mysql的查詢緩存初步瞭解:
mysql的查詢緩存功能由query_cache_type系統變量控制,有如下取值:
OFF:表示不開啓查詢
ON: 表示對所有語句都開啓查詢緩存, 除非sql語句以SELECT SQL_NO_CACHE開頭明確表示不對該句子緩存
DEMAND: 當語句以select SQL_CACHE開頭的纔會緩存
代碼實現
1.找到你的mysql配置文件在 [mysqld] 塊下添加以下內容
[mysqld]
query_cache_type=2
2. 然後重啓你的mysql服務, 進入mysql命令行,執行如下命令
show variables like "query_cache_type"; //再次確認下查詢緩存是否開啓
show variables like "query_cache_size"; //確認查詢緩存大小,注意需要大於0
set global query_cache_size = 1048576 //設置查詢緩存大小,可設置的值爲1024倍數(單位byte)需要大於40k
3. 對於你要緩存的select語句使用 SELECT SQL_CACHE開頭, 以下舉個栗子:
SELECT SQL_CACHE * from table_name order by create_time desc limit 50000, 10;
當mysql看到SELECT SQL_CACHE開頭時會去查詢緩存中找有沒有緩存過,沒有則執行語句並將結果緩存起來以供後續相同的查詢使用.