---
# System prepended metadata

title: 面試準備
tags: [Interview]

---

# 面試準備
###### tags: `Interview`
[TOC]
## 資料庫類問題

### 資料庫關聯（Database Relationships）
* 一對一（One-to-One）：
    * 在一對一關係中，每一個實體A的記錄僅能對應一個實體B的記錄，反之亦然。
    * 例子：一個人（Person）只有一個身份證（ID Card），一個身份證只對應一個人。這樣可以將身份證資料獨立為另一個表，然後通過主鍵-外鍵的方式來維持關聯。
    * 實現方式：可以通過在其中一個表中添加一個外鍵，或在兩個表中使用相同的主鍵來實現。
* 一對多（One-to-Many）：
    * 在一對多關係中，一個實體A的記錄可以對應多個實體B的記錄，但每個B的記錄只能對應一個A的記錄。
    * 例子：一個客戶（Customer）可以擁有多個訂單（Orders），但每個訂單只屬於一個客戶。
    * 實現方式：通常是在多的一方的表中添加一個外鍵來實現，比如在 orders 表中添加一個 customer_id 外鍵來指向 customers 表。
* 多對多（Many-to-Many）：
    * 在多對多關係中，多個實體A的記錄可以對應多個實體B的記錄，反之亦然。
    * 例子：學生（Student）和課程（Course）之間是一種多對多關係，一個學生可以選多門課程，而一門課程可以有多名學生。
    * 實現方式：通常使用一個中間表（Junction Table）來實現。這個中間表包含兩個外鍵，分別指向兩個表的主鍵。例如 student_courses 表中有 student_id 和 course_id，分別指向 students 表和 courses 表。
    
### SQL 指令的執行順序
![image](https://hackmd.io/_uploads/SJp42Pc-Jl.png)

### 資料庫正規化（Database Normalization）
> 正規化是一種分解數據的過程，將數據拆分為多個表，並通過鍵的關聯來消除數據冗餘和依賴，從而提高資料庫的完整性和一致性。正規化由一系列「正規形式」構成，從 第一正規形式（1NF） 到 第五正規形式（5NF），常用的有前三個。
主要的正規化階段：

* 第一正規形式（1NF）：
    * 要求每個欄位中的數據都是原子性的，不能有多值屬性，數據必須是不可再分的單一值。
    * 例子：一個表格中不應該出現「電話號碼」欄位中包含多個號碼的情況，而應該將其拆分為多行或建立獨立的電話號碼表。
* 第二正規形式（2NF）：
    * 要求表格符合第一正規形式，且每個非主鍵欄位都完全依賴於主鍵（消除部分依賴）。
    * 主要針對複合鍵的情況，確保每個非主鍵屬性依賴於整個複合主鍵，而不是其中一部分。
    * 例子：一個訂單明細表中，如果複合主鍵是 order_id 和 product_id，而 product_name 只依賴於 product_id，就需要將 product_name 移到產品表中，這樣表只需存儲與訂單有關的數據。
* 第三正規形式（3NF）：
    * 要求表格符合第二正規形式，且所有非主鍵屬性相互之間沒有傳遞依賴（消除傳遞依賴）。
    * 例如，若表中存在 student_id → department_id → department_name 的依賴關係，那麼應將 department_name 分離至 departments 表，以避免冗餘。
* Boyce-Codd 正規形式（BCNF）：
    * BCNF 是第三正規形式的強化版本，要求每個非主鍵屬性都依賴於候選鍵，而不只是主鍵。
    * 這通常應用於複雜的多候選鍵情況下，以消除任何潛在的依賴性。

**正規化的優點和缺點**
* 優點：
    * 減少數據冗餘：正規化通過分解表和消除依賴，減少了數據的重複存儲。
    * 提升數據一致性：避免了重複數據的更新異常，保證數據的完整性。
    * 提高查詢性能：通過更小的表和更具針對性的索引，優化了查詢性能。
* 缺點：
    * 增加查詢複雜性：正規化後數據分散在多個表中，導致查詢時需要進行多表連接，增加了查詢的複雜性。
    * 可能降低插入/更新性能：正規化的結構可能導致在插入和更新時需要更新多個表，特別是在需要維護關聯的情況下。

### DB Lock（資料庫鎖）
#### 概念
> 資料庫鎖（DB Lock）是一種機制，用於控制並發環境下對資料庫資源的訪問，防止數據不一致或衝突。鎖機制在多用戶或多進程情況下非常重要，確保數據的完整性和一致性。

#### 為什麼需要 DB Lock？
1. 髒讀（Dirty Read）：一個事務讀取了另一個事務尚未提交的數據。
2. 不可重複讀（Non-repeatable Read）：一個事務多次讀取同一數據，另一事務在兩次讀取之間修改了數據，導致讀取結果不一致。
3. 幻讀（Phantom Read）：一個事務在多次查詢之間，另一個事務插入或刪除了符合條件的數據，導致數據集合不一致。
#### 按操作範圍分類
1. 行鎖（Row Lock）：僅鎖定單行記錄。行鎖的粒度最小，因此並發性最高，但會消耗更多的資源。
    * 行鎖適用於需要鎖定特定行，而不影響其他行的情況。
大部分關係型資料庫（如 MySQL InnoDB）支援行鎖。
2. 表鎖（Table Lock）：鎖定整個表，當一個事務鎖定表後，其他事務無法對該表進行操作。
    * 表鎖的粒度較大，並發性低，但實現相對簡單且佔用資源較少。
常用於資料更新頻率低的表或批量操作。
3. 頁鎖（Page Lock）：鎖定資料庫的一個頁（通常是數據塊或數據頁）。頁鎖粒度介於行鎖和表鎖之間，並發性和資源佔用也介於兩者之間。
    * 頁鎖在一些資料庫中（如 SQL Server）被使用，用於批量操作時的性能優化。

#### 按鎖定行為分類
1. 共享鎖（Shared Lock）：允許多個事務讀取同一資源，但禁止寫入操作。共享鎖適合在多個讀操作的情況下使用。
    * 又稱 讀鎖（Read Lock），一個事務加上共享鎖後，其他事務也可以加共享鎖，但不能加排他鎖。
2. 排他鎖（Exclusive Lock）：當一個事務獲得排他鎖後，其他事務無法讀取或寫入該資源。排他鎖適合在進行數據寫入時使用。
    * 又稱 寫鎖（Write Lock），用於保護寫操作，防止其他事務讀取或修改該數據。

#### 按鎖的模式分類
1. 意向鎖（Intent Lock）：用於在表級別上標記事務的意圖。當一個事務需要鎖定某行時，先在表上設置意向鎖，這樣其他事務知道該表中有行已被鎖。
    * 意向共享鎖（IS）：事務意圖在表中某行上加共享鎖。
    * 意向排他鎖（IX）：事務意圖在表中某行上加排他鎖。
2. 死鎖（Deadlock）：當兩個或多個事務因互相等待對方的鎖資源而進入死循環，造成資源無法釋放的情況。資料庫系統通常會檢測死鎖並中止其中一個事務。
    * 這邊可以延伸到Update Lock，當兩個以上的transaction進行時都會獲得Shared Lock，而其中之一作完工作後應釋放Shared Lock生成Update Lock，這樣才不會造成Deadlock。

#### DB Lock 的隔離級別
1. 未提交讀（Read Uncommitted）：允許事務讀取未提交的更改，會出現髒讀。幾乎不使用鎖。
> 這種隔離層級可能產生：
> 髒讀
> 不可重複讀
> 幻讀
2. 提交讀（Read Committed）：事務只能讀取已提交的更改，解決了髒讀問題。通常使用共享鎖和排他鎖。
> 這種隔離層級可能產生：
> 不可重複讀
> 幻讀
3. 可重複讀（Repeatable Read）：事務在範圍內的多次讀取結果一致，防止了不可重複讀。大多數實現行鎖。
> 這種隔離層級可能產生：
> 幻讀
4. 可序列化（Serializable）：最高隔離級別，所有事務依序執行，避免所有並發問題，但性能較低，常用於行鎖或表鎖。
> 一個用效能換取一致性的隔離層級，讓所有 transaction 序列化執行，避免併發可能會造成的問題。

### B+ Tree
> 是一種平衡樹結構，廣泛應用於資料庫和文件系統中，作為索引結構來實現高效的數據存取。B+ Tree 是 B-Tree 的一種變體，專門設計用來優化磁碟 I/O 操作，特別適合大規模數據的範圍查詢和排序操作。

#### B+ Tree 的結構與特性
* 所有數據都儲存在葉節點：
    在 B+ Tree 中，非葉節點僅存儲索引鍵（Key），而不包含具體的數據。所有的實際數據都存儲在葉節點中。這樣的設計可以讓非葉節點更小，從而在內存中存放更多的索引鍵，減少磁碟 I/O 操作。
* 平衡性：
    B+ Tree 是一棵平衡樹，所有葉節點的高度相同，這意味著從根節點到任意葉節點的路徑長度是相同的。因此，查詢、插入和刪除操作的時間複雜度均為 𝑂(log𝑛)。
* 順序存取：
    葉節點之間形成了鏈表，便於範圍查詢和順序遍歷。從一個葉節點開始，可以通過鏈表直接訪問後續的葉節點，這在進行範圍查詢和排序查詢時非常高效。
* 高效的磁碟 I/O：
    B+ Tree 的節點數量設計可以讓每個節點剛好適合磁碟的一個頁面，從而減少 I/O 操作次數，提升查詢和插入性能。

#### B+ Tree 與 B-Tree 的主要區別
* 數據儲存位置：
    在 B-Tree 中，數據既可以儲存在葉節點，也可以儲存在非葉節點。而在 B+ Tree 中，所有數據都儲存在葉節點，非葉節點只存儲索引鍵。
* 鏈表結構：
    B+ Tree 的葉節點之間是鏈接的，形成了一條鏈表，因此支援範圍查詢。B-Tree 則沒有這個鏈表結構。
* 節點佔用空間：
    B+ Tree 的非葉節點僅存儲索引鍵，因而佔用空間較少，B+ Tree 的層數通常比 B-Tree 更少，這在處理大量數據時可以提升效率。

#### B+ Tree 的基本操作
1. 搜索
    過程：從根節點開始，根據索引鍵逐層向下搜索，直到到達葉節點。在葉節點中找到具體的數據。
    範圍查詢：由於葉節點之間有鏈表結構，可以從查找到的葉節點開始，通過鏈表直接遍歷後續的葉節點，獲取範圍內的所有數據。
2. 插入
    定位插入位置：首先通過索引鍵從根節點開始遍歷，找到應插入的葉節點。
    * 插入數據到葉節點：
        * 如果葉節點有空間，直接插入數據。
        * 如果葉節點滿了，則進行「節點分裂」。
        * 節點分裂：將葉節點分成兩個部分，並將中間鍵提升到父節點作為索引鍵。如果父節點也滿了，則遞歸向上分裂，直到整棵樹的高度平衡。
3. 刪除
    * 定位刪除目標：通過索引鍵找到葉節點中的目標數據。
    * 刪除數據：從葉節點中刪除該數據。
        * 如果刪除後葉節點中數據量低於最小限制，則進行合併或重分配，以保持樹的平衡。

#### B+ Tree 的優點
* 高效的範圍查詢：B+ Tree 的葉節點之間形成了鏈表結構，這使得範圍查詢非常高效，適合資料庫中的範圍查詢需求。
* 更小的非葉節點：非葉節點不存儲具體數據，只存儲索引鍵，這意味著非葉節點的結構更小，能夠存儲更多的索引鍵，提升了查詢效率。
* 適合大規模數據：B+ Tree 能夠在磁碟上進行高效的數據讀寫操作，適合用於需要處理大量數據的系統中（如資料庫和文件系統）。

#### B+ Tree 的應用場景
* 資料庫系統：B+ Tree 是大多數資料庫（如 MySQL、PostgreSQL）的索引結構，適合範圍查詢、排序查詢等需求。
* 文件系統：在文件系統（如 NTFS）中，用 B+ Tree 來實現目錄查找和文件訪問。
### Index（索引）
> 索引是一種加速資料庫查詢的數據結構，通過為特定的列或多列建立索引，資料庫可以更快地查找匹配的記錄。

#### 常見的索引類型：
1. B-Tree 索引：大多數資料庫的默認索引，適合範圍查詢、排序查詢等。
1. Hash 索引：基於 Hash 表結構，適合精確匹配查詢，但不支援範圍查詢。
1. 全文索引（Full-text Index）：用於全文搜索，在文本文檔或大文本欄位中特別有用。
1. 空間索引（Spatial Index）：用於地理空間數據的索引，常見於 GIS 應用。

#### 使用場景：
1. 快速查找：當需要在大量數據中進行頻繁的查找操作時，使用索引可以顯著提升查詢速度。
1. 排序查詢：索引可以用於加速排序查詢，避免對整個表進行排序操作。
1. 範圍查詢：B-Tree 索引支援範圍查詢，如查找特定範圍內的數值

#### 注意事項：
1. 索引會佔用額外的儲存空間。
1. 過多的索引會影響插入和更新操作的性能。


### Secondary Index（二級索引）
> 資料庫中用來加速非主鍵（Non-Primary Key）列查詢的索引。二級索引是一種輔助索引，因為它不依賴於主鍵，主要用於加速特定列的查詢操作。

#### 概念
* Primary Index（主鍵索引）：依據表的主鍵（Primary Key）來構建索引，每條記錄在資料庫中都有唯一的主鍵索引。
* Secondary Index（二級索引）：根據非主鍵列（例如名稱、地區等）建立的索引，允許通過非主鍵列進行高效查詢。

:::success
二級索引的設計主要用於提升查詢性能。如果表中的某些欄位經常被查詢（如通過 WHERE 條件進行篩選），為這些欄位建立二級索引可以避免全表掃描，顯著提高查詢效率。
:::

#### Secondary Index 的類型
1. 單列索引：在一個非主鍵列上建立的索引，適合單一條件的查詢。
1. 複合索引（Composite Index）：在多個列上建立的索引，適合多條件的查詢。
1. 唯一索引（Unique Index）：保證索引列中的值是唯一的，有助於數據完整性。
1. 全文索引（Full-Text Index）：用於全文搜索，通常用於查詢大文本數據。
#### Secondary Index 的工作原理
當查詢使用了二級索引的欄位時，資料庫會通過以下步驟進行查詢：
1. 定位索引鍵：二級索引將記錄的非主鍵欄位（如名稱、地區等）作為索引鍵進行查詢。
1. 獲取主鍵參照：二級索引儲存了該記錄的主鍵值。當二級索引找到匹配的索引鍵後，會返回主鍵值。
1. 回表（Row Lookup）：資料庫會根據主鍵值回到主索引（通常是聚簇索引）中找到完整的數據行。
:::success
資料庫的索引是為了加速查詢性能而設計的，它們只包含部分列的資料，這樣可以節省存儲空間並加快查詢速度。然而，當查詢需要的列不全在索引中時，資料庫需要回到主表中獲取完整的行資料。這種查詢方式就是所謂的「回表」。
:::
這種回表的過程增加了一些 I/O 開銷，但仍然比全表掃描效率更高，特別是在查詢條件匹配大量行時。

#### Secondary Index 的優缺點
* 優點：
    * 加速非主鍵查詢：對於頻繁查詢的非主鍵列，二級索引可以顯著提升查詢速度。
    * 支持範圍查詢和排序查詢：B-Tree 結構的二級索引支持範圍查詢和排序查詢，適合用於進行篩選和排序。
    * 減少 I/O 操作：二級索引減少了查詢時的資料掃描次數，節省了系統的 I/O 資源。
* 缺點：
    * 增加寫操作的開銷：插入、更新、刪除操作都需要同步更新所有二級索引，因此會降低寫入性能。
    * 佔用存儲空間：二級索引需要額外的空間來儲存索引結構，索引列越多，佔用的空間越大。
    * 回表開銷：當查詢通過二級索引命中記錄後，需要回表查詢完整記錄，這增加了 I/O 操作。

### 樂觀與悲觀鎖
在資料庫或並發控制中，樂觀鎖（Optimistic Lock）和悲觀鎖（Pessimistic Lock）是兩種不同的鎖定機制，用於管理多個事務或線程對同一資源的訪問。這兩種鎖定策略的核心區別在於它們對資源衝突的預期態度：
* 樂觀鎖 : 假設資源衝突不常發生，因此允許多個事務同時進行操作，只有在提交數據時才進行衝突檢查。
* 悲觀鎖 : 假設資源衝突是常見的，因此在操作資源之前，會先加鎖，確保當前事務獲得資源的排他性，避免其他事務同時訪問。

#### 樂觀鎖（Optimistic Lock）
> 樂觀鎖假設資源衝突的概率較低，因此允許多個事務同時操作資源。當事務準備提交變更時，樂觀鎖會檢查在操作過程中是否有其他事務修改過同一資源。如果沒有衝突，則變更成功；如果有衝突，則根據應用需求進行重試或回滾操作。

##### 實現方式：
* 版本號（Version Number）：在表中增加一個版本號字段，每次更新數據時，版本號自增。事務在更新時會先檢查版本號是否與讀取時一致，如果一致則更新並自增版本號；否則說明數據已被其他事務修改，更新失敗。
* 時間戳（Timestamp）：使用時間戳記錄每次修改的時間，事務在提交時比較時間戳是否一致。

##### 使用場景：
* 高並發的讀取場景：樂觀鎖適合讀多於寫的情況下，衝突概率低，可以在不加鎖的情況下進行操作。
* 無需嚴格同步的應用：在一些應用中，對數據的同步要求不高，允許偶爾的衝突和重試。

##### 優點：
* 高並發性能：樂觀鎖不需要長時間的鎖住資源，因此在高並發場景下性能較好。
* 無死鎖問題：樂觀鎖不會加鎖，因此不會出現死鎖情況。
##### 缺點：
* 重試開銷：如果衝突發生，則需要進行重試，這可能會增加系統的處理開銷。
* 不適合高衝突場景：在頻繁更新的場景中，樂觀鎖可能導致過多的重試和失敗，降低性能。

#### 悲觀鎖（Pessimistic Lock）
> 悲觀鎖假設資源衝突的概率較高，因此在操作資源之前，會先獲取鎖，將其他事務排除在外，直到當前操作完成並釋放鎖。這樣可以確保在操作期間沒有其他事務修改資源，但可能會導致阻塞等待。

##### 實現方式：
* 資料庫鎖機制：許多資料庫提供原生的悲觀鎖機制。例如，在 MySQL 中可以使用 SELECT ... FOR UPDATE 來鎖定選定行，防止其他事務修改。
* 排他鎖（Exclusive Lock）：在資料庫中，排他鎖是一種悲觀鎖，當一個事務獲得排他鎖後，其他事務必須等待該鎖釋放。

##### 使用場景：

* 高頻寫入場景：在大量更新或刪除的場景下，悲觀鎖可以避免衝突，確保資料的一致性。
* 強一致性需求的應用：如果應用對數據一致性有嚴格要求（例如銀行轉賬、訂單扣款等），可以使用悲觀鎖來確保一致性。
##### 優點：
* 確保數據一致性：悲觀鎖可以確保數據的一致性，適合並發修改的情況。
* 無需重試：悲觀鎖保證了資源的排他性，因此通常無需重試。
##### 缺點：
* 阻塞開銷：悲觀鎖會導致其他事務等待鎖釋放，因此在高併發場景下性能較差。
* 死鎖風險：當多個事務互相等待鎖釋放時，可能發生死鎖。

## ACID
> 是資料庫管理系統（DBMS）中用於描述事務（Transaction）性質的四個重要屬性，這些屬性保證了資料庫操作的可靠性和一致性。ACID 的四個特性分別是：原子性（Atomicity）、一致性（Consistency）、隔離性（Isolation）和持久性（Durability）。

### 原子性（Atomicity）
> 定義：原子性確保事務中的所有操作要麼全部成功，要麼全部失敗。在一個事務內，如果某一步操作失敗，事務中已經執行的所有操作都必須回滾（Rollback），使資料庫返回到事務開始之前的狀態。
:::success
示例：銀行轉帳操作 假設有一個事務用於將 A 帳戶的 100 元轉移到 B 帳戶。如果將 A 帳戶的金額減少了 100 元，但在將這 100 元添加到 B 帳戶時出現錯誤，原子性將保證 A 帳戶的扣款操作也會被回滾，使得兩個帳戶的金額都保持原始狀態。
:::

### 一致性（Consistency）
> 定義：一致性確保資料庫在事務執行之前和執行之後都處於一致的狀態。這意味著，事務必須從一個有效的資料庫狀態轉換到另一個有效的資料庫狀態，不會破壞資料庫的完整性約束（例如唯一性、外鍵、檢查約束等）。
:::success
示例：銀行帳戶餘額不能為負數 在轉帳事務中，如果 A 帳戶的餘額不足 100 元，則事務不應該成功。這樣可以保證資料庫中不會存在餘額為負的帳戶，從而保持一致性。
:::

### 隔離性（Isolation）
> 定義：隔離性確保多個事務同時執行時，各事務的操作互不影響。每個事務的中間狀態對其他事務是不可見的。隔離性使得事務看起來像是順序執行的，即使它們實際上是並行執行的。

**隔離級別：**
1. 讀未提交（Read Uncommitted）：一個事務可以讀取到其他事務未提交的數據，可能會導致「髒讀」（Dirty Read）。
1. 讀已提交（Read Committed）：一個事務只能讀取到其他事務已提交的數據，避免了髒讀，但可能出現「不可重複讀」（Non-repeatable Read）。
1. 可重複讀（Repeatable Read）：一個事務在整個過程中多次讀取相同數據時，讀取結果保持一致，避免了不可重複讀，但可能出現「幻讀」（Phantom Read）。
1. 序列化（Serializable）：最高的隔離級別，所有事務串行執行，避免了所有讀寫問題，但性能較低。

![image](https://hackmd.io/_uploads/S1gyogsbJe.png)


:::success
示例：避免髒讀 假設有兩個事務 T1 和 T2。如果 T1 在更新 A 帳戶的餘額但尚未提交時，T2 可以讀取到 T1 的未提交變更，這就是髒讀。隔離性確保 T2 無法讀取 T1 的未提交變更，從而防止髒讀。
:::

### 持久性（Durability）
> 定義：持久性確保一旦事務提交，其對資料庫的變更將永久保留，即使系統發生崩潰（例如斷電、系統故障）也不會丟失。這通常通過寫入日誌或快照的方式來保證，確保數據在崩潰後可以恢復。

:::success
示例：確保轉帳操作的安全性 在銀行轉帳事務中，一旦轉帳操作完成並提交，即使之後發生系統故障，也能保證轉帳的結果被正確保留，不會因故障而丟失。
:::

### ACID 的總結

![image](https://hackmd.io/_uploads/S1afxS9bkl.png)

#### ACID 的重要性
ACID 屬性在資料庫管理系統中非常重要，因為它們能夠確保多使用者、多事務併發環境下數據的一致性和可靠性。具體來說：
1. 保證數據一致性：ACID 可以確保在任何情況下，資料庫中的數據始終保持一致的狀態。
1. 提升資料庫穩定性：通過隔離性和持久性，可以避免數據競爭和崩潰時數據丟失的風險。
1. 簡化應用開發：開發者可以依賴資料庫的 ACID 屬性來簡化並發操作中的數據一致性問題，而無需手動管理多事務之間的同步。

### 總整理
ACID 是關於資料庫事務的四個核心特性，它保證了事務操作的可靠性和一致性：
1. 原子性：保證事務內所有操作的不可分割性。
1. 一致性：確保事務完成後資料庫處於有效狀態。
1. 隔離性：確保併發事務之間的操作互不干擾。
1. 持久性：確保事務提交後數據的永久保存。

這四個特性在傳統關係型資料庫中非常重要，是保證數據安全和一致性的基礎，但在分布式系統中，實現 ACID 可能會有較大的性能開銷。理解 ACID 特性有助於設計可靠的資料庫操作，特別是在多使用者、多事務併發環境中。

## 分布式資料庫實現 ACID 的挑戰與策略
> 在分布式資料庫中，網絡延遲、節點失效、網絡分區（Partition）等因素使得在多節點環境中保持 ACID 屬性變得複雜。以下是每個 ACID 屬性在分布式環境中的實現挑戰及解決方法：

* 原子性（Atomicity）：
    * 挑戰：分布式事務涉及多個節點，如何確保所有節點的操作要麼全部成功，要麼全部失敗？
    * 解決方案：使用 兩階段提交（2PC） 或 三階段提交（3PC） 協議來實現原子性。這些協議會在多個節點間協調，以確保事務的原子性。2PC 主要由「準備階段」和「提交階段」組成，但在網絡分區或節點失敗時可能會導致事務卡住。3PC 在 2PC 基礎上增加了協調器，進一步減少了鎖死的風險。
* 一致性（Consistency）：
    * 挑戰：如何在多節點的分布式環境中確保數據一致性，尤其是網絡分區或節點異常時？
    * 解決方案：通過 強一致性 機制，例如分布式鎖、事務提交協議等來保證一致性。這通常伴隨一定的性能開銷。此外，分布式資料庫也可以採用 最終一致性 策略，允許數據在短時間內不一致，但最終會達成一致。
* 隔離性（Isolation）：
    * 挑戰：多個事務並行執行時，如何確保它們之間不互相影響？
    * 解決方案：採用多版本並發控制（MVCC）或分布式鎖機制來確保隔離性。MVCC 可以讓讀操作不受寫操作影響，從而提升併發性能。此外，分布式資料庫還可以使用事務的隔離級別來控制隔離性，但在分布式環境中通常需要在隔離性和性能之間進行權衡。
* 持久性（Durability）：
    * 挑戰：如何確保事務提交後的數據在所有節點中永久保留，即使發生系統崩潰？
    * 解決方案：分布式資料庫通常會在多個節點間進行數據複製（Replication），並使用分布式日誌（如 Raft 或 Paxos 協議）來確保數據持久性。這樣，即使某個節點失效，數據仍然可以從其他副本中恢復。

## CAP 理論
> 用來描述分布式系統的三個特性，並指出分布式系統在網絡分區（Partition Tolerance）存在的情況下，無法同時滿足一致性（Consistency）和可用性（Availability）。CAP 三個特性分別是：

* 一致性（Consistency）：
    每次讀取操作都可以獲取到最新的數據。如果一個事務在一個節點上寫入了數據，那麼該數據在其他節點上立即可見。
* 可用性（Availability）：
    每次請求都能獲得響應（不一定是最新數據）。即使某些節點出現故障，系統仍然可以提供服務。
* 分區容錯性（Partition Tolerance）：
    系統能夠應對網絡分區的情況，即使節點之間的網絡斷開，系統仍能正常工作。
    
CAP 不可能定理：CAP 理論指出，在分佈式系統中，無法同時滿足一致性、可用性和分區容錯性三個特性，只能在其中兩個特性之間進行選擇：
* CP 系統：選擇一致性和分區容錯性，捨棄可用性。在網絡分區時保證數據一致性，但可能無法及時響應用戶請求。常見的 CP 系統包括 HBase、MongoDB 等。
* AP 系統：選擇可用性和分區容錯性，捨棄一致性。在網絡分區時保證系統可用性，但不同節點上的數據可能暫時不一致。常見的 AP 系統包括 Cassandra、DynamoDB 等。

## BASE 理論
> 對 CAP 理論的補充，特別針對分布式系統中對一致性要求較弱的場景。BASE 理論的核心思想是：在確保基本可用性的基礎上，允許數據暫時不一致，但最終達成一致。BASE 理論的三個特性如下：

* 基本可用（Basically Available）：
    系統保證基本的可用性，即在系統部分功能出現故障的情況下，仍然可以為用戶提供部分可用的服務。
* 軟狀態（Soft state）：
    允許系統中的數據狀態在一段時間內不是一致的，也就是說不同節點之間可以存在暫時的不一致。
* 最終一致性（Eventual consistency）：
    系統保證最終一致性，即隨著時間的推移，所有節點的數據將會達成一致。最終一致性適合於對一致性要求不高但可用性要求高的分布式系統。
    
### BASE 與 ACID 的區別：
* ACID：強調數據的一致性和可靠性，適合對數據一致性要求高的應用。
* BASE：允許數據在短時間內不一致，以換取系統的可用性和性能，適合分布式系統中對一致性要求較低的場景。

## ACID、CAP、BASE 比較
![image](https://hackmd.io/_uploads/BJ0wfScW1x.png)
在分布式系統中，根據具體需求選擇合適的理論和技術：
* 如果應用需要強一致性（如金融系統），可選擇 CP 系統或使用 ACID 特性的資料庫。
* 如果應用對一致性要求不高，但需要高可用性（如社交網絡、網購等），則可以選擇 BASE 理論或 AP 系統，保證最終一致性。

## MVCC（Multi-Version Concurrency Control，多版本並發控制）
> 是一種資料庫並發控制技術，允許多個事務同時訪問數據，並在讀取和寫入之間提供隔離，以提高並發性能。MVCC 通過為每個數據行保存多個版本，使得讀操作無需等待寫操作完成，從而減少鎖定和衝突，並提供更高的並發性和讀寫效率。
### MVCC 的核心概念
1. 多版本存儲：
    * MVCC 通過為每行數據保留多個版本，使得資料庫可以在不同的事務中同時讀取和修改數據。
    * 每個事務在操作數據時，會讀取數據的特定版本，從而避免讀寫衝突。
2. 快照讀取：
    * 每個事務在開始時會獲取一個一致性的數據快照。該快照包含事務開始時的數據狀態。
    * 事務的所有讀操作都基於這個快照進行，不會受到其他事務寫入操作的影響。
3. 版本控制（讀取一致性）：
    * MVCC 通常使用版本號或時間戳來區分不同事務對數據的修改。
    * 事務在開始時獲取當前版本的數據，並在提交新數據時自動更新數據版本，從而確保數據的一致性。
4. 避免鎖定：
    * 由於每個事務讀取的是自己的快照，MVCC 可以避免傳統鎖機制帶來的等待，允許更高的併發度。
    * 寫操作則依然需要進行鎖定來避免衝突，但僅限於修改的數據行，影響範圍較小。

### MVCC 的工作原理
1. 快照讀取（Snapshot Read）：
    * 當一個事務開始時，它會獲取資料庫當前的一個數據快照，並根據這個快照進行所有的讀操作。
    * 每個事務只能讀取在該事務開始之前已提交的數據變更，事務進行期間不會讀到其他事務的未提交變更。
2. 提交讀取（Commit Read）：
    * 寫操作在提交時更新版本或時間戳，為該數據創建一個新的版本。這意味著在事務提交之前，其他事務無法讀到新版本。
    * 當事務提交後，新的數據版本才對其他事務可見。
3. 寫衝突解決：
    * 寫操作的事務在更新數據時，會檢查目標數據行的最新版本。如果版本不匹配，則說明有其他事務在更新這行數據，系統會觸發回滾或重試操作。

### MVCC 的優缺點
* 優點：
    1. 提高並發性：MVCC 避免了讀寫操作的直接衝突，允許多個事務並發訪問，特別適合高讀取操作的場景。
    1. 無鎖讀取：讀操作無需加鎖，減少了傳統鎖機制下的等待時間。
    1. 更好的用戶體驗：MVCC 可以在不鎖定的情況下提供一致的讀取體驗，即使有事務在修改數據，其他事務仍然可以進行無衝突的讀取操作。
* 缺點：
    1. 存儲開銷：MVCC 需要存儲數據的多個版本，可能會增加儲存空間的需求，並需要後續的垃圾回收來清理過期版本。
    1. 寫操作開銷：寫操作需要創建新版本，並進行版本管理，對寫密集型的工作負載可能會影響性能。
    1. 版本管理和回收：隨著時間推移，資料庫可能會積累許多過期版本，需要定期進行清理，否則會影響系統性能。

### MVCC 的應用場景
MVCC 通常應用於以下場景：
* 高讀取併發的系統：例如網絡應用、查詢密集的系統，MVCC 可以提供快速的無鎖讀取，提升系統的併發處理能力。
* 需要快照隔離的場景：在一些對隔離性有較高需求的應用中（如財務系統），MVCC 提供的快照隔離能保證數據的一致性。
* 長時間的查詢操作：長時間運行的查詢操作可能會影響其他事務，但使用 MVCC 可以避免查詢操作對其他事務的影響，從而提升查詢性能。

### MVCC 在資料庫中的實現
許多資料庫系統採用了 MVCC 來提升並發性能，以下是幾個常見資料庫中的實現：
1. MySQL（InnoDB 引擎）：
    * MySQL InnoDB 使用隱藏的版本號來實現 MVCC，為每行數據增加 DB_TRX_ID（事務 ID）和 DB_ROLL_PTR（回滾指針），每次操作都會更新這些隱藏欄位。
    * InnoDB 提供了「快照讀取」（Snapshot Read）和「提交讀取」（Read Committed）兩種隔離級別，利用 MVCC 實現無鎖的讀取。
2. PostgreSQL：
    * PostgreSQL 中的 MVCC 是基於多版本存儲控制的，每次更新會創建新版本的數據行，而不刪除舊版本。
    * PostgreSQL 使用 VACUUM 操作清理過期版本，從而釋放存儲空間。
3. Oracle：
    * Oracle 使用回滾段（Rollback Segments）來實現 MVCC，允許事務在進行快照讀取時，根據回滾段中的歷史版本來進行數據訪問。

### MVCC 與傳統鎖機制的對比
![image](https://hackmd.io/_uploads/HJ3F7rc-Jl.png)

### 總結
MVCC 是一種提高資料庫並發性能的技術，通過為每個數據行保存多個版本，使得讀操作無需等待寫操作完成，有效減少了鎖定和衝突。MVCC 適合高併發、查詢密集的應用場景，但會增加存儲和寫操作的開銷。

主要特點包括：
1. 支援快照讀取，減少讀寫衝突。
1. 提供更高的併發性能，特別適合高讀取應用。
1. 需要處理存儲空間管理和過期版本清理。

MVCC 在 MySQL、PostgreSQL、Oracle 等主流資料庫中得到了廣泛應用，是一種有效的資料庫併發控制技術。


## JWT
> JWT（JSON Web Token） 是一種開放標準（RFC 7519），用於在不同服務之間作為 JSON 對象安全地傳遞信息。JWT 在許多應用中用於身份驗證和授權，尤其在無狀態的分佈式應用中得到廣泛使用。
### JWT 的結構
JWT 由三個部分組成，每個部分之間用點（.）分隔：
1. Header（標頭）
1. Payload（有效負載）
1. Signature（簽名）

這三部分合併成一個字串，最終形成一個形如 header.payload.signature 的 JWT 字串。

#### Header（標頭）
Header 是 JWT 的頭部，通常包含兩部分信息：
* Type：說明令牌的類型，一般是 "JWT"。
* Algorithm：加密算法，通常是 HS256（HMAC SHA-256）或 RS256（RSA SHA-256）。

```jsonld=
{
  "alg": "HS256",
  "typ": "JWT"
}
```
會被轉換成 Base64Url 編碼
#### Payload（有效負載）
Payload 是 JWT 的主體，包含具體的聲明（Claims）。Claims 是一些陳述，比如用戶的身份或權限。Payload 可以包含以下三種聲明：
1. Registered Claims（註冊聲明）：一些建議的、通用的字段，例如 iss（簽發者）、exp（過期時間）、sub（主體）、aud（接收者）等。
1. Public Claims（公共聲明）：可以自行定義，並且可以在 OpenID 等標準中定義，但需要避免與註冊聲明名稱的衝突。
1. Private Claims（私有聲明）：自定義聲明，用於應用之間的特定信息。
```jsonld=
{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true,
  "exp": 1516239022
}
```
會被轉換成 Base64Url 編碼
#### Signature（簽名）
Signature 用於驗證 JWT 的數據完整性，防止數據被篡改。簽名由以下三部分組成：
* 編碼後的 Header
* 編碼後的 Payload
* 秘鑰（Secret）

這三者通過 Header 中指定的算法進行加密，生成簽名部分。例如使用 HMAC SHA-256 的算法：
```jsonld=
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret
)
```
###  JWT 的生成和驗證
#### JWT 的生成
在服務器上生成 JWT 的過程包括以下步驟：
1. 建立 Header、Payload 並轉換為 JSON 格式。
1. 將 Header 和 Payload 轉換為 Base64Url 格式。
1. 根據 Header 中指定的加密算法，使用秘鑰生成簽名。
1. 最後將 Header、Payload 和 Signature 三部分組合成一個字串，即完成 JWT 的生成。

####  JWT 的驗證
當伺服器收到包含 JWT 的請求時，伺服器會進行以下驗證過程：
1. 提取出 JWT 的 Header 和 Payload，並根據 Header 中的算法和事先設置的秘鑰重新生成簽名。
1. 將生成的簽名與 JWT 中的簽名進行比較，確保 JWT 未被篡改。
1. 驗證 Payload 中的 exp（過期時間）、iss（簽發者）等聲明，確認 JWT 的有效性。
1. 如果簽名和聲明都合法，則通過驗證，允許該請求訪問受保護資源。

#### JWT 的使用場景
1. 身份驗證：JWT 最常用於身份驗證。在用戶登錄後，服務器生成一個包含用戶信息的 JWT，並將其返回給客戶端。客戶端在每次請求中附帶此 JWT，服務器通過驗證 JWT 來識別用戶身份。
1. 授權：JWT 也可以用於授權，客戶端攜帶 JWT 訪問不同的資源，服務器根據 JWT 中的權限進行授權。
1. 信息傳遞：JWT 也可以用於多系統之間的數據傳遞。JWT 是無狀態的，並且可以攜帶任何聲明，因此適合跨服務傳遞簡單的數據。

#### JWT 的優缺點
* 優點：
    1. 無狀態：JWT 是無狀態的，伺服器無需存儲會話數據，可以減少伺服器的負擔，特別適合分佈式架構。
    1. 攜帶自定義數據：JWT 可以攜帶任何聲明，並且自定義數據允許應用程式根據需要存儲任意數據。
    1. 安全：JWT 通過簽名確保數據的完整性，並且可以使用私鑰進行簽名，保證數據的安全性。
* 缺點：
    1. 無法撤銷：一旦 JWT 被簽發，無法撤銷或主動使其失效，這可能會帶來風險。
    1. 大小限制：JWT 中包含的數據越多，令牌的大小就越大，這對帶寬和性能有一定的影響。
    1. 安全風險：JWT 的安全性依賴於密鑰管理，如果密鑰洩露，會帶來數據風險。

#### JWT 與 Session 的對比
![image](https://hackmd.io/_uploads/ByTEpEc-Jg.png)

#### JWT 的實際應用案例
1. 用戶登錄系統：在用戶登錄後生成 JWT，然後將其存儲於客戶端。隨後每次請求攜帶該 JWT 作為身份驗證。
1. API 授權：在微服務架構中，每個服務驗證 JWT 來確保請求來自合法用戶，適合分佈式環境。
1. OAuth 2.0 和 OpenID Connect：JWT 常用於 OAuth 2.0 的存取令牌（Access Token），並在 OpenID Connect 中作為身份令牌（ID Token）。

### JWT的過期處理
#### 使用 exp（Expiration）聲明
> 在生成 JWT 時，可以在 Payload 中設置 exp（expiration）屬性。該屬性是一個 UNIX 時間戳，表示 JWT 的過期時間。服務器在驗證 JWT 時會檢查 exp 屬性，如果當前時間超過了 exp 指定的時間，則視為該令牌已過期

```jsonld=
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022,
  "exp": 1516242622  // 過期時間
}
```
#### 過期 Token 的處理策略
處理過期 Token 的策略主要有以下幾種方式：
##### 返回錯誤，要求重新登入
> 這是最簡單的處理方式。如果令牌過期，伺服器會返回一個 "Token Expired" 的錯誤，並要求用戶重新登入以獲取新的令牌。這種方式適合安全性要求高的應用，因為它確保了每次使用者登入都會獲取新的令牌。

* 優點：
    * 安全性高，避免了過期令牌的風險。
* 缺點：
    *用戶體驗可能不佳，因為過期後需要重新登入。
    
##### 使用 Refresh Token 機制
Refresh Token 機制是另一種常見的處理過期 Token 的方法，特別適合需要長時間保持登入狀態的應用。
流程如下：
1. 當用戶登入成功後，伺服器會同時生成Access Token（短期有效）和Refresh Token（長期有效）。
1. Access Token 有較短的有效期（例如 15 分鐘），而 Refresh Token 有較長的有效期（例如 7 天）。
1. 當 Access Token 過期後，客戶端使用 Refresh Token 向伺服器請求新的 Access Token。
1. 伺服器驗證 Refresh Token 的合法性，通過驗證後返回新的 Access Token。這樣，用戶無需重新登入即可繼續訪問資源。
1. Refresh Token 一旦過期，則需要用戶重新登入。

這種方法將用戶的會話控制權轉移到了伺服器上，通過 Refresh Token 延長用戶的登入狀態，同時不影響安全性

* 優點：
    * 提供了無縫的用戶體驗，用戶無需頻繁重新登入。
    * 安全性較高，即使 Access Token 過期，用戶也可以通過 Refresh Token 繼續使用。
* 缺點：
    * 需要額外的機制來存儲和管理 Refresh Token，並防止 Refresh Token 遭到竊取。

##### 短期令牌加頻繁自動刷新
這種策略類似於 Refresh Token 機制，但沒有使用單獨的 Refresh Token。伺服器可以生成短期有效的 JWT（例如 5 分鐘有效），並在客戶端靠近過期時間前自動向伺服器請求刷新令牌。這樣，可以實現無縫體驗，且無需使用 Refresh Token。

客戶端在靠近過期時會自動請求新的令牌，如果用戶不活躍一段時間（沒有進行刷新請求），則 JWT 會自動過期。

* 優點：
    * 簡化了 Refresh Token 的管理和存儲。
無需頻繁重新登入。
* 缺點：
    * 須確保網絡連線穩定，否則可能導致令牌刷新失敗。

##### 安全性考慮
在處理過期 Token 時，還需注意以下安全性問題：
1. 定期清理 Refresh Token：在使用 Refresh Token 機制時，需要定期檢查和清理過期的 Refresh Token，以減少潛在的安全風險。
1. 防止 Refresh Token 被竊取：Refresh Token 的有效期長，應特別注意其存儲方式，避免存放在不安全的地方（例如不受保護的 Local Storage），建議將其存儲在安全的 Cookie 中，並設置 HttpOnly 和 Secure 標誌。
1. 單次使用的 Refresh Token（One-time Use Refresh Token）：在每次使用 Refresh Token 獲取新的 Access Token 時，將原有的 Refresh Token 作廢，並生成新的 Refresh Token，從而防止舊的 Refresh Token 被重用，提升安全性。

## RBAC（Role-Based Access Control，基於角色的訪問控制）
> 是一種授權模型，用於控制系統中的用戶如何訪問資源。RBAC 通過角色來管理和分配權限，從而簡化權限管理流程，提高靈活性和安全性。

**在 RBAC 模型中，有三個主要元素：**
1. 用戶（User）：系統中的個人或實體，每個用戶在系統中可以擁有多個角色。
1. 角色（Role）：角色是一組權限的集合，代表某一類用戶的操作權限。例如，管理員、編輯者、訪客等。
1. 權限（Permission）：具體的操作權限或資源訪問權限，通常指代對系統資源的具體操作（如讀取、寫入、刪除等）。

RBAC 通過將權限分配給角色，並讓用戶扮演角色來獲取權限。這樣，用戶並不是直接與權限綁定，而是通過角色來間接管理權限，從而提高了管理的靈活性。

**RBAC 的核心概念**
* 用戶分配角色（User Assignment of Roles）：
    * 每個用戶可以被分配一個或多個角色，這些角色決定了用戶擁有哪些權限。
    * 例如，Alice 可以被分配為「管理員」，而 Bob 被分配為「訪客」。
* 角色分配權限（Role Assignment of Permissions）：
    * 每個角色可以擁有多個權限，這些權限是角色可以進行的操作。
    * 例如，「管理員」角色可能擁有「讀取」、「寫入」、「刪除」的權限，而「訪客」角色可能只有「讀取」的權限。
* 用戶透過角色獲得權限：
    * 用戶在被分配角色後，便可以通過角色來間接地獲得權限。
    * 當系統進行權限檢查時，會確認用戶是否具備該角色，進而決定是否授予權限。

**RBAC 的優點**
* 簡化權限管理：RBAC 通過角色來管理權限，減少了直接為每個用戶分配權限的工作量，便於維護。
* 靈活性高：可以根據不同的業務需求創建角色，靈活配置每個角色的權限，適應不同角色的需求。
* 安全性高：RBAC 有助於控制誰可以執行哪些操作，從而增強了系統的安全性。
* 便於組織管理：在大型系統中，RBAC 可以對應不同的職位、職能來創建角色，使得權限管理更加貼合組織結構。

**RBAC 的應用場景**
RBAC 適用於大部分具有多層次權限需求的系統，特別是在企業級應用中。以下是常見的應用場景：
* 企業管理系統：例如 ERP、CRM 等系統，通常具有不同的角色（如管理員、員工、訪問者等），每個角色擁有不同的訪問和操作權限。
* 內容管理系統（CMS）：在 CMS 中，編輯、審核、發布等角色可能擁有不同的權限，例如編輯只能創建和修改內容，審核者可以批准內容，發布者可以將內容公開。
* 教育系統：在學校的學習管理系統中，可能會有學生、老師、管理員等不同角色，每個角色擁有不同的功能權限。

**RBAC 的層次模型**
RBAC 模型根據其複雜度和功能進行了分層，從簡單到複雜分為以下三層：
1. RBAC0（基礎 RBAC）：
    * 基礎層級，包含了用戶、角色、權限三個概念，允許將角色分配給用戶，並將權限分配給角色。
2. RBAC1（角色層次結構）：
    * 增加了角色的層次結構，可以設置角色之間的繼承關係，例如高級角色可以繼承低級角色的權限。
    * 例子：管理員角色可以繼承員工角色的權限，這樣管理員就自動擁有了員工的所有權限。
3. RBAC2（約束）：
    * 在 RBAC1 的基礎上引入了「約束」概念，可以定義角色的互斥條件或衝突，例如一個用戶不能同時擁有「審核員」和「被審核人」的角色。
    * 約束可以防止權限過度授予，提高系統的安全性。
4. RBAC3（RBAC1 + RBAC2）：
    * 綜合了角色繼承和約束功能的最複雜模型，適合複雜的大型系統。

## Docker 
> Docker 是一種容器化技術，用於創建、部署和運行應用程式的輕量級虛擬化環境。它通過將應用程式及其依賴打包在「容器（Container）」中，使得應用能夠在不同的環境中一致地運行，從而提高了應用的可移植性和開發效率。

### Docker 的核心概念
1. 容器（Container）：
    * 容器是 Docker 的核心單位，它包含了應用程式及其所有的依賴（如庫、工具、配置文件等），保證應用程式可以在任何支持 Docker 的平台上運行。
    * 容器類似於輕量級的虛擬機，但更輕便高效，因為它共享了主機的操作系統內核。
2. 映像（Image）：
    * Docker 映像是一種只讀的模板，包含應用程序的文件系統和運行環境（如代碼、運行時、庫等）。
    * 每個映像是層級化的，由多個「層」組成，每層代表了不同的文件和配置。Docker 使用聯合文件系統來組合這些層，以便快速構建和重用。
    * 使用映像可以快速生成容器，並且一個映像可以創建多個容器。
3. Dockerfile：
    * Dockerfile 是一種用於描述映像構建過程的腳本文件。通過編寫 Dockerfile，開發者可以定義應用所需的所有安裝步驟和配置，並最終生成一個 Docker 映像。
    * Dockerfile 中的指令（如 FROM、RUN、COPY 等）可以指定基礎映像、運行命令、安裝依賴等。
4. Docker 引擎（Docker Engine）：
    * Docker 引擎是運行在主機上的 Docker 核心組件，負責映像的生成、容器的運行和資源管理等。
    * 它包括 Docker 守護進程（daemon）和 Docker CLI（命令行工具），開發者可以通過 CLI 與 Docker 守護進程交互，管理容器和映像。
5. 倉庫（Registry）：
    * Docker 映像可以存儲在倉庫中，類似於代碼存儲庫。Docker Hub 是最流行的公共倉庫，用於存儲和分發映像。
    * 用戶也可以搭建私有倉庫（如 Harbor），以便在企業內部存儲和管理映像。

### Docker 的核心特性
1. 輕量級虛擬化：
    * Docker 容器與傳統的虛擬機相比更輕量，因為容器共享主機的操作系統內核，而不是模擬整個操作系統。
    * 容器的啟動和停止速度非常快，通常在秒級，這樣可以大大提高應用的部署和擴展速度。
2. 可移植性：
    * Docker 容器包含了應用所需的全部依賴，因此可以在任何支持 Docker 的環境中運行，無需重新配置。
    * 開發人員可以在本地開發和測試，並確保應用可以無縫運行在生產環境中，解決了「在我電腦上可以正常運行」的問題。
3. 資源隔離：
    * Docker 使用 Linux 的內核功能（如命名空間和控制組，Namespace 和 cgroups）來實現容器之間的資源隔離。每個容器都是獨立的，不會互相干擾。
    * 這樣可以確保每個應用在自己的隔離環境中運行，並且可以根據需求配置 CPU、內存和存儲資源的使用量。
4. 版本控制和映像層：
    * Docker 映像是分層構建的，每一層都代表了映像的一個修改。這樣，Docker 可以重用相同層，減少存儲需求並加快構建過程。
    * 映像可以版本化，使得開發人員可以回滾到特定的版本。
5. 自動化：
    * Dockerfile 可以自動化映像的構建過程，將映像的生成、依賴安裝、配置過程等全部定義在代碼中。
    * 配合 CI/CD 工具，可以實現從代碼提交到生產部署的全流程自動化。

### Docker 的底層技術
#### 命名空間（Namespaces）：
* 命名空間 是 Linux 提供的一種機制，用於隔離系統資源（如進程 ID、網絡、掛載點等），使每個容器擁有自己的「命名空間」，從而與主機和其他容器隔離。
* 常見的命名空間包括：
    * PID 命名空間：隔離進程 ID，使容器內的進程擁有獨立的進程樹。
    * NET 命名空間：隔離網絡資源，包括網卡、IP 地址、路由表等，使每個容器擁有獨立的網絡環境。
    * IPC 命名空間：隔離進程間通信資源，使容器內的進程無法直接與其他容器通信。
    * MNT 命名空間：隔離文件系統掛載點，使每個容器擁有自己的文件系統視圖。
    * UTS 命名空間：隔離主機名和域名，使每個容器可以設置自己的主機名。
* 作用：通過命名空間，Docker 可以在單一操作系統內核上運行多個隔離的容器，達到類似虛擬機的效果，但佔用的資源更少
#### 控制組（Control Groups，cgroups）
* cgroups 是 Linux 的一項功能，用於限制、隔離和管理不同進程組的資源使用（如 CPU、內存、磁碟 I/O 等）。
* 資源限制：cgroups 允許 Docker 限制每個容器可以使用的 CPU、內存和磁盤 I/O 等資源，確保每個容器在系統資源的使用上彼此隔離。
* 資源統計：cgroups 提供精確的資源使用統計，便於監控和管理容器的資源消耗。
* 作用：通過 cgroups，Docker 可以確保容器之間的資源隔離，並防止某個容器消耗過多的系統資源，影響其他容器或主機的穩定性。

#### 聯合文件系統（Union FS）：
* Union FS 是一種文件系統技術，允許將多個文件系統層疊合併為一個文件系統視圖。
* 分層映像（Layered Images）：Docker 使用 Union FS 來構建分層的映像，每個 Docker 映像由多個層組成，每層只記錄改動部分（例如新增、修改的文件）。這樣可以提高映像的重用性和構建速度。
* 寫時複製（Copy-on-Write）：容器啟動後會在映像的基礎上創建一個可寫層，對文件的修改僅影響容器內的可寫層，不會影響基礎映像。
* 作用：通過 Union FS，Docker 映像可以快速構建和部署，並且允許映像分層重用，提高存儲效率。

#### 容器格式（如 OCI 標準）：
* OCI（Open Container Initiative） 定義了容器映像和運行時標準，確保不同的容器工具之間的互操作性。
* Docker 映像和 OCI 格式兼容：Docker 映像格式基於 OCI 標準，使得 Docker 映像可以在支持 OCI 的運行環境中執行，提高了可移植性。
#### 網絡虛擬化（Networking）：
* Docker 支持多種網絡模式（bridge、host、overlay 等），每種模式通過虛擬網卡和網橋（bridge）等技術實現網絡隔離和互通。
* 橋接模式（Bridge Networking）：每個容器都連接到一個虛擬網橋，容器之間可以相互通信，並且可以通過主機的 NAT 轉發訪問外部網絡。
* 主機模式（Host Networking）：容器共享主機的網絡堆棧，性能較好但不具備隔離性。
* 覆蓋網絡（Overlay Networking）：多主機之間的容器可以通過 overlay 網絡進行通信，通常用於跨主機的容器編排。
### VM與Docker的差異
![image](https://hackmd.io/_uploads/SySGpIcZkg.png)

![image](https://hackmd.io/_uploads/H1rtAU5Wye.png)


#### 架構層次：
**虛擬機（VM）：**
在 VM 架構中，每個虛擬機運行在一個 Hypervisor（虛擬機管理程序）上。
每個虛擬機都有一個完整的 Guest OS（客體操作系統），即每個 VM 需要自己的操作系統副本，這樣才能支持上層的應用程式。
**Docker 容器：**
Docker 的架構更輕量級，它直接在宿主機的操作系統上運行。所有的容器共享同一個操作系統核心（Host OS）。
Docker 通過 Docker Engine 來管理和運行容器，而不需要每個容器都有一個完整的操作系統副本。
#### 資源佔用：
**虛擬機（VM）：**
* 每個 VM 都包含一個完整的操作系統，這就增加了大量的資源開銷（如內存和 CPU 使用）。此外，啟動每個 VM 時也需要分配足夠的資源來支持其運行的 OS。

**Docker 容器：**
* 容器不需要運行獨立的操作系統，而是共享主機的操作系統核心，因此比 VM 更輕量，佔用的資源更少。
* 容器的啟動速度非常快（通常在秒級），因為不需要加載完整的操作系統。
#### 啟動速度：
**虛擬機（VM）：**
* VM 啟動相對較慢，因為需要加載整個操作系統。啟動時間通常在數十秒到數分鐘之間，具體取決於 OS 和應用的複雜性。

**Docker 容器：**
* Docker 容器啟動非常迅速，因為它們不需要啟動完整的操作系統。容器的啟動時間通常在數秒以內。
#### 隔離性與安全性：
**虛擬機（VM）：**
* 每個 VM 有自己的操作系統內核，因此隔離性更強。如果一個 VM 出現故障或被攻擊，理論上不會直接影響到其他 VM 和主機系統。
    
**Docker 容器：**
* 容器共享主機的操作系統核心，因此隔離性略低於 VM。如果主機操作系統出現問題，可能會影響到所有容器。
* 但 Docker 也有自己的安全機制（如命名空間和控制組），以實現一定程度的隔離。
#### 可移植性：
**虛擬機（VM）：**
* VM 在不同環境間的移動性不如容器靈活，因為 VM 包含整個操作系統，需要更高的計算資源。

**Docker 容器：**
* 容器包含應用和其依賴，可以輕鬆地在不同環境中移動和運行（例如從開發環境到生產環境），非常適合 DevOps 流程。
#### 運行效率與性能：
**虛擬機（VM）：**
* 因為 VM 運行自己的操作系統，性能上略低，存在一定的性能開銷（例如 Hypervisor 的開銷）。

**Docker 容器：**
* 容器運行效率較高，因為它直接運行在主機的操作系統上，不需要 Hypervisor，減少了系統的資源消耗。

## 事件驅動
基於「事件」來驅動程式的執行流。這意味著應用程式的邏輯執行不是按照順序進行，而是由發生的事件來觸發對應的動作。這種模型廣泛應用於 GUI 應用程式、網絡服務、高併發 I/O 操作等場景，因為它可以高效地管理和響應大量並發請求。
:::danger
不適合CPU密集型的任務。
事件驅動模型主要適合 I/O 密集型任務，如網絡請求、文件讀寫等。如果應用程式包含大量的 CPU 密集型任務，單一執行緒可能會無法滿足需求。
:::
### 事件驅動模型的基本原理
事件驅動模型的核心思想是應用程式進入一個 事件循環（Event Loop） 中，等待並監控各種事件的發生。當事件發生時，事件循環會觸發對應的 回調函數（Callback） 來處理該事件。事件驅動通常包含以下幾個要素：
* 事件：
    * 事件是系統中發生的動作或變化，如按下按鈕、網絡請求、定時器到期、文件可讀寫等。
    * 事件可以是來自用戶的輸入操作，也可以是來自系統的 I/O 事件。
* 事件循環（Event Loop）：
    * 事件循環是事件驅動模型的核心組件，它會持續運行，等待和監控各種事件的發生。
    * 當某個事件發生時，事件循環會將控制權交給對應的回調函數來處理該事件。
    * 事件循環允許單個執行緒在非阻塞的情況下處理多個事件，因此適合高併發場景。
* 事件觸發器：
    * 當事件發生時，事件觸發器會負責檢測和捕捉該事件，並通知事件循環去執行相應的回調。
* 回調函數（Callback Function）：
    * 回調函數是事先定義好的處理函數，當事件發生時會被調用。
    * 不同的事件可以綁定不同的回調函數，這樣應用程式可以根據事件類型來執行對應的邏輯。

### 事件驅動模型的工作流程
1. 初始化事件循環：
    * 應用程式啟動時會初始化一個事件循環，準備進入等待狀態。
2. 註冊事件與回調：
    * 為各類事件（如網絡請求、文件 I/O、定時器）設置回調函數，並將這些事件註冊到事件循環中，讓事件循環知道在事件發生時應調用哪個回調函數。
3. 事件等待和檢測：
    * 事件循環進入等待狀態，監控已註冊的事件。這通常是通過輪詢機制（如 select、poll、epoll）來實現的。
4. 事件觸發與執行：
    * 當某個事件發生（如文件可讀、定時器觸發）時，事件循環會檢測到該事件並觸發相應的回調函數。
    * 事件循環將控制權交給對應的回調函數來處理該事件。例如，讀取文件數據、處理網絡請求等。
5. 重複事件循環：
    * 當所有事件處理完成後，事件循環繼續監控其他事件，直到程式退出。

## OOP
物件導向程式設計（OOP,Object-oriented programming）是一種程式設計範式，它將程式中的資料和操作進行封裝，並將其組織成物件。
1. 類別（Class）和物件（Object）：
    * OOP 將程式中的實體抽象為類別，類別定義了對應的屬性（資料）和方法（操作）。
    * 類別的實例化則產生了物件，也就是類別的具體實例。每個物件都有自己的狀態（屬性值）和行為（方法）。
1. 封裝（Encapsulation）：
    * 可以使用訪問修飾符（public、protected、private）來控制類別成員的訪問權限，從而實現封裝。
    * 封裝是指將資料和操作封裝在物件內部，對外部隱藏實現的細節，只提供公開的介面（方法）供外部使用。
    * 封裝有助於保護資料的完整性，並隱藏了實現的細節，提高了代碼的安全性和可維護性。
1. 繼承（Inheritance）：
    * 繼承是指一個類別（子類別）可以繼承另一個類別（父類別）的屬性和方法，並且可以在其基礎上進行擴展和修改。
    * 繼承允許代碼的重用，減少了重複編碼，同時使代碼更加結構化和模組化。
1. 多型（Polymorphism）：
    * 在 PHP 中，可以通過覆寫父類別的方法來實現多型。
    * 多型是指相同的操作可以在不同的物件類型上具有不同的行為。這使得可以使用相同的介面來處理不同的對象。
    * 多型提高了代碼的靈活性和可擴展性，使代碼更容易理解和維護。

OOP 的設計思想強調將複雜的系統分解為獨立的、可重用的組件，並通過組合這些組件來構建更大的系統。

## 工廠模式
旨在通過將對象的創建和初始化邏輯封裝在一個工廠類中，從而隔離客戶端程式碼與具體類別之間的相依性。工廠模式可以使系統更靈活、可擴展，並且易於維護和測試。
在 PHP 中，常見的工廠模式包括簡單工廠模式、工廠方法模式和抽象工廠模式。
* 簡單工廠模式（Simple Factory Pattern）：
    * 簡單工廠模式是一種最基本的工廠模式，通常由一個工廠類別負責根據客戶端的請求創建適當的對象。
    * 客戶端不需要直接創建對象，而是通過呼叫工廠類別的靜態方法或者非靜態方法來取得對象。
```php=
class CarFactory {
    public static function createCar($type) {
        switch ($type) {
            case 'SUV':
                return new SUV();
            case 'Sedan':
                return new Sedan();
            default:
                throw new Exception("Invalid car type");
        }
    }
}

// 使用簡單工廠模式
$car1 = CarFactory::createCar('SUV');
$car2 = CarFactory::createCar('Sedan');
```
* 工廠方法模式（Factory Method Pattern）：
    * 工廠方法模式將對象的創建延遲到其子類別中，每個子類別都實現了一個工廠方法來創建對應的對象。
    * 客戶端通過調用工廠方法來獲取對象，從而可以根據需要使用不同的對象。
```php=
interface CarFactory {
    public function createCar();
}

class SUVFactory implements CarFactory {
    public function createCar() {
        return new SUV();
    }
}

class SedanFactory implements CarFactory {
    public function createCar() {
        return new Sedan();
    }
}

// 使用工廠方法模式
$suvFactory = new SUVFactory();
$car1 = $suvFactory->createCar();

$sedanFactory = new SedanFactory();
$car2 = $sedanFactory->createCar();
```
* 抽象工廠模式（Abstract Factory Pattern）：
    * 抽象工廠模式提供了一種創建相關或相依對象的方法，而不需要指定具體的類別。
    * 通常由一個抽象工廠類別定義一組相關的工廠方法，每個具體工廠類別實現了這些工廠方法來創建一組相關的產品。
```php=
interface CarFactory {
    public function createSUV();
    public function createSedan();
}

class LuxuryCarFactory implements CarFactory {
    public function createSUV() {
        return new LuxurySUV();
    }
    
    public function createSedan() {
        return new LuxurySedan();
    }
}

class EconomicCarFactory implements CarFactory {
    public function createSUV() {
        return new EconomicSUV();
    }
    
    public function createSedan() {
        return new EconomicSedan();
    }
}

// 使用抽象工廠模式
$luxuryFactory = new LuxuryCarFactory();
$luxurySUV = $luxuryFactory->createSUV();
$luxurySedan = $luxuryFactory->createSedan();

$economicFactory = new EconomicCarFactory();
$economicSUV = $economicFactory->createSUV();
$economicSedan = $economicFactory->createSedan();
```


## 微服務
> 是一種將應用系統分解為一組小型、獨立、可部署的服務的設計模式。每個微服務負責完成特定的業務功能，並通過輕量級的通信協議（通常是 HTTP 或消息隊列）進行交互。這種架構提高了系統的靈活性、可擴展性和可維護性，使得開發團隊可以更快速地構建和迭代大型應用。

### 微服務架構的核心概念
1. 服務拆分：
    * 在微服務架構中，應用程序被分解為一組獨立的服務，每個服務負責一個具體的業務功能，如用戶管理、訂單處理、支付處理等。
    * 每個微服務都是獨立的應用單位，擁有自己的邏輯和數據存儲，並且可以獨立部署和更新。
2. 獨立部署：
    * 微服務可以獨立於其他服務進行開發、測試和部署。這使得開發團隊能夠靈活地迭代和更新每個微服務，而不影響整個系統。
    * 每個微服務都有自己的生命周期，可以根據業務需求獨立升級和擴展。
3. 輕量級通信：
    * 微服務之間通過輕量級的通信協議來進行交互，通常是基於 HTTP 的 REST API 或基於消息的通信（如 RabbitMQ、Kafka 等）。
    * 微服務之間的通信是無狀態的，這樣可以避免耦合，並且提高了服務的獨立性和可測試性。
4. 去中心化數據管理：
    * 在微服務架構中，每個服務通常擁有自己的數據存儲，這樣避免了數據的集中化管理。
    * 每個微服務的數據庫是獨立的，可以根據服務的需求選擇不同的數據庫技術（如 NoSQL 或 SQL），這樣可以提高數據存儲的靈活性和性能。
5. 容錯性和高可用性：
    * 微服務架構允許每個服務在故障發生時不影響其他服務，這提高了系統的容錯能力和高可用性。
    * 使用負載均衡、服務註冊和發現等技術，實現自動故障轉移和動態擴展，提高系統的穩定性和可用性。

### 微服務架構的優點
1. 可擴展性：
    * 每個微服務可以獨立擴展，根據需求增加或減少資源。例如，流量大的服務可以獨立水平擴展，而不需要影響其他服務。
    * 根據服務的需求，可以選擇合適的技術棧和數據庫，針對不同業務需求進行優化。
2. 靈活性和技術多樣性：
    * 不同的微服務可以使用不同的編程語言、框架和數據庫，這樣可以根據每個服務的具體需求選擇最佳的技術棧。
    * 開發團隊可以更加靈活地選擇技術，適應快速變化的業務需求。
3. 獨立部署和迭代：
    * 微服務架構允許每個服務獨立部署，這樣可以更頻繁地進行發布，而不影響其他服務的穩定性。
    * 每個團隊可以專注於自己的服務，實現持續交付和持續部署。
4. 容錯和高可用性：
    * 微服務架構通過隔離每個服務的故障，提高了整體系統的容錯能力。
    * 如果某個服務出現故障，其他服務仍然可以繼續運行，從而提高系統的高可用性。
5. 便於持續交付（CI/CD）：
    * 微服務架構允許各個服務獨立開發和部署，便於實現自動化測試和持續集成。
    * 因為每個微服務都是相對獨立的，因此可以對其進行自動化測試和快速發布。
### 微服務架構的挑戰
1. 分布式系統的複雜性：
    * 微服務是一種分布式系統，這意味著要面對網絡延遲、異常處理、分佈式數據一致性等挑戰。
    * 對於開發團隊來說，這需要更高的技能來處理分佈式系統的問題。
2. 數據一致性問題：
    * 在微服務架構中，每個服務擁有自己的數據存儲，這可能會導致數據一致性問題。
    * 例如，訂單服務和支付服務分別管理不同的數據，必須使用分佈式事務或最終一致性策略來確保數據一致。
3. 運維和部署的複雜性：
    * 微服務架構中有大量的獨立服務，這使得部署、運維和監控變得更為複雜。
    * 需要引入容器化技術（如 Docker）、集群管理工具（如 Kubernetes）來進行微服務的管理和編排。
4. 通信和安全：
    * 微服務之間的通信通常使用 HTTP、gRPC 或消息隊列，這需要額外的設計來處理通信安全、加密和認證等問題。
    * 因為各個服務之間存在跨網絡的交互，需實施 API Gateway 和服務安全機制來保障通信的安全性。
5. 測試的複雜性：
    * 在微服務架構中，單一服務的變更可能會影響整個系統，因此需要進行單元測試、集成測試和端到端測試來確保系統的穩定性。
    * 測試和調試分佈式微服務可能比較困難，需要專門的測試環境和工具。
### 微服務架構的組件
在微服務架構中，常見的組件包括：

1. API Gateway：
    * API Gateway 是微服務架構中的入口，負責路由請求、負載均衡、認證和授權、限流等功能。
    * 它允許前端應用通過統一的接口訪問後端的不同微服務，簡化了客戶端與後端之間的通信。
2. 服務註冊與發現：
    * 服務註冊和發現系統允許微服務之間自動檢測和連接。
    * 例如，當一個新的微服務啟動時，它會向服務註冊中心註冊自己的位置，這樣其他微服務可以通過服務註冊中心找到它。
3. 配置管理：
    * 在分佈式系統中，集中式配置管理可以方便地管理所有微服務的配置。
    * 常見的配置管理工具如 Spring Cloud Config、Consul 等，可以讓微服務動態地加載和更新配置。
4. 消息隊列（Message Queue）：
    * 微服務之間的通信可以使用消息隊列來實現非同步處理和解耦。
    * 常見的消息隊列有 RabbitMQ、Kafka、ActiveMQ 等，允許微服務以事件驅動的方式進行交互。
5. 監控與日誌：
    * 在微服務架構中，需要對每個服務進行獨立的監控和日誌記錄，以便於快速診斷和解決問題。
    * 如 Prometheus、ELK 堆疊（Elasticsearch, Logstash, Kibana）可以幫助收集和分析服務的性能數據和日誌。


### 我的論文 (AnserGateway)
基於workerman開發，透過workerman可替換的event loop機制，加上Swow所支援的coroutine機制，完成更高效的請求處理。

:::success
* 簡而言之，AnserGateway的base就是多process，每個process都只有一個thread，每個thread都有多個coroutine。
* 對於AnserGateway而言，每個request都會被coroutine處理，等於thread內會塞很多個coroutine，而coroutine在遭遇I/O(API 調用或read file)時，將會自動把控制權讓出並交給其他coroutine使用，而這段切換並不是CPU的Context switch，而是用戶態切換（User-mode switching），因為在做coroutine資源切換時，會將當前資料存在暫存器(記憶體)當中，等I/O做完後在切換回來，與System call的資源消耗相比，coroutine切換的cost會小很多。
* coroutine的切換是交給協程調度器去做的，而協程調度器，負責協程的創建、切換和銷毀。調度器會將所有協程放入一個「待執行隊列」，根據優先級或執行條件（如 I/O 等待）進行協程切換。Swow 支持多種調度策略，例如 FIFO 調度、循環調度等。
* Worker = process
:::

#### Coroutine的生命週期
協程的生命週期階段
* 創建（Created）
    * 協程在被初始化和創建時處於「創建」狀態。此時，協程已經分配了所需的資源（如棧內存），並且已經設置了初始的上下文，但尚未執行。
    * 創建過程通常涉及分配協程棧和上下文結構，並準備協程要執行的函數或任務。
* 就緒（Ready）
    * 創建完成後，協程進入「就緒」狀態，等待調度器分配時間片來執行。
    * 在這個狀態下，協程已經準備好執行，但尚未獲得 CPU 資源，因此還沒有執行過。
* 運行（Running）
    * 當調度器將 CPU 資源分配給協程時，協程進入「運行」狀態，並開始執行其中的任務。
    * 運行狀態下，協程的代碼正在被執行，協程可以進行各種操作，例如計算、I/O 操作等。
* 讓出（Suspended/Waiting）
    * 協程可以在運行過程中主動讓出執行權，進入「讓出」或「等待」狀態，通常是因為協程遇到了阻塞操作（如 I/O 請求）或需要等待某個條件。
    * 在讓出狀態下，協程會保存當前的上下文，並讓出 CPU 控制權，調度器會將 CPU 資源分配給其他協程。
    * 協程可以在以下情況下讓出：
        * 主動讓出：協程通過調用 yield 或類似方法主動讓出執行權。
        * 被動等待：協程執行 I/O 操作或需要等待其他協程結果，此時進入等待狀態。
* 恢復（Resumed）
    當讓出狀態的協程的條件滿足時（如 I/O 完成），調度器會將該協程放回就緒隊列，並在下一次調度時恢復該協程，進入「運行」狀態繼續執行。
    恢復過程中，協程的上下文（如棧指針、寄存器）會被重新加載，以便協程能夠從上次中斷的地方繼續執行。
* 結束（Completed/Terminated）
    當協程的任務執行完成後，協程進入「結束」狀態，釋放相關資源。
    結束狀態意味著協程的生命周期已經完結，不再會被調度器調度。
    在結束過程中，協程的棧空間和上下文結構會被釋放，協程的結果（如果有）會返回給調用者。
#### 解決什麼?
:::danger
Q1. PHP 在 Web 服務器環境中傳統上是單請求-單進程的模式，每個請求使用一個獨立的進程處理，並在請求結束後釋放所有資源。
:::
:::success
A. 在PHP的擴展中，PHP 可以使用 pcntl 等擴展來創建多進程應用，常見於 CLI 和一些常駐程序（如 Workerman）。
因此能提升不少效率，且Workerman的process本身就是個常駐process，因此在處理每個請求時，都可不必重新run一次依賴檔案。
:::

:::danger
Q2. 為何要在workerman中加上swow?
:::
:::success
A. 因為workerman本身有支援event loop不支援coroutine，但Swow程式庫有支援coroutine的event loop，可完美配合workerman的process進行事件處理，且Swow程式庫是基於C語言所撰寫的，因此在效能上會獲得不錯的表現。
:::
:::danger
Q3. Workerman是如何做到多process的
:::
:::success
A. 主要依靠 PHP 的 pcntl 擴展 提供的進程控制功能，具體來說，使用了 pcntl_fork() 函數來創建多個子進程。
**實現流程**
* Master 進程啟動：
    * 當 Workerman 啟動時，首先創建一個主進程（Master 進程），這個主進程負責管理和監控子進程的狀態。
    * 主進程不直接處理請求，它的主要職責是創建和管理工作進程（Worker 進程），並監控其健康狀況。
* 創建 Worker 進程：
    * 主進程啟動後，根據配置的 count（進程數量）設置，通過 pcntl_fork() 創建指定數量的子進程，每個子進程即一個 Worker 進程。
    * 這些 Worker 進程是相互獨立的，彼此之間不共享內存，因此每個進程都有自己的 PHP 執行環境和內存空間，從而避免了多線程環境中的資源爭用問題。
* Worker 進程處理請求：
    * 每個 Worker 進程都會綁定一個監聽的端口，並進入事件循環，等待客戶端的請求。
    * 當客戶端發來請求時，Worker 進程會接受該請求並處理，處理完成後返回結果，並準備接受下一個請求。
* 負載均衡：
    * 在多進程模式下，不同的 Worker 進程可以同時處理多個客戶端請求，這樣能夠分攤負載，提高並發處理能力。
    * 每個進程會獨立處理請求，因此不會互相阻塞，即便一個進程處理較慢，也不會影響其他進程。
* 進程管理與重啟：
    * 主進程不會處理具體的業務邏輯，而是負責管理 Worker 進程的生命週期，包括啟動、監控和重啟。
    * 如果某個 Worker 進程異常退出，主進程會自動監控到這一變化，並重新創建一個新的 Worker 進程來代替它，以保證服務穩定性。
    * 這種自動重啟機制可以提高系統的可靠性和容錯能力。
* 事件循環與非阻塞 I/O：
    * 每個 Worker 進程啟動後，會進入一個事件循環，等待並處理網絡 I/O 事件。Workerman 通常會使用 PHP 的 stream_select() 函數或底層的事件驅動擴展（如 libevent、ev 等）來監控和管理多個 I/O 事件。
    * 這使得每個 Worker 進程可以同時處理多個 I/O 請求，而不需要為每個請求創建一個線程或進程，進一步提升了性能和資源利用率。
:::
:::danger
Q4. workerman + Swow的架構中的資源使用分配?
:::
:::success
CPU 分配和使用
* 多進程模式利用多核 CPU：每個 Worker 進程都是一個獨立的進程，可以運行在不同的 CPU 核心上。假設 Workerman 啟動 4 個 Worker 進程，那麼系統會將這些進程分配到多個 CPU 核心，從而可以同時利用多核的計算能力。
* 單線程內的協程切換：在每個 Worker 進程內，只有一個線程，但 Swow 的協程調度器會在同一個線程內調度多個協程運行。協程是輕量級的，並且執行是非阻塞的，即使有大量協程在運行，因為它們是「合作式」調度，所以不會導致線程阻塞。
* 協程切換的 CPU 開銷低：協程在執行過程中，遇到 I/O 操作（如讀寫數據庫、網絡請求等）時會讓出控制權，而協程之間的切換是輕量的，並不需要系統層面的上下文切換（不像多進程或多線程），因此 CPU 的開銷比較低。這種協程切換通常在用戶態完成，而不是內核態，大大減少了 CPU 的負擔。
* I/O 密集型應用的 CPU 利用率：協程通常更適合 I/O 密集型應用，因為這種應用的主要瓶頸是等待 I/O 完成而不是 CPU 計算。在 I/O 密集型應用中，CPU 的利用率會比較低，而協程的非阻塞特性可以有效地利用 CPU 來處理多個請求，避免 CPU 空閒等待。

記憶體分配
* 每個 Worker 進程都有自己的內存空間：每個 Worker 進程是獨立的，它們之間的內存空間不共享。這意味著每個進程需要獨立分配內存來存儲運行數據和協程的狀態。
* 協程的內存開銷小：協程比線程和進程更輕量級，它們的內存開銷主要來自於協程的堆疊（通常非常小）和協程內部的狀態數據（如局部變量）。Swow 的協程可以在單一進程中創建大量協程，內存佔用相對較小，因此在高併發場景中非常適合使用協程。
* 內存回收：當協程完成執行或不再需要時，Swow 會自動回收內存，釋放協程佔用的資源。這樣可以避免內存洩漏，使系統資源得到更好的利用。

I/O 資源使用
* 非阻塞 I/O：Swow 提供了非阻塞 I/O，這意味著當某個協程在等待 I/O 完成時（如等待網絡數據、文件讀寫等），協程會讓出控制權，讓其他協程繼續執行。這樣一來，不會因為一個 I/O 操作而阻塞整個進程，從而有效提高 I/O 資源的利用率。
* 事件循環管理 I/O 資源：事件循環負責監控所有的 I/O 事件，當 I/O 操作完成時會立即通知相關協程恢復執行。這樣的設計可以減少進程的阻塞等待，使系統能夠並行處理大量 I/O 操作。

系統資源的總體分配
* 多核 CPU 的利用：多個 Worker 進程可以運行在不同的 CPU 核心上，從而實現並行處理，充分利用多核 CPU 的性能。
* 單核內的高效切換：在每個進程內的單線程中，協程切換由 Swow 的協程調度器控制，協程切換開銷非常小。這樣即使在單個 CPU 核心上，也能實現高效的併發處理。
* 內存開銷和管理：協程的內存佔用小，因此在單個進程中可以創建大量協程，而不會帶來大量內存消耗。協程結束後內存會自動釋放，避免內存洩漏。
* I/O 資源的高效使用：非阻塞 I/O 使得在處理 I/O 操作時不會阻塞進程，使 I/O 資源得到了更高效的利用。

總結
* CPU 資源：多進程模式允許利用多核 CPU，每個進程內的協程切換開銷小，適合 I/O 密集型應用。協程的調度避免了繁重的上下文切換，使得 CPU 利用率更高。
* 內存資源：協程內存開銷小，可以在單一進程內創建大量協程。內存的自動回收避免了內存洩漏。
* I/O 資源：非阻塞 I/O 和事件循環提高了 I/O 資源的利用效率，使得進程在等待 I/O 時不會被阻塞。
:::