1. 程式人生 > 實用技巧 >資料庫原理

資料庫原理

記錄物理儲存

定長記錄

database-fixlength

變長記錄

database-changelength

多記錄儲存

物理鄰接儲存

database-record-toget

指標連線儲存

database-record-link
database-record-link2

大欄位儲存(BLOBS)

database-blobs

檔案組織方式

  • 堆檔案
  • 順序檔案
    • 搜尋快
    • 插入刪除慢
  • 雜湊檔案
    • 插入刪除快
    • 存取快
    • 不需要為儲存索引
    • 記錄隨機
    • 不能排序
    • 有可能導致桶傾斜
  • 聚簇檔案
    • 把多個表物理儲存在一起
    • 提高多表關聯查詢
    • 降低單表查詢效率
  • 按列儲存
    • 提高提取單列的效率
    • 資料壓縮更好

索引

聚集索引

索引的順序與資料檔案的順序一致,例如mysql的主鍵索引,一般一個表只有一個聚集索引

  • 當記錄的物理位置發生變化,索引檔案需要同步變化

輔助索引

索引的順序與資料檔案的順序不一致,索引的值為主鍵,需要通過二級查詢才能到達記錄

  • 當記錄的物理位置發生變化,索引檔案不需要變化

稠密索引

為每個鍵建立索引

稀疏索引

在順序檔案中,只為每個塊建立一個索引鍵

有序索引

索引是按序排列的,例如B+樹索引

雜湊索引

索引是無序的,通過hash來訪問資料

靜態雜湊
動態雜湊
  • 可擴充套件雜湊

database-hash-extended

  • 線性雜湊

    index=hash(key)%n,當發生衝突時,index++

  • 多段雜湊

多級索引

使用稠密和稀疏索引建立多級索引

查詢與優化

查詢代價的度量

響應時間 = 磁碟儲存時間+CPU運算時間+資料傳輸時間

對於本地大型資料庫一般只考慮磁碟存取時間

查詢處理過程

  • SQL語句

database-sql

  • 語法分析

database-yufatree

  • 生成邏輯查詢

database-sql-logic

  • 優化邏輯查詢

database-sql-logic-opt

  • 建立並生成物理查詢

database-sql-search

  • 查詢執行引擎

  • 查詢結果

關係查詢演算法

選擇/投影/連線
  • 投影:select 子句
  • 選擇:where子句
  • 連線:join子句

查詢演算法

  • 基於排序的演算法
  • 基於雜湊的演算法
  • 基於索引的演算法
  • 一趟或多趟演算法
一趟排序演算法

實現的演算法包括並,交,差,消除重複元組

如果記憶體可以至少容納一個表的資料,可以把一個表的資料全部載入在記憶體,然後對鍵進行排序,然後通過掃描第二表表,一趟把結果計算出來

多趟排序演算法

在兩個表的資料都非常非常大,此時則需要先用歸併演算法得出兩個表的排序記錄,然後再在記憶體堆兩個排序表進行並交差等運算,運算的過程中設計多次的磁碟讀入和寫出

多趟雜湊演算法
  • 為每個表建立k個桶
  • 掃描每個表併發記錄hash到k個桶
  • 在記憶體中載入兩個表的第i個桶,進行並交差運算
  • 如果第i個桶的資料量大於記憶體可以裝載的數量,則需要對第i個進行二次hash,直到記憶體可以裝載為止
基於索引的演算法
  • 遍歷第一個表
  • 通過第一個表的鍵直接通過索引查詢第二個表
  • 在記憶體中進行並交叉運算

運算過程方式

實體化方法

每次運算的中間結果重新寫回磁碟,下一個運算再次從磁碟讀取中間結果並進行再次運算,直到得出結果

  • 非常穩定安全
流水式方法

每次運算的中間結果儲存在記憶體,下次運算直接從記憶體獲得中間結果並進行再次運算,直到得出結果

  • 不安全,有可能記憶體無法達到要求,發生記憶體溢位

查詢優化

  • 通過關係運算的等價變換簡化邏輯查詢
  • 通過選擇運算下移而減少查詢代價
  • 通過對多個方案的估量,選擇一種物理查詢,確定每個查詢子句是否使用索引查詢還是線性查詢,是否使用歸併

故障恢復

  • 錯誤的資料輸入

    例如使用者錯誤資料型別,效率低的sql

    需要對客戶端進行限制,並制定策略檢測和禁止某些危險行為

  • 系統錯誤

    由於掉電,軟體錯誤造成記憶體中資料丟失或者錯誤

    通過資料庫的恢復機制,從日誌和事務中對資料進行恢復

  • 介質故障

    由於磁碟區域性故障,例如磁頭損壞,扇區損壞

    通過儲存的奇偶校驗或者使用RAID技術

  • 災難性故障

    發生不可逆轉的災難,導致資料據介質完全損壞,無法對介質進行任何的恢復

    通過多地即時備份

事務

特定ACID
  • Atomicity 原子性

    事務是一個不可分割的單元,要麼全部成功,要麼全部失敗

  • Consistency 一致性

    事務前後資料的完整性必須保證業務的一致性

  • Isolation 隔離性

    多個事務在併發處理時,互相隔離

  • Durability 永續性

    事務一旦提交,它對資料的修改是永久的,即時此時發生資料庫發生系統故障,也不應該對此有任何影響

日誌

SQL執行過程
  • 寫入日誌
  • 執行SQL
日誌記錄的資訊
  • 事務開始
  • 事務成功提交
  • 事務提交失敗
  • 資料表更: 事務ID update A oldValue newValue
  • 檢查點
undo日誌
規則
  • 寫日誌到磁碟
  • 更新資料到磁碟
  • 把Commit T寫入日誌
恢復機制

從後往前找日誌,把沒有Commit T的事務進行撤銷

檢查點

檢查點包括開始部分和結束部分

開始節點對目前正在活動的事務IdList進行記錄

如果資料庫檢查到所有的IdList都已經完成,則列印一個結束檢查點

  • 如果從後往前遍歷第一個遇到檢查點的開始,則找上一個檢查點的開始的位置開始遍歷
  • 如果從後往前遍歷第一個遇到檢查點的結束,則找此檢查點的開始位置開始遍歷
  • 上一個檢查點之前的事務是已經寫入磁碟並進行了撤銷
  • 掃描此檢查點之後的事務,對沒有提交的事務進行撤銷
  • 事務有可能跨越多個檢查點,此時需要再往前遍歷
缺點

頻繁將資料更新到磁碟,導致效能不高

redo日誌
規則
  • 先記錄資料更新日誌
  • 寫入Commit T
  • 後把資料一次性從記憶體寫入磁碟
恢復機制

重做已經提交的事務

檢查點

與undo類似,只是把撤銷改成重做

事務併發

問題

  • 資料丟失
  • 髒讀,讀到了未提交的資料
  • 幻讀,同一個事務兩次讀同一個變數,是不一樣的值

排程

序列排程

兩個事務,按照順序一個接一個的執行

可序列化排程

兩個事務,通過指令穿插的方式執行,執行的結果與序列執行的結果一致

衝突可序列化排程
不可序列化排程

在某個事務的一個併發結果與序列化排程的結果不一致

保證可序列化的預防型策略

  • 共享鎖:讀鎖S
  • 排它鎖:寫鎖X
加鎖產生的問題
  • 在事務內隨意的解鎖,會導致不可序列化排程
  • 兩個事務加鎖順序不對,導致死鎖
  • 多個事務不斷的加讀鎖,導致先加寫鎖的事務餓死
兩階段鎖協議
  • 增長階段:事務獲得鎖,而不能解鎖
  • 縮減階段:事務解鎖,而不能獲得鎖
嚴格兩階段鎖

在提交前釋放鎖

遵循兩階段鎖的併發控制演算法是衝突可序列化排程。但仍然存在死鎖和級聯回滾的發生。

強兩階段鎖

在提交之前不允許釋放任何鎖

可以避免級聯回滾,但依然存在死鎖

多粒度鎖

database-lock-multi-item

  • 顯式加鎖:對元組單獨加鎖時,為顯式加鎖
  • 隱式加鎖:對元組加鎖時,會同時對它的所有祖先加意向鎖
意向鎖
  • IS鎖:當後代顯式加S鎖時,對此節點加意向讀鎖
  • IX鎖:當後代顯式加X鎖時,對此節點加意向寫鎖
  • SIX鎖:對該節點加S鎖,並加IX鎖
可序列化多粒度鎖協議

database-lock-multi-matric

  • 加鎖時遵循上面的矩陣
  • 從根節點由上往下加鎖
  • 遵循兩階段鎖協議
  • 由下往上解鎖
死鎖
  • 對鎖進行排序,再加鎖
  • 死鎖檢測,系統定時加測是否存在T1->T2->T1這樣的依賴環,並根據策略回滾環中的其中一些事務,來打破死鎖等待的局面
  • 搶佔與回滾技術:對事務賦予一個時間戳,如果事務回滾,則重啟該事務,並保持原有時間戳,通過比較兩個事務的時間錯的大小,來決定回滾哪個事務
  • 超時機制:為每個事務定義一個等待超時時間,超時則回滾

選擇事務回滾的策略:

  • 選擇代價最小的事務
  • 為了防止餓死,將回滾次數考慮到代價中
  • 鎖的協議開銷大,併發提升有限
基於時間戳的排程協議

保證可序列化的預防型策略

  • TS(T):事務開始執行的時間戳
  • W(Q):在Q資料項上,寫入的事務最大的時間戳
  • R(Q):在Q資料項上,讀取的事務最大的時間戳

執行協議

  • T執行Read(Q)

    if TS(T) < W(Q),回退

    if TS(T) > W(Q),執行,並更新R(Q) = (TS(T), R(Q))

  • T執行Write(Q)

    if TS(T) < R(Q),回退

    If TS(T) < W(Q),回退(可以優化成忽略T對Q的寫,並繼續執行來提高並行度)

    否則執行,並更新W(Q) = TS(T)

  • T回退,並賦予新的時間戳,重新執行

基於有效性檢驗的排程協議

保證可序列化的診治型策略

  • 讀階段:讀資料並儲存在區域性變數,並在區域性變數中更新資料
  • 有效性檢查階段:檢查如果把區域性變數更新到資料庫中而不違背可序列性
  • 寫階段:把資料更新到資料庫

協議的術語

  • start(T):T開始執行的時間
  • validation(T):完成有效性檢查的時間
  • Finish(T):完成寫的時間
  • RS(T):讀資料項的集合
  • WS(T):寫資料項的集合
有效性檢查失敗情況
  • 場景1
    • U比T先開始
    • RS(T)和WS(U)有交集
    • U先確認
    • T校驗失敗
  • 場景2
    • U比T先執行
    • T寫入X
    • U寫入X
    • U先確認
    • T校驗失敗
規則

對於T和先於它執行的U滿足以下某一條件,則U和T可序列化

  • finish(U)<start(T)
  • WS(U)與RS(T)無交集,finish(U)<validation(T)
  • WS(T)與RS(T)無交集,WS(U)與WS(T)無交集,validation(U)<validation(T)

事務隔離性級別

嚴格執行隔離會嚴重降低吞吐量,因此需要系統根據應用靈活設定隔離級別

  • 可序列化
  • 可重複度
  • 讀已提交
  • 讀未提交