一張900w的數據表,17s執行的SQL優化到300ms?

2020-12-12 計算機java編程

先說一下背景

有一張9555695條數據量的財務流水錶,未做分庫分表,使用limit關鍵字來進行分頁查詢,未做優化前常規查詢需要耗時16 s 938 ms (execution: 16 s 831 ms, fetching: 107 ms),下面我將一步一步講解如何做優化,使得優化後的SQL能在耗時347 ms (execution: 163 ms, fetching: 184 ms)內查詢出數據。

那麼該如何操作呢?

查詢條件放到子查詢中,子查詢只查主鍵ID,然後使用子查詢中確定的主鍵關聯查詢其他的屬性欄位;

原理:減少回表操作

前言

首先說明一下MySQL的版本:

表結構:

id:自增主鍵

val:欄位上建立非唯一索引

然後批量插入大量數據,共計500萬條

由於分頁採用limit關鍵字,當limit offset rows中的offset很大時,表掃描範圍相應的增大,這時會出現效率問題:

這裡我們可以改寫一下這條SQL腳本,改寫如下:

通過查詢結果可以看到:結果數據是一樣的,耗時相差也是很明顯。

上面的結果到底是怎麼回事呢?我們看一下

select * from test where val=4 limit 300000,5;

的查詢過程:

查詢到索引葉子節點數據。根據葉子節點上的主鍵值去聚簇索引上查詢需要的全部欄位值。類似於下面這張圖:

根據上圖描述,執行完這條SQL需要查詢300005次索引節點,查詢300005次聚簇索引的數據,最後再將結果過濾掉前300000條,取出最後5條。MySQL耗費了大量隨機I/O在查詢聚簇索引的數據上,而有300000次隨機I/O查詢到的數據是不會出現在結果集當中的。

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

其實我也想問這個問題。

證實

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

為了證實

select * from test where val=4 limit 300000,5

是掃描300005個索引節點和300005個聚簇索引上的數據節點,我們需要知道MySQL有沒有辦法統計在一個sql中通過索引節點查詢數據節點的次數。我先試了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次數據頁。

select * from test where val=4 limit 300000,5      

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

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

select * from test ainner join (select id from test where val=4 limit 300000,5) ;

為了防止上次試驗的影響,我們需要清空buffer pool,重啟mysql。

mysqladmin shutdown/usr/local/bin/mysqld_safe &

運行sql:

如此我們可以看明顯的看出兩者的差別:第一個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的數據。

相關焦點

  • 一張6000w數據表的查詢優化到0.023s
    詳細需求老大給我安排了個任務,讓我寫個按天分表的定時任務,每次把一天的數據轉移到按天生成的表中,並刪除原表中的數據,主要目的是不想再增長表空間了,保持一個平衡,因為每天刪 500w 也會加 500w。表空間和數據量如下圖:實現思路
  • Oracle優化:sql語句的執行順序
    理解 sql 語句的執行順序對我們優化 sql 有很大的幫助,那麼 sql 語句的執行順序是怎樣的呢,以一條簡單的的語句做分析:① 先執行 from 子句,明確數據的來源,從哪個表或哪個視圖來查詢② 接著執行
  • 數據量很大,分頁查詢很慢,推薦個優化方案!
    對於資料庫分頁查詢,也有很多種方法和優化的點。下面簡單說一下我知道的一些方法。準備工作為了對下面列舉的一些優化進行測試,下面針對已有的一張表進行說明。MySQL版本:5.7.16線下找一張百萬級的測試表可不容易,如果需要自己測試的話,可以寫shell腳本什麼的插入數據進行測試。
  • 最強解讀MyBatis是如何執行SQL語句的?
    上一張圖的 parse.evalNode 方法將配置文件中 configuration 標籤下的內容進行解析,封裝到一個對象,這個對象作為參數傳入 parseConfiguration 方法中。繼續執行後面的方法,最終資料庫源信息封裝到一個 Environment 類型的實例,這個實例又通過 set 方法保存到了 configuration 。configuration 已經處理就緒,被 parse 方法返回。回到之前的 build 方法,將 configuration 作為參數傳入至另一個重載的 build 方法。
  • 面試官扎心一問:數據量很大,分頁查詢很慢,有什麼優化方案?
    對於資料庫分頁查詢,也有很多種方法和優化的點。下面簡單說一下我知道的一些方法。準備工作為了對下面列舉的一些優化進行測試,下面針對已有的一張表進行說明。MySQL版本:5.7.16線下找一張百萬級的測試表可不容易,如果需要自己測試的話,可以寫shell腳本什麼的插入數據進行測試。
  • Select * from user的千層套路——一個sql是如何執行的
    對於MySQL來說,常見的存儲引擎有:InnoDB和MyISAM    而且我們也可以從中看出一條SQL語句從開始運行到最後展出執行結果大概會經歷:連接管理、解析與優化以及最後到存儲引擎這三關。下面我們就分別分析一下這三關都有啥具體的內容,Let's go!
  • hive sql 優化心得
    如何寫好一個hql作為一個數據開發工程師,hive sql是我們必備的技能,可能大家都知道一些基本的優化方法(例如:使用分區、小表join大表、不使用distinct、where條件儘量寫到子查詢裡面減少數據量等等),但是你有沒有想過為什麼?是不是真的對執行效率有提升。
  • 一次神奇的 sql 查詢經歷,group by 慢查詢優化記錄
    一、問題背景  現網出現慢查詢,在500萬數量級的情況下,單表查詢速度在30多秒,需要對sql進行優化,sql如下:  簡單來說,就是查詢一定條件下,都有哪些用戶的,很簡單的sql,可以看到,查詢耗時為37秒。  說一下app_account欄位的分布情況,隨機生成了5000個不同的隨機數,然後分布到了這500萬條數據裡,平均來說,每個app_account都會有1000個是重複的值,種類共有5000個。  二、看執行計劃
  • 急起直追,魅族17s曝光,或一步到位留住魅友
    而能夠剛好拉開300元差價並且以最具競爭力的身份出現,可能就是傳聞當中的魅族17s了。儘管發布時間較遲,但魅族17系列依然給人一種較為倉促的感覺。先不論做工細節問題,畢竟品牌體量無法逾越。讓人最無奈的,還有魅族17對於相機尤其是超廣角端的優化甚至趨於原始,就連120Hz刷新率等亮點技術,最終也只能以「彩蛋」營銷收尾。
  • mysql┃多個角度說明sql優化,讓你吊打面試官!
    ,也是重中之重,在很多大廠中會有專門的DBA來做這件事情,甚至更過分的是連應屆生的招聘崗位要求上都寫了需要懂一點sql優化,最近moon一直在寫關於mysql的文章,包括之前寫的索引相關,其實也都是為了這篇文章做個鋪墊,所以你懂了嗎,今天我將從表結構、索引、查詢語句、分庫分表這四個維度來和大家聊聊,在工作中,怎麼進行sql優化?
  • T-SQL | 你需要了解的執行計劃
    估計查詢計劃是在查詢執行之前生成,而實際執行計劃則是在查詢輸出的同時得到的。Step1.示例查詢介紹(1)假設我們有一張Orders表,裡面有100w行訂單數據,從2005年開始到2009年。縮放到合適的大小*.如果得到了一個無法適合屏幕顯示的計劃時,可以使用一個很酷的功能,叫做縮放到合適大小。(點擊滑鼠右鍵出現菜單,選擇即可)
  • 資料庫之SQL查詢語句
    包括:CREATE :創建表或其他對象的結構ALTER :修改表或其他對象的結構DROP:刪除表或其他對象的結構TRUNCATE:刪除表數據,保留表結構(2) 數據操縱語言(DML):Data Manipulation Language用於改變數據表中的數據。
  • 資料庫SQL腳本優化
    而前者的執行流程是:順序掃描表a,每一行用id條件到表b中去查,匹配到記錄之後再判斷是否滿足where條件,滿足的話就作為結果集的一部分返回2、儘量使用inner join(等值連接)代替 left join或者right join(外連接),結果集篩選率以及執行時間會有很大提升
  • Java web安全黑客攻防之sql注入
    1.什麼是sql注入sql注入通過把SQL命令插入到Web表單提交或輸入域名或頁面請求的查詢字符串,最終達到欺騙伺服器執行惡意的SQL命令通過把SQL命令插入到Web表單提交或輸入域名或頁面請求的查詢字符串,最終達到欺騙伺服器執行惡意的SQL
  • SQL SERVER 使用DATEDIFF篩選查詢記錄百分比
    一張數據表內記錄著運維服務記錄,其中有一個欄位是響應時間,還有一個欄位是服務完成時間,假設定義8小時內完成為準時完成,查詢準時完成率。
  • BeetlSQL 3.1.4 發布,Java 資料庫訪問工具
    本次修復了@Version和@LogicDelete 實現Bug 修復從2升級到3後@Version和@
  • SQL語句性能調整之ORACLE的執行計劃
    這個語句的優點就是它的缺點,這樣在用該方法查看執行時間較長的sql語句時,需要等待該語句執行成功後,才返回執行計劃,使優化的周期大大增長。  如果不想執行語句而只是想得到執行計劃可以採用:  Sql> set autotrace traceonly  這樣,就只會列出執行計劃,而不會真正的執行語句,大大減少了優化時間。
  • 我將 SQL 耗時從 30248.271s 優化到 0.001s
    但是1s的時間還是太長了,還能進行優化嗎,仔細看執行計劃:  image  查看優化後的sql:  SELECT`YSB`.`s`.`s_id` AS `s_id`,`YSB`.`s`.`name` AS `name`FROM`YSB`.
  • sqltoy-orm-4.17.2 發版,部分功能優化
    開源地址:更新內容1、優化update
  • MySQL-SQL語句執行流程
    全面地了解SQL語句執行的每個過程,才能更好的進行SQL的設計和優化。當希望MySQL能夠以更高的性能運行查詢時,最好的辦法就是弄清楚MySQL是如何優化和執行查詢的。一旦理解了這一點,很多查詢優化工作實際上就是遵循一些原則能夠按照預想的合理的方式運行。