0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

項目常見的線程池有哪些

小林coding ? 來源:小林coding ? 2023-06-17 14:30 ? 次閱讀

大家好,我是小林。

今天分享一篇一位同學(xué)暑期實習(xí)面試阿里Java后端崗位的一面的面經(jīng)。

主要拷打了項目+Java 集合+Java并發(fā)+網(wǎng)絡(luò)+mysql,一場面試大概問了 20 個題目,問的還是比較基礎(chǔ),不算太難。

問題記錄

自我介紹

balabala(略)

簡歷上有兩個項目,選一個你比較熟悉的介紹

balabala(略)

項目用到了哪個線程實現(xiàn)類?

用了ScheduledThreadPool這個線程實現(xiàn)類

為什么要使用這個線程類?

這個實現(xiàn)類可以設(shè)置定期的執(zhí)行任務(wù),它支持定時或周期性執(zhí)行任務(wù),比如每隔 10 秒鐘執(zhí)行一次任務(wù),我通過這個實現(xiàn)類設(shè)置定期執(zhí)行任務(wù)的策略。

你還了解別的線程實現(xiàn)類嗎?

除了這個之外,還有就是newSingleThreadExecuter,別的就不太熟悉了。

小林補(bǔ)充:

除了 ScheduledThreadPool 線程池之外,還有 4 種常見的線程池如下:

FixedThreadPool:它的核心線程數(shù)和最大線程數(shù)是一樣的,所以可以把它看作是固定線程數(shù)的線程池,它的特點是線程池中的線程數(shù)除了初始階段需要從 0 開始增加外,之后的線程數(shù)量就是固定的,就算任務(wù)數(shù)超過線程數(shù),線程池也不會再創(chuàng)建更多的線程來處理任務(wù),而是會把超出線程處理能力的任務(wù)放到任務(wù)隊列中進(jìn)行等待。而且就算任務(wù)隊列滿了,到了本該繼續(xù)增加線程數(shù)的時候,由于它的最大線程數(shù)和核心線程數(shù)是一樣的,所以也無法再增加新的線程了。

CachedThreadPool:可以稱作可緩存線程池,它的特點在于線程數(shù)是幾乎可以無限增加的(實際最大可以達(dá)到 Integer.MAX_VALUE,為 2^31-1,這個數(shù)非常大,所以基本不可能達(dá)到),而當(dāng)線程閑置時還可以對線程進(jìn)行回收。也就是說該線程池的線程數(shù)量不是固定不變的,當(dāng)然它也有一個用于存儲提交任務(wù)的隊列,但這個隊列是 SynchronousQueue,隊列的容量為0,實際不存儲任何任務(wù),它只負(fù)責(zé)對任務(wù)進(jìn)行中轉(zhuǎn)和傳遞,所以效率比較高。

SingleThreadExecutor:它會使用唯一的線程去執(zhí)行任務(wù),原理和 FixedThreadPool 是一樣的,只不過這里線程只有一個,如果線程在執(zhí)行任務(wù)的過程中發(fā)生異常,線程池也會重新創(chuàng)建一個線程來執(zhí)行后續(xù)的任務(wù)。這種線程池由于只有一個線程,所以非常適合用于所有任務(wù)都需要按被提交的順序依次執(zhí)行的場景,而前幾種線程池不一定能夠保障任務(wù)的執(zhí)行順序等于被提交的順序,因為它們是多線程并行執(zhí)行的。

SingleThreadScheduledExecutor:它實際和 ScheduledThreadPool 線程池非常相似,它只是 ScheduledThreadPool 的一個特例,內(nèi)部只有一個線程。

看你項目上有用到雪花算法,你為什么要使用雪花算法?

除了考慮使用主鍵自增保持主鍵的唯一性外,我就使用到了雪花算法,算出一個不會重復(fù)的數(shù)做為id,保證主鍵唯一。

那你還了解別的生成主鍵的策略嗎,你覺得他們能代替雪花算法嗎

除了主鍵自增和雪花算法,別的我暫時沒了解的,但是只要是能保證主鍵唯一的主鍵生成策略都可以使用

小林補(bǔ)充:

除了雪花算法之外,還有很多優(yōu)秀的互聯(lián)網(wǎng)公司也提供了唯一 ID 生成的方案或框架,比如美團(tuán)開源的 Leaf ,百度開源的 UidGenerator 等等。

UUID 雖然也可以保證唯一性,但是 UUID 的值是隨機(jī)的,無序的,不太適合作為主鍵,因為隨機(jī)插入,可能會引起頁分裂的問題,從而影響查詢性能。

List的實現(xiàn)類

ArrayList、Vector、LinkedList

小林補(bǔ)充:

Java中的List接口有多個實現(xiàn)類,常用的包括:

ArrayList:基于動態(tài)數(shù)組實現(xiàn),優(yōu)勢在于支持隨機(jī)訪問和快速插入/刪除元素,適用于頻繁讀取和遍歷的場景。

LinkedList:基于雙向鏈表實現(xiàn),優(yōu)勢在于支持快速插入/刪除元素,適用于頻繁插入/刪除元素的場景。

Vector:和ArrayList類似,但由于其線程安全性,適用于多線程環(huán)境。

Stack:基于Vector實現(xiàn),是一個后進(jìn)先出(LIFO)的數(shù)據(jù)結(jié)構(gòu),適用于需要按照后進(jìn)先出順序處理元素的場景。

List和Set的區(qū)別

List是有序的,Set是無序的

List可以存放相同的元素,Set不可以存放重復(fù)的元素

小林補(bǔ)充:

順序:List是有序的集合,它可以按照元素插入的順序進(jìn)行存儲和訪問。而Set是無序的集合,元素在集合中的位置是不固定的。

重復(fù)元素:List允許存儲重復(fù)的元素,即可以有多個相同的對象。Set不允許存儲重復(fù)的元素,即每個對象在集合中只能出現(xiàn)一次。

實現(xiàn)類:List的常用實現(xiàn)類有ArrayList和LinkedList,分別使用數(shù)組和鏈表作為底層數(shù)據(jù)結(jié)構(gòu)。Set的常用實現(xiàn)類有HashSet、LinkedHashSet和TreeSet,分別基于哈希表、鏈表+哈希表和紅黑樹實現(xiàn)。

性能:由于底層數(shù)據(jù)結(jié)構(gòu)的差異,List和Set在增加、刪除、查找等操作上的性能表現(xiàn)有所不同。例如,ArrayList在隨機(jī)訪問元素時性能較好,而LinkedList在插入和刪除元素時性能較好。HashSet在查找、添加和刪除元素時性能較好,但不保證元素順序。TreeSet在保持元素排序的同時,也能提供較好的查找性能。

針對你說的List和Set的性質(zhì),那你會用這兩種結(jié)構(gòu)解決哪些問題

對于取消重復(fù)數(shù)據(jù)的場景,選擇set,對于只是保存數(shù)據(jù)、或者需要按存儲順序進(jìn)行訪問的場景使用List。

小林補(bǔ)充:

List(列表)適用于以下場景:

有序數(shù)據(jù):列表中的元素按照插入順序存儲,因此適用于需要保持元素順序的場景。

允許重復(fù)元素:列表允許存儲重復(fù)的元素,因此適用于需要統(tǒng)計元素出現(xiàn)次數(shù)的場景。

需要根據(jù)索引進(jìn)行查找、插入和刪除操作:列表允許通過索引值直接訪問、插入或刪除元素,適用于需要頻繁進(jìn)行這些操作的場景。

Set(集合)適用于以下場景:

去重:集合中的元素不能重復(fù),因此適用于去除數(shù)據(jù)中重復(fù)元素的場景。

無需關(guān)心元素順序:集合中的元素沒有固定順序,適用于元素順序無關(guān)緊要的場景。

快速判斷元素是否存在:集合提供了高效率的查找算法,適用于需要快速判斷某個元素是否存在于數(shù)據(jù)集中的場景。

集合運(yùn)算:集合支持交集、并集、差集等運(yùn)算,適用于需要進(jìn)行這些運(yùn)算的場景。

常用的網(wǎng)絡(luò)狀態(tài)碼有哪些

100開頭是表示協(xié)議執(zhí)行的中間狀態(tài),一般不常用,400開頭的表示協(xié)議執(zhí)行失敗,例如404是指服務(wù)端找不到頁面的請求地址,500是協(xié)議的完成(這個沒記住答錯了,面試官提示了,說那200表示什么)。

小林補(bǔ)充:

常用的網(wǎng)絡(luò)狀態(tài)碼分為五類,分別是:

1xx(信息):表示接收到請求,需要繼續(xù)處理。

100 Continue:繼續(xù),客戶端應(yīng)繼續(xù)其請求。

2xx(成功):表示請求已成功被服務(wù)器接收、理解和接受。

200 OK:請求成功,請求所希望的響應(yīng)頭或數(shù)據(jù)體將隨此響應(yīng)返回。

201 Created:請求已成功,并因此創(chuàng)建了一個新的資源。

204 No Content:無內(nèi)容,服務(wù)器成功處理,但未返回內(nèi)容。

3xx(重定向):需要后續(xù)操作才能完成這一請求。

301 Moved Permanently:永久重定向,請求的資源已被永久移動到新位置。

302 Found:臨時重定向,請求的資源臨時從不同位置響應(yīng)。

304 Not Modified:資源未修改,使用緩存的資源。

4xx(客戶端錯誤):請求包含錯誤語法或無法完成。

400 Bad Request:客戶端請求的語法錯誤,服務(wù)器無法理解。

401 Unauthorized:請求需要用戶驗證。

403 Forbidden:服務(wù)器理解請求客戶端的請求,但是拒絕執(zhí)行它。

404 Not Found:請求的資源無法在服務(wù)器上找到。

5xx(服務(wù)器錯誤):服務(wù)器未能實現(xiàn)合法的請求。

500 Internal Server Error:服務(wù)器內(nèi)部錯誤,無法完成請求。

501 Not Implemented:服務(wù)器不支持請求的功能。

503 Service Unavailable:服務(wù)器暫不可用,可能是服務(wù)器過載或停機(jī)維護(hù)。

ps:《HTTP 常見面試題》完整詳細(xì)講解:https://xiaolincoding.com/network/2_http/http_interview.html

流量控制和擁塞控制的原理

流量控制是服務(wù)端和客戶端協(xié)議的窗口實現(xiàn),在客戶端發(fā)送數(shù)據(jù)的時候,服務(wù)端返回窗口的容量,客戶端通過容量來調(diào)整發(fā)送信息的大小

擁塞控制是通過滑動窗口實現(xiàn),服務(wù)端只接收擁塞窗口大小內(nèi)的數(shù)據(jù),客服端發(fā)送的報文丟失后,沒有收到服務(wù)端的確認(rèn)信息,就將沒有收到確認(rèn)信息的保溫再發(fā)送。

小林補(bǔ)充:

TCP一種面向連接的、可靠的傳輸層協(xié)議。為了確保數(shù)據(jù)的有效傳輸,TCP 提供了兩種重要的控制機(jī)制:流量控制和擁塞控制。

流量控制(Flow Control)

流量控制的主要目的是防止發(fā)送方向接收方發(fā)送過多數(shù)據(jù),導(dǎo)致接收方處理不過來。TCP 使用滑動窗口機(jī)制來實現(xiàn)流量控制。在 TCP 連接中,接收方為每個連接分配一個接收緩沖區(qū)。接收方通過通知發(fā)送方自己的窗口大小,告知發(fā)送方可以發(fā)送的數(shù)據(jù)量。窗口大小表示接收方當(dāng)前能接受的數(shù)據(jù)字節(jié)數(shù)。

滑動窗口的工作原理如下:

發(fā)送方根據(jù)接收方的窗口大小來確定發(fā)送的數(shù)據(jù)量。

當(dāng)接收方收到數(shù)據(jù)后,發(fā)送確認(rèn)報文,同時更新窗口大小。

發(fā)送方收到確認(rèn)報文后,更新已發(fā)送但未確認(rèn)的數(shù)據(jù)量,并根據(jù)新的窗口大小調(diào)整發(fā)送速率。

擁塞控制(Congestion Control)

擁塞控制的目的是防止過多的數(shù)據(jù)進(jìn)入網(wǎng)絡(luò),導(dǎo)致網(wǎng)絡(luò)擁塞。TCP 使用四種算法來實現(xiàn)擁塞控制:慢開始、擁塞避免、快速重傳和快速恢復(fù)。

慢開始:發(fā)送方初始擁塞窗口設(shè)置為一個較小的值。隨后,每收到一個確認(rèn)報文,擁塞窗口大小加倍。這樣,發(fā)送速率會以指數(shù)形式增長,直到達(dá)到一個閾值(ssthresh)。

擁塞避免:當(dāng)擁塞窗口到達(dá)閾值后,發(fā)送方轉(zhuǎn)入擁塞避免階段,窗口大小每經(jīng)過一個往返時間(RTT)就增加1。這樣,擁塞窗口的大小呈線性增長,避免了網(wǎng)絡(luò)擁塞。

快速重傳:當(dāng)發(fā)送方連續(xù)收到三個重復(fù)的確認(rèn)報文,表示可能有一個數(shù)據(jù)包丟失。此時,發(fā)送方立即重傳丟失的數(shù)據(jù)包,而不是等待超時重傳。

快速恢復(fù):快速重傳后,發(fā)送方降低擁塞窗口閾值,然后進(jìn)入擁塞避免階段。這樣可以在丟包后盡快恢復(fù)傳輸速率。

通過這兩種控制機(jī)制,TCP 能確保在各種網(wǎng)絡(luò)條件下有效、可靠地傳輸數(shù)據(jù)。

ps:《TCP 重傳、滑動窗口、流量控制、擁塞控制》完整詳細(xì)講解:https://xiaolincoding.com/network/3_tcp/tcp_feature.html

一條url請求頁面的執(zhí)行過程

(這塊我答的不太好,忘了dns服務(wù)器的名字)瀏覽器先解析url地址,然后生成http消息,生成的消息需要知道ip地址才能發(fā)送,就先去瀏覽器的緩存中查詢,沒有的話查看操作系統(tǒng)的緩存,如果還是沒有就在本地dns中查詢,本地dns查詢不到后會先訪問根dns,根dns查詢的是存放這個ip的二級dns服務(wù)器(忘了名字),二級dns服務(wù)器會向?qū)?yīng)的權(quán)威dns服務(wù)器查詢,權(quán)威dns服務(wù)器會向本地返回ip地址,然后本地通過這個ip地址和請求的服務(wù)端建立起tcp連接,服務(wù)端向本地發(fā)送請求的資源。

小林補(bǔ)充:

afe82c8c-0cd5-11ee-962d-dac502259ad0.png

ps:《鍵入網(wǎng)址到網(wǎng)頁顯示,期間發(fā)生了什么?》完整詳細(xì)講解:https://xiaolincoding.com/network/1_base/what_happen_url.html

TCP是如何建立連接的

三次握手

1、客戶端發(fā)送請求建立連接報文,報文的SYN=1

2、服務(wù)端收到后,發(fā)送連接報文,報文的SYN=1,并且發(fā)送一個序號字段

3、客戶端收到后報文后,客戶端到服務(wù)端的連接已經(jīng)建立,客戶端發(fā)送報文對上一個報文的序號端進(jìn)行確認(rèn)

小林補(bǔ)充:

TCP 是面向連接的協(xié)議,所以使用 TCP 前必須先建立連接,而建立連接是通過三次握手來進(jìn)行的。三次握手的過程如下圖:

b00ae970-0cd5-11ee-962d-dac502259ad0.pngTCP 三次握手

一開始,客戶端和服務(wù)端都處于 CLOSE 狀態(tài)。先是服務(wù)端主動監(jiān)聽某個端口,處于 LISTEN 狀態(tài)

客戶端會隨機(jī)初始化序號(client_isn),將此序號置于 TCP 首部的「序號」字段中,同時把 SYN 標(biāo)志位置為 1,表示 SYN 報文。接著把第一個 SYN 報文發(fā)送給服務(wù)端,表示向服務(wù)端發(fā)起連接,該報文不包含應(yīng)用層數(shù)據(jù),之后客戶端處于 SYN-SENT 狀態(tài)。

服務(wù)端收到客戶端的 SYN 報文后,首先服務(wù)端也隨機(jī)初始化自己的序號(server_isn),將此序號填入 TCP 首部的「序號」字段中,其次把 TCP 首部的「確認(rèn)應(yīng)答號」字段填入 client_isn + 1, 接著把 SYN 和 ACK 標(biāo)志位置為 1。最后把該報文發(fā)給客戶端,該報文也不包含應(yīng)用層數(shù)據(jù),之后服務(wù)端處于 SYN-RCVD 狀態(tài)。

客戶端收到服務(wù)端報文后,還要向服務(wù)端回應(yīng)最后一個應(yīng)答報文,首先該應(yīng)答報文 TCP 首部 ACK 標(biāo)志位置為 1 ,其次「確認(rèn)應(yīng)答號」字段填入 server_isn + 1 ,最后把報文發(fā)送給服務(wù)端,這次報文可以攜帶客戶到服務(wù)端的數(shù)據(jù),之后客戶端處于 ESTABLISHED 狀態(tài)。

服務(wù)端收到客戶端的應(yīng)答報文后,也進(jìn)入 ESTABLISHED 狀態(tài)。

ps:《TCP 三次握手與四次揮手面試題》完整詳細(xì)講解:https://xiaolincoding.com/network/3_tcp/tcp_interview.html

http和https的區(qū)別

1、https是需要通過CA申請才能獲得,所以數(shù)量是比較少的

2、http發(fā)送的報文是明文,所以不安全,https在傳輸層之上加了ssl協(xié)議

小林補(bǔ)充:

HTTP是一種用于傳輸超文本的協(xié)議,數(shù)據(jù)傳輸是明文的,不具備加密和安全性。HTTP使用的端口號是 80

HTTPS是在HTTP的基礎(chǔ)上加入了SSL/TLS協(xié)議進(jìn)行加密和身份驗證的安全版本。它使用加密的SSL/TLS協(xié)議進(jìn)行數(shù)據(jù)傳輸,保證了數(shù)據(jù)的機(jī)密性和完整性。HTTPS使用的端口號是443。

ps:《 HTTP 常見面試題》完整詳細(xì)講解:https://xiaolincoding.com/network/2_http/http_interview.html

數(shù)據(jù)庫的索引

B+樹索引,hash索引、全文索引

B+樹索引的話是innodb采用的索引,索引的葉子結(jié)點上是數(shù)據(jù),非葉子結(jié)點是索引信息

hash索引單個的查找效率很高

為什么采用B+樹索引,它有什么優(yōu)點

這里我將B+樹和B樹、紅黑樹做了比較。

B+樹相對于B樹,只有葉子結(jié)點存儲的是數(shù)據(jù)信息,非葉子結(jié)點都是索引信息,所以在查找時加載到內(nèi)存中的數(shù)據(jù)少,B+樹的增刪相對于B樹來說比較穩(wěn)定,不會發(fā)生頻繁的父子結(jié)點替換,B+樹的葉子結(jié)點是連接的,所以很容易實現(xiàn)范圍查詢

B+樹相對于紅黑樹,首先B+樹的層高比較小,意味著讀取數(shù)據(jù)時IO磁盤的次數(shù)比較少,紅黑樹增刪結(jié)點時需要保持子樹的穩(wěn)定性,增刪的效率很低,B+樹更容易實現(xiàn)范圍查詢。

小林補(bǔ)充:

樹的高度決定于磁盤 I/O 操作的次數(shù),因為樹是存儲在磁盤中的,訪問每個節(jié)點,都對應(yīng)一次磁盤 I/O 操作,也就是說樹的高度就等于每次查詢數(shù)據(jù)時磁盤 IO 操作的次數(shù),所以樹的高度越高,就會影響查詢性能。

B 樹和 B+ 都是通過多叉樹的方式,會將樹的高度變矮,所以這兩個數(shù)據(jù)結(jié)構(gòu)非常適合檢索存于磁盤中的數(shù)據(jù)。

但是 MySQL 默認(rèn)的存儲引擎 InnoDB 采用的是 B+ 作為索引的數(shù)據(jù)結(jié)構(gòu),原因有:

B+ 樹的非葉子節(jié)點不存放實際的記錄數(shù)據(jù),僅存放索引,因此數(shù)據(jù)量相同的情況下,相比存儲即存索引又存記錄的 B 樹,B+樹的非葉子節(jié)點可以存放更多的索引,因此 B+ 樹可以比 B 樹更「矮胖」,查詢底層節(jié)點的磁盤 I/O次數(shù)會更少。

B+ 樹有大量的冗余節(jié)點(所有非葉子節(jié)點都是冗余索引),這些冗余索引讓 B+ 樹在插入、刪除的效率都更高,比如刪除根節(jié)點的時候,不會像 B 樹那樣會發(fā)生復(fù)雜的樹的變化;

B+ 樹葉子節(jié)點之間用鏈表連接了起來,有利于范圍查詢,而 B 樹要實現(xiàn)范圍查詢,因此只能通過樹的遍歷來完成范圍查詢,這會涉及多個節(jié)點的磁盤 I/O 操作,范圍查詢效率不如 B+ 樹。

ps:《為什么 MySQL 采用 B+ 樹作為索引?》完整詳細(xì)講解:https://xiaolincoding.com/mysql/index/why_index_chose_bpuls_tree.html

數(shù)據(jù)庫中事務(wù)可能帶來的問題

有臟讀、不可重復(fù)讀、幻讀三個問題:

臟讀:一個事務(wù)讀取另一個事務(wù)沒有提交的數(shù)據(jù)

不可重復(fù)讀:一個事務(wù)重復(fù)讀取一條數(shù)據(jù)時發(fā)現(xiàn)讀取到的數(shù)據(jù)不相同

幻讀:一個事務(wù)后讀取的數(shù)據(jù)相比之前讀取的數(shù)據(jù)中多了一些數(shù)據(jù)

小林補(bǔ)充:

MySQL 服務(wù)端是允許多個客戶端連接的,這意味著 MySQL 會出現(xiàn)同時處理多個事務(wù)的情況。

那么在同時處理多個事務(wù)的時候,就可能出現(xiàn)臟讀(dirty read)、不可重復(fù)讀(non-repeatable read)、幻讀(phantom read)的問題。

1、臟讀:如果一個事務(wù)「讀到」了另一個「未提交事務(wù)修改過的數(shù)據(jù)」,就意味著發(fā)生了「臟讀」現(xiàn)象。

舉個栗子,假設(shè)有 A 和 B 這兩個事務(wù)同時在處理,事務(wù) A 先開始從數(shù)據(jù)庫中讀取小林的余額數(shù)據(jù),然后再執(zhí)行更新操作,如果此時事務(wù) A 還沒有提交事務(wù),而此時正好事務(wù) B 也從數(shù)據(jù)庫中讀取小林的余額數(shù)據(jù),那么事務(wù) B 讀取到的余額數(shù)據(jù)是剛才事務(wù) A 更新后的數(shù)據(jù),即使沒有提交事務(wù)。

b019e98e-0cd5-11ee-962d-dac502259ad0.png圖片

因為事務(wù) A 是還沒提交事務(wù)的,也就是它隨時可能發(fā)生回滾操作,如果在上面這種情況事務(wù) A 發(fā)生了回滾,那么事務(wù) B 剛才得到的數(shù)據(jù)就是過期的數(shù)據(jù),這種現(xiàn)象就被稱為臟讀。

2、不可重復(fù)讀:在一個事務(wù)內(nèi)多次讀取同一個數(shù)據(jù),如果出現(xiàn)前后兩次讀到的數(shù)據(jù)不一樣的情況,就意味著發(fā)生了「不可重復(fù)讀」現(xiàn)象。

舉個栗子,假設(shè)有 A 和 B 這兩個事務(wù)同時在處理,事務(wù) A 先開始從數(shù)據(jù)庫中讀取小林的余額數(shù)據(jù),然后繼續(xù)執(zhí)行代碼邏輯處理,在這過程中如果事務(wù) B 更新了這條數(shù)據(jù),并提交了事務(wù),那么當(dāng)事務(wù) A 再次讀取該數(shù)據(jù)時,就會發(fā)現(xiàn)前后兩次讀到的數(shù)據(jù)是不一致的,這種現(xiàn)象就被稱為不可重復(fù)讀。

b03c803e-0cd5-11ee-962d-dac502259ad0.png圖片

3、幻讀:在一個事務(wù)內(nèi)多次查詢某個符合查詢條件的「記錄數(shù)量」,如果出現(xiàn)前后兩次查詢到的記錄數(shù)量不一樣的情況,就意味著發(fā)生了「幻讀」現(xiàn)象。

舉個栗子,假設(shè)有 A 和 B 這兩個事務(wù)同時在處理,事務(wù) A 先開始從數(shù)據(jù)庫查詢賬戶余額大于 100 萬的記錄,發(fā)現(xiàn)共有 5 條,然后事務(wù) B 也按相同的搜索條件也是查詢出了 5 條記錄。

b048851e-0cd5-11ee-962d-dac502259ad0.png圖片

接下來,事務(wù) A 插入了一條余額超過 100 萬的賬號,并提交了事務(wù),此時數(shù)據(jù)庫超過 100 萬余額的賬號個數(shù)就變?yōu)?6。然后事務(wù) B 再次查詢賬戶余額大于 100 萬的記錄,此時查詢到的記錄數(shù)量有 6 條,發(fā)現(xiàn)和前一次讀到的記錄數(shù)量不一樣了,就感覺發(fā)生了幻覺一樣,這種現(xiàn)象就被稱為幻讀。

ps:《事務(wù)隔離級別是怎么實現(xiàn)的?》完整詳細(xì)講解:https://xiaolincoding.com/mysql/transaction/mvcc.html

通過什么隔離級別解決這些問題

臟讀:讀寫已提交

不可重復(fù)讀:可重復(fù)讀

幻讀:串行化

小林補(bǔ)充:

針對不同的隔離級別,并發(fā)事務(wù)時可能發(fā)生的現(xiàn)象也會不同。

b04e8752-0cd5-11ee-962d-dac502259ad0.png圖片

也就是說:

在「讀未提交」隔離級別下,可能發(fā)生臟讀、不可重復(fù)讀和幻讀現(xiàn)象;

在「讀提交」隔離級別下,可能發(fā)生不可重復(fù)讀和幻讀現(xiàn)象,但是不可能發(fā)生臟讀現(xiàn)象;

在「可重復(fù)讀」隔離級別下,可能發(fā)生幻讀現(xiàn)象,但是不可能臟讀和不可重復(fù)讀現(xiàn)象;

在「串行化」隔離級別下,臟讀、不可重復(fù)讀和幻讀現(xiàn)象都不可能會發(fā)生。

所以,要解決臟讀現(xiàn)象,就要升級到「讀提交」以上的隔離級別;要解決不可重復(fù)讀現(xiàn)象,就要升級到「可重復(fù)讀」的隔離級別,要解決幻讀現(xiàn)象不建議將隔離級別升級到「串行化」。

不同的數(shù)據(jù)庫廠商對 SQL 標(biāo)準(zhǔn)中規(guī)定的 4 種隔離級別的支持不一樣,有的數(shù)據(jù)庫只實現(xiàn)了其中幾種隔離級別,我們討論的 MySQL 雖然支持 4 種隔離級別,但是與SQL 標(biāo)準(zhǔn)中規(guī)定的各級隔離級別允許發(fā)生的現(xiàn)象卻有些出入。

MySQL 在「可重復(fù)讀」隔離級別下,可以很大程度上避免幻讀現(xiàn)象的發(fā)生(注意是很大程度避免,并不是徹底避免),所以 MySQL 并不會使用「串行化」隔離級別來避免幻讀現(xiàn)象的發(fā)生,因為使用「串行化」隔離級別會影響性能。

ps:《事務(wù)隔離級別是怎么實現(xiàn)的?》完整詳細(xì)講解:https://xiaolincoding.com/mysql/transaction/mvcc.html

mysql的隔離級別是什么?mysql是如何實現(xiàn)的?

不可重復(fù)讀,但是很大程度上避免幻讀

快照讀(只讀):MVCC

當(dāng)前讀(更新操作):記錄鎖+間隙鎖

小林補(bǔ)充:

MySQL InnoDB 引擎的默認(rèn)隔離級別雖然是「可重復(fù)讀」,但是它很大程度上避免幻讀現(xiàn)象(并不是完全解決了),解決的方案有兩種:

針對快照讀(普通 select 語句),是通過 MVCC 方式解決了幻讀,因為可重復(fù)讀隔離級別下,事務(wù)執(zhí)行過程中看到的數(shù)據(jù),一直跟這個事務(wù)啟動時看到的數(shù)據(jù)是一致的,即使中途有其他事務(wù)插入了一條數(shù)據(jù),是查詢不出來這條數(shù)據(jù)的,所以就很好了避免幻讀問題。

針對當(dāng)前讀(select ... for update 等語句),是通過 next-key lock(記錄鎖+間隙鎖)方式解決了幻讀,因為當(dāng)執(zhí)行 select ... for update 語句的時候,會加上 next-key lock,如果有其他事務(wù)在 next-key lock 鎖范圍內(nèi)插入了一條記錄,那么這個插入語句就會被阻塞,無法成功插入,所以就很好了避免幻讀問題。

ps:《事務(wù)隔離級別是怎么實現(xiàn)的?》完整詳細(xì)講解:https://xiaolincoding.com/mysql/transaction/mvcc.html

算法

寫一個數(shù)據(jù)庫的多表聯(lián)查問題

沒寫出來(平時寫的少,只知道命令,不熟練),講解下思路
責(zé)任編輯:彭菁

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 數(shù)據(jù)
    +關(guān)注

    關(guān)注

    8

    文章

    6715

    瀏覽量

    88316
  • 存儲
    +關(guān)注

    關(guān)注

    13

    文章

    4123

    瀏覽量

    85279
  • 線程池
    +關(guān)注

    關(guān)注

    0

    文章

    55

    瀏覽量

    6811

原文標(biāo)題:阿里問的相當(dāng)基礎(chǔ)!

文章出處:【微信號:小林coding,微信公眾號:小林coding】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    跨平臺的線程組件--TP組件

    /銷毀代價是很高的。那么我們要怎么去設(shè)計多線程編程呢???答案:對于長駐的線程,我們可以創(chuàng)建獨立的線程去執(zhí)行。但是非長駐的線程,我們可以通過線程
    的頭像 發(fā)表于 04-06 15:39 ?779次閱讀

    Java中的線程包括哪些

    java.util.concurrent 包來實現(xiàn)的,最主要的就是 ThreadPoolExecutor 類。 Executor: 代表線程的接口,一個 execute() 方法,給一個 Runnable 類型對象
    的頭像 發(fā)表于 10-11 15:33 ?730次閱讀
    Java中的<b class='flag-5'>線程</b><b class='flag-5'>池</b>包括哪些

    線程是如何實現(xiàn)的

    線程的概念是什么?線程是如何實現(xiàn)的?
    發(fā)表于 02-28 06:20

    基于線程技術(shù)集群接入點的應(yīng)用研究

    本文在深入研究高級線程技術(shù)的基礎(chǔ)上,分析、研究了固定線程數(shù)目的線程線程數(shù)目動態(tài)變化的
    發(fā)表于 01-22 14:21 ?5次下載

    如何正確關(guān)閉線程

    前言本章分為兩個議題 如何正確關(guān)閉線程 shutdown 和 shutdownNow 的區(qū)別 項目環(huán)境jdk 1.8 github 地址:https://github.com
    的頭像 發(fā)表于 09-29 14:41 ?9782次閱讀

    基于Nacos的簡單動態(tài)化線程實現(xiàn)

    本文以Nacos作為服務(wù)配置中心,以修改線程核心線程數(shù)、最大線程數(shù)為例,實現(xiàn)一個簡單的動態(tài)化線程
    發(fā)表于 01-06 14:14 ?766次閱讀

    線程線程

    線程通常用于服務(wù)器應(yīng)用程序。 每個傳入請求都將分配給線程池中的一個線程,因此可以異步處理請求,而不會占用主線程,也不會延遲后續(xù)請求的處理
    的頭像 發(fā)表于 02-28 09:53 ?671次閱讀
    多<b class='flag-5'>線程</b>之<b class='flag-5'>線程</b><b class='flag-5'>池</b>

    Java線程核心原理

    看過Java線程源碼的小伙伴都知道,在Java線程池中最核心的類就是ThreadPoolExecutor,
    的頭像 發(fā)表于 04-21 10:24 ?756次閱讀

    線程線程怎么釋放

    線程分組看,pool名開頭線程占616條,而且waiting狀態(tài)也是616條,這個點就非常可疑了,我斷定就是這個pool開頭線程導(dǎo)致的問題。我們先排查為何這個
    發(fā)表于 07-31 10:49 ?2066次閱讀
    <b class='flag-5'>線程</b><b class='flag-5'>池</b>的<b class='flag-5'>線程</b>怎么釋放

    Spring 的線程應(yīng)用

    我們在日常開發(fā)中,經(jīng)常跟多線程打交道,Spring 為我們提供了一個線程方便我們開發(fā),它就是 ThreadPoolTaskExecutor ,接下來我們就來聊聊 Spring 的線程
    的頭像 發(fā)表于 10-13 10:47 ?523次閱讀
    Spring 的<b class='flag-5'>線程</b><b class='flag-5'>池</b>應(yīng)用

    了解連接、線程、內(nèi)存、異步請求

    可被重復(fù)使用像常見線程、內(nèi)存、連接、對象都具有以上的共同特點。 連接
    的頭像 發(fā)表于 11-09 14:44 ?870次閱讀
    了解連接<b class='flag-5'>池</b>、<b class='flag-5'>線程</b><b class='flag-5'>池</b>、內(nèi)存<b class='flag-5'>池</b>、異步請求<b class='flag-5'>池</b>

    線程基本概念與原理

    一、線程基本概念與原理 1.1 線程概念及優(yōu)勢 C++線程簡介
    的頭像 發(fā)表于 11-10 10:24 ?408次閱讀

    線程的基本概念

    ? 呃呃,我這么問就很奇怪,因為線程是什么我都沒說,怎么會知道為什么會有線程呢?所以我打算帶大家去思考一個場景: 當(dāng)我們的程序中:一批
    的頭像 發(fā)表于 11-10 16:37 ?428次閱讀
    <b class='flag-5'>線程</b><b class='flag-5'>池</b>的基本概念

    線程的創(chuàng)建方式幾種

    的開銷。線程的創(chuàng)建方式多種,下面將詳細(xì)介紹幾種常用的線程創(chuàng)建方式。 手動創(chuàng)建線程
    的頭像 發(fā)表于 12-04 16:52 ?661次閱讀

    什么是動態(tài)線程?動態(tài)線程的簡單實現(xiàn)思路

    因此,動態(tài)可監(jiān)控線程一種針對以上痛點開發(fā)的線程管理工具。主要可實現(xiàn)功能有:提供對 Spring 應(yīng)用內(nèi)線程
    的頭像 發(fā)表于 02-28 10:42 ?470次閱讀