MySQL深度解析筆記(事務和索引)

MySQL5.7學習的筆記,如果有什麼問題歡迎留言談論,另外因爲內容過多且有些還沒深入瞭解所以可能記得不全後續會邊學邊補上

簡單瞭解MySQL的架構體系

爲什麼要先了解架構?因爲後面的事務、索引都是和架構相關的,而且在實操中可以對應架構體系中的每一層進行優化。
在網上找到了一個圖,如下大致爲MySQL的架構體系:
在這裏插入圖片描述
下面是另外一個圖(這個相對上面的圖簡略些):
在這裏插入圖片描述

整個流程還是很好理解的:

  • 首先是客戶端發送請求到,MySQL服務端進行建立連接,然後server中的連接器需要判斷用戶名和密碼是否正確,正確則通過否則返回。
  • SQL爲結構化查詢語言,說到底就是字符串,我們向服務端發送字符串,服務端就要對該字符串進行分析,所以分析器就是做這件事情的。這裏就包含詞法分析和語法分析,詞法分析顧名思義就是對sql語句中的每個詞的正確性進行判斷,語法分析就是對整句話的語法進行分析,比如你輸入select * 就沒了,後面就缺少from哪張表就會報錯。(另外語法分析完後還會將sql映射成AST抽象語法樹)
  • 另外在我們sql語句執行之前,server中的優化器已經幫我們先優化了。比如有時候我們創建了索引,但是實際運行sql語句時卻沒有使用到索引,這正是優化器幫我們進行了優化,優化器認爲不使用索引更加合適。(比如說現在有很少的數據,並且查詢的結果在這堆數據中佔比很高,就可能出現這樣的結果。)
    優化器有兩種:
    ①基於規則的優化器(RBO):它有着一套嚴格的使用規則,只要你按照它去寫SQL語句,無論數據表中的內容怎樣,也不會影響到你的“執行計劃”,也就是說RBO對數據不“敏感”。
    ②基於代價的優化器(CBO): CBO是計算各種可能“執行計劃”的“代價”,即COST,從中選用COST最低的執行方案,作爲實際運行方案。
    現在大部分主流的數據庫都是用CBO。
  • 接下來就是執行器,執行器是直接跟存儲引擎進行數據交互的。數據交互就涉及到IO的問題,也是要重視的一個組件。比我們常說查詢儘量不用select *來查詢表的所有列,而是select 具體的屬性,這是因爲如果select * 進行查詢的話,執行器要到存儲引擎進行取數據,而像InonDB存儲引擎是將數據存到硬盤中的,而select *就要到硬盤中取所有列的數據,性能會比select 具體列要低很多,而且硬盤的存取速度沒有內存快。
  • 另外這裏有一個緩存組件(mysql8移除該模塊),這個組件存那些之前已經查詢過的數據,如果下一次客戶端所需的數據在緩存中已經有了,就沒必要再按上面的流程進行查詢。但是緩存組件是需要佔用內存空間的,而且重複查詢同個數據的需求比較低(緩存的命中率低),這樣我們就會經常的更新緩存的數據,這些來說性能更加不好,這也是MySQL8爲什麼把它移除的原因,但是如果說你有一些常用到的數據,並且不會需要經常修改,放到裏面性能還是很快的。

存儲引擎
在這裏插入圖片描述
這裏MySQL的存儲引擎是可插拔的,也就是說你可以在MySQL服務中插入別的存儲引擎。
這裏主要講InnoDB、MyISAM、MEMORY,用的比較多的還是InnoDB、MyISAM。

  1. 不同的存儲引擎在數據文件上它所對應的組織形式是不一樣的。存儲引擎是在表級別,而不是在庫級別,即同個數據庫中的不同表的存儲引擎可以不一樣。
  2. 索引和事務都有區別。如上圖中Transactions列中可以知道只用InnoDB支持事務。
  3. 鎖也不一樣。InnoDB即支持表鎖也支持行鎖。但是MyISAM只支持表鎖。
    後續在講事務鎖索引的時候再細講。

事務(只用InnoDB才支持事務)

爲什麼要引入事務?事務是構成單一邏輯工作單元的操作集合,保證了數據的一致性。

數據庫事務4種隔離級別及7種傳播行爲
事務分類:扁平事務、帶保存點的扁平事務、鏈式事務、嵌套事務。

事務四大特性及原理

事務的4大特性(ACID):

  • 原子性(Atomicity):事務中的所有操作作爲一個整體像原子一樣不可分割,要麼全部
    成功,要麼全部失敗。
  • 一致性(Consistency):事務的執行結果必須使數據庫從一個一致性狀態到另一個一
    致性狀態。一致性狀態是指:1.系統的狀態滿足數據的完整性約束(主碼,參照完整
    性,check約束等) 2.系統的狀態反應數據庫本應描述的現實世界的真實狀態,比如轉
    賬前後兩個賬戶的金額總和應該保持不變。
  • 隔離性(Isolation):併發執行的事務不會相互影響,其對數據庫的影響和它們串行執行
    時一樣。比如多個用戶同時往一個賬戶轉賬,最後賬戶的結果應該和他們按先後次序
    轉賬的結果一樣。
  • 持久性(Durability):事務一旦提交,其對數據庫的更新就是持久的。任何事務或系統
    故障都不會導致數據丟失。

保證事務特性和下面的知識點有關:

  1. 鎖:共享鎖、排它鎖、獨佔鎖、自增鎖、間隙鎖
  2. 日誌:MySQL到底有多少種日誌類型需要我們記住的,主要還是redo和undo,undo保證了事務原子性,同時實現多版本併發控制(MVCC)。MVCC舉個例子:A事務對某個表進行查詢操作,B事務在此之前更新這個表,則A事務查尋更改之前的數據,之前的數據就放在undo裏面。這就是爲什麼事務隔離級別中Read Committed不能防止可重複度和 Repeatable Read可以防止可重複度。

原子性實現原理:Undo log(回滾日誌)

① Undo Log是爲了實現事務的原子性,在MySQL數據庫InnoDB存儲引擎中,還用Undo Log來實現多版本併發控制(MVCC),也即一致非鎖定讀。
② 在操作任何數據之前,首先將數據備份到一個地方(在這個存儲數據備份的地方稱爲Undo Log)。然後進行數據的修改。如果出現了錯誤或者用戶執行了ROLLBACK語句,系統可以利用Undo Log中的備份將數據恢復到事務開始之前的狀態。
③ 注意undo log是邏輯日誌,可以理解爲:
- 當delete一條記錄是,undo log中會記錄一條對應的insert記錄
- 當insert一條記錄是,undo log中會記錄一條對應的delete記錄
- 當update一條記錄時,它記錄一條對應相反的update記錄

MySQL中的邏輯日誌:拿InnoDB來舉例子,我們都知道InnoDB引擎的數據文件有且僅有一個,後綴爲ibd文件,所以索引數據和實際的數據等等都存在同一個文件,可想而知文件會很大,特別是數據表特別多的情況下,所以我們在讀取文件記錄的時候不會讀取整個文件並且硬件條件也不允許【文件數據是存在硬盤上的,讀取後會存在內存中,假如整個文件數據爲1T,你不可能有1T的內存】,而是讀取某一部分。內存和硬盤進行數據交互是以‘頁’爲單位的,頁爲邏輯上的概念,一般大小爲4kb或8kb等4kb整數倍【InnoDB存儲引擎每一次讀取爲16kb】,這個值和硬盤的扇區【512B】有關係。實際生產環境中,某個時刻會有多條記錄或事務在進行併發執行,這就出現每個併發操作會對同一個頁中的不同數據進行操作【例如事務A對這個頁中id爲1的數據進行修改,事務B對這個頁中id爲2的數據進行修改】,所以我們不能一整個頁爲整體去記錄undo log日誌,因爲這樣的話只能記錄到一條數據操作日誌,其他的就沒法被記錄到了。所以我們只能以當行操作去記錄undo log日誌【如上面的事務A要記錄一條undo log日誌記錄,事務B則記錄另外一條undo log日誌記錄】。所以邏輯日誌就是指實際操作的並不是記錄實際存儲的一個物理頁,而是記錄每個頁中每一行數據的更新信息。

持久性實現原理:Redo log(前滾日誌/重做日誌)

①從上面的MySQL架構中我們知道,當我們對MySQL中的數據進行修改時,並不是立馬寫到磁盤中,而是先寫到緩存中,再寫到磁盤中(fsync)。這時假設寫到緩存中但是還沒來得及寫到磁盤中,系統就發生了斷電,這時候重啓系統數據也會丟失,很明顯數據也不一致了。
這時候有一個概念叫WAL: Write-Ahead Logging 預寫日誌系統,數據庫中一種高效的日誌算法,對於非內存數據庫而言,磁盤I/O操作是數據庫效率的一大瓶頸。在相同的數據量下,採用WAL日誌的數據庫系統在事務提交時,磁盤寫操作只有傳統的回滾日誌的一半左右,大大提高了數據庫磁盤I/O操作的效率,從而提高了數據庫的性能。
關於wal預寫日誌系統這裏舉個例子:假設你開了一個飯店,然後你有一個記賬本,這個記賬本十分厚有100萬個人的記錄,在你中午飯點時候飯店很多人你很忙,這時候有個人忘記帶錢了要賒賬,而你也不可能在這麼忙的時候去100萬個人的記賬本中找出那個人,這時候你就可以弄一個小黑板,然後把那個人記下來,等有空餘時間的時候再把小黑板中的記錄記到記賬本中。這裏的小黑板就是相當於數據庫事務中Redo log前滾日誌。當然如果小黑板上面的記錄記滿了的話也要及時的把數據整理到記賬本中。
②和Undo Log相反,Redo Log記錄的是新數據的備份。==在事務提交前,只要將Redo Log持久化即可,不需要將數據持久化。==當系統崩潰時,雖然數據沒有持久化,但是Redo Log已經持久化。系統可以根據Redo Log的內容,將所有數據恢復到最新的狀態(innodb_flush_log_at_trx_commit)。Redo Log是物理日誌,它實際操作的是記錄實際存儲的一個物理頁。

數據存儲過程:
如下圖所示,向數據庫寫數據的時候會先寫到用戶空間(當前進程內存中),在寫到內核空間(操作系統內存),最後才寫到磁盤中
在這裏插入圖片描述
Redo Log(前滾日誌/重做日誌)中innodb_flush_log_at_trx_commit參數說明:

  • 0:如果innodb_flush_log_at_trx_commit的值爲0,log buffer每秒就會被刷寫日誌文件到磁盤,提交事務的時候不做任何操作。
    當設置爲0,該模式速度最快,但不太安全,mysqld進程的崩潰會導致上一秒鐘所有事務數據的丟失。
  • 1:當設爲默認值1的時候,每次提交事務的時候,都會將log buffer刷寫到日誌。
    當設置爲1,該模式是最安全的,但也是最慢的一種方式。在mysqld 服務崩潰或者服務器主機crash的情況下,binary log 只有可能丟失最多一個語句或者一個事務。
  • 2:如果設爲2,每次提交事務都會寫日誌,但並不會執行刷的操作。每秒定時會刷到日誌文件。要注意的是,並不能保證100%每秒一定都會刷到磁盤,這要取決於進程的調度。
    當設置爲2,該模式速度較快,也比0安全,只有在操作系統崩潰或者系統斷電的情況下,上一秒鐘所有事務數據纔可能丟失。
    在這裏插入圖片描述

undo和redo對比總結:以當前時間點來區分,undo log是保持之前數據的狀態,redo log是保持之後數據的狀態。

隔離性實現原理:鎖

①事務具有隔離性,理論上來說事務之間的執行不應該相互產生影響,其對數據庫的影響應該和它們串行執行時一樣。
②然而完全的隔離性會導致系統併發性能很低,降低對資源的利用率,因而實際上對隔離性的要求會有所放寬,這也會一定程度造成對數據庫一致性要求降低。
③SQL標準爲事務定義了不同的隔離級別,從低到高依次是:
讀未提交(READ UNCOMMITTED):對事務處理的讀取沒有任何限制,不推薦
讀已提交(READ COMMITTED)
可重複讀(REPEATEBLE READ:MySQL默認的隔離級別
串行化(SERIALIZABLE)
在這裏插入圖片描述
這篇文章講的很詳細,這裏就不再擴展了:髒讀、幻讀和不可重複讀 + 事務隔離級別
另外這一篇是代碼演示:MYSQL演示關係型數據庫的隔離級別

MySQL鎖機制

  • 在MySQL中,鎖可以分爲兩類:
    共享鎖:共享鎖定是將對象數據別爲只讀形式,不能進行更新,所以也成爲讀取鎖定(select 。。。。lock in share mode);
    排它鎖:排他鎖定是當前執行INSERT/UPDATE/DALETE的時候,其它事務不能讀取該數據,因此也成爲寫入鎖定(例:select 。。。。for update)。
    (如果不加上面的鎖,則MySQL默認情況下爲一致性非鎖定讀;加了上面的鎖則爲一致性鎖定讀。
    另外InnoDB存儲引擎和MyISAM存儲引擎的鎖是有區別的,InnoDB加的是行鎖也可以加表鎖,MyISAM加的是表鎖,對於InnoDB存儲引擎來說如果不通過索引條件查詢的時候,則默認爲表鎖。)

  • 排他鎖和共享鎖兼容性問題:兩個都用共享鎖的sql語句不會衝突,都能運行;其中有一個sql語句先使用排他鎖的話,就會導致另外一條sql語句被鎖死

兼容性 排他鎖 共享鎖
排他鎖 不兼容 不兼容
共享鎖 不兼容 兼容
  • 基於鎖的併發控制流程
    ①事務根據自己對數據項進行的操作類型申請相應的鎖(讀申請共享鎖,寫申請排他鎖) 。
    ②申請鎖的請求被髮送給鎖管理器。鎖管理器根據當前數據項是否已經有鎖以及申請的和持有的鎖是否衝突決定是否爲該請求授予鎖。
    ③若鎖被授予,則申請鎖的事務可以繼續執行;若被拒絕,則申請鎖的事務將進行等待,直到鎖被其他事務釋放。

  • 鎖的粒度:鎖定對象的大小是鎖的粒度,分別有記錄、表、數據庫三個粒度

  • 另外數據庫也會有死鎖的情況:如下圖所視(這裏先設定了sql語句不自動提交,即寫完sql語句後還需要commit纔會隱式的提交事務),因爲該表有主鍵,所以sql語句執行是行鎖,即對每一個行記錄進行加鎖(或者說每個索引;主鍵是特殊的索引)
    ①這時候左邊第一條sql語句先對id爲1的記錄進行查詢,但還沒提交,右邊第一條sql語句對id爲2的記錄進行查詢也是沒提交。
    ②這時候左邊第二條sql語句對id爲2的記錄進行查詢,這時候會先卡住,然後再去右邊輸入第二條sql語句對id爲1的記錄進行查詢(對應第一個圖),這是因爲id爲2的記錄已經被右邊sql語句先排他鎖了。
    ③這時候到右邊輸入第二條sql語句對id爲1的記錄進行查詢,這時候左邊的第二條sql語句就會顯示出結果,右邊第二條sql語句則報錯(對應第二個圖),這是因爲發生了死鎖瞭然後InnoDB引擎對死鎖有檢測機制:會優先回滾undo log日誌量比較少的,所以右邊的undo log日誌量比較少所以就回滾並爆出一個錯誤提示,而左邊因爲右邊回滾了導致所釋放所以id爲2的語句獲得行鎖進而得到結果。

MySQL對死鎖檢測有兩種方式:
第一種爲超時等待,如果發生死鎖,在一定時間內不能兩方都不釋放鎖的話,會自動把undo log日誌量少的先把鎖釋放掉。
第二種爲等待圖,圖有結點和邊組成,每個結點就代表一個事務,當這個圖有迴環的情況時候就代表發生了死鎖,就會回滾。圖有兩種遍歷規則:廣度優先遍歷和深度優先遍歷,等待圖就用深度優先遍歷進行檢測。

在這裏插入圖片描述
在這裏插入圖片描述

  • 另外有一種鎖叫間隙鎖,這種鎖比較少被提及到,間隙鎖主要是來解決幻讀這種情況的。舉個例子有三行數據,分別爲2、4和7。這三行數據用的就是行鎖,而三行數據之間範圍的數據就爲間隙鎖,如(-∞,2)、(2,4)、(4,7)和(7,+∞)用的就是間隙鎖,用了間隙鎖後就不能往中間這些範圍插入數據,防止幻讀現象出現。此外如果用的是唯一索引,則間隙鎖蛻化爲行鎖。(有一種叫next key的鎖,它表示行鎖+間隙鎖)

  • 擴展:除了鎖可以實現併發控制之外,還有其他策略:
    基於時間戳的併發控制
    基於有效性檢查的併發控制
    基於快照隔離的併發控制

一致性實現原理

在事務的四個特點中,一致性是事務的根本追求,而在某些情況下會對事務的一致性造成破壞:
- 事務的併發執行
- 事務故障或系統故障
數據庫系統通過併發控制技術和日誌恢復技術來避免這種情況的發生
- 併發控制技術保證了事務的隔離性,使數據庫的一致性狀態不會因爲併發執行的操作被破壞。
- 日誌恢復技術保證了事務的原子性,使一致性狀態不會因事務或系統故障被破壞。同時使已提交的對數據庫的修改不會因系統崩潰而丟失,保證了事務的持久性。

索引

索引大致內容如下

  • 索引的數據結構。
  • 索引的分類。
  • 索引的技術名詞:回表、最左匹配、索引覆蓋、索引下推
  • 索引的優化

索引是什麼?
索引幫助MySQL高效獲取數據的數據結構。
索引存儲在文件系統中。
索引的文件存儲形式與存儲引擎有關。(InnoDB存儲引擎文件爲idb文件,MyISAM存儲引擎文件有兩個,一個叫myd放數據文件,一個叫myi放索引文件)
索引文件的結構:hash(Memory存儲引擎)、二叉樹、B樹、B+樹(大部分數據庫)。另外InnoDB存儲引擎可以轉化爲自適應的hash表(即MySQL內部有判斷機制會將B+樹轉換會hash表,但是該過程用戶無法干預)。

索引的數據結構

索引的結構主要爲B+樹(官網上寫了B樹,但其實是B+樹,另外還有hash索引和R-Tree,但是主要還是B+樹)
那MySQL索引爲什麼選擇用B+樹,而不用別的數據結構,例如紅黑樹,二叉樹或B樹呢?
爲什麼MySQL數據庫索引選擇使用B+樹?爲什麼Mysql用B+樹做索引而不用B樹 這兩篇篇文章說的很詳細,可以看一下。
這裏大致說一下:

  • 用hash表的索引格式:
    hash取模存儲數據的時候可能照成有些桶的鏈表會特別長,而有些則很短,即存儲散列不均勻,導致存儲的浪費。
    利用hash存儲的話需要將所有的數據文件添加到內存,比較耗費內存空間;
    如果所有的查詢都是等值查詢,那麼hash確實很快,但是在企業或者實際工作環境中範圍查找的數據更多,而不是等值查詢,因此hash就不太合適了。
  • 二叉樹和紅黑樹的索引格式:無論是二叉樹還是紅黑樹都會因爲樹的深度過深而造成io次數變多,影響數據讀取的效率。(數據結構中各種樹
  • B樹的索引格式:
    B樹相對上面的索引格式更加合適,B樹有以下的特點
    優點:
    1.所有鍵值分佈在整棵樹中
    2.搜索有可能在非葉子結點結束,在關鍵字全集內做一次查找,性能逼近二分查找
    3.每個節點最多擁有M個子樹
    4.根節點至少有2個子樹
    5.分支節點至少擁有m/2顆子樹(除根節點和葉子節點外都是分支節點)
    6.所有葉子節點都在同一層,每個節點最多可以有m-1個key,並且以升序排列
    缺點:
    1.每個節點都有key,同時也包含data,而每個頁存儲空間是有限的,如果data比較大的話會導致每個節點存儲key數量變小
    2.當存儲的數據量很大的時候會導致深度較大,增大查詢時磁盤io次數,進而影響查詢性能

B樹如下圖所視,但是B樹存取數據量還是不夠大。
在這裏插入圖片描述

  • B+樹的索引格式:
    1.B+樹每個節點可以包含更多的節點,這個做的原因有兩個,第一個原因是爲了降低樹的高度,第二個原因是將數據範圍變爲多個區間,區間越多,數據檢索越快。
    2.非葉子節點存儲key,葉子節點存儲key和數據。
    3.葉子節點兩兩指針互相連接(符合磁盤的預讀特性·),順序查詢性能更高。B+樹上有兩個頭指針,一個指向根節點,另一個指向關鍵字最小的葉子節點,而且所有葉子節點(即數據節點)之間是一種鏈式環結構,因此可以對B+樹進行兩種查找運算,另一種是從根節點開始,進行隨機查找。
    在這裏插入圖片描述
    在這裏插入圖片描述

B+樹的增刪

瞭解B+樹的增刪之前先了解一下聚創索引和非聚簇索引:MySQL聚簇索引和非聚簇索引的理解
聚簇索引就有點像字典中按拼音排序的目錄,目錄的先後排序位置是和正文中先後的排序是一樣的(例如:目錄中拼音a在排第一,正文中拼音爲a的文字也拍在最前面)。
而非聚簇索引就是除了拼音排序外的其他排序,例如偏旁部首的排序。他們正文的實際順序和目錄中的順序不一致

然後是B樹和B+樹的增刪(圖有點多就不畫了,這一篇已經講的很詳細了,而且不難理解): B樹和B+樹的插入、刪除圖文詳解

然後還要注意一點InnoDB和MyISAM雖然索引都是B+樹結構,但是還是有區別的,如下圖所視:InnoDB的葉子節點data是直接存數據(上文說過InnoDB存儲引擎文件結構只有一個文件);而MyISAM的葉子節點Data是存儲地址值(MyISAM存儲引擎文件結構有兩個文件,一個叫MYD爲數據文件,一個叫MYI爲索引文件)
在這裏插入圖片描述
注意:

  1. InnoDB是通過B+樹結構對主鍵創建索引,然後葉子節點中存儲記錄,如果沒有主鍵,那麼就會選唯一鍵,如果沒有唯一鍵,那麼會生成一個6字節的row id作爲主鍵(上文說的聚簇索引)
  2. 如果創建索引的鍵是其他字段,那麼在葉子節點中存儲的是該記錄的主鍵,然後再通過主鍵索引找到對應的記錄,叫做回表(非聚簇索引)
    在這裏插入圖片描述

索引的分類

索引有以下的分類:主鍵索引、輔助索引、唯一索引、全文索引、組合索引

  • 主鍵索引:數據庫關係圖中爲表定義主鍵將自動創建主鍵索引,主鍵索引是唯一索引的特定類型。該索引要求主鍵中的每個值都唯一。當在查詢中使用主鍵索引時,它還允許對數據的快速訪問。

  • 輔助索引:也叫普通索引或二級索引,最基本的索引類型,沒有唯一性之類的限制。例如一個表中有ID號和NAME兩個屬性,ID號爲主鍵,NAME爲普通列,給NAME添加索引就是輔助索引。但是要注意的是,輔助索引的葉子節點數據塊放的不是實際的行記錄,而是主鍵,然後通過主鍵到主鍵索引中找實際的行記錄。(叫做回表,上面的B+樹增刪的第一個鏈接:MySQL聚簇索引和非聚簇索引的理解,裏面最後一個圖和例子就是回表,不懂可以看一下)

    這裏用兩條語句,用上面說的例子:
    select * from table where name =a(要回表)
    select id from table where name=a(不需要回表,速度更快)
    

    上面的第二條SQL語句爲覆蓋索引:在查詢輔助索引的時候,如果葉子節點中保存的剛好是要查詢的字段數據,那麼此時就叫做索引覆蓋,換句話說查詢列要被所建的索引覆蓋。

  • 組合索引:多個屬性組成一個索引,即爲組合索引。
    組合索引相關的知識點: 最左匹配原則
    索引下推
    select * from table where name=?and age=?其中name和age爲組合索引。
    在MySQL5.6之前的解決方案爲先根據name把所有的數據查詢回來,然後再server層進行age字段的數據篩選。
    在MySQL5.6及以上版本則從存儲引擎拉取數據的時候,會根據name,age兩個字段做篩選,將符合條件的拉去回,這就是索引下推。詳細一點可以看上面的鏈接。

    擴展:謂詞下推
    直接一點看個例子:select t1.name,t2.name from t1 join t2 on t1.id=t2.id;這裏有兩個表進行連接查詢,每個表有100個列。
    這時候有個問題思考一下,有兩種執行方法,思考一下哪一種執行的更快?
    1、先把兩張表按照id字段做關聯,然後取出name字段。
    2、先把所有需要用到的字段查詢出來,然後在做關聯。
    答案是第2個,這裏100個列爲重點,也就是說除了name和id外還有98個屬性,那麼如果用第一種方法做關聯時就會涉及到很多多餘的數據,而第二種方法取出需要用的字段再關聯相對會好很多。

  • 唯一索引:不允許具有索引值相同的行,從而禁止重複的索引或鍵值。系統在創建該索引時檢查是否有重複的鍵值,並在每次使用 INSERT 或 UPDATE 語句添加數據時進行檢查。
    唯一索引和主鍵索引的區別:
    (1)對於主健/unique constraint , oracle/sql server/mysql等都會自動建立唯一索引;
    (2)主鍵不一定只包含一個字段,所以如果你在主鍵的其中一個字段建唯一索引還是必要的;
    (3)主健可作外健,唯一索引不可;
    (4)主健不可爲空,唯一索引可以;
    (5)主健也可是多個字段的組合;
    (6)主鍵與唯一索引不同的是:
    主鍵索引有not null屬性;
    主鍵索引每個表只能有一個。

  • 全文索引:這個用的比較少,大概瞭解一下。

MySQL的優化策略

MySQL explain詳解

explain select XXX from XXX這裏有個關鍵字叫‘explain’,它是用來SQL語句的執行計劃。
在這裏插入圖片描述
這裏講幾個關鍵的列,具體可以看上面的鏈接:

  • id:表示查詢語句執行的順序,有多少條select語句就有多少個id。語句的執行順序也是按照id的順序來的。
  • type:表示語句的關聯類型或訪問類型。即MySQL決定如何查找表中的行。
    依次從最優到最差分別爲:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL(每個代表什麼具體看鏈接)
  • possible_keys:這一列顯示查詢可能使用哪些索引來查找。
  • key:這一列顯示mysql實際採用哪個索引來優化對該表的訪問。
  • Extra:一些額外的信息。
    1.distinct: 一旦mysql找到了與行相聯合匹配的行,就不再搜索了
    2.Using index:這發生在對錶的請求列都是同一索引的部分的時候,返回的列數據只使用了索引中的信息,而沒有再去訪問表中的行記錄。是性能高的表現。(上文的覆蓋索引)
    3.Using where:mysql服務器將在存儲引擎檢索行後再進行過濾。就是先讀取整行數據,再按 where 條件進行檢查,符合就留下,不符合就丟棄。
    4.Using temporary:mysql需要創建一張臨時表來處理查詢。出現這種情況一般是要進行優化的,首先是想到用索引來優化。
    5.Using filesort:mysql 會對結果使用一個外部索引排序,而不是按索引次序從表裏讀取行。此時mysql會根據聯接類型瀏覽所有符合條件的記錄,並保存排序關鍵字和行指針,然後排序關鍵字並按順序檢索行信息。這種情況下一般也是要考慮使用索引來優化的。

MySQL調優

  1. 性能監控
  • 使用show profile查詢剖析工具,可以指定具體的type,具體如下:
    在這裏插入圖片描述
    從上面的圖可以看出我們查詢的時候顯示的時間爲0.00秒,並不是真的0秒,而是時間過小無法顯示 在這裏插入圖片描述
    這時候我們就要先把profiling開啓,然後再進行查詢一條語句。
    在這裏插入圖片描述
    並用show profile顯示出具體過程所需要的時間,左邊爲執行這條語句的過程,右邊爲具體多長時間。
    在這裏插入圖片描述
    另外可以用上面的語句查看更多信息。
    *注意:官網顯示show profile 在後續版本可能將會被棄用,提倡用performance schema

  • 使用performance schema來更加容易監控MySQL
    performance schema是單獨的一個庫,這個庫的存儲引擎就是performance schema,它裏面展示MySQL當前性能監控的東西(裏面有87張表,這方面一般是由專業的DBA數據庫管理員來做)。

  • 使用show processlist查看連接的線程個數,來觀察是否有大量線程處於不正常的狀態或者其他不正常的特徵

  1. schema與數據類型優化
    即在設計表時候應當就要對優化,而不是等到寫SQL語句纔來優化
  • 數據類型的優化
  • 合理使用範式和反範式
  • 主鍵的選擇
    代理主鍵:設置id值和業務無關則爲代理主鍵
    自然主鍵:設置id值和業務相關則爲自然主鍵,例如id號就是學生學號
  • 字符集的選擇
  • 存儲引擎的選擇
  • 適當的數據冗餘
  • 適當的拆分
  1. 執行計劃(上問explain已經提及到,或者自行百多MySQL執行計劃)
  2. 通過索引進行優化,上文已經說了很多關於索引的知識點,這裏在進行簡單總結和補充
    在這裏插入圖片描述
  3. 查詢優化
  • 查詢慢的原因:網絡、CPU、IO、上下文切換、系統調用、生成統計信息、鎖等待時間
  • 優化數據訪問:
    ①查詢性能低下的主要原因是訪問的數據太大,某些查詢不可避免的需要篩選大量的數據,我們可以通過減少範文數據量的方式進行優化:一、確認應用程序是否在檢索大量超過需要的數據;二、確認mysql服務層是否在分析大量超過需要的數據行。
    ②是否向數據庫請求了不需要的數據:一、查詢不需要的記錄;二、多表關聯時返回了全部列;三、總是取出全部列;四、重複查詢相同的數據
  • 執行的優化:查詢緩存;查詢優化處理。
  • 優化特定類型的查詢:
    ①優化count()查詢;②優化關聯查詢;③優化子查詢;④優化limit分頁:優化此類查詢的最簡單的方法是儘可能地使用覆蓋索引,而不是查詢所有的列;⑤優化union查詢:除非確實需要服務器消除重複的行,否則一定要使用union all,因此沒有all關鍵字,mysql會在查詢的時候給臨時表加上distinct的關鍵字,這個操作的代價很高;⑥推薦使用用戶自定義變量
  1. 分區表
  • 分區表的應用場景
  • 分區表的限制
  • 分區表的原理
  • 分區表的類型
  • 如何使用分區表
  • 在使用分區表的時候要注意的問題
  1. 服務器參數設置
  • general:通用參數
  • character:字符集參數
  • connection:連接參數
  • log:日誌參數
  • cache:緩存參數
  • INNODB:InnoDB的相關參數

擴展:MySQL集羣相關知識點,主從複製、讀寫分離、分庫分表(後續學習)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章