MySQL基础总结(二)
MySQL基礎總結(二)
文章目錄
- MySQL基礎總結(二)
- 四、索引
- 7.MyISAM主鍵索引與輔助索引的結構
- 8.InnoDB主鍵索引與輔助索引的結構
- **`主鍵索引`**
- **`輔助(非主鍵)索引:`**
- InnoDB 索引結構需要注意的點
- 9.那為什么推薦使用整型自增主鍵而不是選擇UUID?
- 10.為什么非主鍵索引結構葉子節點存儲的是主鍵值?
- 11.Hash索引
- 12.full-text全文索引
- 13.R-Tree空間索引
- 14.為什么Mysql索引要用B+樹不是B樹?
- 15.面試官:為何不采用Hash方式?
- 16.哪些情況需要創建索引
- 17.哪些情況不要創建索引
- 18.MySQL高效索引
- 五、SQL
- 1.count(*) 和 count(1)和count(列名)區別
- 執行效果上:
- 執行效率上:
- 2.MySQL中 in和 exists 的區別?
- 3.UNION和UNION ALL的區別?
- 4.SQL執行順序
- 六、事務
- 1.ACID — 事務基本要素
- 并發事務處理帶來的問題
- 幻讀和不可重復讀的區別:
- 并發事務處理帶來的問題的解決辦法:
- 2.事務隔離級別
- Read uncommitted
- Read committed
- Repeatable read
- Serializable 序列化
- 3.MVCC 多版本并發控制
- 4.事務日志
- 5.事務的實現
- redo log(重做日志) 實現持久化和原子性
- undo log(回滾日志) 實現一致性
- 6.你知道MySQL 有多少種日志嗎?
- 7.MySQL對分布式事務的支持
四、索引
7.MyISAM主鍵索引與輔助索引的結構
MyISAM引擎的索引文件和數據文件是分離的。MyISAM引擎索引結構的葉子節點的數據域,存放的并不是實際的數據記錄,而是數據記錄的地址。
索引文件與數據文件分離,這樣的索引稱為"非聚簇索引"。MyISAM的主索引與輔助索引區別并不大,只是主鍵索引不能有重復的關鍵字。
- 在MyISAM中,索引(含葉子節點)存放在單獨的.myi文件中,葉子節點存放的是數據的物理地址偏移量(通過偏移量訪問就是隨機訪問,速度很快)。
- 主索引是指主鍵索引,鍵值不可能重復;輔助索引則是普通索引,鍵值可能重復。
- 通過索引查找數據的流程:先從索引文件中查找到索引節點,從中拿到數據的文件指針,再到數據文件中通過文件指針定位了具體的數據。輔助索引類似。
8.InnoDB主鍵索引與輔助索引的結構
InnoDB引擎索引結構的葉子節點的數據域,存放的就是實際的數據記錄(對于主索引,此處會存放表中所有的數據記錄;對于輔助索引此處會引用主鍵,檢索的時候通過主鍵到主鍵索引中找到對應數據行),或者說,InnoDB的數據文件本身就是主鍵索引文件,這樣的索引被稱為“聚簇索引”,一個表只能有一個聚簇索引。
主鍵索引
我們知道InnoDB索引是聚集索引,它的索引和數據是存入同一個.idb文件中的,因此它的索引結構是在同一個樹節點中同時存放索引和數據,如下圖中最底層的葉子節點有三行數據,對應于數據表中的id、stu_id、name數據項。
在Innodb中,索引分葉子節點和非葉子節點,非葉子節點就像新華字典的目錄,單獨存放在索引段中,葉子節點則是順序排列的,在數據段中。
Innodb的數據文件可以按照表來切分(只需要開啟innodb_file_per_table),切分后存放在xxx.ibd中,默認不切分,存放在xxx.ibdata中。
輔助(非主鍵)索引:
這次我們以示例中學生表中的name列建立輔助索引,它的索引結構跟主鍵索引的結構有很大差別,在最底層的葉子結點有兩行數據,第一行的字符串是輔助索引,按照ASCII碼進行排序,第二行的整數是主鍵的值。
- 這就意味著,對name列進行條件搜索,需要兩個步驟:
① 在輔助索引上檢索name,到達其葉子節點獲取對應的主鍵;
② 使用主鍵在主索引上再進行對應的檢索操作
這也就是所謂的“回表查詢”
InnoDB 索引結構需要注意的點
- 數據文件本身就是索引文件
- 表數據文件本身就是按 B+Tree 組織的一個索引結構文件
- 聚集索引中葉節點包含了完整的數據記錄
- InnoDB 表必須要有主鍵(原因:聚集索引),并且推薦使用整型自增主鍵(原因:索引字段不應該太長)
- 正如我們上面介紹 InnoDB 存儲結構,索引與數據是共同存儲的,不管是主鍵索引還是輔助索引,在查找時都是通過先查找到索引節點才能拿到相對應的數據,如果我們在設計表結構時沒有顯式指定索引列的話,MySQL 會從表中選擇數據不重復的列建立索引,如果沒有符合的列,則 MySQL 自動為 InnoDB 表生成一個隱含字段作為主鍵,并且這個字段長度為6個字節,類型為整型。
9.那為什么推薦使用整型自增主鍵而不是選擇UUID?
- UUID是字符串,比整型消耗更多的存儲空間;
- 在B+樹中進行查找時需要跟經過的節點值比較大小,整型數據的比較運算比字符串更快速;
- 自增的整型索引在磁盤中會連續存儲,在讀取一頁數據時也是連續;
- UUID是隨機產生的,讀取的上下兩行數據存儲是分散的,不適合執行where id > 5 && id < 20的條件查詢語句。
- 在插入或刪除數據時,整型自增主鍵會在葉子結點的末尾建立新的葉子節點,不會破壞左側子樹的結構;
- UUID主鍵很容易出現這樣的情況,B+樹為了維持自身的特性,有可能會進行結構的重構,消耗更多的時間。
10.為什么非主鍵索引結構葉子節點存儲的是主鍵值?
保證數據一致性和節省存儲空間
- 可以這么理解:商城系統訂單表會存儲一個用戶ID作為關聯外鍵,而不推薦存儲完整的用戶信息,因為當我們用戶表中的信息(真實名稱、手機號、收貨地址···)修改后,不需要再次維護訂單表的用戶數據,同時也節省了存儲空間。
11.Hash索引
主要就是通過Hash算法(常見的Hash算法有直接定址法、平方取中法、折疊法、除數取余法、隨機數法),將數據庫字段數據轉換成定長的Hash值,與這條數據的行指針一并存入Hash表的對應位置;如果發生Hash碰撞(兩個不同關鍵字的Hash值相同),則在對應Hash鍵下以鏈表形式存儲。
檢索算法:在檢索查詢時,就再次對待查關鍵字再次執行相同的Hash算法,得到Hash值,到對應Hash表對應位置取出數據即可,如果發生Hash碰撞,則需要在取值時進行篩選。目前使用Hash索引的數據庫并不多,主要有Memory等。
MySQL目前有Memory引擎和NDB引擎支持Hash索引。
12.full-text全文索引
全文索引也是MyISAM的一種特殊索引類型,主要用于全文索引,InnoDB從MYSQL5.6版本提供對全文索引的支持。
它用于替代效率較低的LIKE模糊匹配操作,而且可以通過多字段組合的全文索引一次性全模糊匹配多個字段。
同樣使用B-Tree存放索引數據,但使用的是特定的算法,將字段數據分割后再進行索引(一般每4個字節一次分割),索引文件存儲的是分割前的索引字符串集合,與分割后的索引信息,對應Btree結構的節點存儲的是分割后的詞信息以及它在分割前的索引字符串集合中的位置。
13.R-Tree空間索引
空間索引是MyISAM的一種特殊索引類型,主要用于地理空間數據類型
14.為什么Mysql索引要用B+樹不是B樹?
用B+樹不用B樹考慮的是IO對性能的影響,B樹的每個節點都存儲數據,而B+樹只有葉子節點才存儲數據,所以查找相同數據量的情況下,B樹的高度更高,IO更頻繁。
數據庫索引是存儲在磁盤上的,當數據量大時,就不能把整個索引全部加載到內存了,只能逐一加載每一個磁盤頁(對應索引樹的節點)。
其中在MySQL底層對B+樹進行進一步優化:在葉子節點中是雙向鏈表,且在鏈表的頭結點和尾節點也是循環指向的。
15.面試官:為何不采用Hash方式?
因為Hash索引底層是哈希表,哈希表是一種以key-value存儲數據的結構,所以多個數據在存儲關系上是完全沒有任何順序關系的,所以,對于區間查詢是無法直接通過索引查詢的,就需要全表掃描。
所以, 哈希索引只適用于等值查詢的場景。而B+ Tree是一種多路平衡查詢樹,所以他的節點是天然有序的(左子節點小于父節點、父節點小于右子節點),所以對于范圍查詢的時候不需要做全表掃描
哈希索引不支持多列聯合索引的最左匹配規則,如果有大量重復鍵值得情況下,哈希索引的效率會很低,因為存在哈希碰撞問題。
16.哪些情況需要創建索引
- 主鍵自動建立唯一索引
- 頻繁作為查詢條件的字段
- 查詢中與其他表關聯的字段,外鍵關系建立索引
- 單鍵/組合索引的選擇問題,高并發下傾向創建組合索引
- 查詢中排序的字段,排序字段通過索引訪問大幅提高排序速度
- 查詢中統計或分組字段
17.哪些情況不要創建索引
- 表記錄太少
- 經常增刪改的表
- 數據重復且分布均勻的表字段,只應該為最經常查詢和最經常排序的數據列建立索引(如果某個數據類包含太多的重復數據,建立索引沒有太大意義)
- 頻繁更新的字段不適合創建索引(會加重IO負擔)
- where條件里用不到的字段不創建索引
18.MySQL高效索引
覆蓋索引(Covering Index),或者叫索引覆蓋, 也就是平時所說的不需要回表操作就是select的數據列只用從索引中就能夠取得,不必讀取數據行,MySQL可以利用索引返回select列表中的字段,而不必根據索引再次讀取數據文件,換句話說查詢列要被所建的索引覆蓋。
索引是高效找到行的一個方法,但是一般數據庫也能使用索引找到一個列的數據,因此它不必讀取整個行。畢竟索引葉子節點存儲了它們索引的數據,當能通過讀取索引就可以得到想要的數據,那就不需要讀取行了。一個索引包含(覆蓋)滿足查詢結果的數據就叫做覆蓋索引。
判斷標準:使用explain,可以通過輸出的extra列來判斷,對于一個索引覆蓋查詢,顯示為using index,MySQL查詢優化器在執行查詢前會決定是否有索引覆蓋查詢
五、SQL
1.count(*) 和 count(1)和count(列名)區別
執行效果上:
- count(*)包括了所有的列,相當于行數,在統計結果的時候,不會忽略列值為NULL
- count(1)包括了所有列,用1代表代碼行,在統計結果的時候,不會忽略列值為NULL
- count(列名)只包括列名那一列,在統計結果的時候,會忽略列值為空(這里的空不是只空字符串或者0,而是表示null)的計數,即某個字段值為NULL時,不統計。
執行效率上:
- 列名為主鍵,count(列名)會比count(1)快
- 列名不為主鍵,count(1)會比count(列名)快
- 如果表多個列并且沒有主鍵,則 count(1) 的執行效率優于 count(*)
- 如果有主鍵,則 select count(主鍵)的執行效率是最優的
- 如果表只有一個字段,則 select count(*) 最優。
2.MySQL中 in和 exists 的區別?
- exists:exists對外表用loop逐條查詢,每次查詢都會查看exists的條件語句,當exists里的條件語句能夠返回記錄行時(無論記錄行是的多少,只要能返回),條件就為真,返回當前loop到的這條記錄;反之,如果exists里的條件語句不能返回記錄行,則當前loop到的這條記錄被丟棄,exists的條件就像一個bool條件,當能返回結果集則為true,不能返回結果集則為false
- in:in查詢相當于多個or條件的疊加
- 如果查詢的兩個表大小相當,那么用in和exists差別不大。
- 如果兩個表中一個較小,一個是大表,則子查詢表大的用exists,子查詢表小的用in
3.UNION和UNION ALL的區別?
- UNION和UNION ALL都是將兩個結果集合并為一個,兩個要聯合的SQL語句 字段個數必須一樣,而且字段類型要“相容”(一致);
- UNION在進行表連接后會篩選掉重復的數據記錄(效率較低),而UNION ALL則不會去掉重復的數據記錄;
- UNION會按照字段的順序進行排序,而UNION ALL只是簡單的將兩個結果合并就返回;
4.SQL執行順序
手寫:
SELECT DISTINCT <select_list> FROM <left_table> <join_type> JOIN <right_table> ON <join_condition> WHERE <where_condition> GROUP BY <group_by_list> HAVING <having_condition> ORDER BY <order_by_condition> LIMIT <limit_number>機讀:
FROM <left_table> ON <join_condition> <join_type> JOIN <right_table> WHERE <where_condition> GROUP BY <group_by_list> HAVING <having_condition> SELECT DISTINCT <select_list> ORDER BY <order_by_condition> LIMIT <limit_number>總結:
六、事務
MySQL 事務主要用于處理操作量大,復雜度高的數據。比如說,在人員管理系統中,你刪除一個人員,你即需要刪除人員的基本資料,也要刪除和該人員相關的信息,如信箱,文章等等,這樣,這些數據庫操作語句就構成一個事務!
1.ACID — 事務基本要素
事務是由一組SQL語句組成的邏輯處理單元,具有4個屬性,通常簡稱為事務的ACID屬性。
- A (Atomicity) 原子性:整個事務中的所有操作,要么全部完成,要么全部不完成,不可能停滯在中間某個環節。事務在執行過程中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務從來沒有執行過一樣
- C (Consistency) 一致性:在事務開始之前和事務結束以后,數據庫的完整性約束沒有被破壞
- I (Isolation)隔離性:一個事務的執行不能其它事務干擾。即一個事務內部的操作及使用的數據對其它并發事務是隔離的,并發執行的各個事務之間不能互相干擾
- D (Durability) 持久性:在事務完成以后,該事務所對數據庫所作的更改便持久的保存在數據庫之中,并不會被回滾
并發事務處理帶來的問題
- 更新丟失(Lost Update):事務A和事務B選擇同一行,然后基于最初選定的值更新該行時,由于兩個事務都不知道彼此的存在,就會發生丟失更新問題
- 臟讀(Dirty Reads):事務A讀取了事務B更新的數據,然后B回滾操作,那么A讀取到的數據是臟數據
- 不可重復讀(Non-Repeatable Reads):事務 A 多次讀取同一數據,事務B在事務A多次讀取的過程中,對數據作了更新并提交,導致事務A多次讀取同一數據時,結果不一致。
- 幻讀(Phantom Reads):幻讀與不可重復讀類似。它發生在一個事務A讀取了幾行數據,接著另一個并發事務B插入了一些數據時。在隨后的查詢中,事務A就會發現多了一些原本不存在的記錄,就好像發生了幻覺一樣,所以稱為幻讀。
幻讀和不可重復讀的區別:
- 不可重復讀的重點是修改:在同一事務中,同樣的條件,第一次讀的數據和第二次讀的數據不一樣。(因為中間有其他事務提交了修改)
- 幻讀的重點在于新增或者刪除:在同一事務中,同樣的條件,,第一次和第二次讀出來的記錄數不一樣。(因為中間有其他事務提交了插入/刪除)
并發事務處理帶來的問題的解決辦法:
- “更新丟失”通常是應該完全避免的。但防止更新丟失,并不能單靠數據庫事務控制器來解決,需要應用程序對要更新的數據加必要的鎖來解決,因此,防止更新丟失應該是應用的責任。
- “臟讀” 、 “不可重復讀”和“幻讀” ,其實都是數據庫讀一致性問題,必須由數據庫提供一定的事務隔離機制來解決:
- 一種是加鎖:在讀取數據前,對其加鎖,阻止其他事務對數據進行修改。
- 另一種是數據多版本并發控制(MultiVersion Concurrency Control,簡稱 MVCC 或 MCC),也稱為多版本數據庫:不用加任何鎖, 通過一定機制生成一個數據請求時間點的一致性數據快照 (Snapshot), 并用這個快照來提供一定級別 (語句級或事務級) 的一致性讀取。從用戶的角度來看,好象是數據庫可以提供同一數據的多個版本。
2.事務隔離級別
數據庫事務的隔離級別有4種,由低到高分別為
-
READ-UNCOMMITTED(讀未提交): 最低的隔離級別,允許讀取尚未提交的數據變更,可能會導致臟讀、幻讀或不可重復讀。
-
READ-COMMITTED(讀已提交): 允許讀取并發事務已經提交的數據,可以阻止臟讀,但是幻讀或不可重復讀仍有可能發生。
-
REPEATABLE-READ(可重復讀): 對同一字段的多次讀取結果都是一致的,除非數據是被本身事務自己所修改,可以阻止臟讀和不可重復讀,但幻讀仍有可能發生。
-
SERIALIZABLE(可串行化): 最高的隔離級別,完全服從ACID的隔離級別。所有的事務依次逐個執行,這樣事務之間就完全不可能產生干擾,也就是說,該級別可以防止臟讀、不可重復讀以及幻讀。
-
查看當前數據庫的事務隔離級別:
下面通過事例一一闡述在事務的并發操作中可能會出現臟讀,不可重復讀,幻讀和事務隔離級別的聯系。
數據庫的事務隔離越嚴格,并發副作用越小,但付出的代價就越大,因為事務隔離實質上就是使事務在一定程度上“串行化”進行,這顯然與“并發”是矛盾的。
同時,不同的應用對讀一致性和事務隔離程度的要求也是不同的,比如許多應用對“不可重復讀”和“幻讀”并不敏感,可能更關心數據并發訪問的能力。
Read uncommitted
-
讀未提交,就是一個事務可以讀取另一個未提交事務的數據。
-
事例:老板要給程序員發工資,程序員的工資是3.6萬/月。但是發工資時老板不小心按錯了數字,按成3.9萬/月,該錢已經打到程序員的戶口,但是事務還沒有提交,就在這時,程序員去查看自己這個月的工資,發現比往常多了3千元,以為漲工資了非常高興。但是老板及時發現了不對,馬上回滾差點就提交了的事務,將數字改成3.6萬再提交。
-
分析:實際程序員這個月的工資還是3.6萬,但是程序員看到的是3.9萬。他看到的是老板還沒提交事務時的數據。這就是臟讀。
-
那怎么解決臟讀呢?Read committed!讀提交,能解決臟讀問題。
Read committed
-
讀提交,顧名思義,就是一個事務要等另一個事務提交后才能讀取數據。
-
事例:程序員拿著信用卡去享受生活(卡里當然是只有3.6萬),當他埋單時(程序員事務開啟),收費系統事先檢測到他的卡里有3.6萬,就在這個時候!!程序員的妻子要把錢全部轉出充當家用,并提交。當收費系統準備扣款時,再檢測卡里的金額,發現已經沒錢了(第二次檢測金額當然要等待妻子轉出金額事務提交完)。程序員就會很郁悶,明明卡里是有錢的…
-
分析:這就是讀提交,若有事務對數據進行更新(UPDATE)操作時,讀操作事務要等待這個更新操作事務提交后才能讀取數據,可以解決臟讀問題。但在這個事例中,出現了一個事務范圍內兩個相同的查詢卻返回了不同數據,這就是不可重復讀。
-
那怎么解決可能的不可重復讀問題?Repeatable read !
Repeatable read
- 重復讀,就是在開始讀取數據(事務開啟)時,不再允許修改操作。MySQL的默認事務隔離級別
- 事例:程序員拿著信用卡去享受生活(卡里當然是只有3.6萬),當他埋單時(事務開啟,不允許其他事務的UPDATE修改操作),收費系統事先檢測到他的卡里有3.6萬。這個時候他的妻子不能轉出金額了。接下來收費系統就可以扣款了。
- 分析:重復讀可以解決不可重復讀問題。寫到這里,應該明白的一點就是,不可重復讀對應的是修改,即UPDATE操作。但是可能還會有幻讀問題。因為幻讀問題對應的是插入INSERT操作,而不是UPDATE操作。
- 什么時候會出現幻讀?
- 事例:程序員某一天去消費,花了2千元,然后他的妻子去查看他今天的消費記錄(全表掃描FTS,妻子事務開啟),看到確實是花了2千元,就在這個時候,程序員花了1萬買了一部電腦,即新增INSERT了一條消費記錄,并提交。當妻子打印程序員的消費記錄清單時(妻子事務提交),發現花了1.2萬元,似乎出現了幻覺,這就是幻讀。
- 那怎么解決幻讀問題?Serializable!
Serializable 序列化
- Serializable 是最高的事務隔離級別,在該級別下,事務串行化順序執行,可以避免臟讀、不可重復讀與幻讀。簡單來說,Serializable會在讀取的每一行數據上都加鎖,所以可能導致大量的超時和鎖爭用問題。這種事務隔離級別效率低下,比較耗數據庫性能,一般不使用。
比較:
- 需要說明的是,事務隔離級別和數據訪問的并發性是對立的,事務隔離級別越高并發性就越差。所以要根據具體的應用來確定合適的事務隔離級別,這個地方沒有萬能的原則。
- MySQL InnoDB 存儲引擎的默認支持的隔離級別是 REPEATABLE-READ(可重讀)。我們可以通過SELECT @@tx_isolation;命令來查看,MySQL 8.0 該命令改為SELECT @@transaction_isolation;
- 這里需要注意的是:與 SQL 標準不同的地方在于InnoDB 存儲引擎在 REPEATABLE-READ(可重讀)事務隔離級別下使用的是Next-Key Lock 算法,因此可以避免幻讀的產生,這與其他數據庫系統(如 SQL Server)是不同的。
- 所以說InnoDB 存儲引擎的默認支持的隔離級別是 REPEATABLE-READ(可重讀)已經可以完全保證事務的隔離性要求,即達到了 SQL標準的 SERIALIZABLE(可串行化)隔離級別,而且保留了比較好的并發性能。
- 因為隔離級別越低,事務請求的鎖越少,所以大部分數據庫系統的隔離級別都是READ-COMMITTED(讀已提交),但是你要知道的是InnoDB 存儲引擎默認使用 REPEATABLE-READ(可重讀)并不會有任何性能損失。
3.MVCC 多版本并發控制
MySQL的大多數事務型存儲引擎實現都不是簡單的行級鎖。基于提升并發性考慮,一般都同時實現了多版本并發控制(MVCC),包括Oracle、PostgreSQL。只是實現機制各不相同。
可以認為 MVCC 是行級鎖的一個變種,但它在很多情況下避免了加鎖操作,因此開銷更低。雖然實現機制有所不同,但大都實現了非阻塞的讀操作,寫操作也只是鎖定必要的行。
- MVCC 的實現是通過保存數據在某個時間點的快照來實現的。也就是說不管需要執行多長時間,每個事物看到的數據都是一致的。
- 典型的MVCC實現方式,分為樂觀(optimistic)并發控制和悲觀(pressimistic)并發控制。
下邊通過 InnoDB的簡化版行為來說明 MVCC 是如何工作的。
- InnoDB 的 MVCC,是通過在每行記錄后面保存兩個隱藏的列來實現。這兩個列,一個保存了行的創建時間,一個保存行的過期時間(刪除時間)。當然存儲的并不是真實的時間,而是系統版本號(system version number)。
- 每開始一個新的事務,系統版本號都會自動遞增。事務開始時刻的系統版本號會作為事務的版本號,用來和查詢到的每行記錄的版本號進行比較。
REPEATABLE READ(可重讀)隔離級別下MVCC如何工作:
- SELECT
- InnoDB會根據以下兩個條件檢查每行記錄:
- 只有符合上述兩個條件的才會被查詢出來
- InnoDB只查找版本早于當前事務版本的數據行,這樣可以確保事務讀取的行,要么是在開始事務之前已經存在要么是事務自身插入或者修改過的
- 行的刪除版本號要么未定義,要么大于當前事務版本號,這樣可以確保事務讀取到的行在事務開始之前未被刪除
- INSERT:InnoDB為新插入的每一行保存當前系統版本號作為行版本號
- DELETE:InnoDB為刪除的每一行保存當前系統版本號作為行刪除標識
- UPDATE:InnoDB為插入的一行新紀錄保存當前系統版本號作為行版本號,同時保存當前系統版本號到原來的行作為刪除標識
保存這兩個額外系統版本號,使大多數操作都不用加鎖。使數據操作簡單,性能很好,并且也能保證只會讀取到符合要求的行。不足之處是每行記錄都需要額外的存儲空間,需要做更多的行檢查工作和一些額外的維護工作。
MVCC 只在 COMMITTED READ(讀提交)和REPEATABLE READ(可重復讀)兩種隔離級別下工作。
4.事務日志
InnoDB 使用日志來減少提交事務時的開銷。因為日志中已經記錄了事務,就無須在每個事務提交時把緩沖池的臟塊刷新(flush)到磁盤中。
事務修改的數據和索引通常會映射到表空間的隨機位置,所以刷新這些變更到磁盤需要很多隨機 IO。
InnoDB 假設使用常規磁盤,隨機IO比順序IO昂貴得多,因為一個IO請求需要時間把磁頭移到正確的位置,然后等待磁盤上讀出需要的部分,再轉到開始位置。
InnoDB 用日志把隨機IO變成順序IO。一旦日志安全寫到磁盤,事務就持久化了,即使斷電了,InnoDB可以重放日志并且恢復已經提交的事務。
InnoDB 使用一個后臺線程智能地刷新這些變更到數據文件。這個線程可以批量組合寫入,使得數據寫入更順序,以提高效率。
事務日志可以幫助提高事務效率:
- 使用事務日志,存儲引擎在修改表的數據時只需要修改其內存拷貝,再把該修改行為記錄到持久在硬盤上的事務日志中,而不用每次都將修改的數據本身持久到磁盤。
- 事務日志采用的是追加的方式,因此寫日志的操作是磁盤上一小塊區域內的順序I/O,而不像隨機I/O需要在磁盤的多個地方移動磁頭,所以采用事務日志的方式相對來說要快得多。
- 事務日志持久以后,內存中被修改的數據在后臺可以慢慢刷回到磁盤。
- 如果數據的修改已經記錄到事務日志并持久化,但數據本身沒有寫回到磁盤,此時系統崩潰,存儲引擎在重啟時能夠自動恢復這一部分修改的數據。
目前來說,大多數存儲引擎都是這樣實現的,我們通常稱之為預寫式日志(Write-Ahead Logging),修改數據需要寫兩次磁盤。
5.事務的實現
事務的實現是基于數據庫的存儲引擎。不同的存儲引擎對事務的支持程度不一樣。MySQL 中支持事務的存儲引擎有 InnoDB 和 NDB。
事務的實現就是如何實現ACID特性。
事務的隔離性是通過鎖實現,而事務的原子性、一致性和持久性則是通過事務日志實現 。
事務是如何通過日志來實現的,說得越深入越好。
事務日志包括:重做日志redo和回滾日志undo
redo log(重做日志) 實現持久化和原子性
- 在innoDB的存儲引擎中,事務日志通過重做(redo)日志和innoDB存儲引擎的日志緩沖(InnoDB Log Buffer)實現。
- 事務開啟時,事務中的操作,都會先寫入存儲引擎的日志緩沖中,在事務提交之前,這些緩沖的日志都需要提前刷新到磁盤上持久化,這就是DBA們口中常說的“日志先行”(Write-Ahead Logging)。
- 當事務提交之后,在Buffer Pool中映射的數據文件才會慢慢刷新到磁盤。此時如果數據庫崩潰或者宕機,那么當系統重啟進行恢復時,就可以根據redo log中記錄的日志,把數據庫恢復到崩潰前的一個狀態。
- 未完成的事務,可以繼續提交,也可以選擇回滾,這基于恢復的策略而定。
- 在系統啟動的時候,就已經為redo log分配了一塊連續的存儲空間,以順序追加的方式記錄Redo Log,通過順序IO來改善性能。
- 所有的事務共享redo log的存儲空間,它們的Redo Log按語句的執行順序,依次交替的記錄在一起。
undo log(回滾日志) 實現一致性
- undo log 主要為事務的回滾服務。在事務執行的過程中,除了記錄redo log,還會記錄一定量的undo log。
- undo log記錄了數據在每個操作前的狀態,如果事務執行過程中需要回滾,就可以根據undo log進行回滾操作。
- 單個事務的回滾,只會回滾當前事務做的操作,并不會影響到其他的事務做的操作。
- Undo記錄的是已部分完成并且寫入硬盤的未完成的事務,默認情況下回滾日志是記錄下表空間中的(共享表空間或者獨享表空間)
- 二種日志均可以視為一種恢復操作,redo_log是恢復提交事務修改的頁操作,而undo_log是回滾行記錄到特定版本。
- 二者記錄的內容也不同,redo_log是物理日志,記錄頁的物理修改操作,而undo_log是邏輯日志,根據每行記錄進行記錄。
6.你知道MySQL 有多少種日志嗎?
- 錯誤日志:記錄出錯信息,也記錄一些警告信息或者正確的信息。
- 查詢日志:記錄所有對數據庫請求的信息,不論這些請求是否得到了正確的執行。
- 慢查詢日志:設置一個閾值,將運行時間超過該值的所有SQL語句都記錄到慢查詢的日志文件中。
- 二進制日志:記錄對數據庫執行更改的所有操作。
- 中繼日志:中繼日志也是二進制日志,用來給slave 庫恢復
- 事務日志:重做日志redo和回滾日志undo
7.MySQL對分布式事務的支持
分布式事務的實現方式有很多,既可以采用 InnoDB 提供的原生的事務支持,也可以采用消息隊列來實現分布式事務的最終一致性。
這里我們主要聊一下 InnoDB 對分布式事務的支持。
MySQL 從 5.0.3 InnoDB 存儲引擎開始支持XA協議的分布式事務。一個分布式事務會涉及多個行動,這些行動本身是事務性的。所有行動都必須一起成功完成,或者一起被回滾。
在MySQL中,使用分布式事務涉及一個或多個資源管理器和一個事務管理器。
如圖,MySQL 的分布式事務模型。模型中分三塊:應用程序(AP)、資源管理器(RM)、事務管理器(TM):
- 應用程序:定義了事務的邊界,指定需要做哪些事務;
- 資源管理器:提供了訪問事務的方法,通常一個數據庫就是一個資源管理器;
- 事務管理器:協調參與了全局事務中的各個事務。
分布式事務采用兩段式提交(two-phase commit)的方式:
- 第一階段所有的事務節點開始準備,告訴事務管理器ready。
- 第二階段事務管理器告訴每個節點是commit還是rollback。如果有一個節點失敗,就需要全局的節點全部rollback,以此保障事務的原子性。
總結
以上是生活随笔為你收集整理的MySQL基础总结(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL基础总结(一)
- 下一篇: MySQL基础总结(三)