互聯網面試知識技術總結(javacore、jvm、redis、kafka、zookeeper、mysql、hystrix)-第一章

該筆記是本身在一個月時間內作出的總結,知識點排序不是很整齊,可能有錯漏(歡迎指正),知識體系覆蓋了javacore、jvm、gc優化、多線程開發,redis、kafka、zookeeper,mysql,hystrix,spring等核心知識點,javacore是本身基於jdk1.六、1.七、1.8作出的一些總結,redis重點是在內存管理這塊(基於c++源碼理解作出的總結)html

1,冪等 推薦分佈式鎖(樂觀鎖)。 場景 <1>,一個訂單不容許被屢次支付(包括併發狀態下不容許被多我的同時支付) 下單前對訂單狀態(status字段)校驗,對訂單加上樂觀鎖(加上一個字段lock),只有加鎖成功的人才能進行支付。 或者針對每一個訂單生成惟一支付日誌,保證一個未支付的訂單隻容許被一個線程支付。 <2>,庫存扣減,不容許超賣。 須要考慮場景,在c端展現層,讀取緩存的方式,若是庫存扣減了,消息異步更新緩存。 在對庫存更改的時候,使用分佈式鎖,鎖住某個產品id的庫存,只容許一個線程去更改。前端

2,redis集羣搭建。 基於redis 3.0集羣模式,多個master節點根據hash分佈在16384槽上,每一個master節點掛靠多個slave節點。 集羣是好多個redis一塊兒工做的,若是爲了保證集羣不是那麼容易掛掉,因此呢,理論上就應該給集羣中的每一個節點至少一個slave redis節點。java

3,redis集羣模式: <1>standalone類型架構,單節點結構(非集羣模式)。 缺點:單節點,存儲空間和併發訪問能力有頗有限,很容易發生緩存穿透,流量直接打入db。node

<2>redis主從,一個master掛着多個slave, 優勢:master通常只接受寫入流量,slave負責讀取,提升了負載能力(主從複製是樂觀複製,當客戶端發送寫執行給主,主執行完當即將結果返回客戶端,並異步的把命令發送給從,從而不影響性能)。 缺點: A,Redis不具有自動容錯和恢復功能,主機從機的宕機都會致使前端部分讀寫請求失敗,須要等待機器重啓或者手動切換前端的IP才能恢復。 B,主機宕機,宕機前有部分數據未能及時同步到從機,切換IP後還會引入數據不一致的問題,下降了系統的可用性。 C,主從機器都是全量備份的數據(浪費內存),單機須要更大內存,存儲空間受限,不易擴容。mysql

<3>哨兵模式:Redis 2.8中提供了哨兵工具來實現自動化的系統監控和故障恢復功能,哨兵的做用就是監控redis主、從節點是否正常運行,主出現故障自動將從節點轉換爲主節點。 哨兵的做用就是監控Redis系統的運行情況。它的功能包括如下兩個 (1)監控主節點和從節點是否正常運行。 (2)主節點出現故障時自動將從節點轉換爲主節點。 備註:哨兵也是集羣部署,集羣初始化時配置。nginx

哨兵工做原理: 哨兵(sentinel) 是一個分佈式系統,你能夠在一個架構中運行多個哨兵(sentinel) 進程,這些進程使用流言協議(gossipprotocols)來接收關於Master是否下線的信息,並使用投票協議(agreement protocols)來決定是否執行自動故障遷移,以及選擇哪一個Slave做爲新的Master。 每一個哨兵(sentinel) 會向其它哨兵(sentinel)、master、slave定時發送消息,以確認對方是否」活」着,若是發現對方在指定時間(可配置)內未迴應,則暫時認爲對方已掛(所謂的」主觀認爲宕機」 Subjective Down,簡稱sdown)。若「哨兵羣」中的多數sentinel,都報告某一master沒響應,系統才認爲該master"完全死亡"(即:客觀上的真正down機,Objective Down,簡稱odown),經過必定的vote算法,從剩下的slave節點中,選一臺提高爲master,而後自動修改相關配置. 雖然哨兵(sentinel) 釋出爲一個單獨的可執行文件 redis-sentinel ,但實際上它只是一個運行在特殊模式下的 Redis 服務器,你能夠在啓動一個普通 Redis 服務器時經過給定 --sentinel 選項來啓動哨兵(sentinel).哨兵(sentinel) 的一些設計思路和zookeeper很是相似c++

優勢:哨兵集羣模式是基於主從模式的,全部主從的優勢,哨兵模式一樣具備,主從能夠切換,故障能夠轉移,系統可靠性更高。 缺點: 主從機器都是全量備份的數據(浪費內存),單機須要更大內存,存儲空間受限,不易擴容,在集羣容量達到上限時在線擴容會變得很複雜。爲避免這一問題,運維人員在系統上線時必須確保有足夠的空間,這對資源形成了很大的浪費。 哨兵模式還存在腦裂問題 Redis 哨兵模式腦裂:master所在機器忽然脫離了正常的網絡,跟其餘slave機器不能鏈接,可是實際上master還運行着 此時哨兵可能就會認爲master宕機了,而後開啓選舉,將其餘slave切換成了master,這個時候,集羣裏就會有兩個master,也就是所謂的腦裂redis

解決方案: min-slaves-to-write 1 min-slaves-max-lag 10 要求至少有1個slave,數據複製和同步的延遲不能超過10秒,若是說一旦全部的slave,數據複製和同步的延遲都超過了10秒鐘 那麼這個時候,master就不會再接收任何請求了上面兩個配置能夠減小異步複製和腦裂致使的數據丟失 上面的配置就確保了,若是跟任何一個slave丟了鏈接,在10秒後發現沒有slave給本身ack,那麼就拒絕新的寫請求,所以在腦裂場景下,最多就丟失10秒的數據。 連接:www.jianshu.com/p/f6ceae73c…算法

<4>redis 3.0 cluster:分佈式存儲。即每臺redis存儲不一樣的內容,共有16384個slot。每一個redis分得一些slot,hash_slot = crc16(key) mod 16384 找到對應slot,鍵是可用鍵,集羣至少須要3主3從,且每一個實例使用不一樣的配置文件,主從不用配置,集羣會本身選。 備註:它們之間經過互相的ping-pong判斷是否節點能夠鏈接上。若是有一半以上的節點去ping一個節點的時候沒有迴應,集羣就認爲這個節點宕機了,而後去鏈接它的備用節點。若是某個節點和全部從節點所有掛掉,咱們集羣就進入faill狀態。還有就是若是有一半以上的主節點宕機,那麼咱們集羣一樣進入發力了狀態。這就是咱們的redis的投票機制。spring

優勢:具有哨兵模式的優勢,數據分散存儲,內存利用率更加高,支持在線擴容。 缺點:若是某個slot上面的master和slave都掛掉,就會出現集羣不可用。

備註:在Redis Cluster3.0動態擴容時,新增的Master節點是沒有數據的,主節點若是沒有slots的話,存取數據就都不會被選中,須要手動把slot及其中數據遷移到新增的Master中(參考Redis集羣官方中文教程),操做指令支持節點從新洗牌(調增slot對應的數據)。

<5>Jedis sharding集羣 Redis Sharding能夠說是在Redis cluster出來以前業界廣泛的採用方式,其主要思想是採用hash算法將存儲數據的key進行hash散列,這樣特定的key會被定爲到特定的節點上(採用一致性哈希算法,將key和節點name同時hashing,而後進行映射匹配) <6>中間件代理 常見中間件: Twemproxy Codis nginx

參考:blog.csdn.net/qq_35152037…

3.0 cluster那麼這個集羣是如何判斷是否有某個節點掛掉了呢? 首先要說的是,每個節點都存有這個集羣全部主節點以及從節點的信息。 它們之間經過互相的ping-pong判斷是否節點能夠鏈接上。若是有一半以上的節點去ping一個節點的時候沒有迴應,集羣就認爲這個節點宕機了,而後去鏈接它的備用節點。若是某個節點和全部從節點所有掛掉,咱們集羣就進入faill狀態。還有就是若是有一半以上的主節點宕機,那麼咱們集羣一樣進入發力了狀態。這就是咱們的redis的投票機制,     (1)投票過程是集羣中全部master參與,若是半數以上master節點與master節點通訊超時(cluster-node-timeout),認爲當前master節點掛掉.     (2)何時整個集羣不可用(cluster_state:fail)?     a:若是集羣任意master掛掉,且當前master沒有slave.集羣進入fail狀態,也能夠理解成集羣的slot映射[0-16383]不完整時進入fail狀態.     b:若是集羣超過半數以上master掛掉,不管是否有slave,集羣進入fail狀態.

Redis cluster的slave選舉流程:

當slave發現本身的master變爲FAIL狀態時,便嘗試進行Failover,以期成爲新的master。因爲掛掉的master可能會有多個slave,從而存在多個slave競爭成爲master節點的過程, 其過程以下: 1.slave發現本身的master變爲FAIL 2.將本身記錄的集羣currentEpoch加1,並廣播FAILOVER_AUTH_REQUEST信息 3.其餘節點收到該信息,只有master響應,判斷請求者的合法性,併發送FAILOVER_AUTH_ACK,對每個epoch只發送一次ack 4.嘗試failover的slave收集FAILOVER_AUTH_ACK 5.超過半數後變成新Master 6.廣播Pong通知其餘集羣節點。

SLAVE_RANK表示此slave已經從master複製數據的總量的rank。Rank越小表明已複製的數據越新,持有最新數據的slave將會首先發起選舉(理論上)

參考:www.cnblogs.com/liyasong/p/…

3,redis持久化(rdb和aof) RDB:在指定的時間間隔能對數據進行快照存儲。 優勢:使用單獨子進程來進行持久化,主進程不會進行任何IO操做,保證了redis的高性能 缺點:RDB是間隔一段時間進行持久化,若是持久化之間redis發生故障,會發生數據丟失,數據的準確性不高。 AOF:AOF持久化方式記錄每次對服務器寫的操做,當服務器重啓的時候會從新執行這些命令來恢復原始的數據,AOF命令以redis協議追加保存每次寫的操做到文件末尾.Redis還能對AOF文件進行後臺重寫,使得AOF文件的體積不至於過大。 優勢:能夠保持更高的數據完整性,所以已成爲主流的持久化方案 缺點:AOF文件比RDB文件大,且恢復速度慢。

4,redis高可用原理分析:blog.csdn.net/qq_41849945… 備註:實際仍是一主多從的結構

5,redis如何實現主從複製?以及數據同步機制? Redis主從複製通常都是異步化完成(複製功能不會阻塞主服務器),Redis主從複製能夠根據是不是全量分爲全量同步和增量同步,

<1>Redis全量複製通常發生在Slave初始化階段,這時Slave須要將Master上的全部數據都複製一份(master生成一份全量的rdb快照文件,發送給slave)。具體步驟以下: a,從服務器鏈接主服務器,發送SYNC命令; b,主服務器接收到SYNC命名後,開始執行BGSAVE命令生成RDB文件並使用緩衝區記錄此後執行的全部寫命令; c,主服務器BGSAVE執行完後,向全部從服務器發送快照文件,並在發送期間繼續記錄被執行的寫命令; d,從服務器收到快照文件後丟棄全部舊數據,載入收到的快照; e,主服務器快照發送完畢後開始向從服務器發送緩衝區中的寫命令; f,從服務器完成對快照的載入,開始接收命令請求,並執行來自主服務器緩衝區的寫命令;

<2>Redis增量複製是指Slave初始化後開始正常工做時主服務器發生的寫操做同步到從服務器的過程。 增量複製的過程主要是主服務器每執行一個寫命令就會向從服務器發送相同的寫命令,從服務器接收並執行收到的寫命令。

4,redis如何壓縮AOF文件,具體過程以下: redis調用fork ,如今有父子兩個進程 <1>,子進程根據內存中的數據庫快照,往臨時文件中寫入重建數據庫狀態的命令 <2>,父進程繼續處理client請求,除了把寫命令寫入到原來的aof文件中。同時把收到的寫命令緩存起來。這樣就能保證若是子進程重寫失敗的話並不會出問題。 <3>,當子進程把快照內容寫入已命令方式寫到臨時文件中後,子進程發信號通知父進程。而後父進程把緩存的寫命令也寫入到臨時文件。 <4>,如今父進程可使用臨時文件替換老的aof文件,並重命名,後面收到的寫命令也開始往新的aof文件中追加。 <5>,須要注意到是重寫aof文件的操做,並無讀取舊的aof文件,而是將整個內存中的數據庫內容用命令的方式重寫了一個新的aof文件,這點和快照有點相似。

簡單總結:如何縮小AOF文件大小:文件重寫是指按期重寫AOF文件(產生新的AOF文件),減少AOF文件的體積。須要注意的是,AOF重寫是把Redis進程內的數據轉化爲寫命令,同步到新的AOF文件(爲了壓縮aof的持久化文件。redis提供了bgrewriteaof命令。收到此命令redis將使用與快照相似的方式將內存中的數據 以命令的方式保存到臨時文件中,最後替換原來的文件)

參考:www.cnblogs.com/xingzc/p/59…

5,AOF縮減自身文件大小的時候,來了新的寫請求怎麼辦? 子進程同步完內存中數據以後,會發出指令,通知父進程把最近的寫請求操做刷入新的aof文件。

6,redis multi,pipeline的區別 redis交互流程:業務應用服務器(如hotel-goods-service)--->redis client--->redis server

備註:redis屬於典型的c/s架構

multi特色: <1>實際上當咱們使用multi操做時,redis client是一條條發送數據到 redis server,這些請求是積壓在服務端的queue裏面,而後依次一次執行完畢,redis服務端一次性返回全部命令返回結果,服務端執行這段操做是開啓事務機制的。 <2>因爲每發送一條指令,都須要單獨發給服務器,服務器再單獨返回「該條指令已加入隊列」這個消息。這是比Pipeline慢的緣由之一。 <3>Multi執行的時候會先暫停其餘命令的執行(事務機制),相似於加了個鎖,直到整個Multi結束完成再繼續其餘客戶端的請求。這是Multi能保證一致性的緣由,也是比Pipeline慢的緣由之二 <4>因爲服務端開啓了事務機制,所以multi是原子性的。 <5>因爲redis client逐條發送請求到redis server中的queue,所以multi屬於服務端緩衝。

pipeline特色: <1>redis client將全部命令打包一次性發送。發送成功後,服務端不用返回相似「命令已收到」這樣的消息,而是一次性批量執行全部命令,成功後再一次性返回全部處理結果。 <2>服務端處理命令的時候,不須要加鎖,而是與其餘客戶端的命令混合在一塊兒處理,因此沒法保證一致性。 <3>因爲是redis client一次打包發送出去的請求,所以pipeline是客戶端緩衝 <4>因爲pipeline把請求打包發送給redis server,少了與redis server的屢次交互,所以性能更加好。

參考:blog.walkerx.cn/2018/07/08/…

8,redis動態擴容 過程描述: HASH_SLOT = CRC16(key) mod 16384 經過key與slot的映射算法,計算出當前key應該存儲在哪一個slot中,從公式中能夠看出,當前key與slot的映射是固定不變的。因爲每一個Master負責一部分slot,可知在Master節點數量調整時,slot與Master映射的關係也會調整,也就是說slot和master之間有個映射表的。

在動態擴容過程當中slot的特色: 舉例:MasterA節點(原集羣中的舊機器)遷移部分slot到MasterB節點 MIGRATING狀態是發生在MasterA節點中的一種槽的狀態,預備遷移槽的時候槽的狀態首先會變爲MIGRATING狀態,這種狀態的槽會實際產生什麼影響呢?當客戶端請求的某個Key所屬的槽處於MIGRATING狀態的時候,影響有下面幾條

<1>若是Key存在則成功處理 <2>若是Key不存在,則返回客戶端ASK,僅當此次請求會轉向另外一個節點,並不會刷新客戶端(redis-client)中node的映射關係,也就是說下次該客戶端請求該Key的時候,還會選擇MasterA節點 <3>若是Key包含多個命令,若是都存在則成功處理,若是都不存在,則返回客戶端ASK,若是一部分存在,則返回客戶端TRYAGAIN,通知客戶端稍後重試,這樣當全部的Key都遷移完畢的時候客戶端重試請求的時候回獲得ASK,而後通過一次重定向就能夠獲取這批鍵。

IMPORTING狀態是發生在MasterB節點中的一種槽的狀態,預備將槽從MasterA節點遷移到MasterB節點的時候,槽的狀態會首先變爲IMPORTING。IMPORTING狀態的槽對客戶端的行爲有下面一些影響:

<1>正常命令會被MOVED重定向,若是是ASKING命令則命令會被執行,從而Key沒有在老的節點已經被遷移到新的節點的狀況能夠被順利處理; <2>若是Key不存在則新建; <3>沒有ASKING的請求和正常請求同樣被MOVED,這保證客戶端node映射關係出錯的狀況下不會發生寫錯;

簡述:redis在動態擴容時,須要集羣裏面的舊機器的部分slot遷移到新機器,因爲只涉及部分slot遷移,所以這些待遷移的slot會變成遷移狀態(MIGRATING),遷移過程當中舊機器仍然接受讀取和寫入流量,若是key在舊機器不存在,請求將轉發到新擴容的節點(如MasterB),等數據遷移完成再更新slot映射關係表便可。

參考:www.cnblogs.com/wxd0108/p/5…

9,單機redis如何提升併發 <1>redis性能瓶頸在io,所以單key不該該存儲大值(大key分段存儲)。 <2>pipeline代替multiGet操做。 <3>寫入redis的數據作壓縮。 <4>當業務場景不須要數據持久化時,關閉全部的持久化方式能夠得到最佳的性能(數據持久化時須要在持久化和延遲/性能之間作相應的權衡,其實是在持久化的時候,數據佔用了內核的頁緩存,致使可用頁緩存空間緊張) <5>存儲對象使用hash,避免修改其中一項信息時,須要把整個對象取回,而且修改操做須要對併發進行保護。

參考:www.cnblogs.com/moonandstar…

10,redis內存優化 Redis 最爲經常使用的數據類型主要有如下五種:String,Hash,List,Set,Sorted set Redis任何的數據類型都是由一個叫作redisObject的數據結構管理的,具體參考以下

數據類型(type)
編碼方式(encoding)

數據指針(ptr)
虛擬內存(vm)

LRU計時時鐘

數據類型:string,list,hash,set,sorted set, 編碼方式:raw,int,ht,zipmap,linkedlist,ziplist,intset lru計時時間:記錄對象最後一次被訪問的時間,當配置了 maxmemory和maxmemory-policy=volatile-lru | allkeys-lru 時, 用於輔助LRU算法刪除鍵數據。可使用object idletime {key}命令在不更新lru字段狀況下查看當前鍵的空閒時間

備註:首先最重要的一點是不要開啓 Redis 的 VM 選項,即虛擬內存功能,這個原本是做爲 Redis 存儲超出物理內存數據的一種數據在內存與磁盤換入換出的一個持久化策略,可是其內存管理成本也很是的高,而且咱們後續會分析此種持久化策略並不成熟,因此要關閉 VM 功能,請檢查你的 redis.conf 文件中 vm-enabled 爲 no。 其次最好設置下redis.conf中的 maxmemory 選項,該選項是告訴 Redis 當使用了多少物理內存後就開始拒絕後續的寫入請求,該參數能很好的保護好你的 Redis 不會由於使用了過多的物理內存而致使 swap,最終嚴重影響性能甚至崩潰

Redis內部使用一個redisObject對象來表示全部的key和value,redisObject最主要的信息如上圖所示:type表明一個value對象具體是何種數據類型,encoding是不一樣數據類型在redis內部的存儲方式,好比:type=string表明value存儲的是一個普通字符串,那麼對應的encoding能夠是raw或者是int,若是是int則表明實際redis內部是按數值型類存儲和表示這個字符串的,固然前提是這個字符串自己能夠用數值表示,好比:"123" "456"這樣的字符串,當使用int存儲時,比使用raw存儲原生的字符串更加節省內存。

Redis內存優化點: <1>存儲字符串時,若是值是整數,內部轉成int存儲,節省空間。 <2>Redis Hash是value內部爲一個 HashMap,若是該Map的成員數比較少,則會採用相似一維線性的緊湊格式來存儲該Map即省去了大量指針的內存開銷,具體配置參數以下: hash-max-zipmap-entries 64 hash-max-zipmap-value 512 含義是當 value 這個 Map 內部不超過多少個成員時會採用線性緊湊格式存儲,默認是64,即 value 內部有64個如下的成員就是使用線性緊湊存儲,超過該值自動轉成真正的 HashMap, hash-max-zipmap-value 含義是當 value 這個 Map 內部的每一個成員值長度不超過多少字節就會採用線性緊湊存儲來節省空間。以上2個條件任意一個條件超過設置值都會轉換成真正的 HashMap,也就不會再節省內存了,那麼這個值是否是設置的越大越好呢,答案固然是否認的,HashMap 的優點就是查找和操做的時間複雜度都是 O(1) 的,而放棄 Hash 採用一維存儲則是 O(n) 的時間複雜度,若是成員數量不多,則影響不大,不然會嚴重影響性能,因此要權衡好這個值的設置,整體上仍是最根本的時間成本和空間成本上的權衡。 <3>共享對象池,對象共享池指Redis內部維護[0-9999]的整數對象池。建立大量的整數類型redisObject存在內存開銷,每一個redisObject內部結構至少佔16字節,甚至超過了整數自身空間消耗。因此Redis內存維護一個[0-9999]的整數對象池,用於節約內存。 除了整數值對象,其餘類型如list,hash,set,zset內部元素也可使用整數對象池。所以開發中在知足需求的前提下,儘可能使用整數對象以節省內存。

參考:www.cnblogs.com/jandison/p/…

11,頁緩存技術 Page cache是經過將磁盤中的數據緩存到內存中,從而減小磁盤I/O操做,從而提升性能。此外,還要確保在page cache中的數據更改時可以被同步到磁盤上,後者被稱爲page回寫(page writeback)。一個inode對應一個page cache對象,一個page cache對象包含多個物理page。 對磁盤的數據進行緩存從而提升性能主要是基於兩個因素:第一,磁盤訪問的速度比內存慢好幾個數量級(毫秒和納秒的差距)。第二是被訪問過的數據,有很大機率會被再次訪問。 參考:blog.csdn.net/damontive/a…

12,redis的過時數據刪除策略和內存淘汰策略

1、過時數據刪除策略(redis是兩種策略配合一塊使用) <1>惰性刪除,無論過時的鍵,在這種策略下,當鍵在鍵空間中被取出時,首先檢查取出的鍵是否過時,若過時刪除該鍵,不然,返回該鍵。很明顯,惰性刪除依賴過時鍵的被動訪問,對於內存不友好,若是一些鍵長期沒有被訪問,會形成內存泄露(垃圾數據佔用內存),可是它屬於cpu友好型,不須要佔用太多cpu時間片。 <2>按期刪除,redis建立一個定時任務隨機掃描數據是否過時(CPU空閒時在按期serverCron任務中),逐出部分過時Key,具體刪除過程以下

A,Redis配置項hz定義了serverCron任務的執行週期,默認爲10,即CPU空閒時每秒執行10次; B,每次過時key清理的時間不超過CPU時間的25%,即若hz=1,則一次清理時間最大爲250ms,若hz=10,則一次清理時間最大爲25ms; C,清理時依次遍歷全部的db; D,從db中隨機取20個key,判斷是否過時,若過時,則逐出; E,如有5個以上key過時,則重複步驟4,不然遍歷下一個db; F,在清理過程當中,若達到了25%CPU時間,退出清理過程;

備註:每次刪除限定在25%cpu時間片範圍內,而且還判斷過時的key的比例,好比超過25%過時key才繼續下一此刪除。這是一個基於機率的簡單算法,基本的假設是抽出的樣本可以表明整個key空間,redis持續清理過時的數據直至將要過時的key的百分比降到了25%如下。這也意味着在長期來看任何給定的時刻已通過期但仍佔據着內存空間的key的量最多爲每秒的寫操做量除以4。

2、內存淘汰策略 Redis內存淘汰策略被激發,是內存使用達到必定的閾值纔開始運行的(redis.conf裏面配置),所以內存淘汰策略和key過時刪除策略是兩碼事。

noeviction:當內存不足以容納新寫入數據時,新寫入操做會報錯。 allkeys-lru:當內存不足以容納新寫入數據時,在鍵空間中,移除最近最少使用的key。 allkeys-random:當內存不足以容納新寫入數據時,在鍵空間中,隨機移除某個key。 volatile-lru:當內存不足以容納新寫入數據時,在設置了過時時間的鍵空間中,移除最近最少使用的key。 volatile-random:當內存不足以容納新寫入數據時,在設置了過時時間的鍵空間中,隨機移除某個key。 volatile-ttl:當內存不足以容納新寫入數據時,在設置了過時時間的鍵空間中,有更早過時時間的key優先移除。 以上策略是:(非)過時、隨機、lru的組合

redis過時字典: redisDb結構的expires字典保存了數據庫中全部鍵的過時時間,爲過時字典,鍵就是數據庫鍵,值是long long類型,毫秒經度的unix時間戳(過時時間)。

舉例簡述redis 3.0對allkeys-lru的實現流程 Redis服務器每執行一個命令,都會檢測內存,判斷是否須要進行數據淘汰 <1>判斷目前已經使用的內存大小是否比設置的maxmemory要小,若是小於maxmemory,那麼無須執行進一步操做。 <2>判斷淘汰策略是否爲noeviction,若是是,直接return回去,不進行任何內存淘汰。 <3>根據傳入的對象大小,計算須要釋放多少字節的內存 <4>開始隨機採樣,每次隨機獲取10個key,選出lru時間最小的key放入一個長度爲16的pool裏面,後續再不斷隨機取樣10個key,若是lru時間比pool最小lru時間還小,就加入pool,直至pool填滿,而後開始淘汰pool裏面lru時間最小的,直至淘汰的空間足夠存儲須要的值。

備註:Redis的內存淘汰策略的選取並不會影響過時的key的處理。內存淘汰策略用於處理內存不足時的須要申請額外空間的數據;過時策略用於處理過時的緩存數據。 redis的 lru算法實際上不是很是準確的,是基於快速抽樣比較的實現(若是使用雙向鏈表的指針標記,佔用的空間更加大) 參考:yq.aliyun.com/articles/25…

13,redis集羣訪問流量傾斜 <1>hot key出現形成集羣訪問量傾斜 場景:Hot key,即熱點 key,指的是在一段時間內,該 key 的訪問量遠遠高於其餘的 redis key, 致使大部分的訪問流量在通過 proxy 分片以後,都集中訪問到某一個 redis 實例上。hot key 一般在不一樣業務中,存儲着不一樣的熱點信息。好比:新聞應用中的熱點新聞內容,活動系統中某個用戶瘋狂參與的活動的活動配置,商城秒殺系統中,最吸引用戶眼球,性價比最高的商品信息。 解決方案: 一,對hot key數據進行本地緩存。 二,利用分片算法的特性,對key進行打散處理,好比對key加上0~9的後綴,redis key通過分片分佈到不一樣的實例上,將訪問量均攤到全部實例。 <2>big key在redis存儲優化 若是big value 是個大json 經過 mset 的方式,將這個key的內容打散到各個實例中(分段存儲),減少big key對數據量傾斜形成的影響。

7,mybatis延遲加載。 resultMap能夠實現高級映射(使用association、collection實現一對一及一對多映射),association、collection具有延遲加載功能。   延遲加載:先從主表查詢,須要時再從關聯表去關聯查詢,大大提升數據庫性能,由於查詢單表要比關聯查詢多張錶速度要快。 場景舉例:查詢出符合要求的訂單數據,再去查出這些訂單的用戶信息,整個過程要執行多個sql,可是在一個resultMap返回結果。

實現原理:在createResultObject的時候,會判斷當前返回值是否含有延遲加載的數據,若是有,就建立動態代理對象(Javasisst或者Cglib代理),執行被代理的方法,獲取數據,並封裝到resultMap。 延遲加載的好處:先在單表查詢、須要時再從關聯表去關聯查詢,大大提升 數據庫性能,由於查詢單表要比關聯查詢多張錶速度要快。 參考:my.oschina.net/wenjinglian…

9,mybatis中#{}和{}的區別
  #將傳入的數據都當成一個字符串,會對自動傳入的數據加一個雙引號(很大程度上防止了sql注入),將傳入的數據直接顯示生成在sql中(沒法防止sql注入)。

10,tcp三次握手(兩次不行嗎?),四次揮手,爲何這麼作。 三次握手是爲了創建tcp的雙工通訊,四次揮手是爲了可以保證tcp的半閉合狀態。

11,網絡丟包如何解決,分不一樣業務場景。(ack,滑動窗口)

9,讀寫鎖源碼(todo) 參考:blog.csdn.net/yanyan19880… 10,各類線程實現。

//線程池大小固定爲1
     Executors.newSingleThreadExecutor();

    //固定大小線程池由本身設定,即本身控制資源的固定分配
    Executors.newFixedThreadPool(10);

    //動態調整線程池的大小,最小爲0,最大爲int最大值,,newCachedThreadPool會大幅度提升大量短暫異步任務的性能,
    //若是執行業務邏輯比較慢,會致使持續建立線程,致使cpu資源消耗殆盡
    //爲何使用SynchronousQueue?最多隻能持有一個任務數據,當任務數據插入隊列失敗,會驅動建立新線程,SynchronousQueue做爲主線程池的工做隊列,它是一個沒有容量的阻塞隊列。每一個插入操做必須等待另外一個線程的對應移除操做。這意味着,若是主線程提交任務的速度高於線程池中處理任務的速度時,CachedThreadPool會不斷建立新線程。極端狀況下,CachedThreadPool會由於建立過多線程而耗盡CPU資源
    Executors.newCachedThreadPool();//newCachedThreadPool不適合io密集型的網絡請求,只適合計算密集型(每次請求耗時很短)

    //基於延遲隊列實現的延時任務線程池,週期性的執行所提交的任務
    ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
    scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " run");
        }
    }, 1000,2000, TimeUnit.MILLISECONDS);
複製代碼

11,各類隊列實現。 <1>PriorityBlockingQueue(無界隊列) 內部使用reentrantlock(基於數組實現的堆排序,數組會動態擴容),每次入隊和出隊都須要加鎖,保證線程安全。 PriorityBlockingQueue存儲的對象必須是實現Comparable接口的 由於PriorityBlockingQueue隊列會根據內部存儲的每個元素的compareTo方法比較每一個元素的大小 須要注意的是PriorityBlockingQueue並不會阻塞數據生產者,而只會在沒有可消費的數據時,阻塞數據的消費者。所以使用的時候要特別注意,生產者生產數據的速度絕對不能快於消費者消費數據的速度,不然時間一長,會最終耗盡全部的可用堆內存空間。在實現PriorityBlockingQueue時,內部控制線程同步的鎖採用的是公平鎖。PriorityBlockingQueue在take出來的時候會根據優先級 將優先級最小的最早取出

備註:優先隊列擴容階段爲何釋放鎖,由於只有一把鎖,擴容期間不影響數據讀取(提升併發效率),擴容完以後再拷貝之前的數據(拷貝階段加鎖就能夠了)。 因爲PriorityBlockingQueue在空間不夠的時候,會自增擴容數組進行堆排序,不須要對數據的put操做進行阻塞,只對數據獲取進行阻塞,所以只須要一個condition(喚醒數據查詢的線程)

<2>ArrayBlockingQueue,基於數組實現,只有1個鎖(數據的寫入不須要構造node節點,直接存儲外部傳入的引用,效率已經足夠高,LinkedBlockingQueue構造node節點,耗時相對高一些,所以讀寫鎖分離爲了提升併發效率),添加數據和刪除數據的時候只能有1個被執行,不容許並行執行。使用Condition notEmpty,Condition notFull來實現生產者-消費者模式(通知模式)。

<3>LinkedBlockingQueue,基於鏈表實現,只有2個鎖(因爲是無界,所以不用擔憂隊列寫滿,讀寫能夠分離),放鎖和讀鎖,兩把鎖分別管理head節點和last節點的操做,經過原子變量count控制隊列長度狀態,添加數據和刪除數據是能夠並行進行的,固然添加數據和刪除數據的時候只能有1個線程各自執行。LinkedBlockingQueue將讀和寫操做分離,可讓讀寫操做在不干擾對方的狀況下,完成各自的功能,提升併發吞吐量。使用Condition notEmpty,Condition notFull來實現生產者-消費者模式(通知模式)

備註:ArrayBlockingQueue和LinkedBlockingQueue這兩個阻塞隊列,隊列滿了,放不進去會被阻塞,隊列爲空,取不出結果會被阻塞,所以須要兩個condition。 壓測報告:一千萬條的數據進行多線程插入和讀取,明顯看出ArrayBlockingQueue比LinkedBlockingQueue性能強30%。

<4> SynchronousQueue經過將入隊出隊的線程綁定到隊列的節點上,並藉助LockSupport的park()和unpark()實現等待和喚醒,先到達的線程A需調用LockSupport的park()方法將當前線程進入阻塞狀態,知道另外一個與之匹配的線程B調用LockSupport.unpark(Thread)來喚醒在該節點上等待的線程A。其內部沒有任何容量,任何的入隊操做都須要等待其餘線程的出隊操做,反之亦然。若是將SynchronousQueue用於生產者/消費者模式,那麼至關於生產者和消費者手遞手交易,即生產者生產出一個貨物,則必須等到消費者過來取貨,方可完成交易。

備註:SynchronousQueue沒有使用condition(本質上基於LockSupport實現),直接使用了LockSupport的park()和unpark()實現等待和喚醒 參考:blog.csdn.net/vickyway/ar…

<5> DelayQueue的泛型參數須要實現Delayed接口,Delayed接口繼承了Comparable接口,DelayQueue內部使用非線程安全的優先隊列(PriorityQueue),並使用Leader/Followers模式,最小化沒必要要的等待時間。DelayQueue不容許包含null元素。(藉助LockSupport.parkNanos和unpark實現延時,reentrantlock實現安全操做),available.awaitNanos(delay)實現延時,available.awaitNanos內部基於LockSupport.parkNanos(this, nanosTimeout)實現掛起,指定時間範圍內自動喚醒(由操做系統本身去調度);

特色:元素進入隊列後,先進行排序(調用compareTo方法排序),而後,只有getDelay也就是剩餘時間爲0的時候, 該元素纔有資格被消費者從隊列中取出來,實際上只有隊列頭元素出隊,其它才能出隊,會受到頭結點元素延時時間的影響。 備註:DelayQueue使用PriorityQueue(自動擴容的堆數組),所以數據的存入不須要阻塞,讀取的時候才進行堵塞,所以只須要一個condition。

13,線程池核心線程如何設置。 若是計算密集型一般是cpu核數+1,io密集型是99線*qps/1000 14,Spring如何處理循環引用的 <1>,循環依賴的對象都經過構造器注入,會注入失敗。(不管bean是singleton,仍是prototype,或者是混合了singleton、prototype),由於生成對象必須依賴構造方法,而構造方法裏面須要對方的實例對象,所以造成了死循環。 <2>,循環依賴的bean都是經過屬性注入,若是注入都是singleton對象,都能建立成功。若是注入都是prototype,就會失敗。若是是混合singleton、prototype,只有先建立singleton才能保證成功,不然就會失敗。 Spring對於構造器注入的對象會標記爲正在建立中,若是在循環依賴建立過程當中發生依賴的對象正在建立中,會拋出異常。 歸根結底是spring容器內部保留了singleton對象,prototype對象被丟失。 備註:singleton對象有三級緩存的概念,prototype對象沒有,singleton在建立過程當中會檢查三級緩存,依次從裏面取出數據(前提這些對象都是調用構造方法建立成功了),若是成功取出就完成注入。prototype對象在建立過程當中會被標記爲「正在建立中」,若是循環建立中,發現依賴的bean處於「正在建立中」,就會拋出異常。

參考:blog.csdn.net/chen2526264…

15,spring初始化對象

單例對象(基於三級緩存實現) (1)createBeanInstance:實例化,其實也就是調用對象的構造方法實例化對象 (2)populateBean:填充屬性,這一步主要是多bean的依賴屬性進行填充 (3)initializeBean:調用spring xml中的init 方法。 單例來講,在Spring容器整個生命週期內,有且只有一個對象,因此很容易想到這個對象應該存在Cache中,Spring爲了解決單例的循環依賴問題,使用了三級緩存。

prototype對象(完成初始化以前存在一個set裏面) 初始化流程,在初始化屬性的時候,isPrototypeCurrentlyInCreation,會校驗這個屬性的bean是否在建立中,若是在建立中會拋出異常。

16,spring ioc ioc,依賴注入,在之前的軟件工程編碼過程當中,類的屬性須要硬編碼生成對象數據,耦合性較高,若是使用ioc,是在容器啓動過程當中,在bean對象實例化過程當中須要檢查其依賴數據,而且進行數據注入(setter,構造器注入),完成一個對象的實例化並實現瞭解耦合,而且可以對這些對象進行復用。

aop,主要分爲兩大類:一是採用jdk動態代理技術,利用截取消息的方式,對該消息進行裝飾,以取代原有對象行爲的執行;二是採用動態織入的方式,引入特定的語法建立「方面」,是在類加載時期織入有關「方面」的代碼。它利用一種稱爲"橫切"的技術,並將那些影響了多個類的公共行爲封裝到一個可重用模塊,簡單理解是抽象出與業務邏輯無關的公共行爲邏輯。

17,java的future編程 FutureTask實現了Runnable, Future接口,並實例化Callable對象,在線程開啓運行時,執行線程任務的實現類的run方法,run執行完畢將結果引用賦值給outcome屬性(若是任務線程沒執行完,當前主線程會進入阻塞狀態,任務線程執行完畢,會設置outcome,並解除主線程阻塞)。

18,hystrix

<1> 使用場景:在soa架構中,資源隔離(線程池、或者信號量隔離),熔斷(防止雪崩效應)降級,依賴的服務使用不一樣的commandKey(最小隔離單元,可能多個commandKey在一個線程池內)標註,實現隔離,線程池是HystrixCommandGroupKey標識。 <2> hystrix是如何經過線程池實現線程隔離的 Hystrix經過命令模式,將每一個類型的業務請求封裝成對應的命令請求,好比查詢訂單->訂單Command,查詢商品->商品Command,查詢用戶->用戶Command。每一個類型的Command對應一個線程池。建立好的線程池是被放入到ConcurrentHashMap中,好比查詢訂單。 <3> hystrix如何實現熔斷的 用戶請求某一服務以後,Hystrix會先通過熔斷器,此時若是熔斷器的狀態是打開,則說明已經熔斷,這時將直接進行降級處理,不會繼續將請求發到線程池。若是熔斷器是關閉狀態,會檢測最近10秒的請求錯誤率,當錯誤率超過預設的值(默認是50%)且10秒內超過20個請求,則開啓熔斷。熔斷器默認是在5s後開始從新嗅探,會嘗試放過去一部分流量進行試探,肯定依賴服務是否恢復。

<4> hystrix如何統計失敗率 每一個熔斷器默認維護10個bucket 每秒建立一個bucket 每一個blucket記錄成功,失敗,超時,拒絕的次數 當有新的bucket被建立時,最舊的bucket會被拋棄

<5> 核心參數 HystrixCommandGroupKey,線程池分組 HystrixCommandKey,線程池標識。

Circuit Breaker(熔斷器)一共包括以下6個參數。 一、circuitBreaker.enabled 是否啓用熔斷器,默認是TURE。 二、circuitBreaker.forceOpen 熔斷器強制打開,始終保持打開狀態。默認值FLASE。 三、circuitBreaker.forceClosed 熔斷器強制關閉,始終保持關閉狀態。默認值FLASE。 四、circuitBreaker.errorThresholdPercentage 設定錯誤百分比,默認值50%,例如一段時間(10s)內有100個請求,其中有55個超時或者異常返回了,那麼這段時間內的錯誤百分比是55%,大於了默認值50%,這種狀況下觸發熔斷器-打開。 五、circuitBreaker.requestVolumeThreshold 默認值20.意思是至少有20個請求才進行errorThresholdPercentage錯誤百分比計算。好比一段時間(10s)內有19個請求所有失敗了。錯誤百分比是100%,但熔斷器不會打開,由於requestVolumeThreshold的值是20. 這個參數很是重要,熔斷器是否打開首先要知足這個條件。 六、circuitBreaker.sleepWindowInMilliseconds 半開試探休眠時間,默認值5000ms。當熔斷器開啓一段時間以後好比5000ms,會嘗試放過去一部分流量進行試探,肯定依賴服務是否恢復

參考:itindex.net/detail/5778…

歡迎打賞

相關文章
相關標籤/搜索