mysql基本知識點梳理和查詢優化

来源:https://www.cnblogs.com/chenfangzhi/archive/2018/11/18/9979676.html
-Advertisement-
Play Games

[TOC] 本文主要是總結了工作中一些常用的操作,以及不合理的操作,在對慢查詢進行優化時收集的一些有用的資料和信息,本文適合有mysql基礎的開發人員。 一、索引相關 1. 索引基數:基數是數據列所包含的不同值的數量。例如,某個數據列包含值1、3、7、4、7、3,那麼它的基數就是4。索引的基數相對於 ...


目錄

本文主要是總結了工作中一些常用的操作,以及不合理的操作,在對慢查詢進行優化時收集的一些有用的資料和信息,本文適合有mysql基礎的開發人員。

一、索引相關

  1. 索引基數:基數是數據列所包含的不同值的數量。例如,某個數據列包含值1、3、7、4、7、3,那麼它的基數就是4。索引的基數相對於數據表行數較高(也就是說,列中包含很多不同的值,重覆的值很少)的時候,它的工作效果最好。如果某數據列含有很多不同的年齡,索引會很快地分辨數據行。如果某個數據列用於記錄性別(只有"M"和"F"兩種值),那麼索引的用處就不大。如果值出現的幾率幾乎相等,那麼無論搜索哪個值都可能得到一半的數據行。在這些情況下,最好根本不要使用索引,因為查詢優化器發現某個值出現在表的數據行中的百分比很高的時候,它一般會忽略索引,進行全表掃描。慣用的百分比界線是"30%"。
  2. 索引失效原因

    1. 對索引列運算,運算包括(+、-、*、/、!、<>、%、like'%_'(%放在前面)
    2. 類型錯誤,如欄位類型為varchar,where條件用number。
    3. 對索引應用內部函數,這種情況下應該建立基於函數的索引
      如select * from template t where ROUND(t.logicdb_id) = 1
      此時應該建ROUND(t.logicdb_id)為索引,mysql8.0開始支持函數索引,5.7可以通過虛擬列的方式來支持,之前只能新建一個ROUND(t.logicdb_id)列然後去維護
    4. 如果條件有or,即使其中有條件帶索引也不會使用(這也是為什麼建議少使用or的原因),如果想使用or,又想索引有效,只能將or條件中的每個列加上索引
    5. 如果列類型是字元串,那一定要在條件中數據使用引號,否則不使用索引;
    6. B-tree索引 is null不會走,is not null會走,點陣圖索引 is null,is not null 都會走
    7. 組合索引遵循最左原則

索引的建立

  1. 最重要的肯定是根據業務經常查詢的語句
  2. 儘量選擇區分度高的列作為索引,區分度的公式是 COUNT(DISTINCT col) / COUNT(*)。表示欄位不重覆的比率,比率越大我們掃描的記錄數就越少
  3. 如果業務中唯一特性最好建立唯一鍵,一方面可以保證數據的正確性,另一方面索引的效率能大大提高

二、EXPLIAN中有用的信息

基本用法

  1. desc 或者 explain 加上你的sql
  2. extended explain加上你的sql,然後通過show warnings可以查看實際執行的語句,這一點也是非常有用的,很多時候不同的寫法經過sql分析之後實際執行的代碼是一樣的

提高性能的特性

  1. 索引覆蓋(covering index):需要查詢的數據在索引上都可以查到不需要回表 EXTRA列顯示using index
  2. ICP特性(Index Condition Pushdown):本來index僅僅是data access的一種訪問模式,存數引擎通過索引回表獲取的數據會傳遞到MySQL server層進行where條件過濾,5.6版本開始當ICP打開時,如果部分where條件能使用索引的欄位,MySQL server會把這部分下推到引擎層,可以利用index過濾的where條件在存儲引擎層進行數據過濾。EXTRA顯示using index condition。需要瞭解mysql的架構圖分為server和存儲引擎層
  3. 索引合併(index merge):對多個索引分別進行條件掃描,然後將它們各自的結果進行合併(intersect/union)。一般用OR會用到,如果是AND條件,考慮建立複合索引。EXPLAIN顯示的索引類型會顯示index_merge,EXTRA會顯示具體的合併演算法和用到的索引

extra欄位
1、using filesort: 說明MySQL會對數據使用一個外部的索引排序,而不是按照表內的索引順序進行讀取。MySQL中無法利用索引完成的排序操作稱為“文件排序” ,其實不一定是文件排序,內部使用的是快排
2、using temporary:  使用了臨時表保存中間結果,MySQL在對查詢結果排序時使用臨時表。常見於排序order by和分組查詢group by
3、using index: 表示相應的SELECT操作中使用了覆蓋索引(Covering Index),避免訪問了表的數據行,效率不錯。
6、impossible where: WHERE子句的值總是false,不能用來獲取任何元組
7、select tables optimized away: 在沒有GROUP BY子句的情況下基於索引優化MIN/MAX操作或者對於MyISAM存儲引擎優化COUNT(*)操作, 不必等到執行階段再進行計算,查詢執行計劃生成的階段即完成優化
8、distinct: 優化distinct操作,在找到第一匹配的元祖後即停止找同樣值的操作

using filesort,using temporary這兩項出現時需要註意下,這兩項是十分耗費性能的,在使用group by的時候,雖然沒有使用order by,如果沒有索引,是可能同時出現using filesort,using temporary的,因為group by就是先排序在分組,如果沒有排序的需要,可以加上一個order by NULL來避免排序,這樣using filesort就會去除,能提升一點性能。

type欄位
system:表只有一行記錄(等於系統表),這是const類型的特例,平時不會出現
const:如果通過索引依次就找到了,const用於比較主鍵索引或者unique索引。 因為只能匹配一行數據,所以很快。如果將主鍵置於where列表中,MySQL就能將該查詢轉換為一個常量
eq_ref:唯一性索引掃描,對於每個索引鍵,表中只有一條記錄與之匹配。常見於主鍵或唯一索引掃描
ref:非唯一性索引掃描,返回匹配某個單獨值的所有行。本質上也是一種索引訪問,它返回所有匹配 某個單獨值的行,然而它可能會找到多個符合條件的行,所以它應該屬於查找和掃描的混合體
range:只檢索給定範圍的行,使用一個索引來選擇行。key列顯示使用了哪個索引,一般就是在你的where語句中出現between、<、>、in等的查詢,這種範圍掃描索引比全表掃描要好,因為只需要開始於縮印的某一點,而結束於另一點,不用掃描全部索引
index:Full Index Scan ,index與ALL的區別為index類型只遍歷索引樹,這通常比ALL快,因為索引文件通常比數據文件小。 (也就是說雖然ALL和index都是讀全表, 但index是從索引中讀取的,而ALL是從硬碟讀取的)
all:Full Table Scan,遍歷全表獲得匹配的行

參考地址:https://blog.csdn.net/DrDanger/article/details/79092808

三、欄位類型和編碼

  1. mysql返回字元串長度:CHARACTER_LENGTH方法(CHAR_LENGTH一樣的)返回的是字元數,LENGTH函數返回的是位元組數,一個漢字三個位元組
  2. varvhar等欄位建立索引長度計算語句:select count(distinct left(test,5))/count(*) from table; 越趨近1越好
  3. mysql的utf8最大是3個位元組不支持emoji表情符號,必須只用utf8mb4。需要在mysql配置文件中配置客戶端字元集為utf8mb4。jdbc的連接串不支持配置characterEncoding=utf8mb4,最好的辦法是在連接池中指定初始化sql,例如:hikari連接池,其他連接池類似spring.datasource.hikari.connection-init-sql=set names utf8mb4。否則需要每次執行sql前都先執行set names utf8mb4
  4. msyql排序規則(一般使用_bin和_genera_ci):
    1. utf8_genera_ci不區分大小寫,ci為case insensitive的縮寫,即大小寫不敏感,
    2. utf8_general_cs區分大小寫,cs為case sensitive的縮寫,即大小寫敏感,但是目前MySQL版本中已經不支持類似於***_genera_cs的排序規則,直接使用utf8_bin替代。
    3. utf8_bin將字元串中的每一個字元用二進位數據存儲,區分大小寫。

      那麼,同樣是區分大小寫,utf8_general_cs和utf8_bin有什麼區別?
      cs為case sensitive的縮寫,即大小寫敏感;bin的意思是二進位,也就是二進位編碼比較。
      utf8_general_cs排序規則下,即便是區分了大小寫,但是某些西歐的字元和拉丁字元是不區分的,比如ä=a,但是有時並不需要ä=a,所以才有utf8_bin
      utf8_bin的特點在於使用字元的二進位的編碼進行運算,任何不同的二進位編碼都是不同的,因此在utf8_bin排序規則下:ä<>a

  5. sql yog中初始連接指定編碼類型使用連接配置的初始化命令
    初始連接編碼配置

四、SQL語句總結

常用的但容易忘的

  1. 如果有主鍵或者唯一鍵衝突則不插入:insert ignore into
  2. 如果有主鍵或者唯一鍵衝突則更新,註意這個會影響自增的增量:INSERT INTO room_remarks(room_id,room_remarks) VALUE(1,"sdf") ON DUPLICATE KEY UPDATE room_remarks="234"
  3. 如果有就用新的替代,values如果不包含自增列,自增列的值會變化: REPLACE INTO room_remarks(room_id,room_remarks) VALUE(1,"sdf")
  4. 備份表:CREATE TABLE user_info SELECT * FROM user_info
  5. 複製表結構:CREATE TABLE user_v2 LIKE user
  6. 從查詢語句中導入:INSERT INTO user_v2 SELECT * FROM user或者INSERT INTO user_v2(id,num) SELECT id,num FROM user
  7. 連表更新:UPDATE user a, room b SET a.num=a.num+1 WHERE a.room_id=b.id
  8. 連表刪除:DELETE user FROM user,black WHERE user.id=black.id

鎖相關(作為瞭解,很少用)

  1. 共用鎖: select id from tb_test where id = 1 lock in share mode;
  2. 排它鎖: select id from tb_test where id = 1 for update

優化時用到

  1. 強制使用某個索引: select * from table force index(idx_user) limit 2;
  2. 禁止使用某個索引: select * from table ignore index(idx_user) limit 2;
  3. 禁用緩存(在測試時去除緩存的影響): select SQL_NO_CACHE from table limit 2;

查看狀態

  1. 查看字元集 SHOW VARIABLES LIKE 'character_set%';
  2. 查看排序規則 SHOW VARIABLES LIKE 'collation%';

SQL編寫註意

  1. where語句的解析順序是從右到左,條件儘量放where不要放having
  2. 採用延遲關聯(deferred join)技術優化超多分頁場景,比如limit 10000,10,延遲關聯可以避免回表
  3. distinct語句非常損耗性能,可以通過group by來優化
  4. 連表儘量不要超過三個表

五、踩坑

  1. 如果有自增列,truncate語句會把自增列的基數重置為0,有些場景用自增列作為業務上的id需要十分重視
  2. 聚合函數會自動濾空,比如a列的類型是int且全部是NULL,則SUM(a)返回的是NULL而不是0
  3. mysql判斷null相等不能用“a=null”,這個結果永遠為UnKnown,where和having中,UnKnown永遠被視為false,check約束中,UnKnown就會視為true來處理。所以要用“a is null”處理

六、千萬大表線上修改

mysql在表數據量很大的時候,如果修改表結構會導致鎖表,業務請求被阻塞。mysql在5.6之後引入了線上更新,但是在某些情況下還是會鎖表,所以一般都採用pt工具( Percona Toolkit)

如對錶添加索引:

pt-online-schema-change --user='root' --host='localhost' --ask-pass --alter "add index idx_user_id(room_id,create_time)" D=fission_show_room_v2,t=room_favorite_info --execute

七、慢查詢日誌

有時候如果線上請求超時,應該去關註下慢查詢日誌,慢查詢的分析很簡單,先找到慢查詢日誌文件的位置,然後利用mysqldumpslow去分析。查詢慢查詢日誌信息可以直接通過執行sql命令查看相關變數,常用的sql如下:

-- 查看慢查詢配置
-- slow_query_log  慢查詢日誌是否開啟
-- slow_query_log_file 的值是記錄的慢查詢日誌到文件中
-- long_query_time 指定了慢查詢的閾值
-- log_queries_not_using_indexes 是否記錄所有沒有利用索引的查詢
SHOW VARIABLES LIKE '%quer%';

-- 查看慢查詢是日誌還是表的形式
SHOW VARIABLES LIKE 'log_output'

-- 查看慢查詢的數量
SHOW GLOBAL STATUS LIKE 'slow_queries';

mysqldumpslow的工具十分簡單,我主要用到的是參數如下:

-t:限制輸出的行數,我一般取前十條就夠了
-s:根據什麼來排序預設是平均查詢時間at,我還經常用到c查詢次數,因為查詢次數很頻繁但是時間不高也是有必要優化的,還有t查詢時間,查看那個語句特別卡。
-v:輸出詳細信息

例子:mysqldumpslow -v -s t -t 10 mysql_slow.log.2018-11-20-0500

八、查看sql進程和殺死進程

如果你執行了一個sql的操作,但是遲遲沒有返回,你可以通過查詢進程列表看看他的實際執行狀況,如果該sql十分耗時,為了避免影響線上可以用kill命令殺死進程,通過查看進程列表也能直觀的看下當前sql的執行狀態,如果當前資料庫負載很高,在進程列表可能會出現,大量的進程夯住,執行時間很長。命令如下:

--查看進程列表
SHOW PROCESSLIST;
--殺死某個進程
kill 183665

如果你使用的sqlyog,那麼也有圖形化的頁面,在菜單欄-工具-顯示-進程列表。在進程列表頁面可以右鍵殺死進程。如下所示:

查看進程列表

殺死進程

九、一些資料庫性能的思考

在對公司慢查詢日誌做優化的時候,很多時候可能是忘了建索引,像這種問題很容易解決,加個索引就行了。但是有兩種情況就不是簡單能加索引能解決了:

  1. 業務代碼迴圈讀資料庫: 考慮這樣一個場景,獲取用戶粉絲列表信息 加入分頁是十個 其實像這樣的sql是十分簡單的,通過連表查詢性能也很高,但是有時候,很多開發採用了取出一串id,然後迴圈讀每個id的信息,這樣如果id很多對資料庫的壓力是很大的,而且性能也很低
  2. 統計sql:很多時候,業務上都會有排行榜這種,發現公司有很多地方直接採用資料庫做計算,在對一些大表的做聚合運算的時候,經常超過五秒,這些sql一般很長而且很難優化, 像這種場景,如果業務允許(比如一致性要求不高或者是隔一段時間才統計的),可以專門在從庫裡面做統計。另外我建議還是採用redis緩存來處理這種業務
  3. 超大分頁: 在慢查詢日誌中發現了一些超大分頁的慢查詢如 limit 40000,1000,因為mysql的分頁是在server層做的,可以採用延遲關聯在減少回表。但是看了相關的業務代碼正常的業務邏輯是不會出現這樣的請求的,所以很有可能是有惡意用戶在刷介面,所以最好在開發的時候也對介面加上校驗攔截這些惡意請求。

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

-Advertisement-
Play Games
更多相關文章
  • ubuntu18.04初始化配置 [TOC] 虛擬機掛了, 又得重新安裝配置一遍, 這裡記錄了我幾乎每次都要配置安裝的那些軟體. ifconfig apt install net tools sshd sudo apt install ssh ifconfig xshell smb sudo apt ...
  • https://pan.baidu.com/s/1Yh_xuz39xGRrCtGtwhuqQg RHEL-8.0-beta-1-x86_64-dvd.iso 文件名: E:\rhel-8.0-beta-1-x86_64-dvd.iso 文件大小: 6964641792 位元組 (6.49 GB) 修改 ...
  • 1. CFS如何選擇最合適的進程 每個調度器類sched_class都必須提供一個pick_next_task函數用以在就緒隊列中選擇一個最優的進程來等待調度, 而我們的CFS調度器類中, 選擇下一個將要運行的進程由pick_next_task_fair函數來完成 之前我們在將主調度器的時候, 主調 ...
  • 用了Linux系統之後,很多資源都不方便獲取, 因為很多資料都是放在百某某盤上面。 無意中看到我手機有FTP服務,想到我電腦裝有FileZilla,可以訪問手機了 然後就連接上了。 但是瀏覽的時候發現第一個問題,亂碼問題。不用說,肯定是編碼搞得鬼, 然後搜了一下手機一般是UTF-8字元集,所以改一下 ...
  • 可以寫一個函數: 主要是使用正則來判斷。另外輸入字元是空的話,使用"-"來替換。 CREATE FUNCTION [dbo].[svf_NonNegativeInteger] ( @val NVARCHAR(4000) ) RETURNS BIT AS BEGIN DECLARE @rtv BIT ...
  • 根據這篇《字元是否為SQL的保留字》https://www.cnblogs.com/insus/p/9979789.html 我們可以不必去創建一張表來存儲這些關鍵詞,可以直接寫一個函數來實現: CREATE FUNCTION [dbo].[svf_IsExistDataList] ( @Searc ...
  • 在安裝mysqlclient的時候出現了以下報錯: 解決辦法: 1.到提示網址:https://visualstudio.microsoft.com/download/裡面下載對應VC++版本安裝後繼續安裝mysqlclient。但是本人沒有找到對應文件,故不再過多贅述,這裡說下第二種方法。 2.1 ...
  • 要想知道字元是否為MS SQL Server保留字,那我們必須把SQL所有保留字放在一個數據集中。然後我們才能判斷所查找的字元是否被包含在數據集中。 MS SQL Server保留字: 此時,我們可以把這字元串拆分插入一張表中: CREATE TABLE [dbo].[ReservedWordOfS ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...