社招后端21連問(wèn)(三年工作經(jīng)驗(yàn)一面)
前言
有位朋友工作三年,去面試,給大家整理一下面試題,并附上答案。
Mysql索引在什么情況下會(huì)失效 MySql的存儲(chǔ)引擎InnoDB與MyISAM的區(qū)別 Mysql在項(xiàng)目中的優(yōu)化場(chǎng)景,慢查詢解決等 Mysql有什么索引,索引模型是什么 B-樹(shù)與B+樹(shù)的區(qū)別?為什么不用紅黑樹(shù) Mysql主從同步怎么做 樂(lè)觀鎖與悲觀鎖的區(qū)別? 聊聊binlog日志 redis 持久化有哪幾種方式,怎么選? redis 主從同步是怎樣的過(guò)程? redis 的 zset 怎么實(shí)現(xiàn)的? Redis 過(guò)期策略和內(nèi)存淘汰策略 Hashmap實(shí)現(xiàn)原理 select 和 epoll的區(qū)別 http與https的區(qū)別,https的原理,如何加密的? Raft算法原理 消息中間件如何做到高可用 消息隊(duì)列怎么保證不丟消息的 聊聊Redis的哨兵機(jī)制 算法題:無(wú)重復(fù)字符的最長(zhǎng)子串
1. Mysql索引在什么情況下會(huì)失效
查詢條件包含or,可能導(dǎo)致索引失效 如何字段類型是字符串,where時(shí)一定用引號(hào)括起來(lái),否則索引失效 like通配符可能導(dǎo)致索引失效。 聯(lián)合索引,查詢時(shí)的條件列不是聯(lián)合索引中的第一個(gè)列,索引失效。 在索引列上使用mysql的內(nèi)置函數(shù),索引失效。 對(duì)索引列運(yùn)算(如,+、-、*、/),索引失效。 索引字段上使用(!= 或者 < >,not in)時(shí),可能會(huì)導(dǎo)致索引失效。 索引字段上使用is null, is not null,可能導(dǎo)致索引失效。 左連接查詢或者右連接查詢查詢關(guān)聯(lián)的字段編碼格式不一樣,可能導(dǎo)致索引失效。 mysql估計(jì)使用全表掃描要比使用索引快,則不使用索引。
2. MySql的存儲(chǔ)引擎InnoDB與MyISAM的區(qū)別
InnoDB支持事務(wù),MyISAM不支持事務(wù) InnoDB支持外鍵,MyISAM不支持外鍵 InnoDB 支持 MVCC(多版本并發(fā)控制),MyISAM 不支持 select count(*) from table時(shí),MyISAM更快,因?yàn)樗幸粋€(gè)變量保存了整個(gè)表的總行數(shù),可以直接讀取,InnoDB就需要全表掃描。 Innodb不支持全文索引,而MyISAM支持全文索引(5.7以后的InnoDB也支持全文索引) InnoDB支持表、行級(jí)鎖,而MyISAM支持表級(jí)鎖。 InnoDB表必須有主鍵,而MyISAM可以沒(méi)有主鍵 Innodb表需要更多的內(nèi)存和存儲(chǔ),而MyISAM可被壓縮,存儲(chǔ)空間較小。 Innodb按主鍵大小有序插入,MyISAM記錄插入順序是,按記錄插入順序保存。 InnoDB 存儲(chǔ)引擎提供了具有提交、回滾、崩潰恢復(fù)能力的事務(wù)安全,與 MyISAM 比 InnoDB 寫的效率差一些,并且會(huì)占用更多的磁盤空間以保留數(shù)據(jù)和索引
3. mysql在項(xiàng)目中的優(yōu)化場(chǎng)景,慢查詢解決等
我們面對(duì)慢查詢,首先想到的就是加索引。你可以給面試官描述一下,一個(gè)加了索引的SQL,是怎么執(zhí)行查找的,可以看下我之前這篇文章哈:
阿里一面,給了幾條SQL,問(wèn)需要執(zhí)行幾次樹(shù)搜索操作?
還有就是order by,group by原理,深分頁(yè)等等,都跟慢查詢息息相關(guān),大家可以看下我以前的文章哈,都比較經(jīng)典:
看一遍就理解:order by詳解 看一遍就理解:group by詳解 實(shí)戰(zhàn)!聊聊如何解決MySQL深分頁(yè)問(wèn)題 后端程序員必備:書寫高質(zhì)量SQL的30條建議
最后就是慢查詢的排查解決手段:
打開(kāi)慢查詢?nèi)罩?code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">slow_query_log,確認(rèn)SQL語(yǔ)句是否占用過(guò)多資源,用explain查詢執(zhí)行計(jì)劃、對(duì)group by、order by、join等語(yǔ)句優(yōu)化,如果數(shù)據(jù)量實(shí)在太大,是否考慮分庫(kù)分表等等。
4. Mysql有什么索引,索引模型是什么

數(shù)據(jù)結(jié)構(gòu)維度來(lái)講的話,一般使用都是B+樹(shù)索引,大家想詳細(xì)理解的話,可以看我之前這篇文章哈:MySQL索引底層:B+樹(shù)詳解
5. B-樹(shù)與B+樹(shù)的區(qū)別?為什么不用紅黑樹(shù)
B-樹(shù)與B+樹(shù)的區(qū)別:
B-樹(shù)內(nèi)部節(jié)點(diǎn)是保存數(shù)據(jù)的;而B+樹(shù)內(nèi)部節(jié)點(diǎn)是不保存數(shù)據(jù)的,只作索引作用,它的葉子節(jié)點(diǎn)才保存數(shù)據(jù)。 B+樹(shù)相鄰的葉子節(jié)點(diǎn)之間是通過(guò)鏈表指針連起來(lái)的,B-樹(shù)卻不是。 查找過(guò)程中,B-樹(shù)在找到具體的數(shù)值以后就結(jié)束,而B+樹(shù)則需要通過(guò)索引找到葉子結(jié)點(diǎn)中的數(shù)據(jù)才結(jié)束 B-樹(shù)中任何一個(gè)關(guān)鍵字出現(xiàn)且只出現(xiàn)在一個(gè)結(jié)點(diǎn)中,而B+樹(shù)可以出現(xiàn)多次。
為什么索引結(jié)構(gòu)默認(rèn)使用B+樹(shù),而不是B-Tree,Hash哈希,二叉樹(shù),紅黑樹(shù)?
Hash哈希,只適合等值查詢,不適合范圍查詢。 一般二叉樹(shù),可能會(huì)特殊化為一個(gè)鏈表,相當(dāng)于全表掃描。 紅黑樹(shù),是一種特化的平衡二叉樹(shù),MySQL 數(shù)據(jù)量很大的時(shí)候,索引的體積也會(huì)很大,內(nèi)存放不下的而從磁盤讀取,樹(shù)的層次太高的話,讀取磁盤的次數(shù)就多了。 B-Tree,葉子節(jié)點(diǎn)和非葉子節(jié)點(diǎn)都保存數(shù)據(jù),相同的數(shù)據(jù)量,B+樹(shù)更矮壯,也是就說(shuō),相同的數(shù)據(jù)量,B+樹(shù)數(shù)據(jù)結(jié)構(gòu),查詢磁盤的次數(shù)會(huì)更少。
6. Mysql主從同步怎么做
大家要熟悉MySQL主從復(fù)制原理哈:
詳細(xì)的主從復(fù)制過(guò)程如圖:

上圖主從復(fù)制過(guò)程分了五個(gè)步驟進(jìn)行:
主庫(kù)的更新SQL(update、insert、delete)被寫到binlog 從庫(kù)發(fā)起連接,連接到主庫(kù)。 此時(shí)主庫(kù)創(chuàng)建一個(gè) binlog dump thread,把binlog的內(nèi)容發(fā)送到從庫(kù)。從庫(kù)啟動(dòng)之后,創(chuàng)建一個(gè) I/O線程,讀取主庫(kù)傳過(guò)來(lái)的binlog內(nèi)容并寫入到relay log從庫(kù)還會(huì)創(chuàng)建一個(gè)SQL線程,從 relay log里面讀取內(nèi)容,從ExecMasterLog_Pos位置開(kāi)始執(zhí)行讀取到的更新事件,將更新內(nèi)容寫入到slave的db
主從同步這塊呢,還涉及到如何保證主從一致的、數(shù)據(jù)庫(kù)主從延遲的原因與解決方案、數(shù)據(jù)庫(kù)的高可用方案。
大家可以看下我最近的一篇總結(jié)哈:面試必備:聊聊MySQL的主從
7. 樂(lè)觀鎖與悲觀鎖的區(qū)別?
悲觀鎖:
悲觀鎖她專一且缺乏安全感了,她的心只屬于當(dāng)前事務(wù),每時(shí)每刻都擔(dān)心著它心愛(ài)的數(shù)據(jù)可能被別的事務(wù)修改,所以一個(gè)事務(wù)擁有(獲得)悲觀鎖后,其他任何事務(wù)都不能對(duì)數(shù)據(jù)進(jìn)行修改啦,只能等待鎖被釋放才可以執(zhí)行。

select ...for update就是悲觀鎖一種實(shí)現(xiàn)。
樂(lè)觀鎖:
樂(lè)觀鎖的“樂(lè)觀情緒”體現(xiàn)在,它認(rèn)為數(shù)據(jù)的變動(dòng)不會(huì)太頻繁。因此,它允許多個(gè)事務(wù)同時(shí)對(duì)數(shù)據(jù)進(jìn)行變動(dòng)。實(shí)現(xiàn)方式:樂(lè)觀鎖一般會(huì)使用版本號(hào)機(jī)制或CAS算法實(shí)現(xiàn)。

之前用樂(lè)觀鎖解決過(guò)實(shí)戰(zhàn)的并發(fā)問(wèn)題,大家有興趣可以加我微信,一起聊聊哈。
8. 聊聊binlog日志
binlog是歸檔日志,屬于MySQL Server層的日志??梢詫?shí)現(xiàn)主從復(fù)制和數(shù)據(jù)恢復(fù)兩個(gè)作用。當(dāng)需要恢復(fù)數(shù)據(jù)時(shí),可以取出某個(gè)時(shí)間范圍內(nèi)的binlog進(jìn)行重放恢復(fù)即可。
binlog 日志有三種格式,分別是statement,row和mixed。
如果是statement格式,binlog記錄的是SQL的原文,他可能會(huì)導(dǎo)致主庫(kù)不一致(主庫(kù)和從庫(kù)選的索引不一樣時(shí))。我們來(lái)分析一下。假設(shè)主庫(kù)執(zhí)行刪除這個(gè)SQL(其中a和create_time都有索引)如下:
delete from t where a > '666' and create_time<'2022-03-01' limit 1;
我們知道,數(shù)據(jù)選擇了a索引和選擇create_time索引,最后limit 1出來(lái)的數(shù)據(jù)一般是不一樣的。所以就會(huì)存在這種情況:在binlog = statement格式時(shí),主庫(kù)在執(zhí)行這條SQL時(shí),使用的是索引a,而從庫(kù)在執(zhí)行這條SQL時(shí),使用了索引create_time。最后主從數(shù)據(jù)不一致了。
如何解決這個(gè)問(wèn)題呢?
可以把binlog格式修改為row。row格式的binlog日志,記錄的不是SQL原文,而是兩個(gè)event:Table_map 和 Delete_rows。Table_map event說(shuō)明要操作的表,Delete_rows event用于定義要?jiǎng)h除的行為,記錄刪除的具體行數(shù)。row格式的binlog記錄的就是要?jiǎng)h除的主鍵ID信息,因此不會(huì)出現(xiàn)主從不一致的問(wèn)題。
但是如果SQL刪除10萬(wàn)行數(shù)據(jù),使用row格式就會(huì)很占空間的,10萬(wàn)條數(shù)據(jù)都在binlog里面,寫binlog的時(shí)候也很耗IO。但是statement格式的binlog可能會(huì)導(dǎo)致數(shù)據(jù)不一致,因此設(shè)計(jì)MySQL的大叔想了一個(gè)折中的方案,mixed格式的binlog。所謂的mixed格式其實(shí)就是row和statement格式混合使用,當(dāng)MySQL判斷可能數(shù)據(jù)不一致時(shí),就用row格式,否則使用就用statement格式。
9. Redis 持久化有哪幾種方式,怎么選?
既然它是基于內(nèi)存的,如果Redis服務(wù)器掛了,數(shù)據(jù)就會(huì)丟失。為了避免數(shù)據(jù)丟失了,Redis提供了兩種持久化方式,RDB和AOF。
9.1 AOF 持久化
AOF(append only file) 持久化,采用日志的形式來(lái)記錄每個(gè)寫操作,追加到AOF文件的末尾。Redis默認(rèn)情況是不開(kāi)啟AOF的。重啟時(shí)再重新執(zhí)行AOF文件中的命令來(lái)恢復(fù)數(shù)據(jù)。它主要解決數(shù)據(jù)持久化的實(shí)時(shí)性問(wèn)題。
AOF是執(zhí)行完命令后才記錄日志的。為什么不先記錄日志再執(zhí)行命令呢?這是因?yàn)镽edis在向AOF記錄日志時(shí),不會(huì)先對(duì)這些命令進(jìn)行語(yǔ)法檢查,如果先記錄日志再執(zhí)行命令,日志中可能記錄了錯(cuò)誤的命令,Redis使用日志回復(fù)數(shù)據(jù)時(shí),可能會(huì)出錯(cuò)。
正是因?yàn)閳?zhí)行完命令后才記錄日志,所以不會(huì)阻塞當(dāng)前的寫操作。但是會(huì)存在兩個(gè)風(fēng)險(xiǎn):
更執(zhí)行完命令還沒(méi)記錄日志時(shí),宕機(jī)了會(huì)導(dǎo)致數(shù)據(jù)丟失 AOF不會(huì)阻塞當(dāng)前命令,但是可能會(huì)阻塞下一個(gè)操作。
這兩個(gè)風(fēng)險(xiǎn)最好的解決方案是折中妙用AOF機(jī)制的三種寫回策略 appendfsync:
always,同步寫回,每個(gè)子命令執(zhí)行完,都立即將日志寫回磁盤。 everysec,每個(gè)命令執(zhí)行完,只是先把日志寫到AOF內(nèi)存緩沖區(qū),每隔一秒同步到磁盤。 no:只是先把日志寫到AOF內(nèi)存緩沖區(qū),有操作系統(tǒng)去決定何時(shí)寫入磁盤。
always同步寫回,可以基本保證數(shù)據(jù)不丟失,no策略則性能高但是數(shù)據(jù)可能會(huì)丟失,一般可以考慮折中選擇everysec。
如果接受的命令越來(lái)越多,AOF文件也會(huì)越來(lái)越大,文件過(guò)大還是會(huì)帶來(lái)性能問(wèn)題。日志文件過(guò)大怎么辦呢?AOF重寫機(jī)制!就是隨著時(shí)間推移,AOF文件會(huì)有一些冗余的命令如:無(wú)效命令、過(guò)期數(shù)據(jù)的命令等等,AOF重寫機(jī)制就是把它們合并為一個(gè)命令(類似批處理命令),從而達(dá)到精簡(jiǎn)壓縮空間的目的。
AOF重寫會(huì)阻塞嘛?AOF日志是由主線程會(huì)寫的,而重寫則不一樣,重寫過(guò)程是由后臺(tái)子進(jìn)程bgrewriteaof完成。
AOF的優(yōu)點(diǎn):數(shù)據(jù)的一致性和完整性更高,秒級(jí)數(shù)據(jù)丟失。 缺點(diǎn):相同的數(shù)據(jù)集,AOF文件體積大于RDB文件。數(shù)據(jù)恢復(fù)也比較慢。
9.2 RDB
因?yàn)锳OF持久化方式,如果操作日志非常多的話,Redis恢復(fù)就很慢。有沒(méi)有在宕機(jī)快速恢復(fù)的方法呢,有的,RDB!
RDB,就是把內(nèi)存數(shù)據(jù)以快照的形式保存到磁盤上。和AOF相比,它記錄的是某一時(shí)刻的數(shù)據(jù),,并不是操作。
什么是快照?可以這樣理解,給當(dāng)前時(shí)刻的數(shù)據(jù),拍一張照片,然后保存下來(lái)。
RDB持久化,是指在指定的時(shí)間間隔內(nèi),執(zhí)行指定次數(shù)的寫操作,將內(nèi)存中的數(shù)據(jù)集快照寫入磁盤中,它是Redis默認(rèn)的持久化方式。執(zhí)行完操作后,在指定目錄下會(huì)生成一個(gè)dump.rdb文件,Redis 重啟的時(shí)候,通過(guò)加載dump.rdb文件來(lái)恢復(fù)數(shù)據(jù)。RDB觸發(fā)機(jī)制主要有以下幾種:

RDB通過(guò)bgsave命令的執(zhí)行全量快照,可以避免阻塞主線程。basave命令會(huì)fork一個(gè)子進(jìn)程,然后該子進(jìn)程會(huì)負(fù)責(zé)創(chuàng)建RDB文件,而服務(wù)器進(jìn)程會(huì)繼續(xù)處理命令請(qǐng)求
快照時(shí),數(shù)據(jù)能修改嘛? Redis接住操作系統(tǒng)的寫時(shí)復(fù)制技術(shù)(copy-on-write,COW),在執(zhí)行快照的同時(shí),正常處理寫操作。
雖然bgsave執(zhí)行不會(huì)阻塞主線程,但是頻繁執(zhí)行全量快照也會(huì)帶來(lái)性能開(kāi)銷。比如bgsave子進(jìn)程需要通過(guò)fork操作從主線程創(chuàng)建出來(lái),創(chuàng)建后不會(huì)阻塞主線程,但是創(chuàng)建過(guò)程是會(huì)阻塞主線程的。可以做增量快照。
RDB的優(yōu)點(diǎn):與AOF相比,恢復(fù)大數(shù)據(jù)集的時(shí)候會(huì)更快,它適合大規(guī)模的數(shù)據(jù)恢復(fù)場(chǎng)景,如備份,全量復(fù)制等 缺點(diǎn):沒(méi)辦法做到實(shí)時(shí)持久化/秒級(jí)持久化。
Redis4.0開(kāi)始支持RDB和AOF的混合持久化,就是內(nèi)存快照以一定頻率執(zhí)行,兩次快照之間,再使用AOF記錄這期間的所有命令操作。
9.3 如何選擇RDB和AOF
如果數(shù)據(jù)不能丟失,RDB和AOF混用 如果只作為緩存使用,可以承受幾分鐘的數(shù)據(jù)丟失的話,可以只使用RDB。 如果只使用AOF,優(yōu)先使用everysec的寫回策略。
10. Redis 主從同步是怎樣的過(guò)程?

Redis主從同步包括三個(gè)階段。
第一階段:主從庫(kù)間建立連接、協(xié)商同步。
從庫(kù)向主庫(kù)發(fā)送 psync命令,告訴它要進(jìn)行數(shù)據(jù)同步。主庫(kù)收到 psync命令后,響應(yīng)FULLRESYNC命令(它表示第一次復(fù)制采用的是全量復(fù)制),并帶上主庫(kù)runID和主庫(kù)目前的復(fù)制進(jìn)度offset。
第二階段:主庫(kù)把數(shù)據(jù)同步到從庫(kù),從庫(kù)收到數(shù)據(jù)后,完成本地加載。
主庫(kù)執(zhí)行 bgsave命令,生成RDB文件,接著將文件發(fā)給從庫(kù)。從庫(kù)接收到RDB文件后,會(huì)先清空當(dāng)前數(shù)據(jù)庫(kù),然后加載 RDB 文件。主庫(kù)把數(shù)據(jù)同步到從庫(kù)的過(guò)程中,新來(lái)的寫操作,會(huì)記錄到 replication buffer。
第三階段,主庫(kù)把新寫的命令,發(fā)送到從庫(kù)。
主庫(kù)完成RDB發(fā)送后,會(huì)把 replication buffer中的修改操作發(fā)給從庫(kù),從庫(kù)再重新執(zhí)行這些操作。這樣主從庫(kù)就實(shí)現(xiàn)同步啦。
11. 聊聊Redis的zset,它是怎么實(shí)現(xiàn)的?
zset是Redis常用數(shù)據(jù)類型之一,它的成員是有序排列的,一般用于排行榜類型的業(yè)務(wù)場(chǎng)景,比如 QQ 音樂(lè)排行榜、禮物排行榜等等。
它的簡(jiǎn)單格式舉例: zadd key score member [score member ...],zrank key member它的底層內(nèi)部編碼:ziplist(壓縮列表)、skiplist(跳躍表)
當(dāng) zset 滿足以下條件時(shí)使用壓縮列表:
當(dāng)成員的數(shù)量小于128 個(gè); 每個(gè) member (成員)的字符串長(zhǎng)度都小于 64 個(gè)字節(jié)。
壓縮列表做簡(jiǎn)單介紹,它由以下五部分組成

zlbytes 是一個(gè)無(wú)符號(hào)整數(shù),表示當(dāng)前ziplist占用的總字節(jié)數(shù); zltail 指的是壓縮列表尾部元素相對(duì)于壓縮列表起始元素的偏移量。 zllen 指 ziplist 中 entry 的數(shù)量。當(dāng) zllen 比2^16 - 2大時(shí),需要完全遍歷 entry 列表來(lái)獲取 entry 的總數(shù)目。 entry 用來(lái)存放具體的數(shù)據(jù)項(xiàng)(score和member),長(zhǎng)度不定,可以是字節(jié)數(shù)組或整數(shù),entry 會(huì)根據(jù)成員的數(shù)量自動(dòng)擴(kuò)容。-zlend 是一個(gè)單字節(jié)的特殊值,等于 255,起到標(biāo)識(shí) ziplist 內(nèi)存結(jié)束點(diǎn)的作用。
skiplist(跳躍表)在鏈表的基礎(chǔ)上,增加了多級(jí)索引,通過(guò)索引位置的幾個(gè)跳轉(zhuǎn),實(shí)現(xiàn)數(shù)據(jù)的快速定位,其插入、刪除、查找的時(shí)間復(fù)雜度均為 O(logN)。

12. Redis 過(guò)期策略和內(nèi)存淘汰策略

12.1 Redis的過(guò)期策略
我們?cè)趕et key的時(shí)候,可以給它設(shè)置一個(gè)過(guò)期時(shí)間,比如expire key 60。指定這key60s后過(guò)期,60s后,redis是如何處理的嘛?我們先來(lái)介紹幾種過(guò)期策略哈:
一般有定時(shí)過(guò)期、惰性過(guò)期、定期過(guò)期三種。
定時(shí)過(guò)期
每個(gè)設(shè)置過(guò)期時(shí)間的key都需要?jiǎng)?chuàng)建一個(gè)定時(shí)器,到過(guò)期時(shí)間就會(huì)立即對(duì)key進(jìn)行清除。該策略可以立即清除過(guò)期的數(shù)據(jù),對(duì)內(nèi)存很友好;但是會(huì)占用大量的CPU資源去處理過(guò)期的數(shù)據(jù),從而影響緩存的響應(yīng)時(shí)間和吞吐量。
惰性過(guò)期
只有當(dāng)訪問(wèn)一個(gè)key時(shí),才會(huì)判斷該key是否已過(guò)期,過(guò)期則清除。該策略可以最大化地節(jié)省CPU資源,卻對(duì)內(nèi)存非常不友好。極端情況可能出現(xiàn)大量的過(guò)期key沒(méi)有再次被訪問(wèn),從而不會(huì)被清除,占用大量?jī)?nèi)存。
定期過(guò)期
每隔一定的時(shí)間,會(huì)掃描一定數(shù)量的數(shù)據(jù)庫(kù)的expires字典中一定數(shù)量的key,并清除其中已過(guò)期的key。該策略是前兩者的一個(gè)折中方案。通過(guò)調(diào)整定時(shí)掃描的時(shí)間間隔和每次掃描的限定耗時(shí),可以在不同情況下使得CPU和內(nèi)存資源達(dá)到最優(yōu)的平衡效果。
expires字典會(huì)保存所有設(shè)置了過(guò)期時(shí)間的key的過(guò)期時(shí)間數(shù)據(jù),其中,key是指向鍵空間中的某個(gè)鍵的指針,value是該鍵的毫秒精度的UNIX時(shí)間戳表示的過(guò)期時(shí)間。鍵空間是指該Redis集群中保存的所有鍵。
Redis中同時(shí)使用了惰性過(guò)期和定期過(guò)期兩種過(guò)期策略。
假設(shè)Redis當(dāng)前存放30萬(wàn)個(gè)key,并且都設(shè)置了過(guò)期時(shí)間,如果你每隔100ms就去檢查這全部的key,CPU負(fù)載會(huì)特別高,最后可能會(huì)掛掉。 因此,redis采取的是定期過(guò)期,每隔100ms就隨機(jī)抽取一定數(shù)量的key來(lái)檢查和刪除的。 但是呢,最后可能會(huì)有很多已經(jīng)過(guò)期的key沒(méi)被刪除。這時(shí)候,redis采用惰性刪除。在你獲取某個(gè)key的時(shí)候,redis會(huì)檢查一下,這個(gè)key如果設(shè)置了過(guò)期時(shí)間并且已經(jīng)過(guò)期了,此時(shí)就會(huì)刪除。
但是呀,如果定期刪除漏掉了很多過(guò)期的key,然后也沒(méi)走惰性刪除。就會(huì)有很多過(guò)期key積在內(nèi)存內(nèi)存,直接會(huì)導(dǎo)致內(nèi)存爆的?;蛘哂行r(shí)候,業(yè)務(wù)量大起來(lái)了,redis的key被大量使用,內(nèi)存直接不夠了,運(yùn)維小哥哥也忘記加大內(nèi)存了。難道redis直接這樣掛掉?不會(huì)的!Redis用8種內(nèi)存淘汰策略保護(hù)自己~
12.2 Redis 內(nèi)存淘汰策略
volatile-lru:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),從設(shè)置了過(guò)期時(shí)間的key中使用LRU(最近最少使用)算法進(jìn)行淘汰; allkeys-lru:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),從所有key中使用LRU(最近最少使用)算法進(jìn)行淘汰。 volatile-lfu:4.0版本新增,當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在過(guò)期的key中,使用LFU(最少訪問(wèn)算法)進(jìn)行刪除key。 allkeys-lfu:4.0版本新增,當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),從所有key中使用LFU算法進(jìn)行淘汰; volatile-random:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),從設(shè)置了過(guò)期時(shí)間的key中,隨機(jī)淘汰數(shù)據(jù);。 allkeys-random:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),從所有key中隨機(jī)淘汰數(shù)據(jù)。 volatile-ttl:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在設(shè)置了過(guò)期時(shí)間的key中,根據(jù)過(guò)期時(shí)間進(jìn)行淘汰,越早過(guò)期的優(yōu)先被淘汰; noeviction:默認(rèn)策略,當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),新寫入操作會(huì)報(bào)錯(cuò)。
13. Hashmap 是怎樣實(shí)現(xiàn)的?為什么要用紅黑樹(shù),而不用平衡二叉樹(shù)?為什么在1.8中鏈表大于8時(shí)會(huì)轉(zhuǎn)紅黑樹(shù)?HashMap是線性安全的嘛?如何保證安全?
13.1 Hashmap 是怎樣實(shí)現(xiàn)的?
JDK1.7 Hashmap的底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組+鏈表 JDK1.8 Hashmap的底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組+鏈表+紅黑樹(shù)
數(shù)據(jù)元素通過(guò)映射關(guān)系,即散列函數(shù),映射到桶數(shù)組對(duì)應(yīng)索引的位置,插入該位置時(shí),如果發(fā)生沖突,從沖突的位置拉一個(gè)鏈表,把沖突元素放到鏈表。如果鏈表長(zhǎng)度>8且數(shù)組大小>=64,鏈表轉(zhuǎn)為紅黑樹(shù) 如果紅黑樹(shù)節(jié)點(diǎn)個(gè)數(shù)<6 ,轉(zhuǎn)為鏈表。
13.2 為什么要用紅黑樹(shù),為什么不用二叉樹(shù)?為什么不用平衡二叉樹(shù)?
為什么不用二叉樹(shù)?
紅黑樹(shù)是一種平衡的二叉樹(shù),其插入、刪除、查找的最壞時(shí)間復(fù)雜度都為 O(logn),避免了二叉樹(shù)最壞情況下的O(n)時(shí)間復(fù)雜度。
為什么不用平衡二叉樹(shù)?
平衡二叉樹(shù)是比紅黑樹(shù)更嚴(yán)格的平衡樹(shù),為了保持保持平衡,需要旋轉(zhuǎn)的次數(shù)更多,也就是說(shuō)平衡二叉樹(shù)保持平衡的效率更低,所以平衡二叉樹(shù)插入和刪除的效率比紅黑樹(shù)要低。
13.3 為什么在1.8中鏈表大于8時(shí)會(huì)轉(zhuǎn)紅黑樹(shù)?
紅黑樹(shù)的平均查找長(zhǎng)度是log(n),如果長(zhǎng)度為8,平均查找長(zhǎng)度為log(8)=3,鏈表的平均查找長(zhǎng)度為n/2,當(dāng)長(zhǎng)度為8時(shí),平均查找長(zhǎng)度為8/2=4,這才有轉(zhuǎn)換成樹(shù)的必要;鏈表長(zhǎng)度如果是小于等于6,6/2=3,而log(6)=2.6,雖然速度也很快的,但是轉(zhuǎn)化為樹(shù)結(jié)構(gòu)和生成樹(shù)的時(shí)間并不會(huì)太短。
13.4 HashMap是線性安全的嘛?如何保證安全?
HashMap不是線程安全的,多線程下擴(kuò)容死循環(huán)??梢允褂肏ashTable、Collections.synchronizedMap、以及 ConcurrentHashMap 可以實(shí)現(xiàn)線程安全。
HashTable 是在每個(gè)方法加上 synchronized 關(guān)鍵字,粒度比較大; Collections.synchronizedMap 是使用 Collections 集合工具的內(nèi)部類,通過(guò)傳入 Map 封裝出一個(gè) SynchronizedMap 對(duì)象,內(nèi)部定義了一個(gè)對(duì)象鎖,方法內(nèi)通過(guò)對(duì)象鎖實(shí)現(xiàn); ConcurrentHashMap 在jdk1.7中使用分段鎖,在jdk1.8中使用CAS+synchronized。
14. select 和 epoll的區(qū)別
14.1 IO多路復(fù)用之select
應(yīng)用進(jìn)程通過(guò)調(diào)用select函數(shù),可以同時(shí)監(jiān)控多個(gè)fd,在select函數(shù)監(jiān)控的fd中,只要有任何一個(gè)數(shù)據(jù)狀態(tài)準(zhǔn)備就緒了,select函數(shù)就會(huì)返回可讀狀態(tài),這時(shí)應(yīng)用進(jìn)程再發(fā)起recvfrom請(qǐng)求去讀取數(shù)據(jù)。
非阻塞IO模型(NIO)中,需要N(N>=1)次輪詢系統(tǒng)調(diào)用,然而借助select的IO多路復(fù)用模型,只需要發(fā)起一次詢問(wèn)就夠了,大大優(yōu)化了性能。
但是呢,select有幾個(gè)缺點(diǎn):
監(jiān)聽(tīng)的IO最大連接數(shù)有限,在Linux系統(tǒng)上一般為1024。 select函數(shù)返回后,是通過(guò)遍歷fdset,找到就緒的描述符fd。(僅知道有I/O事件發(fā)生,卻不知是哪幾個(gè)流,所以遍歷所有流) 因?yàn)榇嬖谶B接數(shù)限制,所以后來(lái)又提出了poll。與select相比,poll解決了連接數(shù)限制問(wèn)題。但是呢,select和poll一樣,還是需要通過(guò)遍歷文件描述符來(lái)獲取已經(jīng)就緒的socket。如果同時(shí)連接的大量客戶端,在一時(shí)刻可能只有極少處于就緒狀態(tài),伴隨著監(jiān)視的描述符數(shù)量的增長(zhǎng),效率也會(huì)線性下降。
14.2 IO多路復(fù)用之epoll
為了解決select/poll存在的問(wèn)題,多路復(fù)用模型epoll誕生,它采用事件驅(qū)動(dòng)來(lái)實(shí)現(xiàn),流程圖如下:

epoll先通過(guò)epoll_ctl()來(lái)注冊(cè)一個(gè)fd(文件描述符),一旦基于某個(gè)fd就緒時(shí),內(nèi)核會(huì)采用回調(diào)機(jī)制,迅速激活這個(gè)fd,當(dāng)進(jìn)程調(diào)用epoll_wait()時(shí)便得到通知。這里去掉了遍歷文件描述符的坑爹操作,而是采用監(jiān)聽(tīng)事件回調(diào)的機(jī)制。這就是epoll的亮點(diǎn)。
一下select、poll、epoll的區(qū)別
| select | poll | epoll | |
|---|---|---|---|
| 底層數(shù)據(jù)結(jié)構(gòu) | 數(shù)組 | 鏈表 | 紅黑樹(shù)和雙鏈表 |
| 獲取就緒的fd | 遍歷 | 遍歷 | 事件回調(diào) |
| 事件復(fù)雜度 | O(n) | O(n) | O(1) |
| 最大連接數(shù) | 1024 | 無(wú)限制 | 無(wú)限制 |
| fd數(shù)據(jù)拷貝 | 每次調(diào)用select,需要將fd數(shù)據(jù)從用戶空間拷貝到內(nèi)核空間 | 每次調(diào)用poll,需要將fd數(shù)據(jù)從用戶空間拷貝到內(nèi)核空間 | 使用內(nèi)存映射(mmap),不需要從用戶空間頻繁拷貝fd數(shù)據(jù)到內(nèi)核空間 |
大家可以看我這篇文章哈:看一遍就理解:IO模型詳解
15. http與https的區(qū)別,https的原理,如何加密的?
http與https的區(qū)別
思路: 這道題實(shí)際上考察的知識(shí)點(diǎn)是HTTP與HTTPS的區(qū)別,這個(gè)知識(shí)點(diǎn)非常重要,可以從安全性、數(shù)據(jù)是否加密、默認(rèn)端口等這幾個(gè)方面去回答哈。其實(shí),當(dāng)你理解HTTPS的整個(gè)流程,就可以很好回答這個(gè)問(wèn)題啦。
HTTP,即超文本傳輸協(xié)議,是一個(gè)基于TCP/IP通信協(xié)議來(lái)傳遞明文數(shù)據(jù)的協(xié)議。HTTP會(huì)存在這幾個(gè)問(wèn)題:
請(qǐng)求信息是明文傳輸,容易被竊聽(tīng)截取。 沒(méi)有驗(yàn)證對(duì)方身份,存在被冒充的風(fēng)險(xiǎn) 數(shù)據(jù)的完整性未校驗(yàn),容易被中間人篡改
為了解決Http存在的問(wèn)題,Https出現(xiàn)啦。
HTTPS= HTTP+SSL/TLS,可以理解Https是身披SSL(Secure Socket Layer,安全套接層)的HTTP。
HTTP + HTTPS的區(qū)別

https的原理,如何加密的

客戶端發(fā)起Https請(qǐng)求,連接到服務(wù)器的443端口。 服務(wù)器必須要有一套數(shù)字證書(證書內(nèi)容有公鑰、證書頒發(fā)機(jī)構(gòu)、失效日期等)。 服務(wù)器將自己的數(shù)字證書發(fā)送給客戶端(公鑰在證書里面,私鑰由服務(wù)器持有)。 客戶端收到數(shù)字證書之后,會(huì)驗(yàn)證證書的合法性。如果證書驗(yàn)證通過(guò),就會(huì)生成一個(gè)隨機(jī)的對(duì)稱密鑰,用證書的公鑰加密。 客戶端將公鑰加密后的密鑰發(fā)送到服務(wù)器。 服務(wù)器接收到客戶端發(fā)來(lái)的密文密鑰之后,用自己之前保留的私鑰對(duì)其進(jìn)行非對(duì)稱解密,解密之后就得到客戶端的密鑰,然后用客戶端密鑰對(duì)返回?cái)?shù)據(jù)進(jìn)行對(duì)稱加密,醬紫傳輸?shù)臄?shù)據(jù)都是密文啦。 服務(wù)器將加密后的密文返回到客戶端。 客戶端收到后,用自己的密鑰對(duì)其進(jìn)行對(duì)稱解密,得到服務(wù)器返回的數(shù)據(jù)。
16. Raft算法原理
Raft 算法是分布式系統(tǒng)開(kāi)發(fā)首選的共識(shí)算法,它通過(guò)“一切以領(lǐng)導(dǎo)者為準(zhǔn)”的方式,實(shí)現(xiàn)一系列值的共識(shí)和各節(jié)點(diǎn)日志的一致。Raft 算法一共涉及三種角色(Follower、Candidate、Leader)和兩個(gè)過(guò)程(Leader選舉和日志復(fù)制)。
16.1 Raft 角色
跟隨者(Follower):,默默地接收和處理來(lái)自Leader的消息,當(dāng)?shù)却齃eader心跳信息超時(shí)的時(shí)候,就主動(dòng)站出來(lái),推薦自己當(dāng)候選人(Candidate)。
候選人(Candidate):向其他節(jié)點(diǎn)發(fā)送投票請(qǐng)求,通知其他節(jié)點(diǎn)來(lái)投票,如果贏得了大多數(shù)(N/2+1)選票,就晉升領(lǐng)導(dǎo)(Leader)。
領(lǐng)導(dǎo)者(Leader):負(fù)責(zé)處理客戶端請(qǐng)求,進(jìn)行日志復(fù)制等操作,每一輪選舉的目標(biāo)就是選出一個(gè)領(lǐng)導(dǎo)者;領(lǐng)導(dǎo)者會(huì)不斷地發(fā)送心跳信息,通知其他節(jié)點(diǎn)“我是領(lǐng)導(dǎo)者,我還活著,你們不要發(fā)起新的選舉,不用找個(gè)新領(lǐng)導(dǎo)者來(lái)替代我?!?/p>
16.2 領(lǐng)導(dǎo)選舉過(guò)程
1.在初始時(shí),集群中所有的節(jié)點(diǎn)都是Follower狀態(tài),都被設(shè)定一個(gè)隨機(jī)選舉超時(shí)時(shí)間(一般150ms-300ms):
2. 如果Follower在規(guī)定的超時(shí)時(shí)間,都沒(méi)有收到來(lái)自Leader的心跳,它就發(fā)起選舉:將自己的狀態(tài)切為 Candidate,增加自己的任期編號(hào),然后向集群中的其它Follower節(jié)點(diǎn)發(fā)送請(qǐng)求,詢問(wèn)其是否選舉自己成為Leader:

其他節(jié)點(diǎn)收到候選人A的請(qǐng)求投票消息后,如果在編號(hào)為1的這屆任期內(nèi)還沒(méi)有進(jìn)行過(guò)投票,那么它將把選票投給節(jié)點(diǎn)A,并增加自己的任期編號(hào):

當(dāng)收到來(lái)自集群中過(guò)半節(jié)點(diǎn)的接受投票后,A節(jié)點(diǎn)即成為本屆任期內(nèi) Leader,他將周期性地發(fā)送心跳消息,通知其他節(jié)點(diǎn)我是Leader,阻止Follower發(fā)起新的選舉:

16.2 日志復(fù)制
當(dāng)有了leader,系統(tǒng)可以對(duì)外工作期啦??蛻舳说囊磺姓?qǐng)求來(lái)發(fā)送到leader,leader來(lái)調(diào)度這些并發(fā)請(qǐng)求的順序,并且保證leader與followers狀態(tài)的一致性。Leader接收到來(lái)自客戶端寫請(qǐng)求后,處理寫請(qǐng)求的過(guò)程其實(shí)就是一個(gè)日志復(fù)制的過(guò)程。
日志項(xiàng)長(zhǎng)什么樣呢?如下圖:

請(qǐng)求完整過(guò)程:
當(dāng)系統(tǒng)leader收到一個(gè)來(lái)自客戶端的寫請(qǐng)求,就會(huì)添加一個(gè)log entry(日志項(xiàng))到本地日志。 Leader通過(guò)日志復(fù)制(AppendEntries)RPC 消息,將日志項(xiàng)并行復(fù)制到集群其它Follower節(jié)點(diǎn)。 如果Leader接收到大多數(shù)的“復(fù)制成功”響應(yīng)后,它將日志項(xiàng)應(yīng)用到自己的狀態(tài)機(jī),并返回成功給客戶端。相反沒(méi)有收到大多數(shù)的“復(fù)制成功”響應(yīng),那么就返回錯(cuò)誤給客戶端; 當(dāng)Follower接收到心跳信息,或者新的AppendEntries消息后,如果發(fā)現(xiàn)Leader已經(jīng)提交了某條日志項(xiàng),而自己還沒(méi)應(yīng)用,那么Follower就會(huì)將這條日志項(xiàng)應(yīng)用到本地的狀態(tài)機(jī)中。
Raft算法,Leader是通過(guò)強(qiáng)制Follower直接復(fù)制自己的日志項(xiàng),來(lái)處理不一致日志,從而最終實(shí)現(xiàn)了集群各節(jié)點(diǎn)日志的一致。
大家有興趣可以看這篇文章哈:分布式一致性:Raft算法原理[1](https://www.tpvlog.com/article/66)
17. 消息中間件如何做到高可用
消息中間件如何保證高可用呢?單機(jī)是沒(méi)有高可用可言的,高可用都是對(duì)集群來(lái)說(shuō)的,一起看下kafka的高可用吧。
Kafka 的基礎(chǔ)集群架構(gòu),由多個(gè)broker組成,每個(gè)broker都是一個(gè)節(jié)點(diǎn)。當(dāng)你創(chuàng)建一個(gè)topic時(shí),它可以劃分為多個(gè)partition,而每個(gè)partition放一部分?jǐn)?shù)據(jù),分別存在于不同的 broker 上。也就是說(shuō),一個(gè) topic 的數(shù)據(jù),是分散放在多個(gè)機(jī)器上的,每個(gè)機(jī)器就放一部分?jǐn)?shù)據(jù)。
有些伙伴可能有疑問(wèn),每個(gè)partition放一部分?jǐn)?shù)據(jù),如果對(duì)應(yīng)的broker掛了,那這部分?jǐn)?shù)據(jù)是不是就丟失了?那還談什么高可用呢?
Kafka 0.8 之后,提供了復(fù)制品副本機(jī)制來(lái)保證高可用,即每個(gè) partition 的數(shù)據(jù)都會(huì)同步到其它機(jī)器上,形成多個(gè)副本。然后所有的副本會(huì)選舉一個(gè) leader 出來(lái),讓leader去跟生產(chǎn)和消費(fèi)者打交道,其他副本都是follower。寫數(shù)據(jù)時(shí),leader 負(fù)責(zé)把數(shù)據(jù)同步給所有的follower,讀消息時(shí), 直接讀 leader 上的數(shù)據(jù)即可。如何保證高可用的?就是假設(shè)某個(gè) broker 宕機(jī),這個(gè)broker上的partition 在其他機(jī)器上都有副本的。如果掛的是leader的broker呢?其他follower會(huì)重新選一個(gè)leader出來(lái)。
18. 消息隊(duì)列怎么保證不丟消息的
一個(gè)消息從生產(chǎn)者產(chǎn)生,到被消費(fèi)者消費(fèi),主要經(jīng)過(guò)這3個(gè)過(guò)程:

因此如何保證MQ不丟失消息,可以從這三個(gè)階段闡述:
生產(chǎn)者保證不丟消息 存儲(chǔ)端不丟消息 消費(fèi)者不丟消息
18.1 生產(chǎn)者保證不丟消息
生產(chǎn)端如何保證不丟消息呢?確保生產(chǎn)的消息能到達(dá)存儲(chǔ)端。
如果是RocketMQ消息中間件,Producer生產(chǎn)者提供了三種發(fā)送消息的方式,分別是:
同步發(fā)送 異步發(fā)送 單向發(fā)送
生產(chǎn)者要想發(fā)消息時(shí)保證消息不丟失,可以:
采用同步方式發(fā)送,send消息方法返回成功狀態(tài),就表示消息正常到達(dá)了存儲(chǔ)端Broker。 如果send消息異常或者返回非成功狀態(tài),可以重試。 可以使用事務(wù)消息,RocketMQ的事務(wù)消息機(jī)制就是為了保證零丟失來(lái)設(shè)計(jì)的
18.2 存儲(chǔ)端不丟消息
如何保證存儲(chǔ)端的消息不丟失呢?確保消息持久化到磁盤。大家很容易想到就是刷盤機(jī)制。
刷盤機(jī)制分同步刷盤和異步刷盤:
生產(chǎn)者消息發(fā)過(guò)來(lái)時(shí),只有持久化到磁盤,RocketMQ的存儲(chǔ)端Broker才返回一個(gè)成功的ACK響應(yīng),這就是同步刷盤。它保證消息不丟失,但是影響了性能。 異步刷盤的話,只要消息寫入PageCache緩存,就返回一個(gè)成功的ACK響應(yīng)。這樣提高了MQ的性能,但是如果這時(shí)候機(jī)器斷電了,就會(huì)丟失消息。
Broker一般是集群部署的,有master主節(jié)點(diǎn)和slave從節(jié)點(diǎn)。消息到Broker存儲(chǔ)端,只有主節(jié)點(diǎn)和從節(jié)點(diǎn)都寫入成功,才反饋成功的ack給生產(chǎn)者。這就是同步復(fù)制,它保證了消息不丟失,但是降低了系統(tǒng)的吞吐量。與之對(duì)應(yīng)的就是異步復(fù)制,只要消息寫入主節(jié)點(diǎn)成功,就返回成功的ack,它速度快,但是會(huì)有性能問(wèn)題。
18.3 消費(fèi)階段不丟消息
消費(fèi)者執(zhí)行完業(yè)務(wù)邏輯,再反饋會(huì)Broker說(shuō)消費(fèi)成功,這樣才可以保證消費(fèi)階段不丟消息。
19. Redis如何保證高可用?聊聊Redis的哨兵機(jī)制
主從模式中,一旦主節(jié)點(diǎn)由于故障不能提供服務(wù),需要人工將從節(jié)點(diǎn)晉升為主節(jié)點(diǎn),同時(shí)還要通知應(yīng)用方更新主節(jié)點(diǎn)地址。顯然,多數(shù)業(yè)務(wù)場(chǎng)景都不能接受這種故障處理方式。Redis從2.8開(kāi)始正式提供了Redis Sentinel(哨兵)架構(gòu)來(lái)解決這個(gè)問(wèn)題。
哨兵模式,由一個(gè)或多個(gè)Sentinel實(shí)例組成的Sentinel系統(tǒng),它可以監(jiān)視所有的Redis主節(jié)點(diǎn)和從節(jié)點(diǎn),并在被監(jiān)視的主節(jié)點(diǎn)進(jìn)入下線狀態(tài)時(shí),自動(dòng)將下線主服務(wù)器屬下的某個(gè)從節(jié)點(diǎn)升級(jí)為新的主節(jié)點(diǎn)。但是呢,一個(gè)哨兵進(jìn)程對(duì)Redis節(jié)點(diǎn)進(jìn)行監(jiān)控,就可能會(huì)出現(xiàn)問(wèn)題(單點(diǎn)問(wèn)題),因此,可以使用多個(gè)哨兵來(lái)進(jìn)行監(jiān)控Redis節(jié)點(diǎn),并且各個(gè)哨兵之間還會(huì)進(jìn)行監(jiān)控。

簡(jiǎn)單來(lái)說(shuō),哨兵模式就三個(gè)作用:
發(fā)送命令,等待Redis服務(wù)器(包括主服務(wù)器和從服務(wù)器)返回監(jiān)控其運(yùn)行狀態(tài); 哨兵監(jiān)測(cè)到主節(jié)點(diǎn)宕機(jī),會(huì)自動(dòng)將從節(jié)點(diǎn)切換成主節(jié)點(diǎn),然后通過(guò)發(fā)布訂閱模式通知其他的從節(jié)點(diǎn),修改配置文件,讓它們切換主機(jī); 哨兵之間還會(huì)相互監(jiān)控,從而達(dá)到高可用。
故障切換的過(guò)程是怎樣的呢
假設(shè)主服務(wù)器宕機(jī),哨兵1先檢測(cè)到這個(gè)結(jié)果,系統(tǒng)并不會(huì)馬上進(jìn)行 failover 過(guò)程,僅僅是哨兵1主觀的認(rèn)為主服務(wù)器不可用,這個(gè)現(xiàn)象成為主觀下線。當(dāng)后面的哨兵也檢測(cè)到主服務(wù)器不可用,并且數(shù)量達(dá)到一定值時(shí),那么哨兵之間就會(huì)進(jìn)行一次投票,投票的結(jié)果由一個(gè)哨兵發(fā)起,進(jìn)行 failover 操作。切換成功后,就會(huì)通過(guò)發(fā)布訂閱模式,讓各個(gè)哨兵把自己監(jiān)控的從服務(wù)器實(shí)現(xiàn)切換主機(jī),這個(gè)過(guò)程稱為客觀下線。這樣對(duì)于客戶端而言,一切都是透明的。
哨兵的工作模式如下:
每個(gè)Sentinel以每秒鐘一次的頻率向它所知的Master,Slave以及其他Sentinel實(shí)例發(fā)送一個(gè) PING命令。 如果一個(gè)實(shí)例(instance)距離最后一次有效回復(fù) PING 命令的時(shí)間超過(guò) down-after-milliseconds 選項(xiàng)所指定的值, 則這個(gè)實(shí)例會(huì)被 Sentinel標(biāo)記為主觀下線。 如果一個(gè)Master被標(biāo)記為主觀下線,則正在監(jiān)視這個(gè)Master的所有 Sentinel 要以每秒一次的頻率確認(rèn)Master的確進(jìn)入了主觀下線狀態(tài)。 當(dāng)有足夠數(shù)量的 Sentinel(大于等于配置文件指定的值)在指定的時(shí)間范圍內(nèi)確認(rèn)Master的確進(jìn)入了主觀下線狀態(tài), 則Master會(huì)被標(biāo)記為客觀下線。 在一般情況下, 每個(gè) Sentinel 會(huì)以每10秒一次的頻率向它已知的所有Master,Slave發(fā)送 INFO 命令。 當(dāng)Master被 Sentinel 標(biāo)記為客觀下線時(shí),Sentinel 向下線的 Master 的所有 Slave 發(fā)送 INFO 命令的頻率會(huì)從 10 秒一次改為每秒一次 若沒(méi)有足夠數(shù)量的 Sentinel同意Master已經(jīng)下線, Master的客觀下線狀態(tài)就會(huì)被移除;若Master 重新向 Sentinel 的 PING 命令返回有效回復(fù), Master 的主觀下線狀態(tài)就會(huì)被移除。
20. 無(wú)重復(fù)字符的最長(zhǎng)子串
給定一個(gè)字符串 s ,請(qǐng)你找出其中不含有重復(fù)字符的 最長(zhǎng)子串 的長(zhǎng)度。
示例 1:
輸入: s = "abcabcbb"
輸出: 3
解釋: 因?yàn)闊o(wú)重復(fù)字符的最長(zhǎng)子串是 "abc",所以其長(zhǎng)度為 3。
示例 2:
輸入: s = "bbbbb"
輸出: 1
解釋: 因?yàn)闊o(wú)重復(fù)字符的最長(zhǎng)子串是 "b",所以其長(zhǎng)度為 1。
這道題可以使用滑動(dòng)窗口來(lái)實(shí)現(xiàn)?;瑒?dòng)窗口就是維護(hù)一個(gè)窗口,不斷滑動(dòng),然后更新答案。
滑動(dòng)窗口的大致邏輯框架,偽代碼如下:
int left =0,right = 0;
while (right < s.size()){
//增大窗口
window.add(s[right]);
right++;
while (window needs shrink){
//縮小窗口
window.remove (s[left]);
left ++;
}
}
解法流程如下:
首先呢,就是獲取原字符串的長(zhǎng)度。 接著維護(hù)一個(gè)窗口(數(shù)組、哈希、隊(duì)列) 窗口一步一步向右擴(kuò)展 窗口在向右擴(kuò)展滑動(dòng)過(guò)程,需要判斷左邊是否需要縮減 最后比較更新答案
完整代碼如下:
int lengthOfLongestSubstring(String s){
//獲取原字符串的長(zhǎng)度
int len = s.length();
//維護(hù)一個(gè)哈希集合的窗口
Set<Character> windows = new HashSet<>();
int left=0,right =0;
int res =0;
while(right<len){
char c = s.charAt(right);
//窗口右移
right++;
//判斷是否左邊窗口需要縮減,如果已經(jīng)包含,那就需要縮減
while(windows.contains(c)){
windows.remove(s.charAt(left));
left++;
}
windows.add(c);
//比較更新答案
res = Math.max(res,windows.size());
}
return res;
}
之前寫過(guò)一篇滑動(dòng)窗口解析,大家有興趣可以看下哈:
參考與感謝
分布式理論之分布式一致性:Raft算法原理[2] 一文搞懂Raft算法[3]
參考資料
分布式一致性:Raft算法原理: https://www.tpvlog.com/article/66
[2]分布式理論之分布式一致性:Raft算法原理: https://www.tpvlog.com/article/66
[3]一文搞懂Raft算法: https://www.cnblogs.com/xybaby/p/10124083.html
覺(jué)得本文有幫助的話,給個(gè)點(diǎn)贊、在看、分享
