1. 什么是表鎖?什么是行鎖?什么情況下會使用表鎖?
InnoDB引擎通過“索引”實現行鎖(鎖定滿足條件的行),但如果操作無法通過索引定位行,會導致行鎖失效,進而升級為表鎖。常見的表現為:
(1)條件中未使用索引,InnoDB 無法定位具體行,會鎖整個表;
(2)使用非索引列的范圍查詢,范圍查詢無法通過索引鎖定行,觸發表鎖;
(3)索引失效(如函數 / 類型轉換),索引失效后無法定位行,觸發表鎖;
(4)更新全表的操作,因需更新所有行,行鎖效率低于表鎖。
InnoDB 使用表鎖的核心場景可分為?“主動使用”?和?“被動退化”?兩類,本質是當 “行鎖無法高效實現” 或 “表鎖成本更低” 時的選擇:
(1)主動使用表鎖的場景(顯式或隱式):場景 1:無索引 / 索引失效導致的全表掃描更新、場景 2:執行?LOCK TABLES
?顯式鎖表、場景 3:DDL 操作(數據定義語言):所有 DDL 操作(如?ALTER TABLE
、DROP TABLE
、CREATE INDEX
?等)會自動加?表級排他鎖,防止 DDL 過程中表數據被修改導致結構不一致。
(2)被動退化到表鎖的場景:這類場景是 InnoDB 嘗試加行鎖失敗后,被迫升級為表鎖:場景:行鎖沖突過于頻繁,觸發 “鎖升級”:InnoDB 雖然支持行鎖,但每個行鎖的維護(如鎖結構存儲、沖突檢查)需要消耗內存。當一個事務需要鎖定?極多的行(如鎖定數萬行),且行鎖沖突頻繁時,MySQL 可能會觸發?鎖升級(Lock Escalation)—— 將大量行鎖合并為一個表鎖,減少內存消耗和沖突檢查成本。
2. 責任鏈設計模式?策略模式?模板方法模式?
先明確三者的 “本質定位”—— 不同模式解決的核心問題完全不同:
模板方法模式:解決 “步驟固定但細節可變” 的問題(定義流程骨架,留空細節);
策略模式:解決 “多種算法 / 行為可選” 的問題(封裝不同實現,動態切換);
責任鏈模式:解決 “多個對象依次處理請求” 的問題(請求傳遞,直到被處理)。
使用場景:
模板方法模式:適合 “流程固定,細節可變” 的場景;
策略模式:適合 “多種算法可選,需動態切換” 的場景;
責任鏈模式:適合 “請求需多步處理,且處理者不確定” 的場景。
(1)責任鏈模式
核心是?“將多個處理器(Handler)連成一條鏈,請求沿著鏈傳遞,使用多個節點來處理它”。它的存在主要是為了解決三類核心問題:①解耦 “請求發送者” 與 “請求處理者”:傳統寫法中,發送者需要知道哪個處理器能處理請求,比如說使用if-else來判斷,一旦處理器增減或邏輯變化,發送者代碼必須修改。責任鏈模式中,發送者只需將請求 “丟給鏈的頭部”,無需關心鏈上有多少處理器、誰來處理 —— 處理器的增減 / 順序調整,完全不影響發送者。②支持 “動態組合處理流程”:責任鏈的處理器可以動態添加、刪除或調整順序,靈活適配不同場景。③避免 “if-else/switch” 的代碼臃腫:當處理邏輯有多個分支且可能擴展時,if-else
會導致代碼冗長、可讀性差,責任鏈用 “對象鏈” 替代分支判斷,代碼更符合單一職責原則(每個處理器只處理自己負責的邏輯)。
(2)模板方法模式
核心定義:定義一個固定的流程骨架(父類),將流程中 “可變的步驟” 延遲到子類實現,確保流程的一致性,同時允許細節靈活調整。
核心思想:“骨架不可變,細節可變”,是一種 “父類定規矩,子類填內容” 的模式。
(3)策略模式
核心定義:將多種可替換的算法 / 行為封裝成獨立的 “策略類”,使算法與使用算法的 “上下文” 解耦,上下文可動態切換不同策略(無需修改原有代碼)。
核心思想:“算法家族化,切換動態化”,是一種 “選擇不同實現” 的模式。
3. MySQL中有哪些事務隔離級別?
讀未提交、讀已提交、可重復讀、串行化。
(1)讀已提交如何解決臟讀?
臟讀:一個事務讀取到另一個事務未提交的修改(可能被回滾的數據)。
RC 級別通過 **“只讀取已提交的數據”** 解決臟讀,核心機制是:
每次讀取都獲取最新的已提交版本:事務中每次執行
SELECT
時,都會去讀取其他事務已經提交的數據版本,忽略未提交的修改。實現方式:依賴 MySQL 的多版本并發控制(MVCC)。每個事務修改數據時,會生成一個新的數據版本,并標記版本號(與事務 ID 關聯)。RC 級別下,查詢只會看到 “版本號小于當前事務 ID 且已提交” 的數據,因此不會讀取到未提交的臟數據。
(2)可重復讀如何解決臟讀和不可重復讀?
1. 解決臟讀的機制:與 讀已提交 級別類似,可重復讀 也通過 MVCC 保證 “只讀取已提交的數據”,但對 “已提交版本” 的判斷更嚴格:
事務啟動時會生成一個一致性快照(基于當時的全局事務 ID),整個事務內的所有
SELECT
都讀取這個快照中的數據。快照中只包含 “在事務啟動前已提交的版本”,完全忽略事務啟動后其他事務的未提交修改,因此不會出現臟讀。
2. 解決不可重復讀的機制:可重復讀?通過 **“事務內讀取一致性快照”** 解決不可重復讀:
事務啟動時生成的快照會被整個事務復用,無論其他事務是否提交新的修改,本事務內的
SELECT
始終讀取快照中的舊版本,確保多次讀取結果一致。
(3)可重復讀如何解決幻讀?
幻讀:同一事務內兩次范圍查詢,結果因其他事務插入新數據而增多(“幻覺” 出新行)。
MySQL 的 InnoDB 引擎在 可重復讀 級別通過 **“MVCC 快照讀 + 間隙鎖當前讀”** 組合解決幻讀:
快照讀(普通
SELECT
):
依賴一致性快照,事務內兩次范圍查詢都讀取快照數據,其他事務插入的新數據不在快照中,因此不會看到 “新增的行”。當前讀(加鎖查詢 / 寫操作,如
SELECT ... FOR UPDATE
、INSERT
):
通過間隙鎖(Gap Lock)?鎖定 “可能插入新數據的區間”,阻止其他事務在該區間插入數據,從源頭避免新行產生。
4. 布隆過濾器
布隆過濾器的核心作用是快速判斷一個元素 “是否可能存在”,存在一定的 “誤判率”(但不會漏判)。
原理:通過多個哈希函數將元素映射到一個位數組的多個 bit 位,標記為 1;查詢時,若所有對應 bit 位都是 1,則 “可能存在”,否則 “一定不存在。
特點:優勢:空間效率極高(用 bit 存儲)、查詢速度快(O (k),k 為哈希函數數量);局限:有誤判率(可能把不存在的元素判為 “可能存在”),且不支持刪除操作(刪除會影響其他元素)。
典型使用場景:緩存穿透防護:在緩存前加一層布隆過濾器,提前過濾掉 “一定不存在的 key”,避免請求穿透到數據庫(如惡意查詢不存在的 ID);海量數據去重:如爬蟲 URL 去重(判斷 URL 是否已爬取)、郵件黑名單過濾等。
5. BitMap實現簽到
(1)Key 的設計
用于唯一標識用戶的簽到記錄,通常采用?“業務前綴:用戶 ID: 時間維度”?的結構化命名,例如:user:sign:1001:2024
(用戶 ID=1001 在 2024 年的簽到記錄)
時間維度根據業務需求選擇(年 / 月 / 日),推薦按 “月” 拆分,避免單 Bitmap 過大。
(2)Value 的設計
Bitmap 的 value 是一個?二進制數組(bit 序列),每個 bit 位對應一天的簽到狀態:1
?表示該天已簽到,0
?表示未簽到。位的索引(offset)對應 “當月的第幾天”(通常從 0 開始,如 0 代表 1 號,1 代表 2 號,以此類推)。
(3)具體實現
①簽到操作(記錄簽到狀態):邏輯:用戶簽到時,將對應日期的 bit 位設為 1。命令:SETBIT key offset 1;
②統計簽到情況:檢查某天是否簽到:GETBIT key offset
?→ 返回 1 表示簽到,0 表示未簽到。
③統計當月簽到總次數:BITCOUNT key
?→ 統計 bitmap 中 1 的總數。
④計算連續簽到天數:從當天 offset 往前遍歷,找到第一個 0 的位置,當前 offset 與該位置的差值即為連續天數。
⑤查找當月簽到的所有日期:遍歷 bitmap 所有 bit 位,記錄值為 1 的 offset,再轉換為具體日期。
(4)優勢
極致省內存:1 個月(30 天)的簽到記錄僅需 30 bit(約 4 字節),1000 萬用戶存儲 1 年也僅需約 45MB。
操作高效:基于位運算,簽到和統計的時間復雜度均為 O (1) 或 O (n)(n 為天數,通常很小)。
(5)注意事項
時間維度拆分:避免按年存儲(365 天)導致單 bitmap 過大,推薦按月拆分(最多 31 位)。
offset 計算:需確保日期與 offset 正確映射(如 1 號對應 0,2 號對應 1),避免錯位。
過期清理:對于過期的簽到記錄(如去年的月度數據),可通過?EXPIRE
?設置過期時間自動清理。