mysql之limit分頁優(yōu)化

準(zhǔn)備工作


# 總記錄數(shù)為500000
mysql> select count(id) from edu_test;
+-----------+
| count(id) |
+-----------+
|    500000 |
+-----------+
1 row in set (0.05 sec)

分析過程

從0開始查詢10條:

mysql> select * from edu_test limit 0, 10;

10 rows in set (0.05 sec)

從20萬開始查詢10條:

mysql> select * from edu_test limit 200000, 10;

10 rows in set (0.14 sec)

從50萬開始查詢10條:

mysql> select * from edu_test limit 499000, 10;

10 rows in set (0.21 sec)
  • 現(xiàn)象:隨著分頁越深入盈简,查詢的時(shí)間也越來越長救氯。
mysql> explain select * from edu_test limit 200000, 10;
+----+-------------+----------+------------+------+---------------+------+---------+------+--------+----------+-------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra |
+----+-------------+----------+------------+------+---------------+------+---------+------+--------+----------+-------+
|  1 | SIMPLE      | edu_test | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 499483 |   100.00 | NULL  |
+----+-------------+----------+------------+------+---------------+------+---------+------+--------+----------+-------+
1 row in set (0.09 sec)
  • 思考:limit分頁做了一個(gè)全表掃描驾诈,掃描后將從200000開始往后取10條記錄返回凤瘦。

優(yōu)化


思路:

  • 快速定位到要訪問的數(shù)據(jù)行,縮小掃描范圍拂封。
    方案1
  • 延遲查詢(先定位再查詢).
    方案2茬射、方案3

方案1:通過有序唯一索引縮小掃描范圍
前提必須要id有序,要不然結(jié)果會漏掉一部分?jǐn)?shù)據(jù)的烘苹。

mysql> select * from edu_test where id > 499000 order by id asc limit 10;

10 rows in set (0.14 sec)

mysql> explain select * from edu_test where id > 499000 order by id asc limit 10;
+----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table    | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | edu_test | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL | 1000 |   100.00 | Using where |
+----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set (0.16 sec)
# 再縮小掃描范圍
mysql> select * from edu_test where id between 499000 and 499020 order by id asc limit 10;

10 rows in set (0.09 sec)

mysql> explain select * from edu_test where id between 499000 and 499020 order by id asc limit 10;
+----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table    | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | edu_test | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL |   21 |   100.00 | Using where |
+----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set (0.08 sec)

方案2:子查詢

mysql> SELECT * FROM edu_test WHERE id >=  (SELECT id FROM edu_test ORDER BY id LIMIT 499000, 1) LIMIT 10;

10 rows in set (0.16 sec)

mysql> explain SELECT * FROM edu_test WHERE id >=  (SELECT id FROM edu_test ORDER BY id LIMIT 499000, 1) LIMIT 10;
+----+-------------+----------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
| id | select_type | table    | partitions | type  | possible_keys | key     | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+----------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
|  1 | PRIMARY     | edu_test | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL |   1000 |   100.00 | Using where |
|  2 | SUBQUERY    | edu_test | NULL       | index | NULL          | PRIMARY | 4       | NULL | 499001 |   100.00 | Using index |
+----+-------------+----------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
2 rows in set (0.14 sec)

方案3:join查詢

mysql> select * from edu_test s, (select id from edu_test order by id limit 499000, 10) t where s.id = t.id;

10 rows in set (0.16 sec)

mysql> explain select * from edu_test s, (select id from edu_test order by id limit 499000, 10) t where s.id = t.id;
+----+-------------+------------+------------+--------+---------------+---------+---------+------+--------+----------+-------------+
| id | select_type | table      | partitions | type   | possible_keys | key     | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+------------+------------+--------+---------------+---------+---------+------+--------+----------+-------------+
|  1 | PRIMARY     | <derived2> | NULL       | ALL    | NULL          | NULL    | NULL    | NULL | 499010 |   100.00 | NULL        |
|  1 | PRIMARY     | s          | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | t.id |      1 |   100.00 | NULL        |
|  2 | DERIVED     | edu_test   | NULL       | index  | NULL          | PRIMARY | 4       | NULL | 499010 |   100.00 | Using index |
+----+-------------+------------+------------+--------+---------------+---------+---------+------+--------+----------+-------------+
3 rows in set (0.10 sec)

實(shí)際業(yè)務(wù)場景


  • 場景:
    在我們設(shè)計(jì)數(shù)據(jù)庫id的時(shí)候躲株,可能采用字符串格式、有順序的id镣衡,帶有一定的業(yè)務(wù)邏輯這樣的分布式id霜定。
  • 解決:
    如果我們分頁想要優(yōu)化時(shí)候,根據(jù)減少掃描思路廊鸥,可以通過where id like '10289%' 方式望浩,先縮小范圍再分頁。
  • 啟示:
    在設(shè)計(jì)數(shù)據(jù)庫id主鍵的時(shí)候惰说,盡量保持主鍵唯一且有序磨德,最好能解決熱點(diǎn)業(yè)務(wù)問題(如果依賴很多的非主鍵值,那么我們可能還需要回表操作),而且主鍵本身就是一種唯一索引典挑,這種唯一有序特性可以便于幫助我們后期優(yōu)化酥宴,減少掃描記錄范圍。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末您觉,一起剝皮案震驚了整個(gè)濱河市拙寡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌琳水,老刑警劉巖肆糕,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異在孝,居然都是意外死亡诚啃,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進(jìn)店門私沮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來始赎,“玉大人,你說我怎么就攤上這事顾彰〖模” “怎么了?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵涨享,是天一觀的道長筋搏。 經(jīng)常有香客問我,道長厕隧,這世上最難降的妖魔是什么奔脐? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮吁讨,結(jié)果婚禮上髓迎,老公的妹妹穿的比我還像新娘。我一直安慰自己建丧,他們只是感情好排龄,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著翎朱,像睡著了一般橄维。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拴曲,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天争舞,我揣著相機(jī)與錄音,去河邊找鬼澈灼。 笑死竞川,一個(gè)胖子當(dāng)著我的面吹牛店溢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播委乌,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼床牧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了遭贸?” 一聲冷哼從身側(cè)響起叠赦,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎革砸,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體糯累,經(jīng)...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡算利,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了泳姐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片效拭。...
    茶點(diǎn)故事閱讀 38,664評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖胖秒,靈堂內(nèi)的尸體忽然破棺而出缎患,到底是詐尸還是另有隱情,我是刑警寧澤阎肝,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布挤渔,位于F島的核電站,受9級特大地震影響风题,放射性物質(zhì)發(fā)生泄漏判导。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一沛硅、第九天 我趴在偏房一處隱蔽的房頂上張望眼刃。 院中可真熱鬧,春花似錦摇肌、人聲如沸擂红。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽昵骤。三九已至,卻和暖如春吩抓,著一層夾襖步出監(jiān)牢的瞬間涉茧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工疹娶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留伴栓,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像钳垮,于是被迫代替她去往敵國和親惑淳。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評論 2 349

推薦閱讀更多精彩內(nèi)容