巨型服務架構:Ch6.3 ~ 6.8 資料庫設計與最佳化 閱讀筆記

本文為「巨型服務架構」第五章 輸入輸出設計 閱讀重點筆記。有興趣的朋友們歡迎購買原書來閱讀。

巨型服務架構:Ch6.3 ~ 6.8 資料庫設計與最佳化 閱讀筆記

資料庫索引實現方法比較

HashBTree B-BTree B+Bitmap
使用 Hash 函數實現使用 B- 樹實現使用 B+ 樹實現向量化、位元邏輯運算實現
只能進行 ‘=’, ‘IN’, ‘<=>’支援區間、比較不支援 ‘!=’, ‘<>’, ‘NOT’支援區間、比較不支援 ‘!=’, ‘<>’, ‘NOT’只能進行 ‘=’
將該列所有資料 Hash 結果算出作為索引透過給定欄位中的資料建立 B- tree透過給定欄位中的資料建立 B+ tree將屬性中的每個選項都單獨作為一列成為向量,進行位元操作
1. 可能發生雜湊碰撞,因此尋找紀錄時,一定要讀取資料庫中的資料才能確認正確性
2. 不適用於紀錄可能有大量相同的狀況
1. 所有的節點上都儲存資料,所以 d 相對小,搜尋的時間複雜度較高穩定
2. 不存在雜湊碰撞,效率高
1. 只有在葉節點上儲存資料,其他則儲存子節點位置,d 相對大,時間複雜度較小葉節點僅儲存指向具體紀錄的位置穩定
2 不存在雜湊碰撞,效率高
1. 適用於索引的屬性只有固定的幾個值(枚舉)
2. 不適合非枚舉的屬性僅
3. 適合建立在不常發生變動的屬性上

資料庫索引故障的可能原因

  • 對屬性進行計算
    • ex: SELECT * FROM user WHERE age + 1 = 10
  • 對屬性值型態進行轉化
    • ex: SELECT * FROM user WHERE name = 10
    • 但 name 欄位是字串,條件 10 是整數,發生轉型
  • 聯合索引最左字首原理,誤用後面的屬性做查詢
    • 如果 index 下的是 (name, age),此時搜尋只用 age 做條件時,無法使用此 index
  • 模糊匹配造成
    • 因 BTree 索引至左向右計算屬性值,所以萬用字元開頭時會失效
      • %Foo -> 無法使用索引
      • Foo% -> 可以使用索引
    • 因 Hash 是對整個屬性做計算,所以萬用字元在任何位置都會失效
  • 其他狀況
    • BTree 不支援 ‘!=’, ‘<>’, ‘NOT’,因為他是使用相等性建立的
    • BTree 不適用 ‘IN’, ‘NOT IN’,因為掃 BTree 可能還不如全表掃描比較快
    • Hash 和 BTree 不能使用 IS NULL, IS NOT NULL, = NULL, != NULL,因為 null 無法透過索引找到

資料庫鎖的類型

樂觀鎖悲觀鎖
資料使用方自己實現資料庫提供
1. 實際並沒有對資料加鎖
2. 透過寫入資料時檢查版本號,或者確認資料源是否與現有的一致來判斷能否寫入
3. 適用於經常被讀取,極少寫入修改的物件上
類型
1. 共用鎖:S 鎖,某 A 對物件加了 S 鎖,其他人只能再對該物件加 S 鎖。大家可同時讀取但不得寫入
2. 更新鎖:U 鎖,如同 S 鎖,但加鎖的人可以讓其轉化成 X 鎖,實現先讀後寫,避免鎖死
3. 排他鎖:X 鎖,只有加鎖的人可以讀寫物件

範圍
1. 行鎖:只鎖每一行
2. 頁鎖:鎖定相鄰幾行的紀錄
3. 表鎖:整個表鎖住

資料庫發生鎖死的原因

  • 鎖死產生的四必要條件
    • 互斥條件:鑰匙只能一個人拿著
    • 不剝奪條件:任何人都不能搶奪對方鑰匙
    • 請求和保持條件:每個人都拿一把鑰匙不放手,又想要另一把鑰匙
    • 迴圈等待條件:A 的鑰匙被 B 請求,B 的鑰匙被 A 請求,互不相讓,互相等待
  • 鎖死四必要條件打破法
    • 打破互斥:鑰匙大家都能共用
    • 允許爭搶:搶對方鑰匙失敗的話,必須主動放棄自己手上的鑰匙給需要的人
    • 不得同時請求且保持:每次請求新的鑰匙,都必須放棄自己手上的鑰匙。或是有了鑰匙,就不能再請求新的鑰匙
    • 打破迴圈:資源依賴不要形成環即可

資料庫交易

交易 ACID

  • 原子性 Atomicity:交易操作是一體的,全部成功或是全部不成功
  • 一致性 Consistency:交易執行不會破壞資料庫完整性
  • 隔離性 Isolation:多交易平行進行時完全互相隔離
  • 持久性 Durability:交易一但被提交,就不會消失

交易併發導致的問題

  • 中途讀取
    • 一個交易讀取另一個交易尚未提交的資料
  • 不可重複讀取
    • 一個交易多次讀取同一個資料,但多次讀取間隔中,另一個交易修改資料並提交,導致每次讀取結果的值不同
    • 是由其他交易修改或刪除目標記錄引發
    • 針對已有記錄,記錄層面的問題
  • 虛設項目讀取
    • 交易一刪除表中所有記錄,但刪除後尚未提交,此時另一個交易插入一筆交易並提交,導致交易一提交後,資料表中還有資料的矛盾現象
    • 是由其他交易插入新記錄引發
    • 表層面的問題

交易隔離等級

  • 讀取未提交
    • 交易更新資料前會對其上 S 鎖,直到交易結束為止
  • 讀取已提交
    • 交易更新資料前會對其上 X 鎖,直到交易結束
    • 讀取資料會對其上 S 鎖,讀取完畢立即釋放
  • 可重複讀取
    • 交易更新資料前會對其上 X 鎖,直到交易結束
    • 讀取資料會對其上 S 鎖,直到交易結束
  • 序列化
    • 交易更新資料前會對整個表上 X 鎖,直到交易結束
    • 讀取資料會對整個表上 S 鎖,直到交易結束

不同隔離等級可能會發生的併發問題

隔離等級中途讀取不可重複讀虛設項目讀取
讀未提交vvv
讀已提交 vv
可重複讀  v
序列化   

巨量資料最佳化

表分區分庫分表讀寫分離
將一個表的內容,根據某個條件,拆成兩個以上的分區將一資料庫中的所有表的內容拆出一半後,放到另一個資料庫中因寫入時的 X 鎖會使紀錄無法被讀取,使用讀寫分離架構解決此問題,讓讀取操作不被寫入操作限制
對外仍展現成一個邏輯的表需要額外的路由操作,將讀寫請求分散到拆出的多個表上需要額外路由操作寫入和讀取的分流
每個分區的檔案可放到不同硬碟上,增加吞吐量需要額外的拼接操作,透過業務邏輯將多個資料表列出的資料進行拼接主從複製操作:
1. 寫入主要在主資料庫上
2. 需要主資料庫的變動同步到從資料庫上
3. 主資料庫透過寫入 BinLog 的方式,再將其傳送到從資料庫,寫入其 RelayLog
4. 從資料庫解析 RelayLog,將資料還原
查詢時儘量落在同一個分區上,變免需要掃描所有分區,造成效率低落主從複製延遲問題
1. 非同步複製:寫入主要資料庫後立刻返回,傳送更新資料到從資料庫為非同步,可能因主資料庫寫入後發生毀損,導致該筆資料遺失
2. 半同步複製:寫入主要資料庫後,必須等至少一個從資料庫更新後才返回,可避免主資料庫損壞造成的資料遺失
3. 全同步複製:寫入主資料庫後,需等所有從資料庫都更新後才返回。所需時間最久,但最能保證資料的儲存

延伸閱讀:巨型服務架構:Ch5 輸入輸出設計 閱讀筆記

Written by J
雖然大學唸的是生物,但持著興趣與熱情自學,畢業後轉戰硬體工程師,與宅宅工程師們一起過著沒日沒夜的生活,做著台灣最薄的 intel 筆電,要與 macbook air 比拼。 離開後,憑著一股傻勁與朋友創業,再度轉戰軟體工程師,一手扛起前後端、雙平台 app 開發,過程中雖跌跌撞撞,卻也累計不少經驗。 可惜不是那 1% 的成功人士,於是加入其他成功人士的新創公司,專職開發後端。沒想到卻在採前人坑的過程中,拓寬了眼界,得到了深層的領悟。