最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MySQL中l(wèi)imit對查詢語句性能的影響

 更新時間:2021年09月20日 13:34:00   投稿:WDC  
我們知道,當limit offset rows中的offset很大時,會出現效率問題,那么如果提高limit的執(zhí)行效率呢

一,前言 

首先說明一下MySQL的版本:

mysql> select version();
+-----------+
| version() |
+-----------+
| 5.7.17    |
+-----------+
1 row in set (.00 sec)

表結構:

mysql> desc test;
+--------+---------------------+------+-----+---------+----------------+
| Field  | Type                | Null | Key | Default | Extra          |
+--------+---------------------+------+-----+---------+----------------+
| id     | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| val    | int(10) unsigned    | NO   | MUL |        |                |
| source | int(10) unsigned    | NO   |     |        |                |
+--------+---------------------+------+-----+---------+----------------+
3 rows in set (.00 sec)

id為自增主鍵,val為非唯一索引。

灌入大量數據,共500萬:

mysql> select count(*) from test;
+----------+
| count(*) |
+----------+
|  5242882 |
+----------+
1 row in set (4.25 sec)

我們知道,當limit offset rows中的offset很大時,會出現效率問題:

mysql> select * from test where val=4 limit 300000,5;
+---------+-----+--------+
| id      | val | source |
+---------+-----+--------+
| 3327622 |   4 |      4 |
| 3327632 |   4 |      4 |
| 3327642 |   4 |      4 |
| 3327652 |   4 |      4 |
| 3327662 |   4 |      4 |
+---------+-----+--------+
5 rows in set (15.98 sec)

為了達到相同的目的,我們一般會改寫成如下語句:

mysql> select * from test a inner join (select id from test where val=4 limit 300000,5) b on a.id=b.id;
+---------+-----+--------+---------+
| id      | val | source | id      |
+---------+-----+--------+---------+
| 3327622 |   4 |      4 | 3327622 |
| 3327632 |   4 |      4 | 3327632 |
| 3327642 |   4 |      4 | 3327642 |
| 3327652 |   4 |      4 | 3327652 |
| 3327662 |   4 |      4 | 3327662 |
+---------+-----+--------+---------+
5 rows in set (.38 sec)

時間相差很明顯。

為什么會出現上面的結果?我們看一下select * from test where val=4 limit 300000,5;的查詢過程:

查詢到索引葉子節(jié)點數據。

根據葉子節(jié)點上的主鍵值去聚簇索引上查詢需要的全部字段值。

類似于下面這張圖:

像上面這樣,需要查詢300005次索引節(jié)點,查詢300005次聚簇索引的數據,最后再將結果過濾掉前300000條,取出最后5條。MySQL耗費了大量隨機I/O在查詢聚簇索引的數據上,而有300000次隨機I/O查詢到的數據是不會出現在結果集當中的。

肯定會有人問:既然一開始是利用索引的,為什么不先沿著索引葉子節(jié)點查詢到最后需要的5個節(jié)點,然后再去聚簇索引中查詢實際數據。這樣只需要5次隨機I/O,類似于下面圖片的過程:

其實我也想問這個問題。

證實

下面我們實際操作一下來證實上述的推論:

為了證實select * from test where val=4 limit 300000,5是掃描300005個索引節(jié)點和300005個聚簇索引上的數據節(jié)點,我們需要知道MySQL有沒有辦法統(tǒng)計在一個sql中通過索引節(jié)點查詢數據節(jié)點的次數。我先試了Handler_read_*系列,很遺憾沒有一個變量能滿足條件。

我只能通過間接的方式來證實:

InnoDB中有buffer pool。里面存有最近訪問過的數據頁,包括數據頁和索引頁。所以我們需要運行兩個sql,來比較buffer pool中的數據頁的數量。預測結果是運行select * from test a inner join (select id from test where val=4 limit 300000,5)之后,buffer pool中的數據頁的數量遠遠少于select * from test where val=4 limit 300000,5;對應的數量,因為前一個sql只訪問5次數據頁,而后一個sql訪問300005次數據頁。

mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like '%test%' group by index_name;
Empty set (.04 sec)

可以看出,目前buffer pool中沒有關于test表的數據頁。

mysql> select * from test where val=4 limit 300000,5;
+---------+-----+--------+
| id      | val | source |
+---------+-----+--------+
| 3327622 |   4 |      4 |
| 3327632 |   4 |      4 |
| 3327642 |   4 |      4 |
| 3327652 |   4 |      4 |
| 3327662 |   4 |      4 |
+---------+-----+--------+
5 rows in set (26.19 sec)

mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like '%test%' group by index_name;
+------------+----------+
| index_name | count(*) |
+------------+----------+
| PRIMARY    |     4098 |
| val        |      208 |
+------------+----------+
2 rows in set (.04 sec)

可以看出,此時buffer pool中關于test表有4098個數據頁,208個索引頁。

select * from test a inner join (select id from test where val=4 limit 300000,5)為了防止上次試驗的影響,我們需要清空buffer pool,重啟mysql。

mysqladmin shutdown
/usr/local/bin/mysqld_safe &
mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like '%test%' group by index_name;
Empty set (0.03 sec)

運行SQL:

mysql> select * from test a inner join (select id from test where val=4 limit 300000,5) b on a.id=b.id;
+---------+-----+--------+---------+
| id      | val | source | id      |
+---------+-----+--------+---------+
| 3327622 |   4 |      4 | 3327622 |
| 3327632 |   4 |      4 | 3327632 |
| 3327642 |   4 |      4 | 3327642 |
| 3327652 |   4 |      4 | 3327652 |
| 3327662 |   4 |      4 | 3327662 |
+---------+-----+--------+---------+
5 rows in set (0.09 sec)

mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like '%test%' group by index_name;
+------------+----------+
| index_name | count(*) |
+------------+----------+
| PRIMARY    |        5 |
| val        |      390 |
+------------+----------+
2 rows in set (0.03 sec)

我們可以看明顯的看出兩者的差別:第一個sql加載了4098個數據頁到buffer pool,而第二個sql只加載了5個數據頁到buffer pool。符合我們的預測。也證實了為什么第一個sql會慢:讀取大量的無用數據行(300000),最后卻拋棄掉。

而且這會造成一個問題:加載了很多熱點不是很高的數據頁到buffer pool,會造成buffer pool的污染,占用buffer pool的空間。

遇到的問題  

為了在每次重啟時確保清空buffer pool,我們需要關閉innodb_buffer_pool_dump_at_shutdown和innodb_buffer_pool_load_at_startup,這兩個選項能夠控制數據庫關閉時dump出buffer pool中的數據和在數據庫開啟時載入在磁盤上備份buffer pool的數據。

參考資料:

1.https://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/

2.https://dev.mysql.com/doc/refman/5.7/en/innodb-information-schema-buffer-pool-tables.html

更多關于SQL執(zhí)行效率問題請查看下面的相關文章

相關文章

  • MySQL 不等于的三種使用及區(qū)別

    MySQL 不等于的三種使用及區(qū)別

    MySQL中常用到判斷符號,而不等于是比較常用的符號,不等于主要是三種,本文主要介紹了三種的使用及區(qū)別,感興趣的同學可以了解一下
    2021-06-06
  • MySQL數據庫定時備份的幾種實現方法

    MySQL數據庫定時備份的幾種實現方法

    本文主要介紹了MySQL數據庫定時備份的幾種實現方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-07-07
  • MYSQL函數的使用梳理

    MYSQL函數的使用梳理

    本篇文章講解是是MySQL的函數方法,涵蓋所有的MySQL常見的方法,MySQL函數,是一種控制流程函數,屬于數據庫用語言,以下列出了這些函數的說明
    2022-05-05
  • MySQL鎖等待與死鎖問題分析

    MySQL鎖等待與死鎖問題分析

    這篇文章主要介紹了MySQL鎖等待與死鎖問題分析,幫助大家更好的理解和學習使用MySQL,感興趣的朋友可以了解下
    2021-03-03
  • mysql的聯合索引(復合索引)的實現

    mysql的聯合索引(復合索引)的實現

    這篇文章主要介紹了mysql的聯合索引(復合索引)的實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-03-03
  • mysql事務隔離級別詳情

    mysql事務隔離級別詳情

    這篇文章主要介紹了mysql事務隔離級別,文章圍繞mysql事務隔離的相關資料詳細展開文章重點,需要的小伙伴可以參考一下,希望對你有所幫助
    2021-10-10
  • MySQL中觸發(fā)器和游標的介紹與使用

    MySQL中觸發(fā)器和游標的介紹與使用

    這篇文章主要給大家介紹了關于MySQL中觸發(fā)器和游標的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-03-03
  • 淺析mysql.data.dll驅動各版本介紹

    淺析mysql.data.dll驅動各版本介紹

    本較詳細的給大家介紹了mysql.data.dll驅動各版本的相關知識,非常不錯,具有一定的參考借鑒價值,需要的朋友參考下吧
    2018-07-07
  • MySQL連接異常報10061錯誤問題解決

    MySQL連接異常報10061錯誤問題解決

    這篇文章主要介紹了MySQL連接異常報10061錯誤問題解決,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下
    2021-08-08
  • 一步步教你MySQL查詢優(yōu)化分析教程

    一步步教你MySQL查詢優(yōu)化分析教程

    這篇文章主要給大家介紹了關于MySQL查詢優(yōu)化分析的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用MySQL具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-11-11

最新評論

临城县| 加查县| 本溪市| 马尔康县| 图们市| 庆云县| 武强县| 福清市| 合肥市| 上饶市| 彭泽县| 台南县| 运城市| 砀山县| 北碚区| 温宿县| 县级市| 称多县| 彰化市| 阜康市| 铜鼓县| 建阳市| 砀山县| 鄢陵县| 五大连池市| 福安市| 临海市| 永兴县| 孝昌县| 文山县| 漯河市| 观塘区| 丹巴县| 铜山县| 肇东市| 临沧市| 古浪县| 柳江县| 桐梓县| 彩票| 唐山市|