0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

MySQL用limit為什么會影響性能

Linux愛好者 ? 來源:簡書 ? 作者:Muscleape ? 2022-06-20 16:31 ? 次閱讀

有一張財務(wù)流水表,未分庫分表,目前的數(shù)據(jù)量為9555695,分頁查詢使用到了limit,優(yōu)化之前的查詢耗時16 s 938 ms(execution: 16 s 831 ms, fetching: 107 ms),按照下文的方式調(diào)整SQL后,耗時347 ms(execution: 163 ms, fetching: 184 ms);

操作:查詢條件放到子查詢中,子查詢只查主鍵ID,然后使用子查詢中確定的主鍵關(guān)聯(lián)查詢其他的屬性字段;

原理:減少回表操作,利用延遲關(guān)聯(lián)或者子查詢優(yōu)化超多分頁場景。

--優(yōu)化前SQL
SELECT各種字段
FROM`table_name`
WHERE各種條件
LIMIT0,10;
--優(yōu)化后SQL
SELECT各種字段
FROM`table_name`main_tale
RIGHTJOIN
(
SELECT子查詢只查主鍵
FROM`table_name`
WHERE各種條件
LIMIT0,10;
)temp_tableONtemp_table.主鍵=main_table.主鍵

找到的原理分析:MySQL 用 limit 為什么會影響性能?

前言

首先說明一下MySQL的版本:

mysql>selectversion();
+-----------+
|version()|
+-----------+
|5.7.17|
+-----------+
1rowinset(0.00sec)

表結(jié)構(gòu):

mysql>desctest;
+--------+---------------------+------+-----+---------+----------------+
|Field|Type|Null|Key|Default|Extra|
+--------+---------------------+------+-----+---------+----------------+
|id|bigint(20)unsigned|NO|PRI|NULL|auto_increment|
|val|int(10)unsigned|NO|MUL|0||
|source|int(10)unsigned|NO||0||
+--------+---------------------+------+-----+---------+----------------+
3rowsinset(0.00sec)

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

灌入大量數(shù)據(jù),共500萬:

mysql>selectcount(*)fromtest;
+----------+
|count(*)|
+----------+
|5242882|
+----------+
1rowinset(4.25sec)

我們知道,當(dāng)limit offset rows中的offset很大時,會出現(xiàn)效率問題:

mysql>select*fromtestwhereval=4limit300000,5;
+---------+-----+--------+
|id|val|source|
+---------+-----+--------+
|3327622|4|4|
|3327632|4|4|
|3327642|4|4|
|3327652|4|4|
|3327662|4|4|
+---------+-----+--------+
5rowsinset(15.98sec)

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

mysql>select*fromtestainnerjoin(selectidfromtestwhereval=4limit300000,5)bona.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|
+---------+-----+--------+---------+
5rowsinset(0.38sec)

時間相差很明顯。

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

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

類似于下面這張圖:fdbcabee-efc7-11ec-ba43-dac502259ad0.jpg

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

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

fdd667fa-efc7-11ec-ba43-dac502259ad0.jpg

其實我也想問這個問題。

證實

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

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

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

InnoDB中有buffer pool。里面存有最近訪問過的數(shù)據(jù)頁,包括數(shù)據(jù)頁和索引頁。所以我們需要運行兩個sql,來比較buffer pool中的數(shù)據(jù)頁的數(shù)量。

預(yù)測結(jié)果是運行select * from test a inner join (select id from test where val=4 limit 300000,5);之后,buffer pool中的數(shù)據(jù)頁的數(shù)量遠(yuǎn)遠(yuǎn)少于select * from test where val=4 limit 300000,5;對應(yīng)的數(shù)量,因為前一個sql只訪問5次數(shù)據(jù)頁,而后一個sql訪問300005次數(shù)據(jù)頁。

select*fromtestwhereval=4limit300000,5
mysql>selectindex_name,count(*)frominformation_schema.INNODB_BUFFER_PAGEwhereINDEX_NAMEin('val','primary')andTABLE_NAMElike'%test%'groupbyindex_name;Emptyset(0.04sec)

可以看出,目前buffer pool中沒有關(guān)于test表的數(shù)據(jù)頁。

mysql>select*fromtestwhereval=4limit300000,5;
+---------+-----+--------+
|id|val|source|
+---------+-----+--------+|
3327622|4|4|
|3327632|4|4|
|3327642|4|4|
|3327652|4|4|
|3327662|4|4|
+---------+-----+--------+
5rowsinset(26.19sec)

mysql>selectindex_name,count(*)frominformation_schema.INNODB_BUFFER_PAGEwhereINDEX_NAMEin('val','primary')andTABLE_NAMElike'%test%'groupbyindex_name;
+------------+----------+
|index_name|count(*)|
+------------+----------+
|PRIMARY|4098|
|val|208|
+------------+----------+2rowsinset(0.04sec)

可以看出,此時buffer pool中關(guān)于test表有4098個數(shù)據(jù)頁,208個索引頁。

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

mysqladminshutdown
/usr/local/bin/mysqld_safe&
mysql>selectindex_name,count(*)frominformation_schema.INNODB_BUFFER_PAGEwhereINDEX_NAMEin('val','primary')andTABLE_NAMElike'%test%'groupbyindex_name;

Emptyset(0.03sec)

運行sql:

mysql>select*fromtestainnerjoin(selectidfromtestwhereval=4limit300000,5)bona.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|
+---------+-----+--------+---------+
5rowsinset(0.09sec)

mysql>selectindex_name,count(*)frominformation_schema.INNODB_BUFFER_PAGEwhereINDEX_NAMEin('val','primary')andTABLE_NAMElike'%test%'groupbyindex_name;
+------------+----------+
|index_name|count(*)|
+------------+----------+
|PRIMARY|5|
|val|390|
+------------+----------+
2rowsinset(0.03sec)

我們可以看明顯的看出兩者的差別:第一個sql加載了4098個數(shù)據(jù)頁到buffer pool,而第二個sql只加載了5個數(shù)據(jù)頁到buffer pool。符合我們的預(yù)測。也證實了為什么第一個sql會慢:讀取大量的無用數(shù)據(jù)行(300000),最后卻拋棄掉。而且這會造成一個問題:加載了很多熱點不是很高的數(shù)據(jù)頁到buffer pool,會造成buffer pool的污染,占用buffer pool的空間。

遇到的問題

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

原文標(biāo)題:一次 SQL 查詢優(yōu)化原理分析:900W+ 數(shù)據(jù),從 17s 到 300ms

文章出處:【微信公眾號:Linux愛好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

審核編輯:湯梓紅
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 數(shù)據(jù)
    +關(guān)注

    關(guān)注

    8

    文章

    6715

    瀏覽量

    88311
  • SQL
    SQL
    +關(guān)注

    關(guān)注

    1

    文章

    750

    瀏覽量

    43900
  • MySQL
    +關(guān)注

    關(guān)注

    1

    文章

    789

    瀏覽量

    26283

原文標(biāo)題:一次 SQL 查詢優(yōu)化原理分析:900W+ 數(shù)據(jù),從 17s 到 300ms

文章出處:【微信號:LinuxHub,微信公眾號:Linux愛好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    MySQL的整體邏輯架構(gòu)

    支持多種存儲引擎是眾所周知的MySQL特性,也是MySQL架構(gòu)的關(guān)鍵優(yōu)勢之一。如果能夠理解MySQL Server與存儲引擎之間是怎樣通過API交互的,將大大有利于理解MySQL的核心
    的頭像 發(fā)表于 04-30 11:14 ?327次閱讀
    <b class='flag-5'>MySQL</b>的整體邏輯架構(gòu)

    mysql密碼忘了怎么重置

    mysql密碼忘了怎么重置? MySQL是一種開源的關(guān)系型數(shù)據(jù)庫管理系統(tǒng),密碼用于保護(hù)數(shù)據(jù)庫的安全性和保密性。如果你忘記了MySQL的密碼,可以通過以下幾種方法進(jìn)行重置。 方法一:使用MySQ
    的頭像 發(fā)表于 12-27 16:51 ?4251次閱讀

    MySQL的執(zhí)行過程 SQL語句性能優(yōu)化常用策略

    回顧 MySQL 的執(zhí)行過程,幫助介紹如何進(jìn)行 sql 優(yōu)化。
    的頭像 發(fā)表于 12-12 10:26 ?536次閱讀
    <b class='flag-5'>MySQL</b>的執(zhí)行過程 SQL語句<b class='flag-5'>性能</b>優(yōu)化常用策略

    mysql數(shù)據(jù)庫基礎(chǔ)命令

    使用以下命令: mysql -u -p 其中,username是您的MySQL用戶名。執(zhí)行此命令后,系統(tǒng)提示您輸入密碼。成功輸入密碼后,您將登錄到MySQL命令行界面。 創(chuàng)建數(shù)據(jù)庫
    的頭像 發(fā)表于 12-06 10:56 ?441次閱讀

    oracle數(shù)據(jù)庫limit怎么

    在Oracle數(shù)據(jù)庫中,可以使用ROWNUM來實現(xiàn)類似LIMIT的功能。ROWNUM是Oracle數(shù)據(jù)庫提供的一個偽列,它在查詢結(jié)果集中為每一行分配一個唯一的數(shù)字。 要使用ROWNUM進(jìn)行分頁查詢
    的頭像 發(fā)表于 12-06 10:05 ?1882次閱讀

    oracle的limit語句

    Oracle數(shù)據(jù)庫中并沒有像MySQL或其他數(shù)據(jù)庫那樣直接使用"LIMIT"關(guān)鍵字來限制查詢結(jié)果的行數(shù)。在Oracle中,可以使用不同的方法來實現(xiàn)類似的功能。下面將詳細(xì)介紹這些方法。 1.
    的頭像 發(fā)表于 12-06 10:03 ?2313次閱讀

    php的mysql無法啟動

    ,以便幫助讀者快速解決相關(guān)問題。 一、安裝環(huán)境配置檢查 1.1 PHP版本檢查 在使用PHP連接MySQL之前,首先要確保PHP版本的兼容性。查看所使用的PHP版本是否與MySQL版本兼容,如果不兼容,可能導(dǎo)致
    的頭像 發(fā)表于 12-04 15:59 ?1091次閱讀

    docker部署mysql的壞處

    的問題和挑戰(zhàn)。在這篇文章中,我們將探討 Docker 部署 MySQL 的壞處。 首先,Docker 部署 MySQL 可能會面臨性能問題。盡管 Docker 提供了輕量級的容器化解決方案,但容器化的應(yīng)用程序在
    的頭像 發(fā)表于 11-23 09:29 ?1169次閱讀

    MySQL性能優(yōu)化方法

    MySQL 性能優(yōu)化是一項關(guān)鍵的任務(wù),可以提高數(shù)據(jù)庫的運行速度和效率。以下是一些優(yōu)化方法,包括具體代碼和詳細(xì)優(yōu)化方案。
    的頭像 發(fā)表于 11-22 09:59 ?447次閱讀

    mysql和sql server區(qū)別

    MySQL和SQL Server是兩種常見的關(guān)系型數(shù)據(jù)庫管理系統(tǒng)(RDBMS),用于存儲和管理數(shù)據(jù)庫。雖然它們都支持SQL語言,但在其他方面存在一些顯著的區(qū)別。以下是MySQL和SQL Server
    的頭像 發(fā)表于 11-21 11:07 ?1247次閱讀

    MySQL忘記root密碼解決方案

    的密碼,可能導(dǎo)致無法正常管理MySQL數(shù)據(jù)庫。 這篇文章將提供詳盡、詳實、細(xì)致的解決方案,幫助解決MySQL忘記root密碼的問題。 解決方案分為以下幾個步驟: 停止MySQL服務(wù):
    的頭像 發(fā)表于 11-21 11:04 ?509次閱讀

    MySQL導(dǎo)出的步驟

    MySQL是一種常用的關(guān)系型數(shù)據(jù)庫管理系統(tǒng),用于存儲和管理大量的結(jié)構(gòu)化數(shù)據(jù)。在實際應(yīng)用中,我們經(jīng)常需要將MySQL數(shù)據(jù)庫中的數(shù)據(jù)導(dǎo)出到其他地方,如備份數(shù)據(jù)、數(shù)據(jù)遷移、數(shù)據(jù)分析等。下面是使用MySQL
    的頭像 發(fā)表于 11-21 10:58 ?662次閱讀

    mysql是一個什么類型的數(shù)據(jù)庫

    MySQL是一種關(guān)系型數(shù)據(jù)庫管理系統(tǒng)(RDBMS),用于存儲和管理大量結(jié)構(gòu)化數(shù)據(jù)。它被廣泛用于各種應(yīng)用程序和網(wǎng)站的后端,包括電子商務(wù)平臺、社交媒體網(wǎng)站、金融系統(tǒng)等等。MySQL的特點是性能高、可靠性
    的頭像 發(fā)表于 11-16 14:43 ?1434次閱讀

    如何提高Mysql數(shù)據(jù)庫的訪問瓶頸

    在學(xué)習(xí)Mysql的時候,我們都有這個常識:對于DB的操作,其實本質(zhì)上是對于磁盤的操作,如果對于DB的訪問次數(shù)過多,其實就是涉及了大量的磁盤IO,這就會導(dǎo)致MYsql出現(xiàn)性能上的瓶頸。 項目背景
    的頭像 發(fā)表于 11-08 16:22 ?931次閱讀
    如何提高<b class='flag-5'>Mysql</b>數(shù)據(jù)庫的訪問瓶頸

    如何在Rust中連接和使用MySQL數(shù)據(jù)庫

    MySQL是一個廣泛使用的關(guān)系型數(shù)據(jù)庫,Rust作為一門相對較新的系統(tǒng)級編程語言,具有C語言般的高性能、安全、并發(fā)等特性,因此與MySQL一起使用是一種非常有趣的選擇。在本教程中,我們將手把手地展示
    的頭像 發(fā)表于 09-30 17:05 ?1405次閱讀