本文為「巨型服務架構」第五章 輸入輸出設計 閱讀重點筆記。有興趣的朋友們歡迎購買原書來閱讀。
目錄
資料庫索引實現方法比較
Hash | BTree 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 索引至左向右計算屬性值,所以萬用字元開頭時會失效
- 其他狀況
- 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 鎖,直到交易結束
不同隔離等級可能會發生的併發問題
隔離等級 | 中途讀取 | 不可重複讀 | 虛設項目讀取 |
---|---|---|---|
讀未提交 | v | v | v |
讀已提交 | v | v | |
可重複讀 | v | ||
序列化 |
巨量資料最佳化
表分區 | 分庫分表 | 讀寫分離 |
---|---|---|
將一個表的內容,根據某個條件,拆成兩個以上的分區 | 將一資料庫中的所有表的內容拆出一半後,放到另一個資料庫中 | 因寫入時的 X 鎖會使紀錄無法被讀取,使用讀寫分離架構解決此問題,讓讀取操作不被寫入操作限制 |
對外仍展現成一個邏輯的表 | 需要額外的路由操作,將讀寫請求分散到拆出的多個表上 | 需要額外路由操作寫入和讀取的分流 |
每個分區的檔案可放到不同硬碟上,增加吞吐量 | 需要額外的拼接操作,透過業務邏輯將多個資料表列出的資料進行拼接 | 主從複製操作: 1. 寫入主要在主資料庫上 2. 需要主資料庫的變動同步到從資料庫上 3. 主資料庫透過寫入 BinLog 的方式,再將其傳送到從資料庫,寫入其 RelayLog 4. 從資料庫解析 RelayLog,將資料還原 |
查詢時儘量落在同一個分區上,變免需要掃描所有分區,造成效率低落 | 主從複製延遲問題 1. 非同步複製:寫入主要資料庫後立刻返回,傳送更新資料到從資料庫為非同步,可能因主資料庫寫入後發生毀損,導致該筆資料遺失 2. 半同步複製:寫入主要資料庫後,必須等至少一個從資料庫更新後才返回,可避免主資料庫損壞造成的資料遺失 3. 全同步複製:寫入主資料庫後,需等所有從資料庫都更新後才返回。所需時間最久,但最能保證資料的儲存 |