大家好,我是小林。
今天分享一篇一位同學(xué)暑期實(shí)習(xí)面試阿里Java后端崗位的一面的面經(jīng)。
主要拷打了項(xiàng)目+Java 集合+Java并發(fā)+網(wǎng)絡(luò)+mysql,一場(chǎng)面試大概問(wèn)了 20 個(gè)題目,問(wèn)的還是比較基礎(chǔ),不算太難。
問(wèn)題記錄
自我介紹
balabala(略)
簡(jiǎn)歷上有兩個(gè)項(xiàng)目,選一個(gè)你比較熟悉的介紹
balabala(略)
項(xiàng)目用到了哪個(gè)線程實(shí)現(xiàn)類(lèi)?
用了ScheduledThreadPool這個(gè)線程實(shí)現(xiàn)類(lèi)
為什么要使用這個(gè)線程類(lèi)?
這個(gè)實(shí)現(xiàn)類(lèi)可以設(shè)置定期的執(zhí)行任務(wù),它支持定時(shí)或周期性執(zhí)行任務(wù),比如每隔 10 秒鐘執(zhí)行一次任務(wù),我通過(guò)這個(gè)實(shí)現(xiàn)類(lèi)設(shè)置定期執(zhí)行任務(wù)的策略。
你還了解別的線程實(shí)現(xiàn)類(lèi)嗎?
除了這個(gè)之外,還有就是newSingleThreadExecuter,別的就不太熟悉了。
小林補(bǔ)充:
除了 ScheduledThreadPool 線程池之外,還有 4 種常見(jiàn)的線程池如下:
FixedThreadPool:它的核心線程數(shù)和最大線程數(shù)是一樣的,所以可以把它看作是固定線程數(shù)的線程池,它的特點(diǎn)是線程池中的線程數(shù)除了初始階段需要從 0 開(kāi)始增加外,之后的線程數(shù)量就是固定的,就算任務(wù)數(shù)超過(guò)線程數(shù),線程池也不會(huì)再創(chuàng)建更多的線程來(lái)處理任務(wù),而是會(huì)把超出線程處理能力的任務(wù)放到任務(wù)隊(duì)列中進(jìn)行等待。而且就算任務(wù)隊(duì)列滿了,到了本該繼續(xù)增加線程數(shù)的時(shí)候,由于它的最大線程數(shù)和核心線程數(shù)是一樣的,所以也無(wú)法再增加新的線程了。
CachedThreadPool:可以稱作可緩存線程池,它的特點(diǎn)在于線程數(shù)是幾乎可以無(wú)限增加的(實(shí)際最大可以達(dá)到 Integer.MAX_VALUE,為 2^31-1,這個(gè)數(shù)非常大,所以基本不可能達(dá)到),而當(dāng)線程閑置時(shí)還可以對(duì)線程進(jìn)行回收。也就是說(shuō)該線程池的線程數(shù)量不是固定不變的,當(dāng)然它也有一個(gè)用于存儲(chǔ)提交任務(wù)的隊(duì)列,但這個(gè)隊(duì)列是 SynchronousQueue,隊(duì)列的容量為0,實(shí)際不存儲(chǔ)任何任務(wù),它只負(fù)責(zé)對(duì)任務(wù)進(jìn)行中轉(zhuǎn)和傳遞,所以效率比較高。
SingleThreadExecutor:它會(huì)使用唯一的線程去執(zhí)行任務(wù),原理和 FixedThreadPool 是一樣的,只不過(guò)這里線程只有一個(gè),如果線程在執(zhí)行任務(wù)的過(guò)程中發(fā)生異常,線程池也會(huì)重新創(chuàng)建一個(gè)線程來(lái)執(zhí)行后續(xù)的任務(wù)。這種線程池由于只有一個(gè)線程,所以非常適合用于所有任務(wù)都需要按被提交的順序依次執(zhí)行的場(chǎng)景,而前幾種線程池不一定能夠保障任務(wù)的執(zhí)行順序等于被提交的順序,因?yàn)樗鼈兪嵌嗑€程并行執(zhí)行的。
SingleThreadScheduledExecutor:它實(shí)際和 ScheduledThreadPool 線程池非常相似,它只是 ScheduledThreadPool 的一個(gè)特例,內(nèi)部只有一個(gè)線程。
看你項(xiàng)目上有用到雪花算法,你為什么要使用雪花算法?
除了考慮使用主鍵自增保持主鍵的唯一性外,我就使用到了雪花算法,算出一個(gè)不會(huì)重復(fù)的數(shù)做為id,保證主鍵唯一。
那你還了解別的生成主鍵的策略嗎,你覺(jué)得他們能代替雪花算法嗎
除了主鍵自增和雪花算法,別的我暫時(shí)沒(méi)了解的,但是只要是能保證主鍵唯一的主鍵生成策略都可以使用
小林補(bǔ)充:
除了雪花算法之外,還有很多優(yōu)秀的互聯(lián)網(wǎng)公司也提供了唯一 ID 生成的方案或框架,比如美團(tuán)開(kāi)源的 Leaf ,百度開(kāi)源的 UidGenerator 等等。
UUID 雖然也可以保證唯一性,但是 UUID 的值是隨機(jī)的,無(wú)序的,不太適合作為主鍵,因?yàn)殡S機(jī)插入,可能會(huì)引起頁(yè)分裂的問(wèn)題,從而影響查詢性能。
List的實(shí)現(xiàn)類(lèi)
ArrayList、Vector、LinkedList
小林補(bǔ)充:
Java中的List接口有多個(gè)實(shí)現(xiàn)類(lèi),常用的包括:
ArrayList:基于動(dòng)態(tài)數(shù)組實(shí)現(xiàn),優(yōu)勢(shì)在于支持隨機(jī)訪問(wèn)和快速插入/刪除元素,適用于頻繁讀取和遍歷的場(chǎng)景。
LinkedList:基于雙向鏈表實(shí)現(xiàn),優(yōu)勢(shì)在于支持快速插入/刪除元素,適用于頻繁插入/刪除元素的場(chǎng)景。
Vector:和ArrayList類(lèi)似,但由于其線程安全性,適用于多線程環(huán)境。
Stack:基于Vector實(shí)現(xiàn),是一個(gè)后進(jìn)先出(LIFO)的數(shù)據(jù)結(jié)構(gòu),適用于需要按照后進(jìn)先出順序處理元素的場(chǎng)景。
List和Set的區(qū)別
List是有序的,Set是無(wú)序的
List可以存放相同的元素,Set不可以存放重復(fù)的元素
小林補(bǔ)充:
順序:List是有序的集合,它可以按照元素插入的順序進(jìn)行存儲(chǔ)和訪問(wèn)。而Set是無(wú)序的集合,元素在集合中的位置是不固定的。
重復(fù)元素:List允許存儲(chǔ)重復(fù)的元素,即可以有多個(gè)相同的對(duì)象。Set不允許存儲(chǔ)重復(fù)的元素,即每個(gè)對(duì)象在集合中只能出現(xiàn)一次。
實(shí)現(xiàn)類(lèi):List的常用實(shí)現(xiàn)類(lèi)有ArrayList和LinkedList,分別使用數(shù)組和鏈表作為底層數(shù)據(jù)結(jié)構(gòu)。Set的常用實(shí)現(xiàn)類(lèi)有HashSet、LinkedHashSet和TreeSet,分別基于哈希表、鏈表+哈希表和紅黑樹(shù)實(shí)現(xiàn)。
性能:由于底層數(shù)據(jù)結(jié)構(gòu)的差異,List和Set在增加、刪除、查找等操作上的性能表現(xiàn)有所不同。例如,ArrayList在隨機(jī)訪問(wèn)元素時(shí)性能較好,而LinkedList在插入和刪除元素時(shí)性能較好。HashSet在查找、添加和刪除元素時(shí)性能較好,但不保證元素順序。TreeSet在保持元素排序的同時(shí),也能提供較好的查找性能。
針對(duì)你說(shuō)的List和Set的性質(zhì),那你會(huì)用這兩種結(jié)構(gòu)解決哪些問(wèn)題
對(duì)于取消重復(fù)數(shù)據(jù)的場(chǎng)景,選擇set,對(duì)于只是保存數(shù)據(jù)、或者需要按存儲(chǔ)順序進(jìn)行訪問(wèn)的場(chǎng)景使用List。
小林補(bǔ)充:
List(列表)適用于以下場(chǎng)景:
有序數(shù)據(jù):列表中的元素按照插入順序存儲(chǔ),因此適用于需要保持元素順序的場(chǎng)景。
允許重復(fù)元素:列表允許存儲(chǔ)重復(fù)的元素,因此適用于需要統(tǒng)計(jì)元素出現(xiàn)次數(shù)的場(chǎng)景。
需要根據(jù)索引進(jìn)行查找、插入和刪除操作:列表允許通過(guò)索引值直接訪問(wèn)、插入或刪除元素,適用于需要頻繁進(jìn)行這些操作的場(chǎng)景。
Set(集合)適用于以下場(chǎng)景:
去重:集合中的元素不能重復(fù),因此適用于去除數(shù)據(jù)中重復(fù)元素的場(chǎng)景。
無(wú)需關(guān)心元素順序:集合中的元素沒(méi)有固定順序,適用于元素順序無(wú)關(guān)緊要的場(chǎng)景。
快速判斷元素是否存在:集合提供了高效率的查找算法,適用于需要快速判斷某個(gè)元素是否存在于數(shù)據(jù)集中的場(chǎng)景。
集合運(yùn)算:集合支持交集、并集、差集等運(yùn)算,適用于需要進(jìn)行這些運(yùn)算的場(chǎng)景。
常用的網(wǎng)絡(luò)狀態(tài)碼有哪些
100開(kāi)頭是表示協(xié)議執(zhí)行的中間狀態(tài),一般不常用,400開(kāi)頭的表示協(xié)議執(zhí)行失敗,例如404是指服務(wù)端找不到頁(yè)面的請(qǐng)求地址,500是協(xié)議的完成(這個(gè)沒(méi)記住答錯(cuò)了,面試官提示了,說(shuō)那200表示什么)。
小林補(bǔ)充:
常用的網(wǎng)絡(luò)狀態(tài)碼分為五類(lèi),分別是:
1xx(信息):表示接收到請(qǐng)求,需要繼續(xù)處理。
100 Continue:繼續(xù),客戶端應(yīng)繼續(xù)其請(qǐng)求。
2xx(成功):表示請(qǐng)求已成功被服務(wù)器接收、理解和接受。
200 OK:請(qǐng)求成功,請(qǐng)求所希望的響應(yīng)頭或數(shù)據(jù)體將隨此響應(yīng)返回。
201 Created:請(qǐng)求已成功,并因此創(chuàng)建了一個(gè)新的資源。
204 No Content:無(wú)內(nèi)容,服務(wù)器成功處理,但未返回內(nèi)容。
3xx(重定向):需要后續(xù)操作才能完成這一請(qǐng)求。
301 Moved Permanently:永久重定向,請(qǐng)求的資源已被永久移動(dòng)到新位置。
302 Found:臨時(shí)重定向,請(qǐng)求的資源臨時(shí)從不同位置響應(yīng)。
304 Not Modified:資源未修改,使用緩存的資源。
4xx(客戶端錯(cuò)誤):請(qǐng)求包含錯(cuò)誤語(yǔ)法或無(wú)法完成。
400 Bad Request:客戶端請(qǐng)求的語(yǔ)法錯(cuò)誤,服務(wù)器無(wú)法理解。
401 Unauthorized:請(qǐng)求需要用戶驗(yàn)證。
403 Forbidden:服務(wù)器理解請(qǐng)求客戶端的請(qǐng)求,但是拒絕執(zhí)行它。
404 Not Found:請(qǐng)求的資源無(wú)法在服務(wù)器上找到。
5xx(服務(wù)器錯(cuò)誤):服務(wù)器未能實(shí)現(xiàn)合法的請(qǐng)求。
500 Internal Server Error:服務(wù)器內(nèi)部錯(cuò)誤,無(wú)法完成請(qǐng)求。
501 Not Implemented:服務(wù)器不支持請(qǐng)求的功能。
503 Service Unavailable:服務(wù)器暫不可用,可能是服務(wù)器過(guò)載或停機(jī)維護(hù)。
ps:《HTTP 常見(jiàn)面試題》完整詳細(xì)講解:https://xiaolincoding.com/network/2_http/http_interview.html
流量控制和擁塞控制的原理
流量控制是服務(wù)端和客戶端協(xié)議的窗口實(shí)現(xiàn),在客戶端發(fā)送數(shù)據(jù)的時(shí)候,服務(wù)端返回窗口的容量,客戶端通過(guò)容量來(lái)調(diào)整發(fā)送信息的大小
擁塞控制是通過(guò)滑動(dòng)窗口實(shí)現(xiàn),服務(wù)端只接收擁塞窗口大小內(nèi)的數(shù)據(jù),客服端發(fā)送的報(bào)文丟失后,沒(méi)有收到服務(wù)端的確認(rèn)信息,就將沒(méi)有收到確認(rèn)信息的保溫再發(fā)送。
小林補(bǔ)充:
TCP一種面向連接的、可靠的傳輸層協(xié)議。為了確保數(shù)據(jù)的有效傳輸,TCP 提供了兩種重要的控制機(jī)制:流量控制和擁塞控制。
流量控制(Flow Control)
流量控制的主要目的是防止發(fā)送方向接收方發(fā)送過(guò)多數(shù)據(jù),導(dǎo)致接收方處理不過(guò)來(lái)。TCP 使用滑動(dòng)窗口機(jī)制來(lái)實(shí)現(xiàn)流量控制。在 TCP 連接中,接收方為每個(gè)連接分配一個(gè)接收緩沖區(qū)。接收方通過(guò)通知發(fā)送方自己的窗口大小,告知發(fā)送方可以發(fā)送的數(shù)據(jù)量。窗口大小表示接收方當(dāng)前能接受的數(shù)據(jù)字節(jié)數(shù)。
滑動(dòng)窗口的工作原理如下:
發(fā)送方根據(jù)接收方的窗口大小來(lái)確定發(fā)送的數(shù)據(jù)量。
當(dāng)接收方收到數(shù)據(jù)后,發(fā)送確認(rèn)報(bào)文,同時(shí)更新窗口大小。
發(fā)送方收到確認(rèn)報(bào)文后,更新已發(fā)送但未確認(rèn)的數(shù)據(jù)量,并根據(jù)新的窗口大小調(diào)整發(fā)送速率。
擁塞控制(Congestion Control)
擁塞控制的目的是防止過(guò)多的數(shù)據(jù)進(jìn)入網(wǎng)絡(luò),導(dǎo)致網(wǎng)絡(luò)擁塞。TCP 使用四種算法來(lái)實(shí)現(xiàn)擁塞控制:慢開(kāi)始、擁塞避免、快速重傳和快速恢復(fù)。
慢開(kāi)始:發(fā)送方初始擁塞窗口設(shè)置為一個(gè)較小的值。隨后,每收到一個(gè)確認(rèn)報(bào)文,擁塞窗口大小加倍。這樣,發(fā)送速率會(huì)以指數(shù)形式增長(zhǎng),直到達(dá)到一個(gè)閾值(ssthresh)。
擁塞避免:當(dāng)擁塞窗口到達(dá)閾值后,發(fā)送方轉(zhuǎn)入擁塞避免階段,窗口大小每經(jīng)過(guò)一個(gè)往返時(shí)間(RTT)就增加1。這樣,擁塞窗口的大小呈線性增長(zhǎng),避免了網(wǎng)絡(luò)擁塞。
快速重傳:當(dāng)發(fā)送方連續(xù)收到三個(gè)重復(fù)的確認(rèn)報(bào)文,表示可能有一個(gè)數(shù)據(jù)包丟失。此時(shí),發(fā)送方立即重傳丟失的數(shù)據(jù)包,而不是等待超時(shí)重傳。
快速恢復(fù):快速重傳后,發(fā)送方降低擁塞窗口閾值,然后進(jìn)入擁塞避免階段。這樣可以在丟包后盡快恢復(fù)傳輸速率。
通過(guò)這兩種控制機(jī)制,TCP 能確保在各種網(wǎng)絡(luò)條件下有效、可靠地傳輸數(shù)據(jù)。
ps:《TCP 重傳、滑動(dòng)窗口、流量控制、擁塞控制》完整詳細(xì)講解:https://xiaolincoding.com/network/3_tcp/tcp_feature.html
一條url請(qǐng)求頁(yè)面的執(zhí)行過(guò)程
(這塊我答的不太好,忘了dns服務(wù)器的名字)瀏覽器先解析url地址,然后生成http消息,生成的消息需要知道ip地址才能發(fā)送,就先去瀏覽器的緩存中查詢,沒(méi)有的話查看操作系統(tǒng)的緩存,如果還是沒(méi)有就在本地dns中查詢,本地dns查詢不到后會(huì)先訪問(wèn)根dns,根dns查詢的是存放這個(gè)ip的二級(jí)dns服務(wù)器(忘了名字),二級(jí)dns服務(wù)器會(huì)向?qū)?yīng)的權(quán)威dns服務(wù)器查詢,權(quán)威dns服務(wù)器會(huì)向本地返回ip地址,然后本地通過(guò)這個(gè)ip地址和請(qǐng)求的服務(wù)端建立起tcp連接,服務(wù)端向本地發(fā)送請(qǐng)求的資源。
小林補(bǔ)充:
ps:《鍵入網(wǎng)址到網(wǎng)頁(yè)顯示,期間發(fā)生了什么?》完整詳細(xì)講解:https://xiaolincoding.com/network/1_base/what_happen_url.html
TCP是如何建立連接的
三次握手
1、客戶端發(fā)送請(qǐng)求建立連接報(bào)文,報(bào)文的SYN=1
2、服務(wù)端收到后,發(fā)送連接報(bào)文,報(bào)文的SYN=1,并且發(fā)送一個(gè)序號(hào)字段
3、客戶端收到后報(bào)文后,客戶端到服務(wù)端的連接已經(jīng)建立,客戶端發(fā)送報(bào)文對(duì)上一個(gè)報(bào)文的序號(hào)端進(jìn)行確認(rèn)
小林補(bǔ)充:
TCP 是面向連接的協(xié)議,所以使用 TCP 前必須先建立連接,而建立連接是通過(guò)三次握手來(lái)進(jìn)行的。三次握手的過(guò)程如下圖:
TCP 三次握手
一開(kāi)始,客戶端和服務(wù)端都處于 CLOSE 狀態(tài)。先是服務(wù)端主動(dòng)監(jiān)聽(tīng)某個(gè)端口,處于 LISTEN 狀態(tài)
客戶端會(huì)隨機(jī)初始化序號(hào)(client_isn),將此序號(hào)置于 TCP 首部的「序號(hào)」字段中,同時(shí)把 SYN 標(biāo)志位置為 1,表示 SYN 報(bào)文。接著把第一個(gè) SYN 報(bào)文發(fā)送給服務(wù)端,表示向服務(wù)端發(fā)起連接,該報(bào)文不包含應(yīng)用層數(shù)據(jù),之后客戶端處于 SYN-SENT 狀態(tài)。
服務(wù)端收到客戶端的 SYN 報(bào)文后,首先服務(wù)端也隨機(jī)初始化自己的序號(hào)(server_isn),將此序號(hào)填入 TCP 首部的「序號(hào)」字段中,其次把 TCP 首部的「確認(rèn)應(yīng)答號(hào)」字段填入 client_isn + 1, 接著把 SYN 和 ACK 標(biāo)志位置為 1。最后把該報(bào)文發(fā)給客戶端,該報(bào)文也不包含應(yīng)用層數(shù)據(jù),之后服務(wù)端處于 SYN-RCVD 狀態(tài)。
客戶端收到服務(wù)端報(bào)文后,還要向服務(wù)端回應(yīng)最后一個(gè)應(yīng)答報(bào)文,首先該應(yīng)答報(bào)文 TCP 首部 ACK 標(biāo)志位置為 1 ,其次「確認(rèn)應(yīng)答號(hào)」字段填入 server_isn + 1 ,最后把報(bào)文發(fā)送給服務(wù)端,這次報(bào)文可以攜帶客戶到服務(wù)端的數(shù)據(jù),之后客戶端處于 ESTABLISHED 狀態(tài)。
服務(wù)端收到客戶端的應(yīng)答報(bào)文后,也進(jìn)入 ESTABLISHED 狀態(tài)。
ps:《TCP 三次握手與四次揮手面試題》完整詳細(xì)講解:https://xiaolincoding.com/network/3_tcp/tcp_interview.html
http和https的區(qū)別
1、https是需要通過(guò)CA申請(qǐng)才能獲得,所以數(shù)量是比較少的
2、http發(fā)送的報(bào)文是明文,所以不安全,https在傳輸層之上加了ssl協(xié)議
小林補(bǔ)充:
HTTP是一種用于傳輸超文本的協(xié)議,數(shù)據(jù)傳輸是明文的,不具備加密和安全性。HTTP使用的端口號(hào)是 80
HTTPS是在HTTP的基礎(chǔ)上加入了SSL/TLS協(xié)議進(jìn)行加密和身份驗(yàn)證的安全版本。它使用加密的SSL/TLS協(xié)議進(jìn)行數(shù)據(jù)傳輸,保證了數(shù)據(jù)的機(jī)密性和完整性。HTTPS使用的端口號(hào)是443。
ps:《 HTTP 常見(jiàn)面試題》完整詳細(xì)講解:https://xiaolincoding.com/network/2_http/http_interview.html
數(shù)據(jù)庫(kù)的索引
B+樹(shù)索引,hash索引、全文索引
B+樹(shù)索引的話是innodb采用的索引,索引的葉子結(jié)點(diǎn)上是數(shù)據(jù),非葉子結(jié)點(diǎn)是索引信息
hash索引單個(gè)的查找效率很高
為什么采用B+樹(shù)索引,它有什么優(yōu)點(diǎn)
這里我將B+樹(shù)和B樹(shù)、紅黑樹(shù)做了比較。
B+樹(shù)相對(duì)于B樹(shù),只有葉子結(jié)點(diǎn)存儲(chǔ)的是數(shù)據(jù)信息,非葉子結(jié)點(diǎn)都是索引信息,所以在查找時(shí)加載到內(nèi)存中的數(shù)據(jù)少,B+樹(shù)的增刪相對(duì)于B樹(shù)來(lái)說(shuō)比較穩(wěn)定,不會(huì)發(fā)生頻繁的父子結(jié)點(diǎn)替換,B+樹(shù)的葉子結(jié)點(diǎn)是連接的,所以很容易實(shí)現(xiàn)范圍查詢
B+樹(shù)相對(duì)于紅黑樹(shù),首先B+樹(shù)的層高比較小,意味著讀取數(shù)據(jù)時(shí)IO磁盤(pán)的次數(shù)比較少,紅黑樹(shù)增刪結(jié)點(diǎn)時(shí)需要保持子樹(shù)的穩(wěn)定性,增刪的效率很低,B+樹(shù)更容易實(shí)現(xiàn)范圍查詢。
小林補(bǔ)充:
樹(shù)的高度決定于磁盤(pán) I/O 操作的次數(shù),因?yàn)闃?shù)是存儲(chǔ)在磁盤(pán)中的,訪問(wèn)每個(gè)節(jié)點(diǎn),都對(duì)應(yīng)一次磁盤(pán) I/O 操作,也就是說(shuō)樹(shù)的高度就等于每次查詢數(shù)據(jù)時(shí)磁盤(pán) IO 操作的次數(shù),所以樹(shù)的高度越高,就會(huì)影響查詢性能。
B 樹(shù)和 B+ 都是通過(guò)多叉樹(shù)的方式,會(huì)將樹(shù)的高度變矮,所以這兩個(gè)數(shù)據(jù)結(jié)構(gòu)非常適合檢索存于磁盤(pán)中的數(shù)據(jù)。
但是 MySQL 默認(rèn)的存儲(chǔ)引擎 InnoDB 采用的是 B+ 作為索引的數(shù)據(jù)結(jié)構(gòu),原因有:
B+ 樹(shù)的非葉子節(jié)點(diǎn)不存放實(shí)際的記錄數(shù)據(jù),僅存放索引,因此數(shù)據(jù)量相同的情況下,相比存儲(chǔ)即存索引又存記錄的 B 樹(shù),B+樹(shù)的非葉子節(jié)點(diǎn)可以存放更多的索引,因此 B+ 樹(shù)可以比 B 樹(shù)更「矮胖」,查詢底層節(jié)點(diǎn)的磁盤(pán) I/O次數(shù)會(huì)更少。
B+ 樹(shù)有大量的冗余節(jié)點(diǎn)(所有非葉子節(jié)點(diǎn)都是冗余索引),這些冗余索引讓 B+ 樹(shù)在插入、刪除的效率都更高,比如刪除根節(jié)點(diǎn)的時(shí)候,不會(huì)像 B 樹(shù)那樣會(huì)發(fā)生復(fù)雜的樹(shù)的變化;
B+ 樹(shù)葉子節(jié)點(diǎn)之間用鏈表連接了起來(lái),有利于范圍查詢,而 B 樹(shù)要實(shí)現(xiàn)范圍查詢,因此只能通過(guò)樹(shù)的遍歷來(lái)完成范圍查詢,這會(huì)涉及多個(gè)節(jié)點(diǎn)的磁盤(pán) I/O 操作,范圍查詢效率不如 B+ 樹(shù)。
ps:《為什么 MySQL 采用 B+ 樹(shù)作為索引?》完整詳細(xì)講解:https://xiaolincoding.com/mysql/index/why_index_chose_bpuls_tree.html
數(shù)據(jù)庫(kù)中事務(wù)可能帶來(lái)的問(wèn)題
有臟讀、不可重復(fù)讀、幻讀三個(gè)問(wèn)題:
臟讀:一個(gè)事務(wù)讀取另一個(gè)事務(wù)沒(méi)有提交的數(shù)據(jù)
不可重復(fù)讀:一個(gè)事務(wù)重復(fù)讀取一條數(shù)據(jù)時(shí)發(fā)現(xiàn)讀取到的數(shù)據(jù)不相同
幻讀:一個(gè)事務(wù)后讀取的數(shù)據(jù)相比之前讀取的數(shù)據(jù)中多了一些數(shù)據(jù)
小林補(bǔ)充:
MySQL 服務(wù)端是允許多個(gè)客戶端連接的,這意味著 MySQL 會(huì)出現(xiàn)同時(shí)處理多個(gè)事務(wù)的情況。
那么在同時(shí)處理多個(gè)事務(wù)的時(shí)候,就可能出現(xiàn)臟讀(dirty read)、不可重復(fù)讀(non-repeatable read)、幻讀(phantom read)的問(wèn)題。
1、臟讀:如果一個(gè)事務(wù)「讀到」了另一個(gè)「未提交事務(wù)修改過(guò)的數(shù)據(jù)」,就意味著發(fā)生了「臟讀」現(xiàn)象。
舉個(gè)栗子,假設(shè)有 A 和 B 這兩個(gè)事務(wù)同時(shí)在處理,事務(wù) A 先開(kāi)始從數(shù)據(jù)庫(kù)中讀取小林的余額數(shù)據(jù),然后再執(zhí)行更新操作,如果此時(shí)事務(wù) A 還沒(méi)有提交事務(wù),而此時(shí)正好事務(wù) B 也從數(shù)據(jù)庫(kù)中讀取小林的余額數(shù)據(jù),那么事務(wù) B 讀取到的余額數(shù)據(jù)是剛才事務(wù) A 更新后的數(shù)據(jù),即使沒(méi)有提交事務(wù)。
圖片
因?yàn)槭聞?wù) A 是還沒(méi)提交事務(wù)的,也就是它隨時(shí)可能發(fā)生回滾操作,如果在上面這種情況事務(wù) A 發(fā)生了回滾,那么事務(wù) B 剛才得到的數(shù)據(jù)就是過(guò)期的數(shù)據(jù),這種現(xiàn)象就被稱為臟讀。
2、不可重復(fù)讀:在一個(gè)事務(wù)內(nèi)多次讀取同一個(gè)數(shù)據(jù),如果出現(xiàn)前后兩次讀到的數(shù)據(jù)不一樣的情況,就意味著發(fā)生了「不可重復(fù)讀」現(xiàn)象。
舉個(gè)栗子,假設(shè)有 A 和 B 這兩個(gè)事務(wù)同時(shí)在處理,事務(wù) A 先開(kāi)始從數(shù)據(jù)庫(kù)中讀取小林的余額數(shù)據(jù),然后繼續(xù)執(zhí)行代碼邏輯處理,在這過(guò)程中如果事務(wù) B 更新了這條數(shù)據(jù),并提交了事務(wù),那么當(dāng)事務(wù) A 再次讀取該數(shù)據(jù)時(shí),就會(huì)發(fā)現(xiàn)前后兩次讀到的數(shù)據(jù)是不一致的,這種現(xiàn)象就被稱為不可重復(fù)讀。
圖片
3、幻讀:在一個(gè)事務(wù)內(nèi)多次查詢某個(gè)符合查詢條件的「記錄數(shù)量」,如果出現(xiàn)前后兩次查詢到的記錄數(shù)量不一樣的情況,就意味著發(fā)生了「幻讀」現(xiàn)象。
舉個(gè)栗子,假設(shè)有 A 和 B 這兩個(gè)事務(wù)同時(shí)在處理,事務(wù) A 先開(kāi)始從數(shù)據(jù)庫(kù)查詢賬戶余額大于 100 萬(wàn)的記錄,發(fā)現(xiàn)共有 5 條,然后事務(wù) B 也按相同的搜索條件也是查詢出了 5 條記錄。
圖片
接下來(lái),事務(wù) A 插入了一條余額超過(guò) 100 萬(wàn)的賬號(hào),并提交了事務(wù),此時(shí)數(shù)據(jù)庫(kù)超過(guò) 100 萬(wàn)余額的賬號(hào)個(gè)數(shù)就變?yōu)?6。然后事務(wù) B 再次查詢賬戶余額大于 100 萬(wàn)的記錄,此時(shí)查詢到的記錄數(shù)量有 6 條,發(fā)現(xiàn)和前一次讀到的記錄數(shù)量不一樣了,就感覺(jué)發(fā)生了幻覺(jué)一樣,這種現(xiàn)象就被稱為幻讀。
ps:《事務(wù)隔離級(jí)別是怎么實(shí)現(xiàn)的?》完整詳細(xì)講解:https://xiaolincoding.com/mysql/transaction/mvcc.html
通過(guò)什么隔離級(jí)別解決這些問(wèn)題
臟讀:讀寫(xiě)已提交
不可重復(fù)讀:可重復(fù)讀
幻讀:串行化
小林補(bǔ)充:
針對(duì)不同的隔離級(jí)別,并發(fā)事務(wù)時(shí)可能發(fā)生的現(xiàn)象也會(huì)不同。
圖片
也就是說(shuō):
在「讀未提交」隔離級(jí)別下,可能發(fā)生臟讀、不可重復(fù)讀和幻讀現(xiàn)象;
在「讀提交」隔離級(jí)別下,可能發(fā)生不可重復(fù)讀和幻讀現(xiàn)象,但是不可能發(fā)生臟讀現(xiàn)象;
在「可重復(fù)讀」隔離級(jí)別下,可能發(fā)生幻讀現(xiàn)象,但是不可能臟讀和不可重復(fù)讀現(xiàn)象;
在「串行化」隔離級(jí)別下,臟讀、不可重復(fù)讀和幻讀現(xiàn)象都不可能會(huì)發(fā)生。
所以,要解決臟讀現(xiàn)象,就要升級(jí)到「讀提交」以上的隔離級(jí)別;要解決不可重復(fù)讀現(xiàn)象,就要升級(jí)到「可重復(fù)讀」的隔離級(jí)別,要解決幻讀現(xiàn)象不建議將隔離級(jí)別升級(jí)到「串行化」。
不同的數(shù)據(jù)庫(kù)廠商對(duì) SQL 標(biāo)準(zhǔn)中規(guī)定的 4 種隔離級(jí)別的支持不一樣,有的數(shù)據(jù)庫(kù)只實(shí)現(xiàn)了其中幾種隔離級(jí)別,我們討論的 MySQL 雖然支持 4 種隔離級(jí)別,但是與SQL 標(biāo)準(zhǔn)中規(guī)定的各級(jí)隔離級(jí)別允許發(fā)生的現(xiàn)象卻有些出入。
MySQL 在「可重復(fù)讀」隔離級(jí)別下,可以很大程度上避免幻讀現(xiàn)象的發(fā)生(注意是很大程度避免,并不是徹底避免),所以 MySQL 并不會(huì)使用「串行化」隔離級(jí)別來(lái)避免幻讀現(xiàn)象的發(fā)生,因?yàn)槭褂谩复谢垢綦x級(jí)別會(huì)影響性能。
ps:《事務(wù)隔離級(jí)別是怎么實(shí)現(xiàn)的?》完整詳細(xì)講解:https://xiaolincoding.com/mysql/transaction/mvcc.html
mysql的隔離級(jí)別是什么?mysql是如何實(shí)現(xiàn)的?
不可重復(fù)讀,但是很大程度上避免幻讀
快照讀(只讀):MVCC
當(dāng)前讀(更新操作):記錄鎖+間隙鎖
小林補(bǔ)充:
MySQL InnoDB 引擎的默認(rèn)隔離級(jí)別雖然是「可重復(fù)讀」,但是它很大程度上避免幻讀現(xiàn)象(并不是完全解決了),解決的方案有兩種:
針對(duì)快照讀(普通 select 語(yǔ)句),是通過(guò) MVCC 方式解決了幻讀,因?yàn)榭芍貜?fù)讀隔離級(jí)別下,事務(wù)執(zhí)行過(guò)程中看到的數(shù)據(jù),一直跟這個(gè)事務(wù)啟動(dòng)時(shí)看到的數(shù)據(jù)是一致的,即使中途有其他事務(wù)插入了一條數(shù)據(jù),是查詢不出來(lái)這條數(shù)據(jù)的,所以就很好了避免幻讀問(wèn)題。
針對(duì)當(dāng)前讀(select ... for update 等語(yǔ)句),是通過(guò) next-key lock(記錄鎖+間隙鎖)方式解決了幻讀,因?yàn)楫?dāng)執(zhí)行 select ... for update 語(yǔ)句的時(shí)候,會(huì)加上 next-key lock,如果有其他事務(wù)在 next-key lock 鎖范圍內(nèi)插入了一條記錄,那么這個(gè)插入語(yǔ)句就會(huì)被阻塞,無(wú)法成功插入,所以就很好了避免幻讀問(wèn)題。
ps:《事務(wù)隔離級(jí)別是怎么實(shí)現(xiàn)的?》完整詳細(xì)講解:https://xiaolincoding.com/mysql/transaction/mvcc.html
算法
寫(xiě)一個(gè)數(shù)據(jù)庫(kù)的多表聯(lián)查問(wèn)題
沒(méi)寫(xiě)出來(lái)(平時(shí)寫(xiě)的少,只知道命令,不熟練),講解下思路
責(zé)任編輯:彭菁
-
數(shù)據(jù)
+關(guān)注
關(guān)注
8文章
6772瀏覽量
88655 -
存儲(chǔ)
+關(guān)注
關(guān)注
13文章
4180瀏覽量
85503 -
線程池
+關(guān)注
關(guān)注
0文章
56瀏覽量
6820
原文標(biāo)題:阿里問(wèn)的相當(dāng)基礎(chǔ)!
文章出處:【微信號(hào):小林coding,微信公眾號(hào):小林coding】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論