螞蟻金服一面:十道經(jīng)典面試題解析

1. 用到分布式事務(wù)嘛?為什么用這種方案,有其他方案嘛?
什么是分布式事務(wù)

分布式事務(wù)基礎(chǔ)
CAP理論
一致性(C:Consistency):一致性是指數(shù)據(jù)在多個副本之間能否保持一致的特性。例如一個數(shù)據(jù)在某個分區(qū)節(jié)點更新之后,在其他分區(qū)節(jié)點讀出來的數(shù)據(jù)也是更新之后的數(shù)據(jù)。 可用性(A:Availability):可用性是指系統(tǒng)提供的服務(wù)必須一直處于可用的狀態(tài),對于用戶的每一個操作請求總是能夠在有限的時間內(nèi)返回結(jié)果。這里的重點是"有限時間內(nèi)"和"返回結(jié)果"。 分區(qū)容錯性(P:Partition tolerance):分布式系統(tǒng)在遇到任何網(wǎng)絡(luò)分區(qū)故障的時候,仍然需要能夠保證對外提供滿足一致性和可用性的服務(wù)。
BASE 理論
基本可用是指,通過支持局部故障而不是系統(tǒng)全局故障來實現(xiàn)的; Soft State表示狀態(tài)可以有一段時間不同步; 最終一致,最終數(shù)據(jù)是一致的就可以了,而不是實時保持強一致。
分布式事務(wù)的幾種解決方案
2PC(二階段提交)方案,事務(wù)的提交分為兩個階段:準備階段和提交執(zhí)行方案。 TCC(即Try、Confirm、Cancel),它采用了補償機制,核心思想是:針對每個操作,都要注冊一個與其對應(yīng)的確認和補償(撤銷)操作。 本地消息表,它的核心思想就是將分布式事務(wù)拆分成本地事務(wù)進行處理。 最大努力通知,實現(xiàn)最大努力通知,可以采用MQ的ack機制。 Saga事務(wù),它的核心思想是將長事務(wù)拆分為多個本地短事務(wù),由Saga事務(wù)協(xié)調(diào)器協(xié)調(diào),如果正常結(jié)束那就正常完成,如果某個步驟失敗,則根據(jù)相反順序一次調(diào)用補償操作。

首先需要有一個消息表,記錄著消息狀態(tài)相關(guān)信息。 業(yè)務(wù)數(shù)據(jù)和消息表在同一個數(shù)據(jù)庫,即要保證它倆在同一個本地事務(wù)。 在本地事務(wù)中處理完業(yè)務(wù)數(shù)據(jù)和寫消息表操作后,通過寫消息到MQ消息隊列。 消息會發(fā)到消息消費方,如果發(fā)送失敗,即進行重試。
處理消息隊列中的消息,完成自己的業(yè)務(wù)邏輯。 此時如果本地事務(wù)處理成功,則表明已經(jīng)處理成功了。 如果本地事務(wù)處理失敗,那么就會重試執(zhí)行。 如果是業(yè)務(wù)上面的失敗,給消息生產(chǎn)方發(fā)送一個業(yè)務(wù)補償消息,通知進行回滾等操作。
2.JDK6、7、8分別提供了哪些新特性

Desktop類(它允許一個Java應(yīng)用程序啟動本地的另一個應(yīng)用程序去處理URI或文件請求) 使用JAXB2來實現(xiàn)對象與XML之間的映射 輕量級 Http Server API 插入式注解處理API(lombok框架基于這個特性實現(xiàn)) STAX(是JDK6中一種處理XML文檔的API)

switch 支持String字符串類型 try-with-resources,資源自動關(guān)閉 整數(shù)類型如(byte,short,int,long)能夠用二進制來表示 數(shù)字常量支持下劃線 泛型實例化類型自動推斷,即”<>” 一個catch中捕獲多個異常類型,用(|)分隔開 增強的文件系統(tǒng) Fork/join 框架

lambada表達式 函數(shù)式接口 方法引用 默認方法 Stream API Optional Date Time API(如LocalDate) 重復(fù)注解 Base64 JVM的新特性(如元空間Metaspace代替持久代)
3. https原理,工作流程
HTTPS = HTTP + SSL/TLS,即用SSL/TLS對數(shù)據(jù)進行加密和解密,Http進行傳輸。 SSL,即Secure Sockets Layer(安全套接層協(xié)議),是網(wǎng)絡(luò)通信提供安全及數(shù)據(jù)完整性的一種安全協(xié)議。 TLS,即Transport Layer Security(安全傳輸層協(xié)議),它是SSL3.0的后續(xù)版本。

客戶端發(fā)起Https請求,連接到服務(wù)器的443端口。 服務(wù)器必須要有一套數(shù)字證書(證書內(nèi)容有公鑰、證書頒發(fā)機構(gòu)、失效日期等)。 服務(wù)器將自己的數(shù)字證書發(fā)送給客戶端(公鑰在證書里面,私鑰由服務(wù)器持有)。 客戶端收到數(shù)字證書之后,會驗證證書的合法性。如果證書驗證通過,就會生成一個隨機的對稱密鑰,用證書的公鑰加密。 客戶端將公鑰加密后的密鑰發(fā)送到服務(wù)器。 服務(wù)器接收到客戶端發(fā)來的密文密鑰之后,用自己之前保留的私鑰對其進行非對稱解密,解密之后就得到客戶端的密鑰,然后用客戶端密鑰對返回數(shù)據(jù)進行對稱加密,醬紫傳輸?shù)臄?shù)據(jù)都是密文啦。 服務(wù)器將加密后的密文返回到客戶端。 客戶端收到后,用自己的密鑰對其進行對稱解密,得到服務(wù)器返回的數(shù)據(jù)。
4. 講講java jmm volatile的實現(xiàn)原理
Java虛擬機規(guī)范試圖定義一種Java內(nèi)存模型,來屏蔽掉各種硬件和操作系統(tǒng)的內(nèi)存訪問差異,以實現(xiàn)讓Java程序在各種平臺上都能達到一致的內(nèi)存訪問效果。 為了更好的執(zhí)行性能,java內(nèi)存模型并沒有限制執(zhí)行引擎使用處理器的特定寄存器或緩存來和主內(nèi)存打交道,也沒有限制編譯器進行調(diào)整代碼順序優(yōu)化。所以Java內(nèi)存模型會存在緩存一致性問題和指令重排序問題的。 Java內(nèi)存模型規(guī)定所有的變量都是存在主內(nèi)存當(dāng)中,每個線程都有自己的工作內(nèi)存。這里的變量包括實例變量和靜態(tài)變量,但是不包括局部變量,因為局部變量是線程私有的。 線程的工作內(nèi)存保存了被該線程使用的變量的主內(nèi)存副本,線程對變量的所有操作都必須在工作內(nèi)存中進行,而不能直接操作操作主內(nèi)存。并且每個線程不能訪問其他線程的工作內(nèi)存。 
程序次序規(guī)則:在一個線程內(nèi),按照控制流順序,書寫在前面的操作先行發(fā)生于書寫在后面的操作。 管程鎖定規(guī)則:一個unLock操作先行發(fā)生于后面對同一個鎖額lock操作 volatile變量規(guī)則:對一個變量的寫操作先行發(fā)生于后面對這個變量的讀操作 線程啟動規(guī)則:Thread對象的start()方法先行發(fā)生于此線程的每個一個動作 線程終止規(guī)則:線程中所有的操作都先行發(fā)生于線程的終止檢測,我們可以通過Thread.join()方法結(jié)束、Thread.isAlive()的返回值手段檢測到線程已經(jīng)終止執(zhí)行 線程中斷規(guī)則:對線程interrupt()方法的調(diào)用先行發(fā)生于被中斷線程的代碼檢測到中斷事件的發(fā)生 對象終結(jié)規(guī)則:一個對象的初始化完成先行發(fā)生于他的finalize()方法的開始 傳遞性:如果操作A先行發(fā)生于操作B,而操作B又先行發(fā)生于操作C,則可以得出操作A先行發(fā)生于操作C
public class Singleton {
private volatile static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
1.重排序時不能把后面的指令重排序到內(nèi)存屏障之前的位置 2.將本處理器的緩存寫入內(nèi)存 3.如果是寫入動作,會導(dǎo)致其他處理器中對應(yīng)的緩存無效。

在每個volatile寫操作的前面插入一個StoreStore屏障。 在每個volatile寫操作的后面插入一個StoreLoad屏障。 在每個volatile讀操作的后面插入一個LoadLoad屏障。 在每個volatile讀操作的后面插入一個LoadStore屏障。

5. 講一講7層網(wǎng)絡(luò)模型,tcp的為什么要三次握手

應(yīng)用層:網(wǎng)絡(luò)服務(wù)與最終用戶的一個接口,常見的協(xié)議有:HTTP FTP SMTP SNMP DNS. 表示層:數(shù)據(jù)的表示、安全、壓縮。,確保一個系統(tǒng)的應(yīng)用層所發(fā)送的信息可以被另一個系統(tǒng)的應(yīng)用層讀取。 會話層:建立、管理、終止會話,對應(yīng)主機進程,指本地主機與遠程主機正在進行的會話. 傳輸層:定義傳輸數(shù)據(jù)的協(xié)議端口號,以及流控和差錯校驗,協(xié)議有TCP UDP. 網(wǎng)絡(luò)層:進行邏輯地址尋址,實現(xiàn)不同網(wǎng)絡(luò)之間的路徑選擇,協(xié)議有ICMP IGMP IP等. 數(shù)據(jù)鏈路層:在物理層提供比特流服務(wù)的基礎(chǔ)上,建立相鄰結(jié)點之間的數(shù)據(jù)鏈路。 物理層:建立、維護、斷開物理連接。
6.說說線程池的工作原理

核心線程比作公司正式員工 非核心線程比作外包員工 阻塞隊列比作需求池 提交任務(wù)比作提需求

當(dāng)產(chǎn)品提個需求,正式員工(核心線程)先接需求(執(zhí)行任務(wù)) 如果正式員工都有需求在做,即核心線程數(shù)已滿),產(chǎn)品就把需求先放需求池(阻塞隊列)。 如果需求池(阻塞隊列)也滿了,但是這時候產(chǎn)品繼續(xù)提需求,怎么辦呢?那就請外包(非核心線程)來做。 如果所有員工(最大線程數(shù)也滿了)都有需求在做了,那就執(zhí)行拒絕策略。 如果外包員工把需求做完了,它經(jīng)過一段(keepAliveTime)空閑時間,就離開公司了。
7.你們數(shù)據(jù)庫的高可用是怎么實現(xiàn)的?
如果數(shù)據(jù)庫節(jié)點宕機,需要盡快回復(fù),保證業(yè)務(wù)不受宕機影響。 從數(shù)據(jù)庫節(jié)點的數(shù)據(jù),盡可能跟主節(jié)點數(shù)據(jù)實時保持一致,至少保證最終一致性。 數(shù)據(jù)庫節(jié)點切換時,數(shù)據(jù)不能缺失。
7.1 主從或主主半同步復(fù)制

7.2 半同步復(fù)制優(yōu)化

優(yōu)點:這種方案架構(gòu)、部署也比較簡單,主機宕機也是直接切換即可。比方案1的半同步復(fù)制,更能保證數(shù)據(jù)的一致性。 缺點:需要修改內(nèi)核源碼或者使用mysql通信協(xié)議,沒有從根本上解決數(shù)據(jù)一致性問題。
7.3 高可用架構(gòu)優(yōu)化

優(yōu)點:保證了整個系統(tǒng)的高可用性,擴展性也較好,可以擴展為大規(guī)模集群。 缺點:數(shù)據(jù)一致性仍然依賴于原生的mysql半同步復(fù)制;引入Zookeeper使系統(tǒng)邏輯更復(fù)雜。
7.4 共享存儲

優(yōu)點:部署簡單,價格合適,保證數(shù)據(jù)的強一致性 缺點:對IO性能影響較大,從庫不提供讀操作
7.5 分布式協(xié)議

優(yōu)點:不依賴于第三方軟件,可以實現(xiàn)數(shù)據(jù)的強一致性; 缺點:配置較復(fù)雜;需要使用NDB儲存引擎;至少三節(jié)點;
8. 讀寫分離的場景下,怎么保證從數(shù)據(jù)庫讀到最新的數(shù)據(jù)?

寫請求是直接寫主庫,然后同步數(shù)據(jù)到從庫 讀請求一般直接讀從庫,除飛強制讀主庫

A發(fā)起寫請求,更新主庫數(shù)據(jù),并在緩存中設(shè)置一個標記,表示數(shù)據(jù)已更新,標記格式為:userId+業(yè)務(wù)Id。 設(shè)置此標記,設(shè)置過期時間(估值為主庫和從庫同步延遲的時間) B發(fā)起讀請求,先判斷此請求,在緩存中有沒有更新標記。 如果存在標記,走主庫;如果沒有,請求走從庫。
9. 如何保證MySQL數(shù)據(jù)不丟?
binlog日志
statement:每一條會修改數(shù)據(jù)的sql都會記錄到binlog中,不建議使用。 row:基于行的變更情況記錄,會記錄行更改前后的內(nèi)容,推薦使用。 mixed:混合statement和row兩個模式,不建議使用。

write:指把日志寫到文件系統(tǒng)的page cache,并沒有把數(shù)據(jù)持久化到磁盤,因此速度較快。 fsync,實際的寫盤操作,即把數(shù)據(jù)持久化到磁盤。

redo log日志

物理上是在MySQL進程內(nèi)存中,存在redo log buffer中, 物理上在文件系統(tǒng)的page cache里,寫到磁盤 (write),但是還沒有持久化(fsync)。 存在hard disk,已經(jīng)持久化到磁盤。
設(shè)置為0時,表示每次事務(wù)提交時都只是把redo log留在redo log buffer 中 ; 設(shè)置為1時,表示每次事務(wù)提交時都將 redo log 直接持久化到磁盤; 設(shè)置為2時,表示每次事務(wù)提交時都只是把redo log 寫到page cache。
10. 高并發(fā)下如何設(shè)計秒殺系統(tǒng)?

頁面靜態(tài)化 按鈕至灰控制 服務(wù)單一職責(zé) 秒殺鏈接加鹽 限流 分布式鎖 MQ異步處理 限流&降級&熔斷
為了防止某個用戶請求過于頻繁,我們可以對同一用戶限流; 為了防止黃牛模擬幾個用戶請求,我們可以對某個IP進行限流; 為了防止有人使用代理,每次請求都更換IP請求,我們可以對接口進行限流。 為了防止瞬時過大的流量壓垮系統(tǒng),還可以使用阿里的Sentinel、Hystrix組件進行限流。
if(jedis.set(key_resource_id, uni_request_id, "NX", "EX", 100s) == 1){ //加鎖
try {
do something //業(yè)務(wù)處理
}catch(){
}
finally {
//判斷是不是當(dāng)前線程加的鎖,是才釋放
if (uni_request_id.equals(jedis.get(key_resource_id))) {
jedis.del(lockKey); //釋放鎖
}
}
}

if redis.call('get',KEYS[1]) == ARGV[1] then
return redis.call('del',KEYS[1])
else
return 0
end;
限流,就是限制請求,防止過大的請求壓垮服務(wù)器; 降級,就是秒殺服務(wù)有問題了,就降級處理,不要影響別的服務(wù); 熔斷,服務(wù)有問題就熔斷,一般熔斷降級是一起出現(xiàn)。
參考資料
五大常見的MySQL高可用方案: https://zhuanlan.zhihu.com/p/25960208
[2]讀寫分離數(shù)據(jù)庫如何保持數(shù)據(jù)一致性: https://blog.csdn.net/baidu_36161424/article/details/107712388
[3]《我們一起進大廠》系列-秒殺系統(tǒng)設(shè)計: https://juejin.cn/post/6844903999083151374#heading-11
[4]《極客時間:MySQL45講實戰(zhàn)》: http://gk.link/a/10vPr
[5]MySQL是如何保證不丟數(shù)據(jù)的(一): https://cloud.tencent.com/developer/article/1674625
有道無術(shù),術(shù)可成;有術(shù)無道,止于術(shù)
歡迎大家關(guān)注Java之道公眾號
好文章,我在看??
評論
圖片
表情
