問(wèn)題-2021-09-21

xxl-job如何保證定時(shí)任務(wù)只執(zhí)行一次

1薄风、業(yè)務(wù)邏輯代碼和定時(shí)任務(wù)邏輯完全分開(kāi)部署
2厢塘、調(diào)度框架集群(rehash將不同任務(wù)注冊(cè)到不同節(jié)點(diǎn))
3溃斋、使用分布式鎖解決本次定時(shí)任務(wù)未執(zhí)行完劳秋,下一次定時(shí)任務(wù)開(kāi)始執(zhí)行的問(wèn)題

Redis擴(kuò)容機(jī)制

Redis常用方法

ArrayList擴(kuò)容機(jī)制

構(gòu)造時(shí)绢馍,容量為0羹膳,添加元素時(shí)才分配容量10睡互;超過(guò)容量時(shí),擴(kuò)容1.5倍陵像;System.arraycopy方法做一個(gè)整體的復(fù)制(排在這次添加元素后面的所有元素)

分布式事務(wù)-本地消息表+消息隊(duì)列

a服務(wù):落業(yè)務(wù)表就珠、本地消息表(事件發(fā)布表,狀態(tài)TODO)
a服務(wù):定時(shí)任務(wù)(將事件發(fā)送到消息隊(duì)列醒颖,確認(rèn)收到(防止消息隊(duì)列數(shù)據(jù)丟失)妻怎,更新事件發(fā)布表狀態(tài)PUBLISHED)
消息隊(duì)列將消息推送到b服務(wù)的事件訂閱表(重復(fù)則失敗,冪等泞歉,狀態(tài)TODO)逼侦,消費(fèi)者確認(rèn)后,消息隊(duì)列將消息刪除
b服務(wù):定時(shí)任務(wù)處理業(yè)務(wù)腰耙,處理成功榛丢,更新事件訂閱表狀態(tài)SUCC
注:MQ的Leader保存數(shù)據(jù),follower未保存挺庞,然后Leader出現(xiàn)問(wèn)題晰赞,導(dǎo)致消息丟失(集群、持久化)

本地服務(wù)調(diào)用其他服務(wù)日志問(wèn)題

將本地服務(wù)注冊(cè)到eurkea上选侨,鏈路追蹤掖鱼,日志攔截包裝(給上層服務(wù))

TCC

杜絕a服務(wù)-b服務(wù)-c服務(wù)-d服務(wù)
清分業(yè)務(wù)拆分:轉(zhuǎn)賬(核心企業(yè)賬戶(hù)轉(zhuǎn)銀行內(nèi)部戶(hù))、還款(內(nèi)部戶(hù)還款給銀行/供應(yīng)商)援制、核銷(xiāo)(核心企業(yè)授信額度退還)
清分總控表
查詢(xún)
轉(zhuǎn)賬定時(shí)任務(wù):查詢(xún)TODO的戏挡,轉(zhuǎn)賬,如果請(qǐng)求超時(shí)/報(bào)錯(cuò)/返回超時(shí)-TOCK隘谣,成功- SUCC
轉(zhuǎn)賬同步定時(shí)任務(wù):查詢(xún)轉(zhuǎn)賬記錄增拥,請(qǐng)求超時(shí)/返回超時(shí)-狀態(tài)不變啄巧,空-FAIL,有數(shù)據(jù)-SUCC
轉(zhuǎn)賬異常查詢(xún)-重試
無(wú)論是強(qiáng)一致性還是最終一致性掌栅,都不能保證100%的一致性秩仆,日終對(duì)賬:當(dāng)天內(nèi)部戶(hù)入賬余額+昨天內(nèi)部戶(hù)入賬余額是否等于查詢(xún)到的余額

代理與裝飾

裝飾器模式關(guān)注于在一個(gè)對(duì)象上動(dòng)態(tài)的添加方法,然而代理模式關(guān)注于控制對(duì)對(duì)象的訪(fǎng)問(wèn)
代理 非業(yè)務(wù)校驗(yàn)攔截(減少冗余代碼猾封,不必關(guān)心這些校驗(yàn)邏輯澄耍,緩存一些信息):api切換、日志晌缘、業(yè)務(wù)參考號(hào)齐莲、項(xiàng)目信息、客戶(hù)信息磷箕、客戶(hù)號(hào)校驗(yàn) 業(yè)務(wù)修飾(拓展自己选酗,增強(qiáng)自身)
裝飾 定制化 業(yè)務(wù)校驗(yàn) 主體邏輯相同 額外的裝飾

Spring-IOC

1、解析注冊(cè):使用Resource定位xml配置岳枷;使用BeanDefinitionReader讀取配置芒填,并封裝成BeanDefinition;使用BeanDefinitionRegistry將BeanDefinition注冊(cè)到BeanDefinitionMap中
2空繁、BeanFatory中單例bean的加載過(guò)程
a殿衰、轉(zhuǎn)換對(duì)應(yīng)的beanName:傳入的參數(shù)可能不是bean的name,可能是別名盛泡、FactoryBean(&開(kāi)頭)
b闷祥、嘗試從緩存(兩級(jí)緩存)中獲取單例bean
c、獲取失敗再?lài)L試從singletonFactories中獲取ObjectFatory傲诵,通過(guò)ObjectFatory去加載:

1凯砍、在創(chuàng)建單例bean時(shí)為解決依賴(lài)注入,不等bean創(chuàng)建完成就將創(chuàng)建bean的工廠ObjectFatory提早曝光并加入緩存中掰吕,其他單例bean創(chuàng)建時(shí)如果需要依賴(lài)該bean果覆,直接從緩存中獲取單例bean或獲取ObjectFatory去創(chuàng)建
2、調(diào)用ObjectFatory的getObject方法先獲取實(shí)例化但還未初始化的單例bean殖熟,加入到earlySingletonObjects緩存中局待,將單例工廠從singletonFactories中移除,返回單例bean(已經(jīng)可以通過(guò)getBean方法獲取到)
3菱属、單例bean的轉(zhuǎn)換:獲取的bean可能是原始狀態(tài)(有可能獲取的是Factorybean钳榨,需要調(diào)用FactoryBean中的getObject方法獲取單例bean)

d、如果緩存中沒(méi)有纽门,以下從頭開(kāi)始創(chuàng)建單例bean
e薛耻、根據(jù)beanName嘗試從beanDefinitionMap中獲取對(duì)應(yīng)的beanDefinition中的配置,如果獲取不到配置赏陵,嘗試遞歸根據(jù)parentBeanFactory去加載(調(diào)用父類(lèi)工廠的getBean方法)
f饼齿、前置處理:創(chuàng)建單例bean之前饲漾,記錄單例bean正在創(chuàng)建狀態(tài),用于檢測(cè)循環(huán)依賴(lài)
g缕溉、通過(guò)ObjectFatory創(chuàng)建單例bean考传,調(diào)用ObjectFatory的getObject方法獲取提早曝光的單例bean(實(shí)例化還未初始化):

1、處理override屬性:為了后面實(shí)例化單例bean時(shí)更好的處理证鸥,這里先預(yù)先判斷一下是否需要覆蓋或重載僚楞,后面處理的原理就是在實(shí)例化單例bean時(shí)如果檢測(cè)到methodOverrides時(shí),會(huì)動(dòng)態(tài)地為當(dāng)前bean生成代理并使用對(duì)應(yīng)的攔截器為bean做增強(qiáng)處理
2枉层、實(shí)例化單例bean前置處理泉褐,短路處理:Spring AOP代理相關(guān),如果需要使用代理bean且代理bean已經(jīng)創(chuàng)建鸟蜡,直接返回
3膜赃、實(shí)例化單例bean
a、將BeanDefinition轉(zhuǎn)換為BeanWrapper(對(duì)反射相關(guān)API的簡(jiǎn)單封裝矩欠,使得上層使用反射完成相關(guān)的業(yè)務(wù)邏輯大大的簡(jiǎn)化
b财剖、需要選擇不同的實(shí)例化策略:如果有需要覆蓋或動(dòng)態(tài)替換的方法悠夯,需要使用cglib進(jìn)行動(dòng)態(tài)代理癌淮,因?yàn)榭梢栽趧?chuàng)建代理的同時(shí)將動(dòng)態(tài)方法織入類(lèi)中,否則可以直接用反射
4沦补、實(shí)例化bean后置處理
5乳蓄、如果需要解決循環(huán)依賴(lài)(滿(mǎn)足3個(gè)條件:?jiǎn)卫⒃试S循環(huán)依賴(lài)夕膀、當(dāng)前bean正在創(chuàng)建)虚倒,則提早曝光單例工廠(將ObjectFactory放入工廠緩存singletonFactories中,其他單例bean在創(chuàng)建時(shí)調(diào)用getObject方法可以獲取未創(chuàng)建好的單例bean产舞,getObject方法中實(shí)現(xiàn)Spring AOP 的advice動(dòng)態(tài)織入)
(構(gòu)造函數(shù)注入循環(huán)依賴(lài)問(wèn)題spring不能解決 循環(huán)依賴(lài)是在實(shí)例化后處理的)
6魂奥、屬性注入(填充)(遞歸初始化)
7、激活aware方法(通過(guò)aware方法可以獲取對(duì)應(yīng)的資源:BeanNameAware 獲取bean名稱(chēng)易猫,BeanClassLoaderAware 獲取bean的類(lèi)加載器耻煤;BeanFactory 獲取bean的工廠,即加載到IOC容器中)
8准颓、初始化單例bean前置處理
9哈蝇、激活用戶(hù)自定義的init方法:如afterPropertiesSet方法、init-method攘已,afterPropertiesSet先執(zhí)行炮赦,init-method后執(zhí)行
10、初始化單例bean后置處理(spring AOP 在這里實(shí)現(xiàn):攔截器生成代理對(duì)象)
11样勃、檢測(cè)循環(huán)依賴(lài)
12吠勘、注冊(cè)destroy-method銷(xiāo)毀方法

h性芬、后置處理:創(chuàng)建單例bean之后,移除單例bean正在創(chuàng)建狀態(tài)剧防,用于檢測(cè)循環(huán)依賴(lài)
i批旺、將單例bean放入單例緩存singletonObjects,從單例工廠緩存singletonFactories中移除單例工廠诵姜,從單例bean緩存earlySingletonObjects中移除單例bean汽煮,保存已注冊(cè)的單例bean
j、類(lèi)型轉(zhuǎn)換:將bean轉(zhuǎn)換為需要的類(lèi)型
3棚唆、ApplicationContext
a暇赤、環(huán)境準(zhǔn)備:如系統(tǒng)屬性或環(huán)境變量的準(zhǔn)備及驗(yàn)證
b、加載BeanFactory宵凌,并讀取配置文件:

創(chuàng)建BeanFactory(DefaultListableBeanFactory)
定制BeanFactory(在基本容器的基礎(chǔ)上鞋囊,增加了是否允許覆蓋是否允許擴(kuò)展的設(shè)置,并提供了對(duì)注解@Qualifier瞎惫、@Autowired的支持)
加載beanDefinition溜腐,讀取配置文件(通過(guò)beanDefinitionReader加載beanDefinition(并注冊(cè)到beanFactory的BeanDefinitionMap中))
使用全局變量記錄beanFactory

c、對(duì)BeanFactory進(jìn)行功能填充:

如對(duì)@Qualifier和@Autowired注解的支持
增加AspectJ支持
增加屬性注冊(cè)編輯器(Spring DI 依賴(lài)注入時(shí) Date類(lèi)型是無(wú)法識(shí)別的)

d瓜喇、子類(lèi)通過(guò)覆蓋方法做額外處理
e挺益、激活(調(diào)用)BeanFactory處理器(容器級(jí)),可以有多個(gè)乘寒,通過(guò)排序依次處理:

beanFactory處理器可以在實(shí)例化任何bean之前獲得配置元數(shù)據(jù)并修改BeanDefinition(如${}替換)
@ComponentScan就是在這里實(shí)現(xiàn)的
注冊(cè)bean處理器(BeanFactory沒(méi)有注冊(cè)(因此不能使用)望众,需要手動(dòng)注冊(cè)),在bean創(chuàng)建時(shí)調(diào)用

f伞辛、注冊(cè)攔截bean創(chuàng)建的bean處理器烂翰,這里只是注冊(cè),真正調(diào)用是在getBean方法中
g蚤氏、國(guó)際化處理
h甘耿、初始化應(yīng)用消息廣播器
i、子類(lèi)覆蓋方法處理
j竿滨、在所有注冊(cè)的bean中查找要監(jiān)聽(tīng)的bean佳恬,注冊(cè)到消息廣播器中(注冊(cè)監(jiān)聽(tīng)器,并在合適的時(shí)候通知監(jiān)聽(tīng)器)
k姐呐、通過(guò)beanFactory加載bean(非延遲加載):ApplicationContext在啟動(dòng)時(shí)會(huì)加載所有的單例bean殿怜,調(diào)用getBean方法(上面Spring中BeanFactory加載bean的過(guò)程)
l、完成刷新曙砂,通知生命周期管理器lifecycleProcessor刷新過(guò)程头谜,并通過(guò)事件通知監(jiān)聽(tīng)者


Spring-FactoryBean、BeanFactory鸠澈、ObjectFactory

1柱告、FactoryBean:

1截驮、一般情況下,Spring通過(guò)反射機(jī)制來(lái)實(shí)例化bean际度,而這樣可能需要很多配置葵袭,可以通過(guò)實(shí)現(xiàn)FactoryBean接口以編碼的方式來(lái)代替
2、在IOC容器的基礎(chǔ)上給Bean的實(shí)現(xiàn)加上了一個(gè)簡(jiǎn)單工廠模式和裝飾模式乖菱,是一個(gè)可以生產(chǎn)對(duì)象和裝飾對(duì)象的工廠bean
它是泛型的坡锡,只能固定生產(chǎn)某一類(lèi)對(duì)象,而不像BeanFactory那樣可以生產(chǎn)多種類(lèi)型的Bean
3窒所、當(dāng)bean實(shí)現(xiàn)FactoryBean接口時(shí)鹉勒,通過(guò)工廠的getBean方法返回的是FactoryBean中的getObject方法返回的實(shí)例,如果想要返回當(dāng)前bean吵取,需要以&開(kāi)頭

2禽额、BeanFactory:對(duì)象工廠,用于實(shí)例化和保存對(duì)象
3皮官、ObjectFactory:某個(gè)特定的工廠脯倒,用于在項(xiàng)目啟動(dòng)時(shí),延遲實(shí)例化對(duì)象捺氢,解決循環(huán)依賴(lài)問(wèn)題藻丢, 調(diào)用它的getObject方法時(shí),才會(huì)觸發(fā) Bean 實(shí)例化


Spring單例下如何解決循環(huán)依賴(lài)(三級(jí)緩存)

Spring中循環(huán)依賴(lài)包括構(gòu)造器依賴(lài)和setter注入依賴(lài)
Spring只能解決單例setter注入依賴(lài)(注入時(shí)會(huì)返回提前暴露的創(chuàng)建中的bean)
構(gòu)造器依賴(lài)無(wú)法解決(因?yàn)橹挥袑?shí)例化之后才能曝光讯沈,實(shí)例化前曝光是有風(fēng)險(xiǎn)的)
對(duì)于原型模式郁岩,循環(huán)依賴(lài)也是無(wú)法解決的(因?yàn)椴皇褂镁彺妫?/p>

bean什么時(shí)候才會(huì)提早曝光:?jiǎn)卫?chuàng)建中缺狠、允許循環(huán)依賴(lài)

1、根據(jù)beanName嘗試從singletionObjects中獲取實(shí)例
2萍摊、根據(jù)beanName嘗試從earlySingletionObjects中獲取實(shí)例
3挤茄、根據(jù)beanName嘗試從singletonFactories中獲取ObjectFactory,調(diào)用getObject方法創(chuàng)建bean(這里只是實(shí)例化了bean)冰木,放到earlySingletionObjects中穷劈,并從singletonFactories中移除ObjectFactory(互斥操作)這時(shí)已經(jīng)可以通過(guò)容器的getBean方法獲取到bean

setter循環(huán)依賴(lài)解決過(guò)程:

1、創(chuàng)建a時(shí)踊沸,暴露ObjectFactory歇终,標(biāo)記a在創(chuàng)建中,根據(jù)構(gòu)造器實(shí)例化bean逼龟,setter注入b
2评凝、創(chuàng)建b時(shí),暴露ObjectFactory腺律,標(biāo)記b在創(chuàng)建中奕短,根據(jù)構(gòu)造器實(shí)例化bean宜肉,setter注入a


Spring-AOP

靜態(tài)代理、動(dòng)態(tài)代理:

1翎碑、靜態(tài)代理直接調(diào)用目標(biāo)類(lèi)方法
2谬返、動(dòng)態(tài)代理通過(guò)反射調(diào)用目標(biāo)類(lèi)方法

JDK動(dòng)態(tài)代理、CGLIB動(dòng)態(tài)代理:

JDK是在運(yùn)行期間創(chuàng)建接口的實(shí)現(xiàn)類(lèi)來(lái)完成對(duì)目標(biāo)對(duì)象的代理
CGLIB采用了非常底層的字節(jié)碼技術(shù)日杈,其原理是通過(guò)字節(jié)碼技術(shù)為目標(biāo)類(lèi)創(chuàng)建子類(lèi):
1遣铝、生成代理類(lèi)Class的二進(jìn)制字節(jié)碼
2、通過(guò)Class.forName加載二進(jìn)制字節(jié)碼莉擒,生成Class對(duì)象
3翰蠢、通過(guò)反射機(jī)制獲取代理類(lèi)實(shí)例構(gòu)造,并初始化代理類(lèi)對(duì)象
4啰劲、在子類(lèi)中采用方法攔截的技術(shù)攔截所有父類(lèi)方法的調(diào)用梁沧,順勢(shì)織入橫切邏輯

要素:連接點(diǎn)(方法執(zhí)行處)、切入點(diǎn)(何處織入通知)蝇裤、通知(處理時(shí)機(jī)及處理邏輯)廷支、切面(包括切入點(diǎn)和通知)
源碼:
1、解析器解析AOP代理注解栓辜,生成BeanDefination恋拍,并注冊(cè)到BeanDefinitionMap中
2、創(chuàng)建自動(dòng)代理創(chuàng)建器(用來(lái)實(shí)現(xiàn)AOP)(AnnotationAwareAspectJAutoProxyCreator)
3藕甩、選擇代理實(shí)現(xiàn)方式:

1施敢、默認(rèn)如果目標(biāo)對(duì)象有實(shí)現(xiàn)接口,則使用JDK動(dòng)態(tài)代理
2狭莱、否則使用CGLIB代理(無(wú)法覆寫(xiě)final方法僵娃,可以通過(guò)proxy-target-class屬性強(qiáng)制使用CGLIB代理)
3、expose-proxy屬性是為了解決有時(shí)候目標(biāo)對(duì)象內(nèi)部的自我調(diào)用無(wú)法實(shí)現(xiàn)切面增強(qiáng)的問(wèn)題(強(qiáng)制暴露代理腋妙,在代碼中可以獲取這個(gè)代理進(jìn)行顯式調(diào)用)

4默怨、創(chuàng)建AOP動(dòng)態(tài)代理:

1、自動(dòng)代理創(chuàng)建器實(shí)現(xiàn)了BeanPostProcessor接口骤素,Spring在目標(biāo)bean初始化完成之后會(huì)調(diào)用其postProcessAfterInitialization方法來(lái)創(chuàng)建AOP動(dòng)態(tài)代理
2匙睹、獲取增強(qiáng),獲取所有增強(qiáng)中目標(biāo)bean可用的增強(qiáng)
3济竹、創(chuàng)建代理工廠痕檬,根據(jù)配置設(shè)置JDK動(dòng)態(tài)代理,或者CGLIB代理送浊,將目標(biāo)bean及其增強(qiáng)添加到代理工廠梦谜,通過(guò)代理工廠創(chuàng)建代理并返回(將增強(qiáng)組成攔截器鏈,執(zhí)行目標(biāo)方法時(shí),執(zhí)行攔截器鏈改淑,中間會(huì)調(diào)用目標(biāo)方法)


Spring事務(wù)失效的場(chǎng)景

1碍岔、注解@Transactional配置的方法非public權(quán)限修飾

可以開(kāi)啟 AspectJ 代理模式解決

2、注解@Transactional所在類(lèi)非Spring容器管理的bean
3朵夏、注解@Transactional所在類(lèi)中蔼啦,注解修飾的方法被類(lèi)內(nèi)部方法調(diào)用

場(chǎng)景:無(wú)事務(wù)方法調(diào)用有事務(wù)方法,事務(wù)失效
解決:使用代理對(duì)象調(diào)用解決仰猖,且要在啟動(dòng)類(lèi)上加注解@EnableAspectJAutoProxy(exposeProxy = true)):
1捏肢、Spring在掃描Bean的時(shí)候會(huì)自動(dòng)為標(biāo)注了@Transactional注解的類(lèi)生成一個(gè)代理對(duì)象(proxy)
2、當(dāng)有注解的方法被調(diào)用的時(shí)候饥侵,實(shí)際上是調(diào)用代理對(duì)象的方法
3鸵赫、代理對(duì)象在調(diào)用之前會(huì)開(kāi)啟事務(wù),執(zhí)行事務(wù)的操作
4躏升、但是同類(lèi)中的方法互相調(diào)用辩棒,相當(dāng)于this.B(),此時(shí)的B方法并非是代理對(duì)象的方法膨疏,而是直接通過(guò)原有的Bean直接調(diào)用一睁,所以注解會(huì)失效

4、業(yè)務(wù)代碼拋出異常類(lèi)型非RuntimeException佃却,事務(wù)失效
5者吁、業(yè)務(wù)代碼中存在異常時(shí),使用try…catch…語(yǔ)句塊捕獲饲帅,而catch語(yǔ)句塊沒(méi)有throw new RuntimeExecption異常(最難被排查到問(wèn)題且容易忽略)
6复凳、注解@Transactional中Propagation屬性值設(shè)置錯(cuò)誤即Propagation.NOT_SUPPORTED(一般不會(huì)設(shè)置此種傳播機(jī)制)
7、mysql關(guān)系型數(shù)據(jù)庫(kù)灶泵,且存儲(chǔ)引擎是MyISAM而非InnoDB育八,則事務(wù)會(huì)不起作用(基本開(kāi)發(fā)中不會(huì)遇到)


Spring Boot-啟動(dòng)原理

@SpringBootApplication注解:
1、@SpringBootConfiguration注解

繼承Configuration丘逸,表示啟動(dòng)類(lèi)是IOC容器的配置類(lèi)

2单鹿、@EnableAutoConfiguration注解:

1、通過(guò)@AutoConfigurationPackage注解獲取自動(dòng)配置包深纲,返回當(dāng)前主類(lèi)的同級(jí)以及子級(jí)的中斷自動(dòng)配置組件
2、開(kāi)啟springboot的配置功能劲妙,借助@Import({EnableAutoConfigurationImportSelector.class})實(shí)現(xiàn)湃鹊,將自動(dòng)配置組件對(duì)應(yīng)的bean定義都加載到IoC容器中
3、通過(guò)Spring的SpringFactoriesLoader(Spring工廠加載器)去讀取META-INF/spring.factories中的配置類(lèi)信息镣奋,通過(guò)反射生成一個(gè)配置類(lèi)(里面有許多bean定義)
4币呵、將這些bean定義加載到IOC容器中(但是不是所有存在于spring.factories中的配置都進(jìn)行加載,而是通過(guò)@ConditionalOnClass注解進(jìn)行判斷條件是否成立(只要導(dǎo)入相應(yīng)的starter,條件就能成立)余赢,如果條件成立則加載配置類(lèi)芯义,否則不加載該配置類(lèi))
https://www.cnblogs.com/xiaopotian/p/11052917.html

3、@ComponentScan注解:

自動(dòng)掃描并加載符合條件的組件(如@Component和@Repository等)或者bean定義妻柒,最終將這些bean加載到IoC容器中扛拨,在beanFactoryProcessor中調(diào)用

run方法:

1、創(chuàng)建監(jiān)聽(tīng)器
2举塔、加載配置環(huán)境
3绑警、創(chuàng)建ConfigurableApplicationContext
4、spring.factories加載央渣,bean實(shí)例化

image.png

分布式事務(wù)

CAP:一致性计盒、可用性、容錯(cuò)性
BASE:可用性芽丹、容錯(cuò)性北启、最終一致性(可能會(huì)存在中間狀態(tài)如:處理中)
1、兩階段提交(2PC)(基于數(shù)據(jù)庫(kù)拔第,MySQL和Oracle支持):準(zhǔn)備階段(資源鎖定咕村,執(zhí)行本地事務(wù),并寫(xiě)日志undo(修改后數(shù)據(jù))/redo日志(修改前數(shù)據(jù)))楼肪、提交/回滾階段培廓,中間由事務(wù)管理器控制全局事務(wù),資源鎖需要等到兩個(gè)階段結(jié)束才釋放春叫,性能較差肩钠,會(huì)出現(xiàn)死鎖問(wèn)題
2、seata改進(jìn)2PC:事務(wù)管理器(事務(wù)發(fā)起服務(wù)引入暂殖,負(fù)責(zé)發(fā)起全局事務(wù)价匠,發(fā)起全局提交或全局回滾的指令)、事務(wù)協(xié)調(diào)器(單獨(dú)的服務(wù)呛每,控制踩窖,維護(hù)全局事務(wù)狀態(tài),協(xié)調(diào)各分支事務(wù)提交/回滾)晨横、資源管理器(控制每個(gè)分支事務(wù)洋腮,使用DataSourceProxy連接數(shù)據(jù)庫(kù),使用ConnectionProxy操作數(shù)據(jù)庫(kù)手形,目的就是在第一階段執(zhí)行本地事務(wù)的同時(shí)啥供,寫(xiě)入undo_log表(保存修改前和修改后的數(shù)據(jù)),因此第一階段就能進(jìn)行事務(wù)提交库糠,并釋放資源伙狐;第二階段提交時(shí)只需要?jiǎng)h除undo_log表數(shù)據(jù),回滾時(shí)反向執(zhí)行即可)
3、TCC:預(yù)處理Try(業(yè)務(wù)檢查(一致性)及資源預(yù)留(隔離)執(zhí)行)贷屎、確認(rèn) Confirm(確認(rèn)提交)罢防、撤銷(xiāo)Cancel(回滾);如處理表(中間有狀態(tài)唉侄、流水號(hào))咒吐、被調(diào)用方保持冪等、并提供查詢(xún)接口/回調(diào)
4美旧、可靠消息最終一致性:本地消息表+消息中間件(通過(guò)本地事務(wù)保證數(shù)據(jù)業(yè)務(wù)操作和消息的一致性渤滞,然后通過(guò)定時(shí)任務(wù)將消息發(fā)送至消息中間件,待確認(rèn)消息發(fā)送給消費(fèi)方成功再將消息刪除)
要解決以下問(wèn)題:
**本地事務(wù)與消息發(fā)送的原子性問(wèn)題
**事務(wù)參與方接收消息的可靠性(一定能夠接收到消息)
**消息重復(fù)消費(fèi)的問(wèn)題(冪等性)
MQ的ack(即消息確認(rèn))機(jī)制榴嗅,消費(fèi)者監(jiān)聽(tīng)MQ妄呕,如果消費(fèi)者接收到消息并且業(yè)務(wù)處理完成后向MQ 發(fā)送ack(即消息確認(rèn)),此時(shí)說(shuō)明消費(fèi)者正常消費(fèi)消息完成嗽测,MQ將不再向消費(fèi)者推送消息绪励,否則消費(fèi)者會(huì)不斷重試向消費(fèi)者來(lái)發(fā)送消息
5、最大努力通知


zookeeper

zookeeper是一個(gè)典型的分布式數(shù)據(jù)一致性解決方案
節(jié)點(diǎn)類(lèi)型:持久節(jié)點(diǎn)唠粥、持久順序節(jié)點(diǎn)疏魏、臨時(shí)節(jié)點(diǎn)(客戶(hù)端會(huì)話(huà),非TCP連接晤愧,只能作為葉子節(jié)點(diǎn))大莫、臨時(shí)順序節(jié)點(diǎn)
集群:Leader、Follower官份、Observer(不參與Leader選舉只厘、也不參與寫(xiě)操作的“過(guò)半寫(xiě)成功”策略)
Leader選舉:

分布式鎖

Redis分布式鎖:
1、加鎖:set 命令要用 set key value px milliseconds nx舅巷,替代 setnx + expire 需要分兩次執(zhí)行命令的方式羔味,保證了原子性;給鎖加上一個(gè)過(guò)期時(shí)間钠右,即使Redis客戶(hù)端中間出現(xiàn)異常(來(lái)不及調(diào)用lua腳本釋放鎖)也可以保證過(guò)期時(shí)鎖會(huì)自動(dòng)釋放(但是如果Redis服務(wù)端異常就沒(méi)辦法解決)赋元;超時(shí)問(wèn)題解決:lua腳本+額外線(xiàn)程進(jìn)行鎖延時(shí)
2、解鎖:將lua腳本傳到j(luò)edis.eval()方法里飒房,并使參數(shù)KEYS[1]賦值為lockKey(鎖標(biāo)志)搁凸,ARGV[1]賦值為requestId(Redis客戶(hù)端標(biāo)志);在執(zhí)行的時(shí)候狠毯,首先會(huì)獲取鎖對(duì)應(yīng)的value值坪仇,檢查是否與requestId相等,如果相等則解鎖(刪除key)垃你;比較requestId是為了解決超時(shí)問(wèn)題:如果在加鎖和釋放鎖之間的邏輯執(zhí)行的太長(zhǎng),以至于超出了鎖的超時(shí)限制,就會(huì)出現(xiàn)問(wèn)題惜颇,因?yàn)檫@時(shí)候鎖過(guò)期了皆刺,第二個(gè)線(xiàn)程重新持有了這把鎖,但是緊接著第一個(gè)線(xiàn)程執(zhí)行完了業(yè)務(wù)邏輯凌摄,就把鎖給釋放了羡蛾,第三個(gè)線(xiàn)程就會(huì)在第二個(gè)線(xiàn)程邏輯執(zhí)行完之前拿到了鎖
3、可重入性:對(duì)客戶(hù)端的 set 方法進(jìn)行包裝锨亏,使用線(xiàn)程的 Threadlocal 變量存儲(chǔ)當(dāng)前線(xiàn)程持有鎖的計(jì)數(shù)痴怨,還需要考慮內(nèi)存鎖計(jì)數(shù)的過(guò)期時(shí)間
問(wèn)題:如果存儲(chǔ)鎖對(duì)應(yīng)key的那個(gè)節(jié)點(diǎn)掛了的話(huà),就可能存在丟失鎖的風(fēng)險(xiǎn)器予,導(dǎo)致出現(xiàn)多個(gè)客戶(hù)端持有鎖的情況浪藻,這樣就不能實(shí)現(xiàn)資源的獨(dú)占了(即Redis服務(wù)端出現(xiàn)問(wèn)題),即使是Redis主從也不能解決問(wèn)題(Redis的主從同步通常是異步的)
解決:
1乾翔、Redlock算法:輪流嘗試在每個(gè)節(jié)點(diǎn)上創(chuàng)建鎖爱葵,過(guò)期時(shí)間較短,一般就幾十毫秒反浓,至少要在大多數(shù)節(jié)點(diǎn)上成功創(chuàng)建鎖萌丈,才說(shuō)明獲取到鎖,客戶(hù)端計(jì)算創(chuàng)建鎖的時(shí)間雷则,如果創(chuàng)建鎖的時(shí)間小于超時(shí)時(shí)間辆雾,就是創(chuàng)建成功了;如果創(chuàng)建鎖失敗了月劈,那么就依次刪除以前創(chuàng)建過(guò)的鎖度迂;如果其他客戶(hù)端已經(jīng)創(chuàng)建鎖,就得不斷輪詢(xún)?nèi)L試獲取鎖
2艺栈、Redisson:RedissonLock 同樣沒(méi)有解決節(jié)點(diǎn)掛掉的時(shí)候英岭,存在丟失鎖的風(fēng)險(xiǎn)的問(wèn)題;Redisson 提供了實(shí)現(xiàn)了redlock算法的 RedissonRedLock湿右,RedissonRedLock 真正解決了單點(diǎn)失敗的問(wèn)題诅妹,代價(jià)是需要額外的為 RedissonRedLock 搭建Redis環(huán)境
zookeeper分布式鎖:
獲取鎖:客戶(hù)端獲取鎖時(shí),調(diào)用create方法創(chuàng)建臨時(shí)順序節(jié)點(diǎn)(Zookeeper會(huì)保證所有的客戶(hù)端中毅人,最終只有一個(gè)客戶(hù)端能夠創(chuàng)建成功吭狡,沒(méi)有獲取到鎖的客戶(hù)端需要?jiǎng)?chuàng)建一個(gè)節(jié)點(diǎn)去Watch監(jiān)聽(tīng)鎖節(jié)點(diǎn))
釋放鎖:當(dāng)前獲取鎖的客戶(hù)端宕機(jī)/業(yè)務(wù)邏輯執(zhí)行完都會(huì)移除臨時(shí)順序鎖節(jié)點(diǎn),并通知所有Watch節(jié)點(diǎn)去重新嘗試獲取鎖

Redis-基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)

1丈莺、String:類(lèi)似于ArrayList划煮,字節(jié)數(shù)組,用途:緩存用戶(hù)信息(序列化和反序列化)
2缔俄、List:類(lèi)似于LinkedList弛秋,鏈表+壓縮列表(數(shù)據(jù)量少時(shí)器躏,只用壓縮列表),增刪快蟹略,查詢(xún)慢登失,用途:異步隊(duì)列
3、Hash:類(lèi)似于HashMap挖炬,數(shù)組+鏈表褪秀,漸進(jìn)式reHash(中間新舊數(shù)據(jù)都會(huì)讀)剧蚣,用途:緩存用戶(hù)信息
4你辣、Set:類(lèi)似于HashSet立轧,value值為空,用途:去重
5草姻、ZSet: 類(lèi)似于SortedSet 和 HashMap 的結(jié)合體钓猬,跳躍列表,既要隨機(jī)增刪碴倾,又要排序逗噩,用途:核心企業(yè)/供應(yīng)商列表
6、跳躍列表:每一層是一個(gè)單向鏈表跌榔,每一層有一個(gè)額外的節(jié)點(diǎn)去定位每一層的頭節(jié)點(diǎn)


image.png

Redis-緩存一致性

1异雁、先刪除緩存,再更新數(shù)據(jù)庫(kù)(緩存設(shè)置過(guò)期時(shí)間)
問(wèn)題:如果兩個(gè)并發(fā)操作僧须,一個(gè)讀操作纲刀,一個(gè)寫(xiě)操作,寫(xiě)操作刪除緩存担平,讀操作從緩存讀取數(shù)據(jù)失敗示绊,從數(shù)據(jù)庫(kù)讀取數(shù)據(jù)成功,然后更新緩存暂论,寫(xiě)操作更新數(shù)據(jù)庫(kù)面褐,無(wú)法避免這種情況的緩存一致性
解決:延遲雙刪:寫(xiě)操作更新數(shù)據(jù)庫(kù)成功后,sleep(睡眠時(shí)間不好控制)一段時(shí)間取胎,再刪除一次緩存
2展哭、先更新數(shù)據(jù)庫(kù),再刪除緩存(緩存設(shè)置過(guò)期時(shí)間)(推薦)
原理:更新數(shù)據(jù)庫(kù)時(shí)闻蛀,會(huì)加鎖匪傍,其他操作不能操作這條數(shù)據(jù)
問(wèn)題:寫(xiě)操作時(shí),更新數(shù)據(jù)庫(kù)成功觉痛,刪除緩存失敗役衡,讀操作仍讀取緩存中的舊數(shù)據(jù)
解決:
**消息隊(duì)列:更新數(shù)據(jù)庫(kù),插入本地消息表薪棒,刪除緩存手蝎,消息隊(duì)列重試去刪除緩存(需要考慮消息隊(duì)列的一些常見(jiàn)問(wèn)題)
**消息隊(duì)列+binlog日志:更新數(shù)據(jù)庫(kù)時(shí)榕莺,會(huì)插入binlog日志,通過(guò)canal讀取binlog日志柑船,推送給消息隊(duì)列帽撑,消息隊(duì)列重試去刪除緩存(與業(yè)務(wù)代碼解耦)
3、為什么是刪除緩存鞍时,而不是更新緩存
**問(wèn)題1:如果兩個(gè)并發(fā)操作,一個(gè)寫(xiě)操作a扣蜻,一個(gè)寫(xiě)操作b逆巍,a更新數(shù)據(jù)庫(kù),釋放鎖莽使,b更新數(shù)據(jù)庫(kù)锐极,釋放鎖,b更新緩存完成芳肌,a更新緩存完成灵再,無(wú)法避免這種情況的緩存一致性
**問(wèn)題2:每次都更新緩存,會(huì)導(dǎo)致性能消耗

Redis-緩存穿透亿笤、緩存雪崩翎迁、緩存擊穿

緩存穿透
定義:查找一定不存在的數(shù)據(jù)(如惡意攻擊),在緩存中根據(jù)key查不到净薛,在數(shù)據(jù)庫(kù)中也查詢(xún)不到汪榔,所以不會(huì)存到緩存中,每次都要查詢(xún)數(shù)據(jù)庫(kù)
解決:
1肃拜、布隆過(guò)濾:將一切可能查詢(xún)的key存到map中痴腌,請(qǐng)求過(guò)來(lái)時(shí)先去map中查找,不存在直接丟棄
2燃领、即使在數(shù)據(jù)庫(kù)中查詢(xún)不到士聪,空值也存到緩存中,設(shè)置過(guò)期時(shí)間(浪費(fèi)空間猛蔽,且在有效期內(nèi)可能數(shù)據(jù)不一致)
緩存雪崩
定義:緩存中大量的數(shù)據(jù)同時(shí)失效(如服務(wù)掛掉和同時(shí)過(guò)期)剥悟,直接去數(shù)據(jù)庫(kù)中查詢(xún)
解決:
1、過(guò)期時(shí)間設(shè)置均勻(避免同時(shí)過(guò)期)(事前)
2枢舶、緩存服務(wù)高可用(主從+哨兵)(事前)
3懦胞、限流:緩存失效時(shí),通過(guò)加鎖或者隊(duì)列來(lái)控制讀數(shù)據(jù)庫(kù)寫(xiě)緩存的線(xiàn)程數(shù)量(如對(duì)某個(gè)key只允許一個(gè)線(xiàn)程查詢(xún)數(shù)據(jù)和寫(xiě)緩存凉泄,其他線(xiàn)程等待)躏尉,避免數(shù)據(jù)庫(kù)崩掉(事中)
4、Redis 持久化:一旦重啟后众,自動(dòng)從磁盤(pán)上加載數(shù)據(jù)胀糜,快速恢復(fù)緩存數(shù)據(jù)(事后)
緩存擊穿
定義:大量請(qǐng)求同時(shí)訪(fǎng)問(wèn)一個(gè)或多個(gè)熱點(diǎn)key颅拦,key如果瞬間失效,會(huì)直接請(qǐng)求數(shù)據(jù)庫(kù)教藻,導(dǎo)致數(shù)據(jù)庫(kù)崩掉
解決:
1距帅、若緩存的數(shù)據(jù)是基本不會(huì)發(fā)生更新的,則可嘗試將該熱點(diǎn)數(shù)據(jù)設(shè)置為永不過(guò)期
2括堤、若緩存的數(shù)據(jù)更新不頻繁碌秸,且緩存刷新的整個(gè)流程耗時(shí)較少的情況下,則可以采用基于 Redis悄窃、zookeeper 等分布式中間件的分布式互斥鎖讥电,或者本地互斥鎖以保證僅少量的請(qǐng)求能請(qǐng)求數(shù)據(jù)庫(kù)并重新構(gòu)建緩存,其余線(xiàn)程則在鎖釋放后能訪(fǎng)問(wèn)到新緩存
3轧抗、若緩存的數(shù)據(jù)更新頻繁或者緩存刷新的流程耗時(shí)較長(zhǎng)的情況下恩敌,可以利用定時(shí)任務(wù)在緩存過(guò)期前主動(dòng)的重新構(gòu)建緩存或者延后緩存的過(guò)期時(shí)間,以保證所有的請(qǐng)求能一直訪(fǎng)問(wèn)到對(duì)應(yīng)的緩存

Redis-持久化

1横媚、RDB全量快照(數(shù)據(jù))直接復(fù)制纠炮,快
需要解決的問(wèn)題:不能影響服務(wù)響應(yīng),如何保證邊持久化邊響應(yīng)
解決:使用Copy On Write機(jī)制來(lái)實(shí)現(xiàn)快照持久化
原理:Redis 在持久化時(shí)會(huì)產(chǎn)生一個(gè)子進(jìn)程灯蝴,子進(jìn)程剛剛產(chǎn)生時(shí)恢口,和父進(jìn)程共享內(nèi)存里面的代碼段和數(shù)據(jù)段,這是 Linux 操作系統(tǒng)的機(jī)制绽乔,在進(jìn)程分離的一瞬間弧蝇,內(nèi)存的增長(zhǎng)幾乎沒(méi)有明顯變化;子進(jìn)程只做數(shù)據(jù)持久化折砸,不會(huì)修改現(xiàn)有的內(nèi)存數(shù)據(jù)結(jié)構(gòu)看疗,只是對(duì)數(shù)據(jù)結(jié)構(gòu)進(jìn)行遍歷讀取,然后序列化寫(xiě)到磁盤(pán)中睦授;當(dāng)父進(jìn)程對(duì)其中一個(gè)頁(yè)面的數(shù)據(jù)進(jìn)行修改時(shí)两芳,會(huì)將被共享的頁(yè)面復(fù)制一份分離出來(lái),然后對(duì)這個(gè)復(fù)制的頁(yè)面進(jìn)行修改去枷,這時(shí)子進(jìn)程相應(yīng)的頁(yè)面是沒(méi)有變化的怖辆,還是進(jìn)程產(chǎn)生時(shí)那一瞬間的數(shù)據(jù)
2、AOF增量日志(指令)需要指令重放删顶,慢竖螃,一般是先存到磁盤(pán),然后再執(zhí)行指令
需要解決的問(wèn)題:文件會(huì)越來(lái)越大逗余,需要定期進(jìn)行AOF重寫(xiě)特咆;服務(wù)宕機(jī),數(shù)據(jù)丟失的問(wèn)題
原理:創(chuàng)建子進(jìn)程對(duì)內(nèi)存遍歷轉(zhuǎn)換成指令录粱,序列化到一個(gè)新的AOF日志文件中腻格;序列化完畢后再將操作期間發(fā)生的增量AOF日志追加到這個(gè)新的AOF日志文件中画拾,追加完畢后就立即替代舊的AOF日志文件,瘦身工作就完成了菜职;AOF日志在內(nèi)存緩存中青抛,需要異步將數(shù)據(jù)刷回到磁盤(pán),如果機(jī)器突然宕機(jī)酬核,AOF日志內(nèi)容可能還沒(méi)有來(lái)得及完全刷到磁盤(pán)中蜜另,這個(gè)時(shí)候就會(huì)出現(xiàn)日志丟失,因此需要每隔 1s(可配置)左右執(zhí)行一次 fsync 操作(強(qiáng)制刷新到緩存)
3愁茁、通常 Redis 的主節(jié)點(diǎn)不會(huì)進(jìn)行持久化操作蚕钦,持久化操作主要在從節(jié)點(diǎn)進(jìn)行,從節(jié)點(diǎn)是備份節(jié)點(diǎn)鹅很,沒(méi)有來(lái)自客戶(hù)端請(qǐng)求的壓力
4、混合持久化:重啟 Redis 時(shí)罪帖,很少使用 RDB 來(lái)恢復(fù)內(nèi)存狀態(tài)促煮,因?yàn)闀?huì)丟失大量數(shù)據(jù)(重啟期間的數(shù)據(jù)沒(méi)有保存),通常使用 AOF 日志重放(重啟期間的數(shù)據(jù)會(huì)追加)整袁,但是重放 AOF 日志性能相對(duì) RDB 來(lái)說(shuō)要慢很多菠齿,這樣在 Redis 實(shí)例很大的情況下,啟動(dòng)需要花費(fèi)很長(zhǎng)的時(shí)間坐昙;可以將RDB的內(nèi)容和增量的AOF存放在一起绳匀,這里的AOF不再是全量的日志,而是自持久化開(kāi)始到持久化結(jié)束的這段時(shí)間發(fā)生的增量 AOF日志炸客,通常這部分AOF日志很小疾棵,于是在Redis重啟的時(shí)候,開(kāi)啟AOF痹仙,先加載RDB的內(nèi)容是尔,然后再重放增量AOF日志就可以完全替代之前的AOF全量文件重放,重啟效率因此大幅得到提升

Redis-過(guò)期策略

Redis是單線(xiàn)的开仰,刪除也會(huì)占用線(xiàn)程的時(shí)間拟枚,Redis采用的是定期刪除 + 懶惰刪除策略(定期刪除是集中處理,惰性刪除是零散處理)
定期刪除策略
Redis單線(xiàn)程默認(rèn)會(huì)每秒進(jìn)行十次過(guò)期掃描众弓,過(guò)期掃描不會(huì)遍歷過(guò)期字典中所有的 key恩溅,而是采用了一種簡(jiǎn)單的貪心策略
1、從過(guò)期字典中隨機(jī) 20 個(gè) key
2谓娃、刪除這 20 個(gè) key 中已經(jīng)過(guò)期的 key
3脚乡、如果過(guò)期的 key 比率超過(guò) 1/4,那就重復(fù)步驟 1
4傻粘、同時(shí)每窖,為了保證過(guò)期掃描不會(huì)出現(xiàn)循環(huán)過(guò)度帮掉,導(dǎo)致線(xiàn)程卡死現(xiàn)象,算法還增加了掃描時(shí)間的上限窒典,默認(rèn)不會(huì)超過(guò) 25ms
為什么不掃描所有的過(guò)期key蟆炊?
1、會(huì)導(dǎo)致線(xiàn)上讀寫(xiě)請(qǐng)求出現(xiàn)明顯的卡頓現(xiàn)象(所以要盡量避免大量key同時(shí)過(guò)期)
2瀑志、即使掃描有 25ms 的時(shí)間上限:假如有 101 個(gè)客戶(hù)端同時(shí)將請(qǐng)求發(fā)過(guò)來(lái)了涩搓,然后前 100 個(gè)請(qǐng)求的執(zhí)行時(shí)間都是25ms,那么第 101 個(gè)指令需要等待多久才能執(zhí)行劈猪?2500ms(因?yàn)閱尉€(xiàn)程)昧甘,這個(gè)就是客戶(hù)端的卡頓時(shí)間
從庫(kù)的過(guò)期策略
1、從庫(kù)不會(huì)進(jìn)行過(guò)期掃描战得,從庫(kù)對(duì)過(guò)期的處理是被動(dòng)的充边,主庫(kù)在 key 到期時(shí),會(huì)在 AOF 文件里增加一條 del 指令常侦,同步到所有的從庫(kù)浇冰,從庫(kù)通過(guò)執(zhí)行這條 del 指令來(lái)刪除過(guò)期的key
2、因?yàn)橹噶钔绞钱惒竭M(jìn)行的聋亡,所以主庫(kù)過(guò)期的 key 的 del 指令沒(méi)有及時(shí)同步到從庫(kù)的話(huà)肘习,會(huì)出現(xiàn)主從數(shù)據(jù)的不一致,主庫(kù)沒(méi)有的數(shù)據(jù)在從庫(kù)里還存在
懶惰刪除策略
為什么要懶惰刪除坡倔?
1漂佩、刪除指令 del 會(huì)直接釋放對(duì)象的內(nèi)存,大部分情況下罪塔,這個(gè)指令非惩恫酰快,沒(méi)有明顯延遲垢袱,不過(guò)如果刪除的 key 是一個(gè)非常大的對(duì)象墓拜,比如一個(gè)包含了千萬(wàn)元素的 hash,那么刪除操作就會(huì)導(dǎo)致單線(xiàn)程卡頓
2请契、Redis 內(nèi)部實(shí)際上并不是只有一個(gè)主線(xiàn)程咳榜,它還有幾個(gè)異步線(xiàn)程專(zhuān)門(mén)用來(lái)處理一些耗時(shí)的操作,可以用異步線(xiàn)程實(shí)現(xiàn)懶惰刪除

Redis-內(nèi)存淘汰機(jī)制

1爽锥、noeviction:當(dāng)內(nèi)存超出最大內(nèi)存涌韩,寫(xiě)入請(qǐng)求會(huì)報(bào)錯(cuò),但是刪除和讀請(qǐng)求可以繼續(xù)(一般不使用氯夷,但是是默認(rèn)的)
2臣樱、allkeys-lru:當(dāng)內(nèi)存超出最大內(nèi)存,在所有的key中,移除最少使用的key雇毫,只把Redis當(dāng)作緩存時(shí)使用(推薦)
3玄捕、allkeys-random:當(dāng)內(nèi)存超出最大內(nèi)存,在所有的key中棚放,隨機(jī)移除某個(gè)key(一般不使用)
4枚粘、volatile-lru:當(dāng)內(nèi)存超出最大內(nèi)存,在設(shè)置了過(guò)期時(shí)間key的字典中飘蚯,移除最少使用的key(不會(huì)移除沒(méi)有設(shè)置過(guò)期時(shí)間的)馍迄,把Redis既當(dāng)緩存,又做持久化的時(shí)候使用
5局骤、volatile-random:當(dāng)內(nèi)存超出最大內(nèi)存攀圈,在設(shè)置了過(guò)期時(shí)間key的字典中,隨機(jī)移除某個(gè)key(不會(huì)移除沒(méi)有設(shè)置過(guò)期時(shí)間的)
6峦甩、volatile-ttl:當(dāng)內(nèi)存超出最大內(nèi)存赘来,在設(shè)置了過(guò)期時(shí)間key的字典中,優(yōu)先移除剩余時(shí)間ttl 最少的(不會(huì)移除沒(méi)有設(shè)置過(guò)期時(shí)間的)
LRU算法:
1凯傲、實(shí)現(xiàn) LRU 算法除了需要 key/value 字典外撕捍,還需要附加一個(gè)鏈表,鏈表中的元素按照一定的順序進(jìn)行排列
2泣洞、當(dāng)空間滿(mǎn)的時(shí)候,會(huì)踢掉鏈表尾部的元素
3默色、當(dāng)字典的某個(gè)元素被訪(fǎng)問(wèn)時(shí)球凰,它在鏈表中的位置會(huì)被移動(dòng)到表頭,所以鏈表的元素排列順序就是元素最近被訪(fǎng)問(wèn)的時(shí)間順序
4腿宰、位于鏈表尾部的元素就是不被重用的元素呕诉,所以會(huì)被踢掉;位于表頭的元素就是最近剛剛被人用過(guò)的元素吃度,所以暫時(shí)不會(huì)被踢(雙向鏈表)
近似 LRU 算法
1甩挫、Redis 使用的是一種近似 LRU 算法,之所以不使用 LRU 算法椿每,是因?yàn)樾枰拇罅康念~外的內(nèi)存伊者,需要對(duì)現(xiàn)有的數(shù)據(jù)結(jié)構(gòu)進(jìn)行較大的改造
2、近似LRU 算法則很簡(jiǎn)單间护,在現(xiàn)有數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ)上使用隨機(jī)采樣法+額外字段(最后一次被訪(fǎng)問(wèn)的時(shí)間戳)來(lái)淘汰元素亦渗,能達(dá)到和 LRU 算法非常近似的效果
LFU算法
1、Redis 4.0 里引入了一個(gè)新的淘汰策略 —— LFU(最近最少使用)算法
2汁尺、LFU 表示按最近的訪(fǎng)問(wèn)頻率進(jìn)行淘汰法精,它比 LRU 更加精準(zhǔn)地表示了一個(gè) key 被訪(fǎng)問(wèn)的熱度
3、如果一個(gè) key 長(zhǎng)時(shí)間不被訪(fǎng)問(wèn),只是剛剛偶然被用戶(hù)訪(fǎng)問(wèn)了一下搂蜓,那么在使用 LRU 算法下它是不容易被淘汰的狼荞,因?yàn)?LRU 算法認(rèn)為當(dāng)前這個(gè) key 是很熱的
4、而 LFU 是需要追蹤最近一段時(shí)間的訪(fǎng)問(wèn)頻率帮碰,如果某個(gè) key 只是偶然被訪(fǎng)問(wèn)一次是不足以變得很熱的相味,它需要在近期一段時(shí)間內(nèi)被訪(fǎng)問(wèn)很多次才有機(jī)會(huì)被認(rèn)為很熱

Redis-哨兵

1、負(fù)責(zé)持續(xù)監(jiān)控主從節(jié)點(diǎn)的健康收毫,當(dāng)主節(jié)點(diǎn)掛掉時(shí)攻走,自動(dòng)選擇一個(gè)最優(yōu)的從節(jié)點(diǎn)切換為主節(jié)點(diǎn)
2、客戶(hù)端來(lái)連接主從時(shí)此再,會(huì)首先連接哨兵昔搂,通過(guò)哨兵來(lái)查詢(xún)主節(jié)點(diǎn)的地址,然后再去連接主節(jié)點(diǎn)進(jìn)行數(shù)據(jù)交互
3输拇、當(dāng)主節(jié)點(diǎn)發(fā)生故障時(shí)摘符,客戶(hù)端會(huì)重新向哨兵獲取主節(jié)點(diǎn)地址,哨兵會(huì)將最新的主節(jié)點(diǎn)地址告訴客戶(hù)端策吠,無(wú)需重啟即可自動(dòng)完成節(jié)點(diǎn)切換
4逛裤、主節(jié)點(diǎn)掛掉了,原先的主從復(fù)制也斷開(kāi)了猴抹,客戶(hù)端和損壞的主節(jié)點(diǎn)也斷開(kāi)了带族,從節(jié)點(diǎn)被提升為新的主節(jié)點(diǎn),其它從節(jié)點(diǎn)開(kāi)始和新的主節(jié)點(diǎn)建立復(fù)制關(guān)系蟀给,客戶(hù)端通過(guò)新的主節(jié)點(diǎn)繼續(xù)進(jìn)行交互
5蝙砌、哨兵會(huì)持續(xù)監(jiān)控已經(jīng)掛掉了主節(jié)點(diǎn),待它恢復(fù)后跋理,集群會(huì)進(jìn)行調(diào)整择克,原先掛掉的主節(jié)點(diǎn)現(xiàn)在變成了從節(jié)點(diǎn),從現(xiàn)在的主節(jié)點(diǎn)那里建立復(fù)制關(guān)系
6前普、哨兵進(jìn)行主從切換時(shí)肚邢,客戶(hù)端如何知道地址變更了 ? 在建立連接的時(shí)候進(jìn)行了主節(jié)點(diǎn)地址變更判斷,查詢(xún)主節(jié)點(diǎn)地址拭卿,然后跟內(nèi)存中的主節(jié)點(diǎn)地址進(jìn)行比對(duì)骡湖,如果變更了,就斷開(kāi)所有連接记劈,重新使用新地址建立新連接勺鸦;如果是舊的主節(jié)點(diǎn)掛掉了,那么所有正在使用的連接都會(huì)被關(guān)閉目木,然后在重連時(shí)就會(huì)用上新地址

Redis-應(yīng)用

1换途、Scan:掃描海量數(shù)據(jù)(有游標(biāo))
2懊渡、HyperLogLog:統(tǒng)計(jì)UV
3、布隆過(guò)濾器:推薦去重(布隆過(guò)濾器能準(zhǔn)確過(guò)濾掉那些已經(jīng)看過(guò)的內(nèi)容军拟,那些沒(méi)有看過(guò)的新內(nèi)容剃执,它也會(huì)過(guò)濾掉極小一部分 (誤判)),當(dāng)布隆過(guò)濾器說(shuō)某個(gè)值存在時(shí)懈息,這個(gè)值可能不存在肾档;當(dāng)它說(shuō)不存在時(shí),那就肯定不存在
原理:對(duì)key多次無(wú)偏hash辫继,每個(gè)hash取模數(shù)組長(zhǎng)度怒见,確定位置,將該位置置為1姑宽;查詢(xún)是否存在時(shí)遣耍,再次對(duì)key多次無(wú)偏hash,取模炮车,確定位置是否為1舵变;位置可以重復(fù)利用,因此會(huì)有誤差

HashMap

ThreadLocal

1瘦穆、一個(gè)線(xiàn)程對(duì)應(yīng)多個(gè)ThreadLocal纪隙,但只有一個(gè)ThreadLocalMap(在當(dāng)前線(xiàn)程內(nèi))
2、多個(gè)線(xiàn)程可以使用同一個(gè)ThreadLocal扛或,但是是隔離的
3绵咱、ThreadLocalMap是ThreadLocal的內(nèi)部類(lèi)
4、ThreadLocalMap的key為T(mén)hreadLocal(弱引用)熙兔,value為存儲(chǔ)的值
5麸拄、內(nèi)存泄漏:當(dāng)ThreadLocal被回收時(shí),ThreadLocalMap中就可能出現(xiàn)key為null的Entry黔姜,沒(méi)有任何辦法訪(fǎng)問(wèn)這些key為null的Entry的value,如果當(dāng)前線(xiàn)程再遲遲不結(jié)束的話(huà)蒂萎,這些key為null的Entry的value就會(huì)一直存在一條強(qiáng)引用鏈:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永遠(yuǎn)無(wú)法回收(沒(méi)有調(diào)用remove)秆吵,造成內(nèi)存泄漏
6、為什么key使用弱引用:如果key使用強(qiáng)引用五慈,如果當(dāng)前線(xiàn)程再遲遲不結(jié)束的話(huà)(如線(xiàn)程池中復(fù)用線(xiàn)程)纳寂,可能會(huì)出現(xiàn)整個(gè)Entry對(duì)象都不會(huì)被回收,也會(huì)出現(xiàn)內(nèi)存泄漏問(wèn)題(更難解決)泻拦;如果key使用弱引用毙芜,即使沒(méi)有手動(dòng)刪除,key也會(huì)被回收争拐,但是會(huì)出現(xiàn)value不會(huì)被回收
7腋粥、內(nèi)存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一樣長(zhǎng),如果沒(méi)有手動(dòng)刪除對(duì)應(yīng)key就會(huì)導(dǎo)致內(nèi)存泄漏,而不是因?yàn)槿跻?br> 8隘冲、內(nèi)存泄漏解決:使用static修飾ThreadLocal引用(這樣保證ThreadLocal始終保持被引用闹瞧,不會(huì)被回收),但是最后還是要調(diào)用remove方法(ThreadLocal不會(huì)被回收展辞,value會(huì)回收)


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末奥邮,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子罗珍,更是在濱河造成了極大的恐慌洽腺,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件覆旱,死亡現(xiàn)場(chǎng)離奇詭異蘸朋,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)通殃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)度液,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人画舌,你說(shuō)我怎么就攤上這事堕担。” “怎么了曲聂?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵霹购,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我朋腋,道長(zhǎng)齐疙,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任旭咽,我火速辦了婚禮贞奋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘穷绵。我一直安慰自己轿塔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布仲墨。 她就那樣靜靜地躺著勾缭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪目养。 梳的紋絲不亂的頭發(fā)上俩由,一...
    開(kāi)封第一講書(shū)人閱讀 52,262評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音癌蚁,去河邊找鬼幻梯。 笑死兜畸,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的礼旅。 我是一名探鬼主播膳叨,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼痘系!你這毒婦竟也來(lái)了菲嘴?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤汰翠,失蹤者是張志新(化名)和其女友劉穎龄坪,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體复唤,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡健田,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了佛纫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片妓局。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖呈宇,靈堂內(nèi)的尸體忽然破棺而出好爬,到底是詐尸還是另有隱情,我是刑警寧澤甥啄,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布存炮,位于F島的核電站,受9級(jí)特大地震影響蜈漓,放射性物質(zhì)發(fā)生泄漏穆桂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一融虽、第九天 我趴在偏房一處隱蔽的房頂上張望享完。 院中可真熱鬧,春花似錦有额、人聲如沸驼侠。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至苛预,卻和暖如春句狼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背热某。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工腻菇, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留胳螟,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓筹吐,卻偏偏與公主長(zhǎng)得像糖耸,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子丘薛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容