查詢優化與並發控制[姊妹篇.第六彈]
前面我們聊過優化的思想和連接查詢的最優方式。本次呢我們來談談查詢優化的整體策略,就是在開發當中,當我們需要調度數據的時候我們的腦海里就需要優先考慮這些策略來達到程式上的優化。
所謂的查詢優化呢,就算我們想要提高查詢效率,查詢佔用時間及空間越少,查詢的效率越高。所以呢,我們需要有一套行之有效的策略按照關係代數等價變化規則對查詢表達式進行變換,來實現優化代價合理、查詢效率高的查詢計劃。
01【查詢優化的一般策略】
選擇運算儘早執行。前面我們提到過,選擇運算就是加條件篩選,在面對數據比較大的表數據時根據條件來命中需要的數據避免無關數據也進行查詢。
投影運算與選擇運算同時進行。投影運算即選擇列查詢,在查詢關係表時需要的列進行查詢避免 「*」 查詢 同時配合選擇運算一起。
將笛卡爾積與隨後的選擇運算合併為連接運算。因為連接運算(尤其是自然連接)要比笛卡爾積所花費的時間要少很多。
投影運算與其他運算同時進行。即投影運算可以搭配任意的運算同時進行。無論如何查詢,選擇需要的列進行查詢往往很有效的一種方式。不必為了刪除關係的某些屬性值而把關係屬性再掃描一遍。
將笛卡爾積與隨後的選擇運算合併為連接運算。因為連接運算(尤其是自然連接)要比笛卡爾積所花費的時間要少很多。
尋找公共子表達式並將結果加以存儲。如果有一個頻繁出現的子表達式,其結果關係並不大,從磁碟讀入這個結果關係所花的時間要比計算該子表達式所花的時間少,那麼先計算該公共子表達式並將結果存儲在磁碟上就能對查詢起到優化作用。當查詢的對象是視圖時,定義視圖的表達式就可看作是公共子表達式。
對文件進行預處理。對適當的屬性預先進行排序或者建立索引將有助於快速有效地找到適當的元組。只要預處理所花費的時候仍然合算,是對查詢優化有作用的。
以上的6種優化策略,在開發的過程中,涉及與資料庫交互時可以優先考慮這6種策略。當然,在面對龐大的數據時,需要另作其他策略,例如分庫分表策略,nosql策略,配合分散式快取等等,這些將在後續分享。
02 【查詢優化步驟】
把查詢轉換成一種內部表示。經常採用樹的方式。
利用關係代數等價變換規則以及查詢優化的一般策略,將語法樹進行優化。
選擇適當的底層存取路徑,要充分利用資料庫中已有的索引等資訊。
生成一組查詢計劃,從中選擇一個代價最小的。
例如我們以一個例子來說明:
對學生-課程資料庫,查詢資訊系學生選修了的所有課程名稱。
例如我們以一個例子來說明:
對學生-課程資料庫,查詢資訊系學生選修了的所有課程名稱。
SELECT course_name FROM student,course,elective_course AS ec WHERE student.sno=ec.sno AND ec.cno=course.cno AND student.sdept=』IS』;
試畫出查詢樹圖、關係代數語法樹圖、優化後的查詢樹。
先選擇,後投影,按照查詢語句的順序 先寫project(cname),也就是最終查詢結果,然後按照條件語句where從後面往前寫,遇到兩個表相關聯的欄位時,可以看看是否這個表後面還有查詢,如果沒有,則表作為葉端,有的話,就繼續連接(join)條件,直到所有的查詢條件都連接完畢,剩下的葉端就是表了。
進行優化語法樹的時候,要全部都轉為選擇σ和投影∏來表示,σ一般表示除了父節點以外的結點,∏ 表示父節點。
查詢語句轉關係代數表達式為:∏ cname(σstudent.sdept=』IS』(student ⋈ course ⋈ ec))
03【 並發調度】
事務:在資料庫上的一個或多個操作的序列,它必須以原子的方式執行,也就是說,所有的操作要麼都做,要麼都不做。
-
SQL語句COMMIT(提交)使事務成功的結束。
-
SQL 語句ROLLBACK(退回)使事務不成功地終止。
數據不一致性:如果對並發操作不進行合理的調度,就有可能導致資料庫中數據的不一致性。
丟失修改:事務T1和T2從資料庫中讀入了同一數據並各自進行修改,在兩個人事務都完成了讀入數據的操作以後,T1先完成修改操作,並將更新的數據寫回資料庫,隨後T2也完成了修改,並將結果寫回資料庫,從而覆蓋了T1的操作結果,導致T1對該數據的修改好像沒有發生。
讀 「臟」 數據:事務T1修改了耨數據並將其寫回資料庫,事務T2隨之讀入這個被T1修改過的數據,之後T1又出於某種原因被撤銷,它修改過的數據恢復原值。這時T2所讀取的數據就與資料庫中的數據不同,就稱為「臟」數據。
不可重複讀:事務T1按一定條件從資料庫讀入某些數據,隨後事務T2對其進行更新並將更新結果寫回資料庫,當T1再次按同一條件讀入數據時,結果發現跟剛才的不一樣。可能有的數據值改變了,也可能有的數據已經刪除,還可能增加了某些數據。
可串列化調度:當且僅當多個事務並發執行的結果與按某一次序串列其結果相同,則認為並發操作是正確的,並稱這種調度策略為可串列化調度。
04 【 封鎖管理】
所謂封鎖,指的是事務在對某數據對象(如關係)進行操作之前,先請求系統對其加鎖,成功加鎖之後該事務就對該數據對象有了控制權,只有該事務對其進行解鎖之後,其他的事務才能更新它。
資料庫管理系統提供基本的封鎖類型有兩種:排它鎖(X鎖)以及共享鎖(S鎖)
若事務T1對數據對象A加了X鎖,則T就可以對A進行讀取以及更新(X鎖因此又稱為寫鎖),在T釋放A上的X鎖以前,任何其他事務都不能再對A加任何類型的鎖,從而也不能讀取和更新A。
若事務T對數據A加上S鎖,則T就可以對A進行讀取,但不能進行更新(S鎖因此又稱為讀鎖),在T釋放A上的S鎖以前,其他事務 可以再對A加上S鎖,但不能加X鎖,從而可以讀取A,但不能更新A。
加鎖的數據對象可以大到整個關係、整個資料庫,也可以小到一個元組、一個元組的某個分量。封鎖對象的大小稱為封鎖的粒度。
封鎖協議:為了保證並發控制正確,在運用封鎖機制時必須遵從一定的規則,例如什麼時候應該申請X鎖或S鎖、什麼時候釋放鎖等等。
不同的封鎖協議( locking protocol)約定了不同的規則,為並發控制提供了不同程度的保證。下面將分別介紹能夠保證數據一致性的三級封鎖協議和保證並行調度可串列性的兩段鎖協議。
1級封鎖協議約定: 事務T在修改數據A之前必須先對其加X鎖,直到事務結束(提交或退回)才釋放該鎖。由於X鎖保證兩個事務不能同時對數據A進行修改,從而使丟失修改的前提條件不可能出現,杜絕了丟失修改的發生。但是1級封鎖協議不要求事務在讀取數據之前加鎖,這樣「不可重複讀」和「讀臟數據」的前提條件仍然成立。
2級封鎖協議:是在1級封鎖協議的基礎上加上這樣的約定:事務T在讀取數據A之前必須對其加S鎖,讀入該數據後即可立即釋放S鎖。
2級封鎖協議不僅避免了丟失修改,還防止了讀「臟」數據其事務T修改數據A之前對其加X鎖,修改後的結果寫回資料庫,事務T2要想讀入數據A,只能等待T1釋放X鎖以後才能對A加S鎖,之後T1出於某種原因被撤銷,它所修改過的數據恢復原值。
3級封鎖協議:在1級封鎖協議的基礎上加上了這樣的約定:事務T在讀取數據A之前必須對其加S鎖,直到事務結束(提交或退回)才能釋放S鎖。
3級封鎖協議除了避免丟失修改、讀「臟」數據之外,又解決了不可重複讀的問題。
事務T1對數據A加S鎖並從資料庫讀入A,隨後事務T2欲對A加X鎖以進行更新操作,然而事務T1尚未釋放S鎖,所以T2不能對A加X鎖,也就是不能修改A,所以T1再次讀入數據A的時候,A的值和剛才一樣