Redis宕机数据丢失解决方案,不吃透都对不起自己
前言
昨天,有個(gè)女孩子問(wèn)我提高數(shù)據(jù)庫(kù)查詢性能有什么立竿見(jiàn)影的好方法?
這簡(jiǎn)直是一道送分題,我自豪且略帶鄙夷的說(shuō),當(dāng)然是加「索引」了。
她又不緊不慢的問(wèn),索引為什么就能提高查詢性能。
這還用問(wèn),索引就像一本書(shū)的目錄,用目錄查當(dāng)然很快。
她失望地?fù)u了搖頭,你說(shuō)的只是一個(gè)類比,可為什么通過(guò)目錄就能提高查詢速度呢。
唉,對(duì)啊,通過(guò)書(shū)目可以快速查詢,這只是一個(gè)現(xiàn)象,真正原因到底是什么呢。
那女孩看著詫異且表情僵硬的我,滿意而又意味深長(zhǎng)的笑笑:原來(lái)你這個(gè)男程序員也不會(huì),看來(lái)我還得靠自己研究了。
哎,熬夜又要憔悴了我這該死的美貌。
來(lái)自同行的羞辱,是可忍孰不可忍?!
于是,我踏上了數(shù)據(jù)庫(kù)索引學(xué)習(xí)的不歸路,原來(lái)數(shù)據(jù)庫(kù)索引使用了一種叫 B+ 樹(shù)的古老數(shù)據(jù)結(jié)構(gòu),當(dāng)然也有 Hash 等類型,暫且不說(shuō),可 B+ 樹(shù) 這是個(gè)什么妖魔鬼怪呢?
下面就來(lái)淺嘗輒止的扒一扒樹(shù)的前世今生。
二、Spring生命周期的大膽猜測(cè)
這里分享一個(gè)閱讀源碼的小技巧:捉大放小,連蒙帶猜!8字真言,我們?cè)陂喿x源碼過(guò)程中,因?yàn)槟阋?#xff0c;每一個(gè)被開(kāi)源出來(lái)的優(yōu)秀框架,其源碼的體系都是極其龐大復(fù)雜的,我們不能面面俱到,所以在看源碼過(guò)程中一定不能被細(xì)枝末節(jié)纏住,一定要先理清楚整個(gè)框架的一個(gè)大致思想和大致的框架體系,再去搞那些細(xì)枝末節(jié),其效率會(huì)好很多,其次在看源碼過(guò)程中,我們一定要大膽的去想,去猜測(cè),如果這個(gè)功能讓你自己去寫(xiě),你會(huì)怎么實(shí)現(xiàn)!
我們今天學(xué)習(xí)SpringBean的生命周期也是按照這個(gè)8字真言去學(xué)習(xí),通過(guò)我們之前所學(xué),Spring大致有以下的功能:
我們從平常的使用中,至少可以得知以上的三點(diǎn),如果讓你自己去實(shí)現(xiàn),必會(huì)如何實(shí)現(xiàn)呢?
- 首先他既然能夠幫我們自己創(chuàng)建對(duì)象,那么他肯定是通過(guò)反射來(lái)創(chuàng)建的,通過(guò)反射來(lái)創(chuàng)建,就必定繞不過(guò)去要使用Class對(duì)象創(chuàng)建,那么我們?nèi)绾潍@取Class對(duì)象呢? 去掃描項(xiàng)目,將指定的包下的加了注解的類文件切割獲取Class名稱,通過(guò)反射加載Class名稱,反射創(chuàng)建java對(duì)象!
- 我們要完成屬性的填充,為了方便和性能方面,我肯定會(huì)把這些創(chuàng)建好的對(duì)象保存起來(lái),無(wú)疑Map容器是最合適的!
- 我們?cè)趧?chuàng)建一個(gè)對(duì)象完成之后,反射拿到里面的屬性,如果需要填充,我們先去我們之前保存的容器里面去取,取不出來(lái)在反射吧這個(gè)依賴的屬性創(chuàng)建出來(lái),然后填充進(jìn)對(duì)象再保存在容器里面,從而完成了屬性的注入!
- 填充完成屬性之后,我們那當(dāng)前對(duì)象,取與Aop邏輯進(jìn)行對(duì)比,判斷是否需要代理,不需要?jiǎng)t創(chuàng)建完成,保存進(jìn)Map容器,需要代理則對(duì)當(dāng)前這個(gè)類進(jìn)行jdk或者cglib的代理然后再保存進(jìn)容器里面!
于是乎,我們自己實(shí)現(xiàn)了一個(gè)Spring管理一個(gè)Bean的所有過(guò)程,畫(huà)個(gè)圖,他大概長(zhǎng)這樣!
自己實(shí)現(xiàn)看起來(lái),整個(gè)流程就很清晰,掃描、創(chuàng)建、注入、代理、保存一應(yīng)俱全,但是Spring的實(shí)現(xiàn)方式遠(yuǎn)比我們自己實(shí)現(xiàn)的要復(fù)雜的多得多!
三、Spring的生命周期流程
Spring作者希望,Spring再著手管理一個(gè)Bean的時(shí)候,它希望能夠讓Spring的使用者能夠插手,Spring把一個(gè)類對(duì)象變成一個(gè)Java Object的每一步,怎么理解呢?
比如我們買了一棟新房子,這個(gè)房子需要取裝修,你自己去裝修誠(chéng)然不夠?qū)I(yè),不能夠面面俱到,所以是我們就找了一個(gè)裝修公司幫助我們裝修新房,于是裝修公司就開(kāi)始預(yù)先畫(huà)好的圖紙進(jìn)行裝修,但是在裝修的過(guò)程中,你為了讓自己的新家更加溫馨,你想掛一些壁畫(huà)在墻上,但是圖紙上卻沒(méi)有!于是你就找裝修公司,要求裝修公司在新家的墻上掛上一些壁畫(huà)!裝修公司在接受到你的請(qǐng)求之后,就吩咐裝修的工人在圖紙之外去給你在墻上掛上壁畫(huà)之后,然后再接著裝修!
上面這個(gè)小故事有 這樣幾個(gè)角色,我們把它和Spring對(duì)照起來(lái)!
- 你:代表框架的使用者!
- 新房:代表一個(gè)Class文件,你自己也能夠裝修,但是不夠?qū)I(yè),所以交給裝修公司! 那么你自己創(chuàng)建對(duì)象可能某些使用用起來(lái)很麻煩,所以我們交給了Spring容器!
- 裝修公司:代表著Spring容器!
- 圖紙:代表預(yù)設(shè)步驟,Spring原本就存在的步驟!
- 工人:Spring提供的各種接口!我們可以通過(guò)Spring工廠提供的接口做各種自定義的配置!
上面的小故事,大致可以描述Spring生命周期的核心思想!Spring再對(duì)一個(gè)Class文件實(shí)例化成具體的Spring Bean的時(shí)候,它提供了各種接口,由我們自己實(shí)現(xiàn)!然后再實(shí)例化過(guò)程中,不同的時(shí)機(jī),去調(diào)用不同的接口!從而完成Spring的整個(gè)生命周期的創(chuàng)建!
Spring的生命周期大致分為以下部分!
掃描項(xiàng)目,將項(xiàng)目指定目錄下的Class文件轉(zhuǎn)換為Class對(duì)象!
讀取Class對(duì)象屬性包裝為BeanDefinition,然后保存再一個(gè)Map中!(不難理解,他是為了后續(xù)創(chuàng)建或者讀取這個(gè)類的信息更加方便取而創(chuàng)立的)
將全部的類轉(zhuǎn)化為 BeanDefinition 并保存之后,開(kāi)始調(diào)用第一個(gè)回調(diào)接口BeanFactoryPostProcessor#postProcessBeanFactory()!
- 它的調(diào)用時(shí)機(jī)是將掃描到的Class文件轉(zhuǎn)換為 BeanDefinition 之后調(diào)用的,我們可以通過(guò)回調(diào)的方法獲取所有的BeanDefinition ,而后續(xù)的所有對(duì)Class的操作都是基于BeanDefinition 操作的,所以,我們可以通過(guò)修改它,來(lái)改變后續(xù)的流程!
先從當(dāng)前的容器對(duì)象取當(dāng)前要?jiǎng)?chuàng)建的對(duì)象,當(dāng)取出來(lái)的對(duì)象為null時(shí)開(kāi)始著手創(chuàng)建對(duì)象!
做一系列的驗(yàn)證,比如驗(yàn)證這個(gè)類是否被排除、是否正在創(chuàng)建中、是否有依賴Bean【@DependsOn】注解、是否時(shí)單例等等!
驗(yàn)證通過(guò)之后,開(kāi)始通過(guò)反射創(chuàng)建這個(gè)對(duì)象!
合并BeanDefinition ,這里涉及到Spring之前版本使用的父子容器的概念,屬于另外一個(gè)知識(shí)點(diǎn)不做講解!
判斷當(dāng)前對(duì)象是不是單例、是不是支持循環(huán)引用、是不是正在創(chuàng)建等!
執(zhí)行第二個(gè)接口回調(diào)InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation()方法!
- 它的執(zhí)行時(shí)機(jī)時(shí)實(shí)例化完成之后,屬性填充之前,它的返回值是一個(gè)布爾值,當(dāng)返回false時(shí),不做自動(dòng)屬性填充!
執(zhí)行第三個(gè)接口回調(diào)InstantiationAwareBeanPostProcessor#postProcessProperties()方法!
- 他的執(zhí)行時(shí)機(jī)是,實(shí)例化之后,屬性填充檢查之后,屬性填充之前!它會(huì)返回一個(gè)屬性,后續(xù)的屬性填充會(huì)使用這個(gè)方法返回的值!我們可以在這個(gè)方法里面修改對(duì)應(yīng)Bean的注入的值!
填充屬性到對(duì)象!
調(diào)用第四個(gè)回調(diào)接口BeanNameAware#setBeanName()方法!
- 調(diào)用時(shí)機(jī):屬性填充給完畢后,調(diào)用初始化方法之前;它的功能是能獲取bean的Name!
調(diào)用第五個(gè)回調(diào)接口BeanClassLoaderAware#setBeanClassLoader()
- 調(diào)用時(shí)機(jī):BeanNameAware之后,他的功能是傳入bean的類加載器;
調(diào)用第六個(gè)回調(diào)接口BeanFactoryAware#setBeanFactory()!
- 調(diào)用時(shí)機(jī):BeanClassLoaderAware之后,用于設(shè)置beanFactory!
調(diào)用第七個(gè)回調(diào)接口BeanPostProcessor#postProcessBeforeInitialization()方法
- 調(diào)用時(shí)機(jī)是部分Aware之后,初始化方法之前!傳入當(dāng)前實(shí)例化好的對(duì)象和beanName,再初始化前做修改!
回調(diào)第八個(gè)比較重要的生命周期的初始化方法,它可以是一個(gè)InitializingBean接口的bean,也可以是xml中配置的類,也可以是被加了@PostConstruct注解的方法!
- 該方法內(nèi)部邏輯可以用戶自己編寫(xiě),調(diào)用時(shí)機(jī)為:實(shí)例化完成之后調(diào)用!
回調(diào)第九個(gè)回調(diào)接口 BeanPostProcessor#postProcessAfterInitialization()方法!
- 該方法的調(diào)用時(shí)機(jī)為初始化方法執(zhí)行之后,這里也是Bean實(shí)例化后的最后一步,也是SpringAop實(shí)現(xiàn)的重要的一步!
注冊(cè)銷毀方法,以便Spring容器銷毀的時(shí)候進(jìn)行方法的銷毀!
整體的方法流程示例圖如下:
四、對(duì)應(yīng)源碼結(jié)構(gòu)圖
最近我根據(jù)上述的技術(shù)體系圖搜集了幾十套騰訊、頭條、阿里、美團(tuán)等公司21年的面試題,把技術(shù)點(diǎn)整理成了視頻(實(shí)際上比預(yù)期多花了不少精力),包含知識(shí)脈絡(luò) + 諸多細(xì)節(jié),由于篇幅有限,這里以圖片的形式給大家展示一部分
戳這里免費(fèi)領(lǐng)取下面所有資料
由于篇幅有限,這里以圖片的形式給大家展示一部分
戳這里免費(fèi)領(lǐng)取下面所有資料
[外鏈圖片轉(zhuǎn)存中…(img-2mwnR2T2-1624600758368)]
總結(jié)
以上是生活随笔為你收集整理的Redis宕机数据丢失解决方案,不吃透都对不起自己的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: P8级别的顶级“并发编程”宝典,最全指南
- 下一篇: Redis成神之路电子版教程已问世,面试