魯迅先生曾經說過,每天進步一點點,媽媽夸我小天才。
依舊今日八股,這是我在多個文檔整合一起的,可能格式有些問題,請諒解。
操作系統
1.進程和線程的區別?
- 進程是代碼在數據集合的一次執行活動,是系統調度資源和分配的基本單位。
- 線程是進程的執行路徑,一個進程至少有一個線程,進程中的多個線程共享進程的資源(堆和方法區)。
- 線程中有自己的程序計數器和棧。
2.線程上下文切換要做什么,進程呢?
說到線程我們得先了解一下進程的上下文切換。
進程:上下文切換是一種將CPU資源從一個進程分配給另一個進程的機制。切換過程中,操作系統需要先存儲當前進程的狀態(包括內存空間的指針、當前執行完的指令等等),再讀入下一個就進程的狀態,然后執行此進程。
線程:
- 當兩個線程不屬于同一個進程,切換過程與進程上下文切換一樣。
- 如果線程屬于同一個進程,因為虛擬內存是共享的,所以切換時,虛擬內存這些資源保持不動,只需要切換線程的私有數據、寄存器等不共享的數據。
3.死鎖的四個條件?如何避免?
- 互斥條件
對某資源同時只允許一個線程占用,如果還有其他線程請求獲取該資源,則請求者只能等待、直至占有資源的線程釋放該資源。
- 請求并持有
一個線程已經占有了某資源,但又提出了新的資源請求,而新的資源已被其他線程占有,所以當前線程會阻塞,但不會釋放自己已持有的資源。
- 不可剝奪條件
指線程占有的資源在使用前不會被其他線程搶占,只能在自己使用完畢后由自己釋放資源。
- 環路等待條件
發生死鎖時必然存在一個 資源–線程的環形鏈。
其他細節
- AOP 日志:
- 為什么用 AOP 而不是過濾器/攔截器?
Spring AOP 底層其實就是基于代理和攔截器機制實現的。
但是從開發者視角來看:
- Filter(過濾器) → Servlet 層面,適合處理請求/響應,比如跨域、編碼、權限。
- Interceptor(攔截器) → Spring MVC 層面,適合處理 Controller 前后的邏輯,比如登錄校驗、參數預處理。
- AOP(切面) → 主要用在業務方法層(Service/DAO),可以做到更細粒度的橫切邏輯,比如公共字段填充、統一日志、事務、埋點。
所以我用 AOP,不是因為它和攔截器完全不同,而是:
- 它提供了更細粒度的切入點(不僅限于 Controller,還能作用在 Service、DAO)。
- 和注解結合更自然,可以精確控制哪些方法要切入,而不是所有請求都走一遍。
- 非侵入性更好,業務代碼不用關心。
- OSS 文件上傳:
- 分片上傳和斷點續傳的原理?
分片上傳和斷點續傳的核心邏輯其實在前端。前端把文件切成固定大小的分片,并發調用 OSS 的分片上傳 API。斷點續傳時,前端會保存 uploadId
和已上傳分片的狀態(本地或數據庫),下次上傳時只補傳未完成分片。
服務端這邊比較輕量,主要就是對接 OSS API,做上傳憑證簽名和回調處理。
后:
-
后端主要做 安全控制:前端要上傳前,先請求后端獲取 OSS 簽名憑證
-
負責接收 OSS 的 回調通知(上傳完成/失敗),更新數據庫中文件的狀態。
-
如果有大文件秒傳需求,還可以在后端做 文件唯一性校驗(MD5 校驗),避免重復上傳。
- 斷點續傳是怎么記錄上傳進度的?
-
Nginx + Tomcat:
- Nginx 常見負載均衡策略?
-
輪詢(默認):請求均勻分配。
-
加權輪詢:根據服務器性能分配權重。
-
IP Hash:同一客戶端 IP 訪問固定節點。
-
最少連接數:請求分配給當前連接數最少的節點。
-
一致性哈希(第三方模塊):保證同一用戶盡量落到相同節點。
- 項目中 session 怎么解決共享問題?
有一個地方涉及到了session共享問題,就是在登錄獲取驗證碼的時候,將驗證碼存到哪里的問題,起初是用HttpSession保存驗證碼。但是因為多臺Tomcat不共享數據,而復制數據給其他Tomcat比較浪費空間,所以考慮使用Redis。
MySQL
Q9: 為什么索引用 B+ 樹而不是 B 樹?
A9:
- 二叉樹/紅黑樹的儲存數據的層數太深,磁盤IO多。
- B+ 樹非葉子節點只存索引,只在葉子節點存儲數據,葉子節點之間由鏈表相連,范圍查詢效率高
- 磁盤頁一般16KB,一個B+樹節點能儲存很多key,樹高一般3、4層就能覆蓋百萬級別的數據
Q10: 覆蓋索引和回表的區別?
A10:
假設user表,id為主鍵,name不加修飾,創建這兩個字段的索引
- 覆蓋索引:查詢的數據都能在輔助索引里拿到,不用回表。
比如select name from user where name = 'xxx'
,這樣本身數據就能從輔助索引里找到,不需要回表。
- 回表:
select * from user where id = 1
這樣是不需要回表的,聚簇索引(也就是主鍵索引)他是通過B+樹儲存數據,在葉子節點儲存所有數據記錄和主鍵索引,所以會直接查詢到id=1的全部數據。但是,如果執行select * from user where name = 'xxx'
,因為name屬于輔助索引,輔助索引儲存name索引和主鍵,需要先根據name索引查詢到對應的主鍵,然后根據主鍵去主鍵索引里找對應數據,所以多掃描了一個索引樹,這個過程叫做回表操作。
Q10:什么是最左匹配原則/最左前綴原則?
A10:
- 當我們為多個列創建 聯合索引(Composite Index)時,索引的查詢利用遵循 從最左邊的列開始匹配。
- 只要中間某個字段沒有使用,就無法繼續利用后續字段的索引。
- 假如有一個聯合索引
CREATE INDEX idx_user_name_age ON user(name, age, gender);
相當于創建了三個索引組合
(name)
(name, age)
(name, age, gender)
B+樹按照最左有序排列,從左到右建立索引樹,也就是name部分是有序的,查詢數據時會優先比較name來確定下一步搜索的方向,但是如果隔過name的話,那就無從下手了,就不知道下一步該查哪個節點了,就用不上索引了。
Q11: MySQL 事務四大特性 & 隔離級別?
A11:
ACID:原子性、一致性、隔離性、持久性。
- 原子性:事務作為一個整體被執行,其中包括的對數據庫的操作要么全部成功,要么全部失敗。
- 一致性:事務執行前,與事務執行后數據不會被破壞。比如a轉賬b賬戶10塊錢,執行完后,帳戶的金額總和是不變的。
- 隔離性:多個事務并發訪問時,事務之間是隔離的,一個事務不會影響另一個事務的執行。
- 持久性:事務執行對數據庫的操作,將持久的保存在數據庫中。
隔離級別:讀未提交、讀已提交、可重復讀(默認)、串行化。
- 臟讀:事務A、B交替執行,事務A讀到了事務B未提交的數據
- 幻讀:事務A查詢一個范圍的結果集,事務B在這之間插入或者刪除了數據,并靜悄悄的提交,導致事務A再次查詢相同的范圍與發現數據行數多了或者少了。
- 不可重復讀:在一個事務范圍,兩個相同的查詢,讀取同一條記錄,返回了不同的數據。
Q12: MVCC 是怎么實現的?
A12:
多版本并發控制。通過維護數據歷史版本,從而解決并發訪問情況下的讀一致性問題。
關鍵點:隱式字段、undo 日志、版本鏈、快照讀&當前讀、Read View。
MVCC 的核心是 版本鏈 + ReadView 可見性判斷。
InnoDB 在每行數據后面維護 trx_id
和 roll_pointer
,更新時不覆蓋舊數據,而是把舊值寫入 Undo Log,形成一個版本鏈。事務在讀的時候會生成 ReadView,通過事務 ID 和活躍事務列表來判斷哪個版本對自己可見。這樣不同事務就能同時讀取到各自一致的數據版本,從而避免了臟讀和不可重復讀。
Q13: SQL 查詢很慢,你怎么排查?
A13:
慢查詢日志和服務監控
- **慢查詢日志: **開啟 MySQL 的慢查詢日志,再通過一些工具比如 mysqldumpslow 去分析對應的慢查詢日志,當然現在一般的云廠商都提供了可視化的平臺。
- 服務監控: 可以在業務的基建中加入對慢SQL的監控,常見的方案有字節碼插樁、連接池擴展ORM 框架過程,對服務運行中的慢SQL進行監控和告警。
Q14:有哪些方式優化?
A14:
SQL語句的優化,數據庫設計的優化
一般我們會從幾個方面考慮:
- SQL 本身:檢查是否有不必要的字段、函數操作,是否可以改寫成更優的形式;
- 索引:查看是否建了合適的索引,能否使用覆蓋索引;
- 執行計劃:用
EXPLAIN
看看是否走了全表掃描,rows 預估量大不大; - 表結構:字段類型是否合理,表是否過大,是否需要拆分;
- 系統層面:比如加緩存、讀寫分離、分庫分表等。