數據庫系統原理


 

一、事務

概念

事務指的是滿足ACID特性的一組操作,可以通過Commit提交一個事務,也可以使用rollback進行回滾。

 

ACID

1.原子性(Atomicity)

事務被視為不可分割的最小單元,事務的所有操作要么全部提交成功,要么全部失敗回滾。

回滾可以用回滾日志(Undo Log)來實現,回滾日志記錄着事務所執行的修改操作,在回滾時反向執行這些修改操作即可。

2.一致性(Consistency)

數據庫在事務執行前后都保持一致性狀態。在一致性狀態下,所有事務對同一個數據的讀取結果都是相同的。

3.隔離性(Isolation)

一個事務所做的修改在最終提交以前,對其他事務是不可見的。

4.持續性(Durability)

一旦事務提交,則其所做的修改將會永遠保存到數據庫中。即使系統發生崩潰,事務執行的結果也不能丟失。

系統發生崩潰可以用重做日志(Redo log)進行恢復,從而實現持久性。與回滾日志記錄數據的邏輯修改不同,重做日志記錄的數據頁的物理修改。

 

總結:事務的ACID特性概念簡單,但不是很好理解,主要是因為這幾個特性不是一種平級關系:

  • 只有滿足一致性,事務的執行結果才是正確的。
  • 在無並發的情況下,事務串行執行,隔離性一定能夠滿足。此時只要滿足原子性,就一定能滿足一致性
  • 在並發的情況下,多個事務並行執行,事務不僅要滿足原子性,還需要滿足隔離性,才能滿足一致性。
  • 事務滿足持久性是為了能應付系統崩潰的情況.

 

 AUTOCOMMIT

MySQL默認采用自動提交模式。也就是說,如果不顯式使用START TRANSACTION語句來開始一個事務,那么每個查詢操作都會被當作一個事務並自動提交。

二、並發一致性問題

在並發環境下,事務的隔離性很難保證,因此會出現很多並發一致性問題。

丟失修改

丟失修改指一個事務的更新操作被另外一個事務的更新操作替換。一般在現實生活中常會遇到,例如:1號事務與2號事務都對一個數據進行修改,1號先修改然后提交生效,2號隨后修改,2號的修改覆蓋了1號的修改。

 

 

讀臟數據

讀臟數據指在不同的事務下,當前事務可以讀到另外事務未提交的數據。

 

 

不可重復讀

不可重復讀指在一個事務內多次讀取同一數據集合。在這一事務還未結束時,另一事務也訪問了該同一數據集合並做了修改,使得第一次事務兩次訪問結果不一致。

 

幻影讀

本質上也屬於不可重復讀

 

 

總結:產生並發不一致問題的主要原因是破壞了事務的隔離性,解決方法是通過並發控制來保證隔離性。並發控制可以通過封鎖來實現,但是封鎖操作需要用戶自己控制,相當復雜。數據庫管理系統提供了事務的隔離級別,讓用戶以一種更輕松的方式處理並發一致性問題。

三、封鎖

封鎖粒度

MySQL中提供了兩種封鎖粒度:行級鎖以及表級鎖。

應該盡量只鎖定需要修改的那部分數據,而不是所有的資源。鎖定的數據量越少,發生鎖爭用的可能就越小,系統的並發度就越高。

但是加鎖需要消耗資源,鎖的各種操作(獲取,釋放,檢查鎖狀態)都會增加系統開銷。因此封鎖粒度越小,系統開銷就越大。

在選擇封鎖粒度時,需要在鎖開銷和並發程度之間做一個權衡。

封鎖類型

1.讀寫鎖

  • 互斥鎖(Exclusive),簡寫為X鎖,又稱寫鎖
  • 共享鎖(Shared),簡寫為S鎖,又稱讀鎖

有以下兩個規定:

  • 一個事務對數據對象加了X鎖,就可以對A進行讀取和更新。加鎖期間其他事務不能對A加任何鎖。
  • 一個事務對數據對象A加了S鎖,可以對A進行讀取操作,但是不能進行更新操作。加鎖期間其他事務能對A加S鎖,但是不能加X鎖。

 2.意向鎖

使用意向鎖(Intention Locks)可以更容易地支持多粒度封鎖。

在存在行級鎖和表級鎖地情況下,事務T想要對表A加X鎖,就需要先檢測是否有其他事務對表A或者表A中地任意一行加了鎖,那么就需要對表A的每一行都檢測一次,這是非常耗時的。

意向鎖在原來的X/S鎖之上引入了IX/IS,IX/IS都是表鎖,用來表示一個事務想要在表中的某個數據行上加X鎖或S鎖。有以下兩個規定:

  • 一個事務在獲得某個數據行對象的S鎖之前,必須先獲得表的IS鎖或者更強的鎖;
  • 一個事務在獲得某個數據行對象的X鎖之前,必須先獲得表的IX鎖。

通過引入意向鎖,事務T想要對表A加X鎖,只需要先檢測是否有其他事務對表A加了X/IX/S/IS鎖,如果加了就表示有其他事務正在使用這個表或者表中某一行的鎖,因此事務T加X鎖失敗。

 封鎖協議

1.三級封鎖協議

一級封鎖協議

事務T要修改數據A時必須加X鎖,直到T結束才釋放鎖。

可以解決丟失修改問題,因為不能同時有兩個事務對同一個數據進行修改,那么事務的修改就不會被覆蓋。

 

二級封鎖協議

在一級的基礎上,要求讀取數據A時必須加S鎖,讀取完馬上釋放S鎖。

可以解決讀臟數據的問題,因為如果一個事務在對數據A進行修改,根據一級封鎖協議,會加X鎖,此時就不能加S鎖了,也就是不會讀入數據。

 

三級封鎖協議

在二級的基礎上,要求讀取數據A時必須加S鎖,直到事務結束了才能釋放S鎖。

可以解決不可重復讀的問題,因為讀A時,其他事務不能對A加X鎖,從而避免了數據在讀的期間發生修改。

2.兩段鎖協議

加鎖和解鎖分為兩個階段進行。

可串行化調度是指,通過並發控制,使得並發執行的事務結果與某個串行執行的事務結果相同。串行執行的事務互不干擾,不會出現並發一致性問題。

事務遵循兩段鎖協議是保證可串行化調度的充分條件。例如以下操作滿足兩段鎖協議,是可串行化調度。

lock-x(A)...lock-s(B)...lock-s(C)...unlock(A)...unlock(C)...unlock(B)

但不是必要條件,例如以下操作不滿足兩段鎖協議,但它還是可串行化調度。

lock-x(A)...unlock(A)...lock-s(B)...unlock(B)...lock-s(C)...unlock(C)

MySQL隱式與顯式鎖定

MySQL的InnoDB存儲引擎采用兩段鎖協議,會根據隔離級別在需要的時候自動加鎖,並且所有的鎖都是在同一時刻被釋放,這被稱為隱式鎖定。

InnoDB也可以使用特定的語句進行顯式鎖定:

SELECT ... LOCK In SHARE MODE;
SELECT ... FOR UPDATE;

四、隔離級別

未提交讀:事務中的修改,即便沒有提交,對其他事務也是可見的。

提交讀:一個事務只能讀取已經提交的事務所做的修改。換句話說,一個事務所做的修改在提交執勤對其他事務是不可見的。

可重復讀:保證在同一事務中多次讀取同一數據的結果是一樣的。

可串行化:強制事務串行執行,這樣多個事務互不干擾,不會出現並發一致性問題。該隔離級別需要加鎖實現,因為要使用加鎖機制保證同一時間只有一個事務執行,也就是保證事務串行執行。

 

 

五、多版本並發控制

多版本並發控制(Multi-Version Concurrency Control, MVCC)是MySQL的InnoDB存儲引擎實現隔離級別的一種具體方式,用於實現提交讀和可重復讀這兩種隔離級別。而未提交讀總是讀取最新的數據行,要求很低,無需使用MVCC。可串行化隔離級別需要對所有讀取的行都加鎖,單純使用MVCC無法實現。

基本思想

在封鎖一節中提到,加鎖能解決多個事務同時執行時出現的並發一致性問題。在實際場景中,讀操作往往多余寫操作,因此又引入讀寫鎖來避免不必要的加鎖操作,例如讀和讀沒有互斥關系。讀寫鎖中讀和寫操作仍然是互斥的,而MVCC利用多版本的思想,寫操作更新最新的版本快照,而讀操作去讀舊版本快照,沒有互斥關系,這一點和CopyOnWrite類似。

在MVCC中事務的修改操作(DELETE , INSERT, UPDATE)會為數據行新增一個版本快照。

臟讀和不可重復讀最根本的原因是事務讀取到其他事務未提交的修改。在事務進行讀取操作時,為了解決臟讀和不可重復讀問題,MVCC規定只能讀取已經提交的快照。當然一個事務可以讀取自身未提交的快照。

版本號

  • 系統版本號 SYS_ID:是一個遞增的數字,每開始一個新事務,系統版本號就會自動遞增
  • 事務版本號 TRX_ID:事務開始時的系統版本號。

Undo日志

MVCC的多版本指的是多個版本的快照,快照存儲在Undo日志中,該日志通過回滾指針 ROLL_PTR把一個數據行的所有快照連接起來。

例如在MySQL創建一個表t,包含主鍵id和一個字段x。先插入一個數據行,然后對該數據行執行兩次更新操作。

INSERT INTO t(id, x) VALUES(1, "a");
UPDATE t SET x="b" WHERE id=1;
UPDATE t SET x="c" WHERE id=1;

因為沒有使用START TRANSACTION將上面的操作當成一個事務來執行,根據MySQL的AUTOCOMMIT機制,每個操作都會被當成一個事務來執行,所以上面的操作總共涉及到三個事務。快照中除了記錄事務版本號TRX_ID和操作之外,還記錄了一個bit的DEL字段,用於標記是否被刪除。

 

 

 INSERT, UPDATE, DELETE操作會創建一個日志,並將事務版本號TRX_ID寫入。DELETE可以看成是一個特殊的UPDATE,還會額外將DEL字段設置為1。

ReadView

MVCC維護了一個ReadView結構,主要包含了當前系統未提交的事務列表TRX_IDs{TRX_ID_1, TRX_ID_2, ···},還有該列表的最小值TRX_ID_MIN和TRX_ID_MAX。

 

 

 在進行SELECT操作時,根據數據行快照的TRX_ID與TRX_ID_MIN和TRX_ID_MAX的關系,從而判斷數據行快照是否可以使用:

  • 小於最小值,表示該數據行快照是在當前所有未提交事務之前進行更改的,因此可以使用。
  • 大於最大值,表示是在事務啟動之后被修改的,因此不可以使用。
  • 在之間,需要根據隔離級別進行再判斷
    • 提交讀:還在列表中,表示還未提交,因此不可用;否則可以用
    • 可重復讀:都不可以使用,

 在數據行快照不可使用的情況下,需要沿着Undo Log的回滾指針找到下一個快照,再進行上面的判斷。

快照讀和當前讀

1.快照讀

MVCC的SELECT操作是快照中的數據,不需要進行加鎖操作。

SELECT * FROM table ...;

2.當前讀

MVCC其他會對數據庫進行修改的操作(INSERT, UPDATE, DELETE)需要進行加鎖操作,從而讀取最新的數據。可以看到MVCC並不是完全不用加鎖,而只是避免了SELECT的加鎖操作。

在進行SELECT操作時,可以強制進行加鎖操作。

SELECT * FROM table WHERE ? lock in share mode;
SELECT * FROM table WHERE ? for update;

六、Next-Key Locks

Next-Key Locks是MySQL的InnoDB存儲引擎的一種鎖實現。

MVCC不能解決幻影讀問題,Next-Key Locks就是為了解決這個問題引入的。在可重復讀隔離級別下,使用MVCC+Next-Key Locks可以解決幻讀問題。

Record Locks

鎖定一個記錄上的索引,而不是記錄本身。

如果表沒有設置索引,InnoDB會自動在主鍵上創建隱藏的聚簇索引。

Gap Locks

鎖定索引之間的間隙,但是不包含索引本身。例如當一個事務執行以下語句,其他事務就不能在t。c中插入15。

SELECT c FROM t WHERE c BETWEEN 10 and 20 FOR UPDATE;

Next-Key Locks

他是以上兩種鎖的結合,不僅鎖定一個記錄上的索引,也鎖定索引之間的間隔。它鎖定一個前開后閉區間,例如一個索引包含以下值:10,11,13,20,那么就需要鎖定以下區間:

(-∞, 10]
(10, 11]
(11, 13]
(13, 20]
(20, +∞)

七、關系數據庫設計理論

函數依賴

記A->B表示A函數決定B,也可以說B函數依賴於A。

如果{A1, A2, ···}是關系的一個或多個屬性的集合,該集合函數決定了關系的其他所有屬性並且是最小的,那么該集合就稱為鍵碼。

對於A->B,如果能找到A的真子集A‘,使得A’->B,那么A->B就是部分函數依賴,否則就是完全函數依賴。

對於A->B, B->C,則A->C是一個傳遞函數依賴。

異常

以下的學生課程關系的函數依賴為 {Sno, Cname} -> {Sname, Sdept, Mname, Grade},鍵碼為 {Sno, Cname}。也就是說,確定學生和課程之后,就能確定其他信息。

Sno Sname Sdept Mname Cname Grade
1 學生-1 學院-1 院長-1 課程-1 90
2 學生-2 學院-2 院長-2 課程-2 80
2 學生-2 學院-2 院長-2 課程-1 100
3 學生-3 學院-2 院長-2 課程-2 95

不符合范式的關系,會產生很多異常,主要有以下四種異常:

  • 冗余數據:例如學生2出現了兩次
  • 修改異常:修改了一個記錄中的信息,但是另一個記錄中相同的信息卻沒有被修改。
  • 刪除異常:刪除一個信息,那么也會丟失其他信息。例如刪除課程1會導致學生1的信息丟失
  • 插入異常:例如想要插入一個學生的信息,如果學生還沒選課,那么就無法插入。

范式

范式理論是為了解決以上提到的四種異常。

高級別的范式依賴於低級別的范式,1NF是最低級別的范式。

第一范式:屬性不可分

第二范式:每個非主屬性完全函數依賴於鍵碼。

可以通過分解滿足。

分解前

 

 

 以上學生課程關系中,{Sno, Cname} 為鍵碼,有如下函數依賴:

  • Sno -> Sname, Sdept
  • Sdept -> Mname
  • Sno, Cname-> Grade

Grade完全函數依賴於鍵碼,它沒有任何冗余數據,每個學生的每門課都有特定的成績。

Sname, Sdept 和 Mname 都部分依賴於鍵碼,當一個學生選修了多門課時,這些數據就會出現多次,造成大量冗余數據。

分解后

關系1

Sno Sname Sdept Mname
1 學生-1 學院-1 院長-1
2 學生-2 學院-2 院長-2
3 學生-3 學院-2 院長-2

有以下函數依賴:

  • Sno -> Sname, Sdept
  • Sdept -> Mname

關系2

Sno Cname Grade
1 課程-1 90
2 課程-2 80
2 課程-1 100
3 課程-2 95

有以下函數依賴:

  • Sno, Cname -> Grade

第三范式:非主屬性不傳遞依賴於鍵碼。

關系1存在如下傳遞函數依賴,可以繼續分解

  • Sno -> Sdept -> Mname

關系11

Sno Sname Sdept
1 學生-1 學院-1
2 學生-2 學院-2
3 學生-3 學院-2

關系12

Sdept Mname
學院-1 院長-1
學院-2 院長-2

八、ER圖

Entity-Relationship,有三個部分組成:實體、屬性、聯系。

用來進行關系數據庫系統的概念設計。

實體的三種聯系

包含一對一,一對多,多對多三種。

  • 如果A到B是一對多關系,那么畫個帶箭頭的線段指向B;
  • 如果是一對一,畫帶兩個箭頭的線段
  • 如果是多對多,畫不帶箭頭的線段

 

 表示出現多次的關系

一個實體在聯系出現幾次,就要用幾條線連接。

下圖表示一個課程的先修關系,先修關系出現兩個課程實體,第一個是先修課程,后一個是后修課程,因此需要用兩條線來表示這種關系。

 

 聯系的多向性

雖然老師可以開設多門課,並且可以教授多名學生,但是對於特定的學生和課程,只有一個老師教授,這就構成了一個三元聯系。

 

 表示子類

用一個三角形和兩條線來連接類和子類,與子類有關的屬性和聯系都連到子類上,而與父類和子類都有關的連到父類上。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM