電子產(chǎn)業(yè)一站式賦能平臺(tái)

PCB聯(lián)盟網(wǎng)

搜索
查看: 27|回復(fù): 0
收起左側(cè)

?低,開(kāi)了體面的薪資!

[復(fù)制鏈接]

281

主題

281

帖子

1775

積分

三級(jí)會(huì)員

Rank: 3Rank: 3

積分
1775
跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 昨天 17:02 | 只看該作者 |只看大圖 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
大家好,我是庫(kù)森。) p# _. s7 M# J# C2 F8 A% h
?低曌鳛橐患殷w面廠,我們來(lái)看看今年?低暤男U行劫Y開(kāi)了多少?
2 K) e$ B  v' ]8 ?- I9 _+ m4 A我根據(jù)一些同學(xué)的反饋,整理了海康威視軟件開(kāi)發(fā)崗位的校招薪資,在目前的就業(yè)背景下,?低暤男U行劫Y還是很體面的。
3 @: J: S) b) L) s* N/ j$ D& a
  • 14k x 15 = 21w(本科 985,武漢)
  • 15k x 15 =  22.5w(碩士雙一流,杭州)
  • 16 x 15 =  24w (本碩 211,杭州)
  • 19 x 15 = 28.5w (本碩 211,杭州)那?低暤拿嬖囯y度如何呢?
    ! Y: L* h) i: ~2 p- R我也找了一位今年秋招面?低曂瑢W(xué)的面經(jīng),給大家做做參考參考,總共 1 輪技術(shù)面 + 1 輪 HR 面,3-5 個(gè)工作日出結(jié)果。
    9 ~- Y% ~* }: [6 B* ?一面是技術(shù)面,問(wèn)的問(wèn)題不算多,主要拷打了 Java、MySQL、Redis 方面的八股文,都屬于經(jīng)典的面試問(wèn)題,不算難。
    0 W$ R% c& e) A  Y6 p * ~0 ~7 ^, U1 r0 y  l
    Java 介紹一下 Spring Boot 整體的啟動(dòng)流程?
  • 首先從main找到run()方法,在執(zhí)行run()方法之前new一個(gè)SpringApplication對(duì)象
  • 進(jìn)入run()方法,創(chuàng)建應(yīng)用監(jiān)聽(tīng)器SpringApplicationRunListeners開(kāi)始監(jiān)聽(tīng)
  • 然后加載SpringBoot配置環(huán)境(ConfigurableEnvironment),然后把配置環(huán)境(Environment)加入監(jiān)聽(tīng)對(duì)象中
  • 然后加載應(yīng)用上下文(ConfigurableApplicationContext),當(dāng)做run方法的返回對(duì)象
  • 最后創(chuàng)建Spring容器,refreshContext(context),實(shí)現(xiàn)starter自動(dòng)化配置和bean的實(shí)例化等工作。[/ol]說(shuō)一說(shuō) Spring MVC 整體的執(zhí)行流程?# Y+ I6 Q. J- D! E
    9 _8 u5 B3 @9 i1 {$ i. E
    流程圖步驟詳解:
  • 發(fā)送請(qǐng)求:用戶發(fā)送的所有請(qǐng)求都會(huì)到前端控制器DispatcherServlet
  • 請(qǐng)求查找Handler:DispatcherServlet收到請(qǐng)求會(huì)調(diào)用HandlerMapping(處理器映射器)查找Handler
  • 返回Handler:處理器映射器根據(jù)url返回具體的處理器,生成HandlerExecutionChain對(duì)象,其中包含了目標(biāo)Handler和若干攔截器(可能沒(méi)有)
  • 請(qǐng)求調(diào)用Handler:DispatcherServlet通過(guò)Handler尋找匹配到HandlerAdapter
  • 執(zhí)行Handler:HandlerAdapter調(diào)用Handler
  • 返回結(jié)果:Handler執(zhí)行完成,返回一個(gè)ModelAndView對(duì)象
  • 返回結(jié)果給DispatcherServlet:HandlerAdapter將Handler執(zhí)行結(jié)果ModelAndView返回給DispatcherServlet
  • 如果Handler返回的View是邏輯視圖名稱而不是真正的View對(duì)象,DispatcherServlet調(diào)用resolveViewName方法在配置的所有視圖解析器(ViewResolver)中,尋找合適的,最終通過(guò)ViewResolver將邏輯視圖名解析成真正的View對(duì)象
  • ViewResolver通過(guò)調(diào)用createView方法嘗試將視圖名解析成View,如果無(wú)法解析會(huì)返回Null(注: 如果ViewResolver是派生自AbstractCachingViewResolver則在調(diào)用createView方法前會(huì)先嘗試根據(jù)viewName和Iocale從緩存中查找對(duì)應(yīng)的視圖對(duì)象)
  • DispatcherServlet調(diào)用View的render方法進(jìn)行渲染視圖 (即將模型數(shù)據(jù)填充至request域)
  • DispatcherServlet響應(yīng)用戶[/ol]MySQL MySQL 索引的機(jī)制,類型有哪些?MySQL可以按照四個(gè)角度來(lái)分類索引。  ~: `3 n5 H  Z0 A) c
  • 按「數(shù)據(jù)結(jié)構(gòu)」分類:B+tree索引、Hash索引、Full-text索引
  • 按「物理存儲(chǔ)」分類:聚簇索引(主鍵索引)、二級(jí)索引(輔助索引)
  • 按「字段特性」分類:主鍵索引、唯一索引、普通索引、前綴索引。
  • 按「字段個(gè)數(shù)」分類:單列索引、聯(lián)合索引。接下來(lái),按照這些角度來(lái)說(shuō)說(shuō)各類索引的特點(diǎn)。2 Y( @' L* D: ~
    按數(shù)據(jù)結(jié)構(gòu)分類
    ; u* B) p0 I2 D
    從數(shù)據(jù)結(jié)構(gòu)的角度來(lái)看,MySQL 常見(jiàn)索引有 B+Tree 索引、HASH 索引、Full-Text 索引。; n1 r( x7 U) N
    每一種存儲(chǔ)引擎支持的索引類型不一定相同,我在表中總結(jié)了 MySQL 常見(jiàn)的存儲(chǔ)引擎 InnoDB、MyISAM 和 Memory 分別支持的索引類型。
    " R& _8 u) a% u: K. I/ _$ o - p, t. M" @( E: @6 E
    InnoDB 是在 MySQL 5.5 之后成為默認(rèn)的 MySQL 存儲(chǔ)引擎,B+Tree 索引類型也是 MySQL 存儲(chǔ)引擎采用最多的索引類型。: A% |! a; |/ M9 e* V5 Q! W
    在創(chuàng)建表時(shí),InnoDB 存儲(chǔ)引擎會(huì)根據(jù)不同的場(chǎng)景選擇不同的列作為索引:, |* l0 S6 J, d
  • 如果有主鍵,默認(rèn)會(huì)使用主鍵作為聚簇索引的索引鍵(key);
  • 如果沒(méi)有主鍵,就選擇第一個(gè)不包含 NULL 值的唯一列作為聚簇索引的索引鍵(key);
  • 在上面兩個(gè)都沒(méi)有的情況下,InnoDB 將自動(dòng)生成一個(gè)隱式自增 id 列作為聚簇索引的索引鍵(key);其它索引都屬于輔助索引(Secondary Index),也被稱為二級(jí)索引或非聚簇索引。創(chuàng)建的主鍵索引和二級(jí)索引默認(rèn)使用的是 B+Tree 索引。' Y# Q& J" G. o5 l- M- L% A: X9 s
    按物理存儲(chǔ)分類, \- h$ e' c; h8 L' @$ i
    從物理存儲(chǔ)的角度來(lái)看,索引分為聚簇索引(主鍵索引)、二級(jí)索引(輔助索引)。
    + ]& f5 B( S5 q0 ]$ A這兩個(gè)區(qū)別在前面也提到了:
    0 j, X* v2 X. w
  • 主鍵索引的 B+Tree 的葉子節(jié)點(diǎn)存放的是實(shí)際數(shù)據(jù),所有完整的用戶記錄都存放在主鍵索引的 B+Tree 的葉子節(jié)點(diǎn)里;
  • 二級(jí)索引的 B+Tree 的葉子節(jié)點(diǎn)存放的是主鍵值,而不是實(shí)際數(shù)據(jù)。所以,在查詢時(shí)使用了二級(jí)索引,如果查詢的數(shù)據(jù)能在二級(jí)索引里查詢的到,那么就不需要回表,這個(gè)過(guò)程就是覆蓋索引。如果查詢的數(shù)據(jù)不在二級(jí)索引里,就會(huì)先檢索二級(jí)索引,找到對(duì)應(yīng)的葉子節(jié)點(diǎn),獲取到主鍵值后,然后再檢索主鍵索引,就能查詢到數(shù)據(jù)了,這個(gè)過(guò)程就是回表。
    $ Z) _7 W( t; m! X' t$ m按字段特性分類
    " q: G9 _% h: H) [( L; t4 p# R5 N
    從字段特性的角度來(lái)看,索引分為主鍵索引、唯一索引、普通索引、前綴索引。+ Y# y/ S+ t% `) t9 B
  • 主鍵索引主鍵索引就是建立在主鍵字段上的索引,通常在創(chuàng)建表的時(shí)候一起創(chuàng)建,一張表最多只有一個(gè)主鍵索引,索引列的值不允許有空值。
    8 j5 a+ N; y3 t+ J  O$ n! G+ Z在創(chuàng)建表時(shí),創(chuàng)建主鍵索引的方式如下:8 V' [. T, E8 a$ ^
    CREATE TABLE table_name  (
    # c! Z* z  I. T' ~  ....
    ) M/ k# |; m6 @+ V  PRIMARY KEY (index_column_1) USING BTREE
    * M% G8 P) A  ?% T  a4 C! x);
    4 b* a) G2 X8 y0 S: I5 d
  • 唯一索引唯一索引建立在 UNIQUE 字段上的索引,一張表可以有多個(gè)唯一索引,索引列的值必須唯一,但是允許有空值。
    5 I7 A  ^5 q7 t9 u' E9 b' u, }9 s在創(chuàng)建表時(shí),創(chuàng)建唯一索引的方式如下:5 m5 l+ e5 j$ K' T  E8 R
    CREATE TABLE table_name  (
    # L' d# j8 m6 {5 W( M" r  ....
    ( \7 E) V2 R" F6 U7 w( {  UNIQUE KEY(index_column_1,index_column_2,...) ( n7 _  m+ }/ b8 n: u" X
    );$ w, N" s3 x6 C) O1 ~
    建表后,如果要?jiǎng)?chuàng)建唯一索引,可以使用這面這條命令:
    3 \2 ]% U, C" [% f$ |1 ICREATE UNIQUE INDEX index_name
    8 u* f2 B, Y7 v8 z. v2 n( p: Q, lON table_name(index_column_1,index_column_2,...);
    3 g9 M: t5 ~! ~  P  |/ H& M
  • 普通索引普通索引就是建立在普通字段上的索引,既不要求字段為主鍵,也不要求字段為 UNIQUE。) ~$ @; g* q" a5 t2 o
    在創(chuàng)建表時(shí),創(chuàng)建普通索引的方式如下:
    8 w" t) Z  e' p/ B) }CREATE TABLE table_name  (4 f+ a4 n0 V5 E) _0 f+ D
      ....: Q% ]0 `& ~  ~' t
      INDEX(index_column_1,index_column_2,...)
    4 h2 ?/ J; _' S) n3 j2 }- B3 Q);- c1 ~. V' i$ x: Z# ?' X
    建表后,如果要?jiǎng)?chuàng)建普通索引,可以使用這面這條命令:6 ]* I" S% u. z2 k
    CREATE INDEX index_name2 |0 q% T5 G/ o3 ?. Q% m
    ON table_name(index_column_1,index_column_2,...);# G5 K. {9 j2 ]
  • 前綴索引前綴索引是指對(duì)字符類型字段的前幾個(gè)字符建立的索引,而不是在整個(gè)字段上建立的索引,前綴索引可以建立在字段類型為 char、 varchar、binary、varbinary 的列上。* h+ u0 D/ u! z% R, M
    使用前綴索引的目的是為了減少索引占用的存儲(chǔ)空間,提升查詢效率。/ _6 p; g; H# H3 I% \# f3 g5 d
    在創(chuàng)建表時(shí),創(chuàng)建前綴索引的方式如下:. o; ^" E& M9 F+ b! t/ w
    CREATE TABLE table_name(
    ( m4 Y" V( b7 `, R  f/ x    column_list,' U# t, z- U# I: b5 `8 V
        INDEX(column_name(length))
    : B- d. F1 b8 `);
    " j7 ]. {3 ^( a  S9 m6 {建表后,如果要?jiǎng)?chuàng)建前綴索引,可以使用這面這條命令:
      q3 Q, X5 [8 N* fCREATE INDEX index_name" q2 b6 h1 h# y' V7 N2 i
    ON table_name(column_name(length));
    : v0 u6 f8 ?5 Z2 ?2 o按字段個(gè)數(shù)分類( O. s3 T( G" p, E8 d" |9 Z
    從字段個(gè)數(shù)的角度來(lái)看,索引分為單列索引、聯(lián)合索引(復(fù)合索引)。1 Y% m$ x' ~3 q" r- v
  • 建立在單列上的索引稱為單列索引,比如主鍵索引;
  • 建立在多列上的索引稱為聯(lián)合索引;通過(guò)將多個(gè)字段組合成一個(gè)索引,該索引就被稱為聯(lián)合索引。. u7 V4 D6 x7 U  m
    比如,將商品表中的 product_no 和 name 字段組合成聯(lián)合索引(product_no, name),創(chuàng)建聯(lián)合索引的方式如下:
    # r! `9 x- w7 N9 gCREATE INDEX index_product_no_name ON product(product_no, name);
    1 y' Q% \  j- T, R聯(lián)合索引(product_no, name) 的 B+Tree 示意圖如下(圖中葉子節(jié)點(diǎn)之間我畫(huà)了單向鏈表,但是實(shí)際上是雙向鏈表,原圖我找不到了,修改不了,偷個(gè)懶我不重畫(huà)了,大家腦補(bǔ)成雙向鏈表就行)。" a% t) ~' {; A% c' R6 L
    8 b$ Z& v# \6 t1 z: F
    可以看到,聯(lián)合索引的非葉子節(jié)點(diǎn)用兩個(gè)字段的值作為 B+Tree 的 key 值。當(dāng)在聯(lián)合索引查詢數(shù)據(jù)時(shí),先按 product_no 字段比較,在 product_no 相同的情況下再按 name 字段比較。
    8 [3 g2 x0 ?6 A3 B  r) H也就是說(shuō),聯(lián)合索引查詢的 B+Tree 是先按 product_no 進(jìn)行排序,然后再 product_no 相同的情況再按 name 字段排序。' `* W8 R* `4 c) p) \3 ]
    因此,使用聯(lián)合索引時(shí),存在最左匹配原則,也就是按照最左優(yōu)先的方式進(jìn)行索引的匹配。在使用聯(lián)合索引進(jìn)行查詢的時(shí)候,如果不遵循「最左匹配原則」,聯(lián)合索引會(huì)失效,這樣就無(wú)法利用到索引快速查詢的特性了。
    ' r2 Z5 z. u' u/ T% ~7 i" `6 |比如,如果創(chuàng)建了一個(gè) (a, b, c) 聯(lián)合索引,如果查詢條件是以下這幾種,就可以匹配上聯(lián)合索引:
    ' K' T  C: l/ _( c* }( Y4 U  C) P
  • where a=1;
  • where a=1 and b=2 and c=3;
  • where a=1 and b=2;需要注意的是,因?yàn)橛胁樵儍?yōu)化器,所以 a 字段在 where 子句的順序并不重要。7 T1 K+ \. L5 X: P3 B- ]- L) Z7 W
    但是,如果查詢條件是以下這幾種,因?yàn)椴环献钭笃ヅ湓瓌t,所以就無(wú)法匹配上聯(lián)合索引,聯(lián)合索引就會(huì)失效:
    ) L' r3 M: ]( \  J7 z
  • where b=2;
  • where c=3;
  • where b=2 and c=3;上面這些查詢條件之所以會(huì)失效,是因?yàn)?a, b, c) 聯(lián)合索引,是先按 a 排序,在 a 相同的情況再按 b 排序,在 b 相同的情況再按 c 排序。所以,b 和 c 是全局無(wú)序,局部相對(duì)有序的,這樣在沒(méi)有遵循最左匹配原則的情況下,是無(wú)法利用到索引的。
    7 @; Y, L# w- H- K聯(lián)合索引有一些特殊情況,并不是查詢過(guò)程使用了聯(lián)合索引查詢,就代表聯(lián)合索引中的所有字段都用到了聯(lián)合索引進(jìn)行索引查詢,也就是可能存在部分字段用到聯(lián)合索引的 B+Tree,部分字段沒(méi)有用到聯(lián)合索引的 B+Tree 的情況。
    ) O9 }/ b. K# ~+ a* n% [2 X# N這種特殊情況就發(fā)生在范圍查詢。聯(lián)合索引的最左匹配原則會(huì)一直向右匹配直到遇到「范圍查詢」就會(huì)停止匹配。也就是范圍查詢的字段可以用到聯(lián)合索引,但是在范圍查詢字段的后面的字段無(wú)法用到聯(lián)合索引; K* H- {$ [: @
    有無(wú)排查索引失效的經(jīng)驗(yàn),展開(kāi)講講?可以使用 EXPLAIN 來(lái)查看 SQL 的執(zhí)行計(jì)劃,判斷SQL是否走了索引,如果沒(méi)有走索引,就代表索引發(fā)生失效了。( T  l* E7 H, G8 `" `
    如下圖,就是一個(gè)沒(méi)有使用索引,并且是一個(gè)全表掃描的查詢語(yǔ)句。
    ) m$ K& e9 ]- u! T9 c0 {% N+ a0 [  n$ @ 8 [9 w4 Y' S5 u' M' [+ Z9 {5 {
    對(duì)于執(zhí)行計(jì)劃,參數(shù)有:& `. a3 P' x% t- C7 D2 B
  • possible_keys 字段表示可能用到的索引;
  • key 字段表示實(shí)際用的索引,如果這一項(xiàng)為 NULL,說(shuō)明沒(méi)有使用索引;
  • key_len 表示索引的長(zhǎng)度;
  • rows 表示掃描的數(shù)據(jù)行數(shù)。
  • type 表示數(shù)據(jù)掃描類型,我們需要重點(diǎn)看這個(gè)。type 字段就是描述了找到所需數(shù)據(jù)時(shí)使用的掃描方式是什么,常見(jiàn)掃描類型的執(zhí)行效率從低到高的順序?yàn)?/strong>:
    ) _8 Q# h; P  U& J  K9 k
  • All(全表掃描):在這些情況里,all 是最壞的情況,因?yàn)椴捎昧巳頀呙璧姆绞健?li>index(全索引掃描):index 和 all 差不多,只不過(guò) index 對(duì)索引表進(jìn)行全掃描,這樣做的好處是不再需要對(duì)數(shù)據(jù)進(jìn)行排序,但是開(kāi)銷依然很大。所以,要盡量避免全表掃描和全索引掃描。
  • range(索引范圍掃描):range 表示采用了索引范圍掃描,一般在 where 子句中使用 、in、between 等關(guān)鍵詞,只檢索給定范圍的行,屬于范圍查找。從這一級(jí)別開(kāi)始,索引的作用會(huì)越來(lái)越明顯,因此我們需要盡量讓 SQL 查詢可以使用到 range 這一級(jí)別及以上的 type 訪問(wèn)方式。
  • ref(非唯一索引掃描):ref 類型表示采用了非唯一索引,或者是唯一索引的非唯一性前綴,返回?cái)?shù)據(jù)返回可能是多條。因?yàn)殡m然使用了索引,但該索引列的值并不唯一,有重復(fù)。這樣即使使用索引快速查找到了第一條數(shù)據(jù),仍然不能停止,要進(jìn)行目標(biāo)值附近的小范圍掃描。但它的好處是它并不需要掃全表,因?yàn)樗饕怯行虻,即便有重?fù)值,也是在一個(gè)非常小的范圍內(nèi)掃描。
  • eq_ref(唯一索引掃描):eq_ref 類型是使用主鍵或唯一索引時(shí)產(chǎn)生的訪問(wèn)方式,通常使用在多表聯(lián)查中。比如,對(duì)兩張表進(jìn)行聯(lián)查,關(guān)聯(lián)條件是兩張表的 user_id 相等,且 user_id 是唯一索引,那么使用 EXPLAIN 進(jìn)行執(zhí)行計(jì)劃查看的時(shí)候,type 就會(huì)顯示 eq_ref。
  • const(結(jié)果只有一條的主鍵或唯一索引掃描):const 類型表示使用了主鍵或者唯一索引與常量值進(jìn)行比較,比如 select name from product where id=1。需要說(shuō)明的是 const 類型和 eq_ref 都使用了主鍵或唯一索引,不過(guò)這兩個(gè)類型有所區(qū)別,const 是與常量進(jìn)行比較,查詢效率會(huì)更快,而 eq_ref 通常用于多表聯(lián)查中。extra 顯示的結(jié)果,這里說(shuō)幾個(gè)重要的參考指標(biāo):  C9 I7 {- S2 t/ G) x& R. T
  • Using filesort :當(dāng)查詢語(yǔ)句中包含 group by 操作,而且無(wú)法利用索引完成排序操作的時(shí)候, 這時(shí)不得不選擇相應(yīng)的排序算法進(jìn)行,甚至可能會(huì)通過(guò)文件排序,效率是很低的,所以要避免這種問(wèn)題的出現(xiàn)。
  • Using temporary:使了用臨時(shí)表保存中間結(jié)果,MySQL 在對(duì)查詢結(jié)果排序時(shí)使用臨時(shí)表,常見(jiàn)于排序 order by 和分組查詢 group by。效率低,要避免這種問(wèn)題的出現(xiàn)。
  • Using index:所需數(shù)據(jù)只需在索引即可全部獲得,不須要再到表中取數(shù)據(jù),也就是使用了覆蓋索引,避免了回表操作,效率不錯(cuò)。索引失效的場(chǎng)景有哪些?會(huì)發(fā)生索引失效的情況:# G  m; b' i( e3 J3 @, y; @
  • 當(dāng)我們使用左或者左右模糊匹配的時(shí)候,也就是 like %xx 或者 like %xx%這兩種方式都會(huì)造成索引失效;
  • 當(dāng)我們?cè)诓樵儣l件中對(duì)索引列使用函數(shù),就會(huì)導(dǎo)致索引失效。
  • 當(dāng)我們?cè)诓樵儣l件中對(duì)索引列進(jìn)行表達(dá)式計(jì)算,也是無(wú)法走索引的。
  • MySQL 在遇到字符串和數(shù)字比較的時(shí)候,會(huì)自動(dòng)把字符串轉(zhuǎn)為數(shù)字,然后再進(jìn)行比較。如果字符串是索引列,而條件語(yǔ)句中的輸入?yún)?shù)是數(shù)字的話,那么索引列會(huì)發(fā)生隱式類型轉(zhuǎn)換,由于隱式類型轉(zhuǎn)換是通過(guò) CAST 函數(shù)實(shí)現(xiàn)的,等同于對(duì)索引列使用了函數(shù),所以就會(huì)導(dǎo)致索引失效。
  • 聯(lián)合索引要能正確使用需要遵循最左匹配原則,也就是按照最左優(yōu)先的方式進(jìn)行索引的匹配,否則就會(huì)導(dǎo)致索引失效。
  • 在 WHERE 子句中,如果在 OR 前的條件列是索引列,而在 OR 后的條件列不是索引列,那么索引會(huì)失效。Redis Redis 為什么這么快?官方使用基準(zhǔn)測(cè)試的結(jié)果是,單線程的 Redis 吞吐量可以達(dá)到 10W/每秒,如下圖所示:& t0 [; B/ J$ K0 N/ G; w% ~& T: y

    8 c. j' r- o& I5 j- e0 T8 c之所以 Redis 采用單線程(網(wǎng)絡(luò) I/O 和執(zhí)行命令)那么快,有如下幾個(gè)原因:$ {' ^7 D/ a2 u* j8 u
  • Redis 的大部分操作都在內(nèi)存中完成,并且采用了高效的數(shù)據(jù)結(jié)構(gòu),因此 Redis 瓶頸可能是機(jī)器的內(nèi)存或者網(wǎng)絡(luò)帶寬,而并非 CPU,既然 CPU 不是瓶頸,那么自然就采用單線程的解決方案了;
  • Redis 采用單線程模型可以避免了多線程之間的競(jìng)爭(zhēng),省去了多線程切換帶來(lái)的時(shí)間和性能上的開(kāi)銷,而且也不會(huì)導(dǎo)致死鎖問(wèn)題。
  • Redis 采用了 I/O 多路復(fù)用機(jī)制處理大量的客戶端 Socket 請(qǐng)求,IO 多路復(fù)用機(jī)制是指一個(gè)線程處理多個(gè) IO 流,就是我們經(jīng)常聽(tīng)到的 select/epoll 機(jī)制。簡(jiǎn)單來(lái)說(shuō),在 Redis 只運(yùn)行單線程的情況下,該機(jī)制允許內(nèi)核中,同時(shí)存在多個(gè)監(jiān)聽(tīng) Socket 和已連接 Socket。內(nèi)核會(huì)一直監(jiān)聽(tīng)這些 Socket 上的連接請(qǐng)求或數(shù)據(jù)請(qǐng)求。一旦有請(qǐng)求到達(dá),就會(huì)交給 Redis 線程處理,這就實(shí)現(xiàn)了一個(gè) Redis 線程處理多個(gè) IO 流的效果。Redis 6.0 之后為什么引入了多線程?Redis 單線程指的是「接收客戶端請(qǐng)求->解析請(qǐng)求 ->進(jìn)行數(shù)據(jù)讀寫(xiě)等操作->發(fā)送數(shù)據(jù)給客戶端」這個(gè)過(guò)程是由一個(gè)線程(主線程)來(lái)完成的,這也是我們常說(shuō) Redis 是單線程的原因。
    9 G; v8 |3 o6 r2 X6 Q, c9 c& P但是,Redis 程序并不是單線程的,Redis 在啟動(dòng)的時(shí)候,是會(huì)啟動(dòng)后臺(tái)線程(BIO)的:+ v, D3 a) C1 T# `: y
  • Redis 在 2.6 版本,會(huì)啟動(dòng) 2 個(gè)后臺(tái)線程,分別處理關(guān)閉文件、AOF 刷盤(pán)這兩個(gè)任務(wù);
  • Redis 在 4.0 版本之后,新增了一個(gè)新的后臺(tái)線程,用來(lái)異步釋放 Redis 內(nèi)存,也就是 lazyfree 線程。例如執(zhí)行 unlink key / flushdb async / flushall async 等命令,會(huì)把這些刪除操作交給后臺(tái)線程來(lái)執(zhí)行,好處是不會(huì)導(dǎo)致 Redis 主線程卡頓。因此,當(dāng)我們要?jiǎng)h除一個(gè)大 key 的時(shí)候,不要使用 del 命令刪除,因?yàn)?del 是在主線程處理的,這樣會(huì)導(dǎo)致 Redis 主線程卡頓,因此我們應(yīng)該使用 unlink 命令來(lái)異步刪除大key。之所以 Redis 為「關(guān)閉文件、AOF 刷盤(pán)、釋放內(nèi)存」這些任務(wù)創(chuàng)建單獨(dú)的線程來(lái)處理,是因?yàn)檫@些任務(wù)的操作都是很耗時(shí)的,如果把這些任務(wù)都放在主線程來(lái)處理,那么 Redis 主線程就很容易發(fā)生阻塞,這樣就無(wú)法處理后續(xù)的請(qǐng)求了。
    & F- |4 T" x$ z$ m9 r0 M后臺(tái)線程相當(dāng)于一個(gè)消費(fèi)者,生產(chǎn)者把耗時(shí)任務(wù)丟到任務(wù)隊(duì)列中,消費(fèi)者(BIO)不停輪詢這個(gè)隊(duì)列,拿出任務(wù)就去執(zhí)行對(duì)應(yīng)的方法即可。
    # {, m. m% `- j* @4 O; ~
    , [# L4 V- t/ X( W& m4 o- b) |( Y. l: M- n: _雖然 Redis 的主要工作(網(wǎng)絡(luò) I/O 和執(zhí)行命令)一直是單線程模型,但是在 Redis 6.0 版本之后,也采用了多個(gè) I/O 線程來(lái)處理網(wǎng)絡(luò)請(qǐng)求這是因?yàn)殡S著網(wǎng)絡(luò)硬件的性能提升,Redis 的性能瓶頸有時(shí)會(huì)出現(xiàn)在網(wǎng)絡(luò) I/O 的處理上。7 B; ~/ m& q' d$ i# c% ?
    所以為了提高網(wǎng)絡(luò) I/O 的并行度,Redis 6.0 對(duì)于網(wǎng)絡(luò) I/O 采用多線程來(lái)處理。但是對(duì)于命令的執(zhí)行,Redis 仍然使用單線程來(lái)處理,所以大家不要誤解Redis 有多線程同時(shí)執(zhí)行命令。
    : _8 {* i' Y% S2 G6 cRedis 官方表示,Redis 6.0 版本引入的多線程 I/O 特性對(duì)性能提升至少是一倍以上。
    6 }: n/ m2 G& f- K, oRedis 6.0 版本支持的 I/O 多線程特性,默認(rèn)情況下 I/O 多線程只針對(duì)發(fā)送響應(yīng)數(shù)據(jù)(write client socket),并不會(huì)以多線程的方式處理讀請(qǐng)求(read client socket)。要想開(kāi)啟多線程處理客戶端讀請(qǐng)求,就需要把 Redis.conf 配置文件中的 io-threads-do-reads 配置項(xiàng)設(shè)為 yes。3 `) Y  p6 a, D7 ]6 l4 m  c5 G
    //讀請(qǐng)求也使用io多線程) A. i' n. T9 B" ?. ~
    io-threads-do-reads yes* Q* A& F) g0 T9 {
    同時(shí), Redis.conf 配置文件中提供了 IO 多線程個(gè)數(shù)的配置項(xiàng)。
    6 K* p% l8 F& J: x; z; P# ?  ?8 a// io-threads N,表示啟用 N-1 個(gè) I/O 多線程(主線程也算一個(gè) I/O 線程)
    3 {  r) Q8 O3 h# Q6 K  [# C9 _io-threads 4  {$ r# u2 d. @, L3 a
    關(guān)于線程數(shù)的設(shè)置,官方的建議是如果為 4 核的 CPU,建議線程數(shù)設(shè)置為 2 或 3,如果為 8 核 CPU 建議線程數(shù)設(shè)置為 6,線程數(shù)一定要小于機(jī)器核數(shù),線程數(shù)并不是越大越好。: J% Z# Q4 S4 t8 [
    因此, Redis 6.0 版本之后,Redis 在啟動(dòng)的時(shí)候,默認(rèn)情況下會(huì)額外創(chuàng)建 6 個(gè)線程(這里的線程數(shù)不包括主線程):6 c% v8 o! K  `
  • Redis-server :Redis的主線程,主要負(fù)責(zé)執(zhí)行命令;
  • bio_close_file、bio_aof_fsync、bio_lazy_free:三個(gè)后臺(tái)線程,分別異步處理關(guān)閉文件任務(wù)、AOF刷盤(pán)任務(wù)、釋放內(nèi)存任務(wù);
  • io_thd_1、io_thd_2、io_thd_3:三個(gè) I/O 線程,io-threads 默認(rèn)是 4 ,所以會(huì)啟動(dòng) 3(4-1)個(gè) I/O 多線程,用來(lái)分擔(dān) Redis 網(wǎng)絡(luò) I/O 的壓力。Redis 分布式鎖怎么解決超賣問(wèn)題的?同一個(gè)鎖key,同一時(shí)間只能有一個(gè)客戶端拿到鎖,其他客戶端會(huì)陷入無(wú)限的等待來(lái)嘗試獲取那個(gè)鎖,只有獲取到鎖的客戶端才能執(zhí)行下面的業(yè)務(wù)邏輯。# o; S4 Y) u! m; A& w
    比如說(shuō),用戶要一次性買 10 臺(tái)手機(jī),那么避免超賣的流程如下:
    . l5 y* v: z* D/ [$ t6 W
  • 只有一個(gè)訂單系統(tǒng)實(shí)例可以成功加分布式鎖,然后只有他一個(gè)實(shí)例可以查庫(kù)存、判斷庫(kù)存是否充足、下單扣減庫(kù)存,接著釋放鎖。
  • 釋放鎖之后,另外一個(gè)訂單系統(tǒng)實(shí)例才能加鎖,接著查庫(kù)存,一下發(fā)現(xiàn)庫(kù)存只有 2 個(gè)了,庫(kù)存不足,無(wú)法購(gòu)買,下單失敗,不會(huì)將庫(kù)存扣減為-8的,就避免超賣的問(wèn)題。這種方案的缺點(diǎn)是同一個(gè)商品在多用戶同時(shí)下單的情況下,會(huì)基于分布式鎖串行化處理,導(dǎo)致沒(méi)法同時(shí)處理同一個(gè)商品的大量下單的請(qǐng)求。
  • 發(fā)表回復(fù)

    本版積分規(guī)則

    關(guān)閉

    站長(zhǎng)推薦上一條 /1 下一條


    聯(lián)系客服 關(guān)注微信 下載APP 返回頂部 返回列表