MySQL-必知必會

1. 索引

1.1 B+ Tree索引

答:InnoDB默認的索引方式。有序索引,將相鄰數據都存在一起,把隨機IO變成順序IO。

1.1.1 底層數據庫爲什麼用B+樹

答:總結:

  • B+樹的數據全部存儲在葉子結點中,B樹的非葉子節點也會存儲數據。使得B+樹的查詢是從root到葉子節點。
  • B+樹的內部結點比B樹更,讀寫代價小。

1.1.2 聚簇索引(主索引)和非聚簇索引(輔助索引)

答:B+ Tree索引分爲主索引和輔助索引。

  • 聚簇索引(主索引):葉子結點存儲整行數據。直接找key就能獲得數據。一個表只能包含一個主索引。
  • 非聚簇索引(輔助索引):葉子結點存儲主鍵的值。需要先取得主鍵,再用此主鍵走一遍主索引。

1.1.3 回表

答:回表就是指普通索引查詢,先搜索普通索引樹獲得主鍵值,再到主鍵索引樹搜索一次。

1.2 哈希索引

答:以O(1)時間搜索,但無法排序分組,只能精準查找
自適應哈希索引:當某個索引被頻繁引用時,在B+樹索引上再創建一個哈希索引,提供快速查找。

1.3 全文索引

答:MyISAM支持全文索引,用於查找文本中的關鍵詞,match against查找。5.6.4版本後InnoDB也支持。

1.4 索引優化

1.4.1 索引列順序

答:把選擇性最強的索引放在前面。索引選擇性=不重複的索引值 / 記錄總數。

1.4.2 覆蓋索引

答:覆蓋索引是select的數據列只用從索引中就能夠取得,不必讀取數據行,即查詢列要被所建的索引覆蓋。

1.4.3 前綴索引

答:根據選擇性,只索引字符串的最左M個字符

1.4.4 索引下推

答:聯合索引時,在遍歷索引時,先對其餘其他字段判斷,過濾不滿足條件的記錄,減少回表次數和數據數量。

1.5 索引使用情景

答:

  • 經常需要條件查詢的列且有區分度,可以建立索引。
  • 小型表,不需索引,全表掃描就可。中到大型表,推薦索引。
  • 特大型表,索引的建立和維護代價過大,不如用分區技術。

2. 存儲引擎

答:MySQL是一個關係型數據庫,默認端口號是3306。5.5前用MyISAM,5.5後用InnoDB。

2.1 InnoDB

答:5.5版本後的默認引擎。默認可重複讀的事務隔離級別,主索引是聚簇索引,內部做了諸多優化(插入緩衝區、哈希索引等),支持熱備份。

2.2 MyISAM

答:5.5版本前的默認引擎。

2.3 MyISAM和InnoDB區別

答:區別如下:

  • 行級鎖。MyISAM只支持表級鎖,InnoDB支持表級鎖和行級鎖。
  • 事務支持。MyISAM不支持事務,InnoDB提供事務支持和崩潰恢復。
  • 外鍵。MyISAM不支持外鍵,InnoDB支持外鍵。
  • InnoDB支持多版本併發控制,可以處理大量數據。
  • MyISAM支持全文索引。

總結一下:InnoDB支持行級鎖、事務、外鍵

3. 事務

答:事務就是邏輯上的一組操作,要麼都執行,要麼都不執行。

3.1 四大特性ACID

答:ACID。

  • 原子性(Atomicity):事務是最小的執行單位,要麼全部執行,要麼全部不執行。
  • 一致性(Consistency):保證數據庫狀態要一致,多個事務對同一數據讀取結果是相同的。
  • 隔離性(Isolation):多個事務併發,各個事務是不受其他事務干擾的。
  • 持久性(Durability):一個事務一旦提交,此修改在數據庫中是持久保存的。

3.2 併發事務的問題

  • 髒讀(Dirty read):事務A讀取了事務B修改未提交的數據,B回滾,A的數據不一致。
  • 丟失修改(Lost to modify):事務A和B同時讀取數據,A修改後先提交,B修改後再提交會將A的修改覆蓋。
  • 不可重複讀(Unrepeatable read):事務A兩次讀取數據間,有事務B修改更新,導致兩次讀取數據不一致。
  • 幻讀(Phantom read):事務A兩次讀取數據間,有事務B增加了記錄,導致兩次讀取數據記錄數不一致。
    注:幻讀指結構上發生變化。不可重複讀指數值上發生變化。

3.3 隔離級別

答:MySQL定義了四個隔離級別。

  • 讀未提交(Read-Uncommitted):最低級別,允許讀取尚未提交的數據。會發生髒讀、幻讀、不可重複讀。
  • 讀已提交(Read-Committed):允許讀取已經提交的數據。阻止髒讀。
  • 可重複讀(Repeatable-Read):讀事務時禁止寫事務,寫事務時阻止一切。阻止髒讀和不可重複讀。InnoDB默認隔離級別
  • 可串行化(Serialization):最高級別。所有事務依次執行,不會互相干擾。阻止三個事務問題。

4. 鎖機制

4.1 按粒度分

答:分爲表級鎖和行級鎖。

  • 表級鎖:粒度最大的鎖。對整張表加鎖,資源消耗少,加鎖快,不會死鎖,但鎖衝突概率高。
  • 行級鎖:粒度最小的鎖。當前操作行加鎖,加鎖慢,開銷大,會死鎖,但大大減少衝突。

4.1.1 常見行級鎖

答:有三種。

  • Record Lock:單行鎖,鎖定符合條件的行。
  • Gap Lock:間隙鎖,鎖定一個範圍,不含記錄本身。
  • Next-key Lock:Record+Gap,鎖定一個範圍,含記錄本身

4.1.2 表級鎖使用場景

答:當事務比較複雜,使用行級鎖容易死鎖回滾。更新大表的大部分數據,表級鎖效率更好。

4.1.3 行級鎖何時會鎖住整張表

答:更新的列沒有建立索引,會直接鎖住整張表。

4.2 按是否可寫分

答:可以進一步劃分爲共享鎖和排他鎖。

  • 共享鎖(Shared Lock):讀鎖。鎖定的資源能被其他用戶讀取,但不能修改,直到資源上的S鎖全部被釋放。
  • 排他鎖(Exclusive Lock):寫鎖。事務T對數據A加X鎖,則只允許T讀取修改,直到X鎖釋放。在更新操作,如insert、update 或 delete時,始終應用排它鎖。

4.3 死鎖

答:MySQL中的死鎖是多個事務使用行級鎖對某行數據加鎖造成

4.3.1 解決方案

答:分爲兩個方面。
業務層面:

  • 指定鎖的獲取資源順序。(操作系統中的哲學家就餐問題)
  • 同一個事務一次鎖定儘可能多的資源。
  • 事務拆分成小事務。

數據庫設置:

  • 設置超時時間。InnoDB默認是50s。
  • 開啓死鎖檢測。發生死鎖時,迴歸死鎖鏈上一個事務,讓其他事務繼續執行。

4.4 悲觀鎖和樂觀鎖

答:分爲:

  • 悲觀鎖:利用數據庫的鎖機制實現,再整個數據處理過程都加鎖,保證排他性
  • 樂觀鎖CAS實現。按照當前事務的數據和數據表中數據是否一致,來決定是否執行操作。

4.4.1 樂觀鎖的ABA問題

答:加入數據版本記錄機制或者使用時間戳。
ABA問題:事務X讀取數據A時,事務Y修改成B,又修改回A,事實上數據發生過改變的,存在併發問題,但事務X無法得到數據發生過變化。

5. 大表優化

答:單表記錄數過大時,數據庫性能下降明顯,需要進行一系列的優化。

5.1 限定數據範圍

最簡單的手段,限制數據範圍條件來查詢數據。

5.2 讀寫分離

主服務器處理寫操作或實時性高的讀操作,從服務器處理讀操作

  • 實現方式:增設代理服務器,決定將應用傳來的請求轉發到哪個服務器。
  • 原因:極大減少了鎖的爭用;用冗餘換可用性;從服務器可用MyISAM節約資源。
    在這裏插入圖片描述

5.3 水平拆分

水平拆分將一張表A拆分成多個相同結構的表a1,a2,a3存儲,每個子表只佔一部分數據。
又細分爲庫內分表和分庫分表。

  • 庫內分表:子表仍在一個數據庫實例中,仍要競爭同一個物理機資源。
  • 分庫分表:子表分散在不同數據庫中。

優點:業務改造簡單;解決了單表數據量過大的問題。
缺點:跨庫的一致性難保證;join關聯性能差;擴容和維護難度過大。

5.4 垂直拆分

又細分爲垂直分庫和垂直分表。

  • 垂直分庫:基於業務劃分數據庫,讓每個業務都有獨立數據庫。
  • 垂直分表:基於數據表的列切分,把一張列1-7的表拆成列1-4和列1 5-7的表。

優點:業務解耦;高併發下提升了性能。
缺點:增加了業務複雜度;主鍵冗餘;數據量過大仍未解決,需要配合水平拆分。

5.5 拆分後的問題

  1. 事務一致性
    兩階段提交性能較差。通常追求最終一致性,出現問題進行事務補償。
  2. 分頁和排序
    排序字段非分片字段,需要先子表內排序,再彙總排序,最後返回給用戶。
  3. 全局唯一主鍵
    子庫的自增主鍵滿足不了需求,所以使用分佈式ID用作全局唯一主鍵
    注:所以推薦使用成熟的中間件,如sharding-jdbc,Atlas,Cobar

6. MySQL架構和執行流程

6.1 基礎架構

答:MySQL主要分爲Server層和存儲引擎層。

  • Server層:跨存儲引擎的功能都在此實現,如視圖、觸發器、函數等,有一個binlog日誌。
  • 存儲引擎:負責數據的存儲和讀取。常用的是InnoDB,自帶redolog模塊。

6.2 基本組件

答:Server層有五個主要組件。

  • 連接器:身份驗證和權限設置。
  • 查詢緩存:執行查詢語句前,先查有沒有緩存的結果集。(8.0後廢棄)
  • 分析器:沒有命中緩存,則進入分析器進行詞法和語法分析。
  • 優化器:按照MySQL認爲最優的方案執行。
  • 執行器:用戶有權限,則調用存儲引擎,執行語句。

6.3 執行流程

6.3.1 查詢語句

權限校驗---->查詢緩存---->分析器---->優化器---->權限校驗---->執行器---->引擎

6.3.2 更新語句

分析器---->權限校驗---->執行器---->引擎---->redo log prepare---->binlog---->redo log commit
以修改張三的年齡爲例:

  • 先查詢到張三這條數據,如果有緩存,用緩存。
  • 拿到查詢的語句,把 age 改爲 19。
  • 調用引擎 API 接口,寫入這一行數據,InnoDB 引擎把數據保存在內存中,同時記錄 redo log,此時 redo log 進入 prepare 狀態。
  • 通知執行器,執行完成,可以提交。
  • 執行器收到通知後記錄 binlog,調用引擎接口,提交 redo log 爲提交狀態。
  • 更新完成。

7. 日誌模塊和關鍵字區分

7.1 日誌模塊

答:MySQL主要有redolog和binlog。

  • redolog(重做日誌):InnoDB特有,物理日誌。記錄哪個數據頁做了修改
  • binlog(歸檔日誌):Server層自帶,邏輯日誌,記錄本次修改的SQL語句。通常使用row二進制格式,保證數據記錄的準確性。

7.1.1 redolog和binlog的兩階段提交

答:用兩階段提交是爲了保證數據一致性。redolog prepare引擎數據保存 -> binlog執行器收到信號 -> redolog commit執行器調用
針對此種提交方式發生異常宕機,MySQL先判斷redolog是否完整,完整則直接提交。redolog只是prepare狀態,查看binlog是否完整,完整就繼續提交redolog,否則回滾事務。

7.1.2 MySQL爲什麼會突然慢一下

答:更新數據庫時,先寫日誌,等合適時間再更新磁盤。當redolog寫滿,需要flush髒頁,將數據寫入磁盤,這是會使執行速度慢一下。

7.2 where、group by和having關鍵字

答:三個關鍵字總結爲:

  • where用來篩選from子句中產生的行;
  • group by用來分組where子句的輸出;
  • having用來從group by分組結果中篩選行。

7.2.1 where和having差別

答:where用在分組前,針對行。having用在分組後,針對組,能用max、avg等聚合函數。

7.2.2 執行順序

where找符合條件的數據---->group by分組---->聚集函數計算每個組---->having去掉不合要求的組

8. 優化

參考 https://www.cnblogs.com/huchong/p/10219318.html 作者:聽風。

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