一個MySQL視圖的優化過程

来源:http://www.cnblogs.com/zhangdk/archive/2016/09/25/5907434.html
-Advertisement-
Play Games

一個mySQL視圖的優化,從索引到union all,再到存儲過程。 ...


1.需要優化的sql

最近做一個基於.net mvc和MySQL的倉儲系統的優化工作,遇到了一個執行特別慢的SQL語句,經過一番折騰,終於搞定啦,分享一下過程。問題就是下麵這個家伙:

create or replace view view_task_meter_info
as
select t1.TASK_ID,t1.task_no,t1.BINDBOX_BARCODE as box_barcode,t1.EQUIP_BAR_CODE,t1.METER_STATUS,t1.ENTITY_TYPE as RSLT_CODE,
-- 設備類別
(SELECT name from data_dictionary_info t01 where t01.domain ='設備類別' and t01.code = t3.EQUIP_CATEG) as T_Equip_categ,
-- 類別
(select name from data_dictionary_info t09 where t09.domain = '類型' and t09.code = t3.TYPE_CODE) as 
T_TYPE_CODE,
-- 類型
(select name from data_dictionary_info t09 where t09.domain = '類別' and t09.code = t3.RATED_CURRENT) as T_SORT_CODE,
t3.EQUIP_CATEG,t3.TYPE_CODE,t3.SORT_CODE
from
data_task_asset t1 left join data_meter_info t3 on t1.EQUIP_BAR_CODE=t3.BAR_CODE
union all 
select t1.CHK_TASK_ID as task_id,t1.TASK_NO,t1.BOX_BARCODE,t1.BAR_CODE as Equip_bar_code,'00' as METER_STATUS,t1.RSLT_CODE,
-- 設備類別
(SELECT name from data_dictionary_info t01 where t01.domain ='設備類別' and t01.code = t3.EQUIP_CATEG) as T_Equip_categ,
-- 類別
(select name from data_dictionary_info t09 where t09.domain = '類型' and t09.code = t3.TYPE_CODE) as 
T_TYPE_CODE,
-- 類型
(select name from data_dictionary_info t09 where t09.domain = '類別' and t09.code = t3.RATED_CURRENT) as T_SORT_CODE,
t3.EQUIP_CATEG,t3.TYPE_CODE,t3.SORT_CODE
from data_check_asset_info t1
LEFT JOIN data_meter_info t3 on t1.BAR_CODE = t3.BAR_CODE

解釋一下業務:

倉儲系統業務分為三塊:出入庫、盤點。data_task_asset是出入庫任務資產明細,data_check_asset_info是盤點任務明細,data_meter_info是資產檔案表。data_task_asset和data_check_asset_info表都使用資產條碼(EQUIP_BAR_CODE/BAR_CODE)和資產檔案表關聯(BAR_CODE是檔案表主鍵)。這個視圖的業務意義就是展示出入庫、盤點任務的資產明細(包括檔案信息),同時需要把檔案信息裡面的大量代碼欄位翻譯成文字信息。上面視圖中只列出3個欄位作為示例,實際上需要翻譯的欄位有十幾個。

這個視圖剛開始沒有感覺慢,但是有一天測試做了一個7萬多條明細的盤點任務後,每次查詢一個任務的明細都要等上十幾到幾十秒,是在難以忍受。開工吧,先診斷一下。

2.查看執行情況

下麵是這個視圖在查詢那個7萬多條明細的盤點任務的執行時間,太可怕了,三十多秒。

image

看看執行計劃

image

額滴神呀,這可肯定不行呀,每個欄位的翻譯都要查詢字典表的1207條記錄,一個記錄需要翻譯10次,7萬條記錄,需要查詢字典表70萬次,每次搜索記錄1000多條,這個當然不行啦。

3.在字典表上加索引!!!

根據查詢字典表的sql語句,我們在domain和code上加聯合索引

SELECT name from data_dictionary_info t01 where t01.domain ='設備類別' and t01.code = t3.EQUIP_CATEG

image

來看現在的執行情況

執行時間:
執行時間一下子降到了兩三秒,效果顯著呀!
image

執行計劃:
看看執行計劃你也許就不吃驚啦,建立索引後每次查詢字典表,只搜索一條記錄。
image

4.再加兩個索引

既然索引這麼厲害,那就繼續加索引唄,可以看到視圖在查詢出入庫和盤點任務明細表時,也是全表查詢。我們加個索引看看效果如何,我分別在data_task_asset的task_id和task_no、data_check_asset_info的chk_task_id和task_no上添加了聯合索引。下麵看看執行情況:

執行時間:
執行時間好像比剛纔還長了一點,這就不合心意啦。
image

執行計劃:
從執行計劃來看,查詢根本就沒有用到索引,why?
image

5.mysql視圖演算法及不使用索引的情況

普及一些百度知識:

當用戶創建視圖時,mysql預設使用一種undefine的處理演算法,就是會自動在合併和臨時表內進行選擇

  • 對於MERGE,會將引用視圖的語句的文本與視圖定義合併起來,使得視圖定義的某一部分取代語句的對應部分。

  • 對於TEMPTABLE,視圖的結果將被置於臨時表中,然後使用它執行語句。

  • 對於UNDEFINED,MySQL將選擇所要使用的演算法。如果可能,它傾向於MERGE而不是TEMPTABLE,這是因為MERGE通常更有效,而且如果使用了臨時表,視圖是不可更新。

對於使用MERGE演算法處理的視圖,可以使用索引。但是,對於使用臨時表演算法處理的視圖,不能在其基表上利用索引提供的優點。MERGE演算法要求視圖中的行和基表中的行具有一對一的關係。如果不具有該關係。必須使用臨時表取而代之。如果視圖包含下述結構中的任何一種,將失去一對一的關係:

  • 聚合函數(SUM(),MIN(),MAX(),COUNT()等)
  • DISTINCT
  • GROUP BY
  • HAVING
  • UNION或UNION ALL

通常的不使用索引的查詢

  • 如果MySQL估計使用索引比全表掃描更慢,則不使用索引。例如,如果列key均勻分佈在1和100之間,下麵的查詢使用索引就不是很好:select * from table_name where key>1 and key<90;

  • 如果使用MEMORY/HEAP表,並且where條件中不使用“=”進行索引列,那麼不會用到索引,head表只有在“=”的條件下才會使用索引

  • 用or分隔開的條件,如果or前的條件中的列有索引,而後面的列沒有索引,那麼涉及到的索引都不會被用到,例如:select * from table_name where key1='a' or key2='b';如果在key1上有索引而在key2上沒有索引,則該查詢也不會走索引

  • 複合索引,如果索引列不是複合索引的第一部分,則不使用索引(即不符合最左首碼),例如,複合索引為(key1,key2),則查詢select * from table_name where key2='b';將不會使用索引

  • 如果like是以‘%’開始的,則該列上的索引不會被使用。例如select * from table_name where key1 like '%a';該查詢即使key1上存在索引,也不會被使用

  • 如果列為字元串,則where條件中必須將字元常量值加引號,否則即使該列上存在索引,也不會被使用。例如,select * from table_name where key1=1;如果key1列保存的是字元串,即使key1上有索引,也不會被使用。

6.幹掉union all

為了證明確實是union all影響了索引的使用,我們去掉視圖中的union all,讓視圖只負責查詢盤點任務的明細及檔案信息,看看效果如何。

-執行時間:
時間又比剛纔短了一秒鐘,不錯,不錯。
image

-執行計劃
從執行計劃可以看出,這次用到了明細表的索引。
image

7.存儲過程使用

我們看到取到union all之後,明細表的索引在查詢中被使用。儘管我們從查詢的時間上感覺明細表使用索引和不使用索引沒有太大差別。但這其實只是數據太少反映不出問題,隨著明細表數據的增多,有索引時每個任務搜索的記錄數只與明細數量有關;而無索引時,每個任務明細查詢是全表搜索。所以,union all必須去掉。

那麼問題來啦,程式結構已經基本定型,單個視圖必須使用union all。這裡有兩種方案:

  • 分開為兩個視圖,出入庫明細一個視圖,盤點明細一個視圖,在程式中控制使用不同視圖;
  • 使用存儲過程,在存儲過程中判斷查詢出入庫明細還是盤點明細。

在Entity Framework中使用存儲過程還沒嘗試過,就用存儲過程啦:

mysql存儲過程代碼:

CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_task_meter_info`(IN `taskId` DECIMAL(16,0), IN `taskNo` VARCHAR(32), IN `ioFlag` VARCHAR(8))
    LANGUAGE SQL
    NOT DETERMINISTIC
    CONTAINS SQL
    SQL SECURITY DEFINER
    COMMENT ''
BEGIN
if ioFlag = '盤點' then
-- 返回盤點明細
select * from ...
where ...
else
-- 返回出入庫明細
select * from ...
where ...
end if;
END

Entity Framework調用存儲過程(看上去也挺方便的):

var dataListProc =
                DbContext.Database.SqlQuery<view_task_meter_info>(
                    string.Format("CALL `sp_task_meter_info`({0}, '{1}','{2}')",searchModel.task_id,searchModel.task_no,searchModel.task_type)).ToList();

8.疑問

  • 總感覺自己的翻譯代碼欄位有點太費時,不知各位園友是怎麼處理這種問題的。

好了,就到這裡啦。這篇博客其實早該發出來的,因為一些耽擱,今天總算趕出來啦。我感覺每次要寫一篇博客前,總感覺有好多東西要說,可是很多時候趕緊文章都寫不流暢啦。動不動就想分條陳述,動不動就想來個問題原因,解決方案。而且一旦不能集中時間寫完,再迴首已是食之無味。成了個整天宥在自己項目、任務圈裡的程式猿啦。這不行!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 很多應用都會在界面中使用某種列表控制項:用戶可以選中、刪除或重新排列列表中的項目。這些控制項其實都是UITableView 對象,可以用來顯示一組對象,例如,用戶地址薄中的一組人名。 UITableView 對象雖然只能顯示一行數據,但是沒有行數限制。 編寫新的應用程式 JXHomepwner 應用 創 ...
  • 本文實現當從splash界面進入hone界面的時候,產生一種漸進淡入的動畫效果,在onCreate中調用一個方法initAnimation(),代碼如下: 其中rl_root在類中定義 private RelativeLayout rl_root; 其中rl_root為splash界面相對佈局的id ...
  • 開發中經常需要使用Shape和Selector,如果每個都用xml設置的話,會占用apk大小,同時命名多了也會混亂,使用代碼來設置會方便很多。 需要用到2個類:GradientDrawable和StateListDrawable 相關API: setColor(rgb); //填充顏色setGrad ...
  • 使用巨集的目的是什麼? 由於實際開發中,有時候一些設置信息需要重覆使用(例如設置列印信息、配置顏色、配置寬度等),如果每次都手動去敲每次都去創建會很麻煩。雖然巨集使用的時候會占用記憶體,可是目前來說大部分開發者都在使用。 1.創建PCH 文件 2.打開配置面板 按照下圖操作 3.按照下圖找到相關配置 4. ...
  • 1.Activity相關。launchmode,OnSaveInstnceState,生命周期等。 4種預設的launchmode,以及OnNewIntent的回調。 OnNewIntent->OnRestart->OnStart->onResume OnActivity->OnResume. On ...
  • 1.排隊機制 接收輸入TCP報文時,有三個隊列: ● 待處理隊列 ● 預排隊隊列 ● 接收隊列 接收隊列包含了處理過的TCP數據段,也就是說,去除了全部的協議頭,正準備將數據複製到用戶應用程式。接收隊列包含了所有按順序接收的數據段,在其他兩個隊列中的TCP數據段則需要進一步處理。 TCP報文首先由t ...
  • 查看原文:http://blog.csdn.net/u010818425/article/details/52490628 Gradle實戰系列文章: 《Gradle基本知識點與常用配置》 《Gradle實戰:Android多渠道打包方案彙總》 《Gradle實戰:不同編譯類型的包同設備共存》 《G ...
  • 對於一門技術的學習,尤其是像Oracle database這種知識體系極其龐雜的技術來講,從巨集觀上瞭解其體繫結構是至關重要的。同時,個人認為,未必是專業DBA人員才需要瞭解其體繫結構(固然對於資料庫專業人員來講,這些都是必備知識了),一般的技術人員如果對其有較深入的瞭解,也是大有益處的,畢竟技術思想 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...