隨便分享點不那麼常規的面試題(二)

NO IMAGE

1. 觀察者模式和訪問者模式有什麼區別?

這兩種設計模式相對來說都偏冷門,本題能夠回答好的話說明對設計模式有過深入的瞭解,在面試偏業務的崗位上有加分

先解釋兩者概念:

  • 觀察者模式定義了一種一對多的依賴關係,當某一對象狀態改變時,會通知所有觀察者
  • 訪問者模式定義了一系列訪問接口,負責接收具體的數據對象,被訪問者通過這些接口將自己的數據交給訪問者來處理

再解釋兩者核心區別和功能不同點:

  • 觀察者模式的核心是觸發器,主要用於廣播通知和動態觸發某些行為
  • 訪問者模式的核心是解耦,主要用於將數據對象和數據分離,避免讓對象上的操作汙染對象所在類

最後解釋兩者優缺點和各自使用場景:

  • 觀察者模式的優點是可以實現動態觸發機制,但是如果存在循環依賴會導致系統崩潰,適用於廣播通知/發佈訂閱/鏈式觸發等場景
  • 訪問者模式的優點是契合單一職責原則,同時擁有高度的靈活性,但是具體數據對訪問者公佈了細節,適用於需要使用數據對象進行很多不同操作的場景

2. 數據庫的頁分裂問題講一下

如果問頁分裂問題,一般會在聚簇索引之後緊接著提出,如果沒有問,在回答完聚簇索引相關的概念後,要儘量地給面試官帶出頁分裂的概念,體現出自己對這一塊的熟悉程度

數據庫索引底層使用B+樹來實現,如果插入不規則的數據(指不按key順序插入),會導致樹結構頻繁發生較大改變

在數據庫中,聚簇索引和非聚簇索引在頻繁插入不規律數據時,都會導致嚴重的頁分裂問題。mysql中一頁大小為8k,是固定的:

  • 聚簇索引中,因為索引順序與物理表順序一致,所以當插入包含不規律的索引列的數據時,會導致索引樹結構發生較大改變,數據頁也同時需要分裂,而且會破壞磁盤順序索引的高效性保證
  • 非聚簇索引中,因為索引是有序的,所以當插入不規律數據時,如果之前的數據頁已滿,會導致數據頁的分裂,同時也會破壞磁盤順序索引的高效性保證

3. 排序的穩定性意義是什麼,如果一定要使用非穩定性排序算法,如何保證穩定性?

本題主要用於考察面試者是否瞭解排序的概念,而不是僅僅背知識點。第一問不難,基本上知道穩定性的概念就能順勢推出其意義,第二問稍微有點難度,但是隻要真正瞭解穩定性的概念,就能想到一些解決辦法

排序的穩定性指擁有相同關鍵字的記錄,在排序後的相對次序保持不變。因為有些數據在排序前的相對順序是有語義的,非穩定性排序會在排序後丟失這些語義,所以在這種場景下需要保證排序的穩定性

當必須使用非穩定性排序算法,且需要保證穩定性時,有兩種解決方案:

  1. 剝奪原數據順序上的語義,比如可以使用另一個數據結構保存語義,或者從業務需求上解決
  2. 如果數據直接擁有在順序上的語義屬性時(比如插入時間/次序等),則重寫數據比較器,當關鍵詞相等時,比較數據的順序屬性

4. HashSet是如何實現的?

實現原理很簡單,但前提是你主動去了解過

HashSet內部維護了一個HashMap類型的變量,其key值為Set的元素類型,value為Object類型

對所有添加到Set中的元素,HashSet會添加一個元素 => PRESENT的鍵值對到HashMap中,這個PRESENT是HashSet中定義的一個普通的Object對象。因為HashMap中key相同的元素會相互覆蓋,所以保證了集合中沒有重複key值的元素存在

5. 事務的ACID特性各自是如何實現的?

ACID的概念很簡單,但是想說清原理不容易,其中隔離性和持久性是重點。隔離性的要點是鎖機制,有的面試官會緊接著出一些場景題,需要提前準備;持久性的要點是redo logbinlog,有的面試官會引申到讀寫分離的數據一致性問題,也需要提前做好準備

A(原子性):原理是undo log,即“撤銷日誌”。當事務對數據庫進行修改時,會生成對應的undo log,如果事務回滾,數據庫會使用undo log中的內容將數據修改到之前的狀態

C(一致性):通過數據庫本身(如外鍵等)和服務端的代碼邏輯層面共同保證

I(隔離性):通過數據庫鎖機制和MVCC來實現。鎖機制保證兩個事務的寫操作不會相互影響,MVCC保證一個事務的讀操作不會被其他事務的寫操作影響

D(持久性):原理是redo logbinlog,當數據修改時,會在redo log中記錄這次操作(記錄的是物理數據,內容基於磁盤page),通常當事務提交時,會調用fsync對redo log進行刷盤(將數據寫入磁盤),如果mysql機器宕機,可以使用redo log對數據進行恢復。同時binlog作為二進制邏輯日誌也可以用戶數據恢復

6. strictfp關鍵詞有了解過嗎?

如果面試官問你這個問題,唯一的原因就是他昨天臨睡前看到了這個知識點,所以就拿來考你(當然也有可能是工作中確實用到)。本題單純的就是知識擴展,如果不會也不影響

stricpy,即strict float point(精確浮點),只能用來修飾方法或類。被strictpy修飾的方法或類中所有的float/double表達式都嚴格遵循FP-strict的限制,所有表達式的結果都必須是IEEE-754對操作數預期的結果

strictpy可以消除因硬件不同而帶來的浮點數計算差異,但是並不能避免類似0.05 + 0.01 != 0.06這樣的情況,所以在要求高精度浮點計算時,需要使用BigDecimal

7. 看下面這段代碼,和fun1相互阻塞的方法有哪幾個,為什麼?

    public synchronized static void fun1() {
try {
Thread.sleep(2000);
} catch(Exception e) {
e.printStackTrace();
}
System.out.println("[同步-靜態方法-1]");
}
// -------------------------------------------------
public synchronized static void fun2() {
System.out.println("[同步-靜態方法-2]");
}
public static void fun3() {
System.out.println("[普通靜態方法]");
}
public void fun4() {
System.out.println("[普通方法]");
}
public void fun5() {
synchronized (Demo.class) {
System.out.println("[類同步-靜態方法]");
}
}
public void fun6() {
synchronized (this) {
System.out.println("[對象同步-方法]");
}
}

本題考察點是對synchronized關鍵字的掌握程度,有些人理論背的很熟,但是隨便丟個場景應用就懵了。所以在學一個知識點的時候,最起碼自己要動手寫幾行代碼跑一下看看

與fun1相互阻塞的方法是fun2和fun5

fun2是另一個被synchronized修飾的方法,和fun1同屬一類,所以會相互阻塞

fun5中使用了類鎖,而synchronized在靜態方法上的鎖也屬於類鎖,所以也會相互阻塞

8. 多個客戶端要同時修改redis中的一個key怎麼辦?

一般這種併發修改的場景,都可以用消息中間件來將並行轉串行解決,但是面試官通常還想要另一種答案,所以最少要準備兩套方案。本題當然不止我下面列出的這兩種方案,只要合理即可

  1. 所有的更新操作全部送入消息中間件中(如kafka/rabbitmq/…)串行化處理
  2. 通過redis的MUTLIWATCH實現:在redis中設置一個key,然後WATCH這個key,通過MUTLI開啟事務,然後自增這個key的值,接著再執行真正的修改操作,最後使用EXEC提交事務。通過這種方法,如果發生多線程競爭,由於WATCH機制,監聽到key值變化的線程會執行redis操作失敗

9. 緩存雪崩、緩存擊穿、緩存穿透分別是什麼,怎麼解決?

本題應該算是偏常規的緩存題,但是有的面試官會把這三個場景放一起說來故意迷惑你,所以要注意區分它們之間的不同點,千萬不要死記硬背,否則一定會搞混的

  • 緩存雪崩:指緩存服務器宕機或大量key同時過期,導致大量請求直接打到數據庫上,致使數據庫服務器宕機。
    • 如果是緩存服務器宕機:部署高可用集群,啟用熔斷限流機制,以及在啟動前使用rdb日誌進行數據預熱
    • 如果是key批量過期:設置緩存時間時乘上一個隨機的負載因子
  • 緩存擊穿:某個頻繁訪問的key過期的瞬間,大量流量直接打到數據庫服務器上,導致宕機
    • 可以將熱點key設置為永不過期
    • 也可以使用雙檢鎖,當在緩存中發現數據不存在時,先獲取鎖,然後再檢查緩存中是否存在數據,如果依然不存在則查詢數據庫
  • 緩存穿透:用戶惡意訪問數據庫不存在的數據,導致每次查詢都會走數據庫
    • 如果某次查詢沒有在數據庫中查到數據,則將該key放入緩存中,值設為空,同時設置一個很短的過期時間
    • 限制同一ip短時間的頻繁訪問

10. 為什麼事務註解不加在Controller層或是Dao層?

本題主要考察對mvc分層的理解程度,只要能把每一層的概念理解了,說個大概還是沒問題的,即使不知道,舉幾個反例出來就可以了

  • 不加在controller層的原因:controller的重點是請求邏輯,而不是數據庫事務。通常controller會調用多個服務,如果失敗只需要一個回滾即可
  • 不加在dao層的原因:當服務發生異常時,需要整體進行回滾,而加在dao層只能回滾一個操作

相關文章

手寫源碼(二):自己實現SpringIOC

手寫源碼(一):自己實現Spring事務

淺談Kafka特性與架構

淺談Spring事務中的7種傳播特性