今天分享粉絲最新投稿的北京貝殼外包面經,我把專案相關的都去掉了,其他的問題整理下來感覺不是很難,你覺得呢?換做是你的話你答得出來嗎?
面經整理如下:
北京貝殼外包
- 自我介紹
- 專案拷打
Redis 跟 MySQL 以及 Kafka 之間是什麼關係?
Redis 是記憶體資料結構儲存系統,用於快速讀寫和高併發訪問。MySQL 是關係型資料庫,用於持久化儲存結構化資料。Kafka 是分散式訊息佇列系統,用於處理大規模訊息流和資料分發。
在應用中,MySQL 存主要資料,Redis 加速訪問,Kafka 傳遞訊息和解耦元件。例如電商系統中,使用者資訊在 MySQL,購物車資料在 Redis,訂單處理訊息透過 Kafka 傳遞。
Kafka partion的概念
在 Kafka 裡,Partion 就是把一個主題的資料分成了好幾個部分。 每個 Partion 都是按順序存訊息的,而且這些訊息改不了。同一主題的不同 Partion 能放在不同的地方,這樣就能讓資料處理得更快更平衡。
Partion 有這些用處: 一是存資料,訊息按順序存進去,還給每個訊息弄個編號來標明位置。 二是能同時處理,多個 Partion 能讓不同的人或者組一起處理,效率高。 三是不容易出錯,如果有地方壞了,也就影響那一部分,整個主題的資料不會丟。
Kafka 偏移量
在 Kafka 裡邊,偏移量就是個數字,專門用來表明訊息在分割槽裡處於啥位置。
打個比方,每個訊息在分割槽裡都有個獨一份的編號,這就是偏移量。消費者讀訊息的時候,會記住自己讀到哪個編號了。下次再讀,就知道從哪兒接著來,不會重複讀,也不會把訊息給漏了。
比如說,偏移量是 5 ,那就說明這是分割槽裡的第 6 條訊息(因為偏移量從 0 開始算)。偏移量能保證 Kafka 裡訊息處理得有順序,而且不會丟。
比如說有你線上上發現一條慢SQL,你怎麼分析它?
使用 explain ,說明:
首先關注“type”這一項。若顯示為“ALL”,意味著進行了全表掃描,這通常不是理想情況,需要尋求改進。若顯示為“index”或“range”等,則相對較好。
其次留意“possible_keys”和“key”這兩項。“possible_keys”表示可能能夠使用的索引,而“key”表示實際使用的索引。倘若“key”這一項為空,意味著未使用索引,此時需要探究原因,例如索引建立是否正確。
再者,“rows”這一項也頗為重要,它代表預計掃描的行數。該數值越大,表明需要處理的資料量越多,可能導致查詢速度變慢。
另外,“extra”這一項也值得關注。如果出現“Using filesort”或者“Using temporary”等內容,往往意味著存在問題,需要進行最佳化。
索引的最左字首原則是什麼意思?a > 1 and b = 1 and c = 1, 你看這個能命中這個索引嗎?
索引的最左字首原則就是在建立聯合索引時,查詢語句用索引的條件得從索引的最左邊開始,而且得連續匹配。
對於“a > 1 and b = 1 and c = 1”這個條件,能不能命中索引得看建立的索引是啥樣。
要是建立的索引是 (a, b, c) ,那這查詢就能命中索引,因為是從最左邊的“a”開始匹配的。 可要是建立的索引是 (b, c, a) 或者其他不是以“a”開頭的組合,那這查詢就不能命中索引。
Redis 常見的資料結構?他們的應用場景?
- 字串(String):可以儲存字串、整數或浮點數。常用於快取使用者資訊、計數器、分散式鎖等。
- 雜湊(Hash):適合儲存物件,例如儲存使用者的詳細資訊。
- 列表(List):可以實現佇列、棧等資料結構。常用於訊息佇列、最新文章列表等。
- 集合(Set):用於儲存不重複的元素,可用於社交關係中的共同好友、抽獎等場景。
- 有序集合(Sorted Set):每個元素都有一個分數,可根據分數排序。適用於排行榜、限時活動等。 例如,在電商網站中,字串可以儲存商品庫存數量,雜湊儲存商品詳情,列表用於儲存使用者瀏覽記錄,集合用於儲存使用者收藏的商品,有序集合用於商品銷量排行。
zset 你一般在什麼場景下會用?底層的資料結構是什麼?
ZSet(有序集合)通常在以下場景中使用:
- 排行榜:例如遊戲得分排行榜、商品銷量排行榜等,按照分數或銷量等進行排序。
- 優先順序佇列:可以根據元素的權重或優先順序來處理任務。
- 時間序列資料:比如按照時間戳排序的事件記錄。
Redis中ZSet底層實際有兩種資料結構:
一、壓縮列表(ziplist)
使用條件(以下兩個條件需同時滿足) 12:
- 儲存的元素數量較少(通常規定是小於配置引數
zset-max-ziplist-entries
,一般預設是128 ,但可以修改配置)。- 所有元素長度都比較小(規定是小於配置引數
zset-max-ziplist-value
,一般預設是64位元組,但可以修改配置)。結構特點:
- 是一種為節省記憶體而設計的特殊編碼結構,將所有的元素和分數緊湊地儲存在一起。
- 元素和它的分數在壓縮列表中是交替儲存,即第一個元素是成員,第二個元素是分數,第三個元素是成員,第四個元素是分數,以此類推1。
- 在redis的原始碼中,壓縮列表(ziplist)的結構並沒有直接定義為一個C結構體,而是透過一系列的宏和函式來操作一段連續的記憶體1。其結構大致包含以下部分 1:
zlbytes
:一個4位元組的整數,表示整個壓縮列表佔用的位元組數量,包括它自身的大小。zltail
:一個4位元組的整數,表示壓縮列表中最後一個元素的偏移量(相對於整個壓縮列表的起始地址)。zllen
:一個2位元組的整數,表示壓縮列表中的元素數量。如果元素數量超過65535,那麼這個值就會被設定為65535,需要遍歷整個壓縮列表才能獲取到實際的元素數量。entry
:壓縮列表中的元素,每個元素都由一個或多個位元組組成。每個元素的第一個位元組(又稱為 "entry header")用於表示這個元素的長度以及編碼方式。zlend
:一個位元組,值為255,表示壓縮列表的結束。二、跳躍表(skiplist)
使用條件(只要有一個不滿足上述ziplist條件就會切換到跳躍表)12 : 當ZSet儲存的元素數量較多,或者元素的字串長度較長時。
結構特點:
- 在Redis的原始碼中,跳躍表的結構定義如下2:
- 有一個
zskiplist
結構體表示整個跳躍表:
header
:指向跳躍表的表頭節點,透過這個指標程式定位表頭節點的時間複雜度是O(1)。tail
:指向跳躍表的表尾節點,透過這個指標程式定位表尾節點的時間複雜度為O(1)。length
:記錄跳躍表的長度,就是跳躍表目前包含節點的數量(表頭節點不算在內)。level
:記錄在目前的跳躍表內,層數最大的那個節點的層數(表頭節點的層數不計算在內)。- 跳躍表節點
zskiplistnode
:
obj
:成員物件。score
:分值(用於排序)。backward
:後退指標,指向位於當前節點的前一個節點(程式從表尾向表頭遍歷時使用,每個節點只有一個後退指標 )。level
:是一個可變長陣列結構(C99標準引入),裡面包含:
forward
:前進指標,用於訪問位於表尾方向的其他節點。span
:這個層跨越的節點數量(記錄前進指標所指向節點和當前節點的距離跨度越大、距離越遠 )。跳躍表透過維護多級索引來實現快速查詢和高效的插入、刪除、更新元素等操作2 。它的時間複雜度平均為O(log n) ,效能比較高。
在Redis中ZSet元素的儲存和訪問等操作過程中:
- 當使用跳躍表時,元素的成員和分值等資訊儲存在跳躍表節點中,同時還有一個雜湊表(在整個ZSet結構層面,不是跳躍表結構內部)用於快速透過成員查詢其分值等操作2 。
- 當使用壓縮列表時,直接在壓縮列表中操作元素和分值等資料。
JWT 是什麼東西?
JWT(JSON Web Token)是一種用於在網路應用環境中進行安全資訊傳遞的開放標準。
它由三部分組成:
頭部(Header),通常包含令牌的型別和使用的加密演算法;
載荷(Payload),包含宣告,比如使用者身份資訊、許可權等;
簽名(Signature),用於驗證訊息的完整性和真實性,防止被篡改。
JWT 的主要優點是無狀態,伺服器不需要儲存會話資訊,減輕了伺服器的儲存負擔。它常用於身份驗證和授權,比如在單點登入、微服務架構等場景中。使用者在登入成功後獲取 JWT 令牌,後續的請求攜帶該令牌,伺服器透過驗證令牌的有效性來確認使用者身份和許可權。
協程瞭解嗎?和執行緒對比
協程和執行緒是在程式設計中常見的概念,它們有一些明顯的區別。
執行緒是作業系統層面的概念,由作業系統進行排程和管理。多個執行緒可以在同一程序中併發執行,它們共享程序的資源。執行緒的切換通常由作業系統控制,開銷相對較大。 協程則是在使用者態實現的輕量級執行緒。協程的切換由程式自身控制,不需要作業系統的干預,因此協程切換的開銷通常比執行緒小得多。協程更適用於協作式的任務排程,多個協程可以在同一個執行緒中執行,透過主動讓出控制權來實現任務的切換。
在資源消耗方面,執行緒由於需要作業系統進行管理和排程,會佔用較多的系統資源,如記憶體等。而協程相對來說資源消耗較少。
在程式設計模型上,執行緒的併發模型相對較為複雜,需要處理執行緒安全、同步等問題。協程的程式設計模型通常更簡單直觀,更容易理解和實現複雜的邏輯控制。
一道演算法題
找到具有最大和的連續子陣列,並返回該子陣列
程式設計題
用兩個協程、兩個channel迴圈列印1, 2
package main
import (
"fmt"
"sync"
)
func Print1(ch1 chan bool, ch2 chan bool, wg *sync.WaitGroup) {
defer wg.Done()
for {
<-ch1
fmt.Print("1")
ch2 <- true
}
}
func Print2(ch1 chan bool, ch2 chan bool, wg *sync.WaitGroup) {
defer wg.Done()
for {
<-ch2
fmt.Print("2")
ch1 <- true
}
}
func main() {
var wg sync.WaitGroup
ch1 := make(chan bool)
ch2 := make(chan bool)
wg.Add(2)
go Print1(ch1, ch2, &wg)
go Print2(ch1, ch2, &wg)
ch1 <- true
wg.Wait()
}
歡迎關注 ❤
我們搞了一個免費的面試真題共享群,互通有無,一起刷題進步。
沒準能讓你能刷到自己意向公司的最新面試題呢。
感興趣的朋友們可以加我微信:wangzhongyang2024,備註:部落格園面試群。