這篇文章主要介紹“redis數(shù)據(jù)結(jié)構(gòu)和內(nèi)存管理方法是什么”,在日常操作中,相信很多人在Redis數(shù)據(jù)結(jié)構(gòu)和內(nèi)存管理方法是什么問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”Redis數(shù)據(jù)結(jié)構(gòu)和內(nèi)存管理方法是什么”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!
網(wǎng)站建設(shè)哪家好,找成都創(chuàng)新互聯(lián)!專(zhuān)注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、微信小程序、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了建水免費(fèi)建站歡迎大家使用!
Redis是典型的基于Reactor的事件驅(qū)動(dòng)模型,單進(jìn)程單線程,高效的框架總是類(lèi)似的。網(wǎng)絡(luò)模型與spp的異步模型幾乎一致。
Redis流程上整體分為接受請(qǐng)求處理器、響應(yīng)處理器和應(yīng)答處理器三個(gè)同步模塊,每一個(gè)請(qǐng)求都是要經(jīng)歷這三個(gè)部分。
Redis集成了libevent/epoll/kqueue/select
等多種事件管理機(jī)制,可以根據(jù)操作系統(tǒng)版本自由選擇合適的管理機(jī)制,其中l(wèi)ibevent是最優(yōu)選擇的機(jī)制。
Redis的網(wǎng)絡(luò)模型有著所有事件驅(qū)動(dòng)模型的優(yōu)點(diǎn),高效低耗。但是面對(duì)耗時(shí)較長(zhǎng)的操作的時(shí)候,同樣無(wú)法處理請(qǐng)求,只能等到事件處理完畢才能響應(yīng),之前在業(yè)務(wù)中也遇到過(guò)這樣的場(chǎng)景,刪除redis中全量的key-value,整個(gè)操作時(shí)間較長(zhǎng),操作期間所有的請(qǐng)求都無(wú)法響應(yīng)。所以了解清楚網(wǎng)絡(luò)模型有助于在業(yè)務(wù)中揚(yáng)長(zhǎng)避短,減少長(zhǎng)耗時(shí)的請(qǐng)求,盡可能多一些簡(jiǎn)單的短耗時(shí)請(qǐng)求發(fā)揮異步模型的最大的威力,事實(shí)上在Redis的設(shè)計(jì)中也多次體現(xiàn)這一點(diǎn)。
Redis的字符串是對(duì)C語(yǔ)言原始字符串的二次封裝,結(jié)構(gòu)如下:
struct sdshdr { long len; long free; char buf[]; };
可以看出,每當(dāng)定義一個(gè)字符串時(shí),除了保存字符的空間,Redis還分配了額外的空間用于管理屬性字段。
動(dòng)態(tài)內(nèi)存管理方式,動(dòng)態(tài)方式最大的好處就是能夠較為充分的利用內(nèi)存空間,減少內(nèi)存碎片化,與此同時(shí)帶來(lái)的劣勢(shì)就是容易引起頻繁的內(nèi)存抖動(dòng),通常采用“空間預(yù)分配”和“惰性空間釋放”兩種優(yōu)化策略來(lái)減少內(nèi)存抖動(dòng),redis也不例外。
每次修改字符串內(nèi)容時(shí),首先檢查內(nèi)存空間是否符合要求,否則就擴(kuò)大2倍或者按M增長(zhǎng);減少字符串內(nèi)容時(shí),內(nèi)存并不會(huì)立刻回收,而是按需回收。
關(guān)于內(nèi)存管理的優(yōu)化,最基本的出發(fā)點(diǎn)就是浪費(fèi)一點(diǎn)空間還是犧牲一些時(shí)間的權(quán)衡,像STL、tcmalloc、protobuf3的arena機(jī)制等采用的核心思路都是“預(yù)分配遲回收”,Redis也是一樣的。
判斷字符串結(jié)束與否的標(biāo)識(shí)是len字段,而不是C語(yǔ)言的'\0',因此是二進(jìn)制安全的。
放心的將pb序列化后的二進(jìn)制字符串存入redis。
簡(jiǎn)而言之,通過(guò)redis的簡(jiǎn)單封裝,redis的字符串的操作更加方便,性能更友好,并且屏蔽了C語(yǔ)言字符串的一些需要用戶關(guān)心的問(wèn)題。
字典的底層一定是hash,涉及到hash一定會(huì)涉及到hash算法、沖突的解決方法和hash表擴(kuò)容和縮容。
Redis使用的就是常用的Murmurhash3,Murmurhash算法能夠給出在任意輸入序列下的散列分布性,并且計(jì)算速度很快。之前做共享內(nèi)存的Local-Cache的需求時(shí)也正是利用了Murmurhash的優(yōu)勢(shì),解決了原有結(jié)構(gòu)的hash函數(shù)散列分布性差的問(wèn)題。
鏈地址法解決hash沖突,通用解決方案沒(méi)什么特殊的。多說(shuō)一句,如果選用鏈地址解決沖突,那么勢(shì)必要有一個(gè)散列性非常好的hash函數(shù),否則hash的性能將會(huì)大大折扣。Redis選用了Murmurhash,所以可以放心大膽的采用鏈地址方案。
維持hash表在一個(gè)合理的負(fù)載范圍之內(nèi),簡(jiǎn)稱(chēng)為rehash過(guò)程。
rehash的過(guò)程也是一個(gè)權(quán)衡的過(guò)程,在做評(píng)估之前首先明確一點(diǎn),不管中間采用什么樣的rehash策略,rehash在宏觀上看一定是:分配一個(gè)新的內(nèi)存塊,老數(shù)據(jù)搬到新的內(nèi)存塊上,釋放舊內(nèi)存塊。
老數(shù)據(jù)何時(shí)搬?怎么搬?就變成了一個(gè)需要權(quán)衡的問(wèn)題。
第一部分的網(wǎng)絡(luò)模型上明確的指出Redis的事件驅(qū)動(dòng)模型特點(diǎn),不適合玩長(zhǎng)耗時(shí)操作。如果一個(gè)hashtable非常大,需要進(jìn)行擴(kuò)容就一次性把老數(shù)據(jù)copy過(guò)去,那就會(huì)非常耗時(shí),違背事件驅(qū)動(dòng)的特點(diǎn)。所以Redis依舊采用了一種惰性的方案:
新空間分配完畢后,啟動(dòng)rehashidx標(biāo)識(shí)符表明rehash過(guò)程的開(kāi)始;之后所有增刪改查涉及的操作時(shí)都會(huì)將數(shù)據(jù)遷移到新空間,直到老空間數(shù)據(jù)大小為0表明數(shù)據(jù)已經(jīng)全部在新空間,將rehashidx禁用,表明rehash結(jié)束。
將一次性的集中問(wèn)題分而治之,在Redis的設(shè)計(jì)哲學(xué)中體現(xiàn)的淋漓盡致,主要是為了避免大耗時(shí)操作,影響Redis響應(yīng)客戶請(qǐng)求。
變長(zhǎng)整數(shù)存儲(chǔ),整數(shù)分為16/32/64三個(gè)變長(zhǎng)尺度,根據(jù)存入的數(shù)據(jù)所屬的類(lèi)型,進(jìn)行規(guī)劃。
每次插入新元素都有可能導(dǎo)致尺度升級(jí)(例如由16位漲到32位),因此插入整數(shù)的時(shí)間復(fù)雜度為O(n)。這里也是一個(gè)權(quán)衡,內(nèi)存空間和時(shí)間的一個(gè)折中,盡可能節(jié)省內(nèi)存。
Redis的skilplist和普通的skiplist沒(méi)什么不同,都是冗余數(shù)據(jù)實(shí)現(xiàn)的從粗到細(xì)的多層次鏈表,Redis中應(yīng)用跳表的地方不多,常見(jiàn)的就是有序集合。
Redis的跳表和普通skiplist沒(méi)有什么特殊之處。
Redis的鏈表是雙向非循環(huán)鏈表,擁有表頭和表尾指針,對(duì)于首尾的操作時(shí)間復(fù)雜度是O(1),查找時(shí)間復(fù)雜度O(n),插入時(shí)間復(fù)雜度O(1)。
Redis的鏈表和普通鏈表沒(méi)有什么特殊之處。
AOF持久化日志,RDB持久化實(shí)體數(shù)據(jù),AOF優(yōu)先級(jí)大于RDB。
機(jī)制:通過(guò)定時(shí)事件將aof緩沖區(qū)內(nèi)的數(shù)據(jù)定時(shí)寫(xiě)到磁盤(pán)上。
為了減少AOF大小,Redis提供了AOF重寫(xiě)功能,這個(gè)重寫(xiě)功能做的工作就是創(chuàng)建一個(gè)新AOF文件代替老的AOF,并且這個(gè)新的AOF文件沒(méi)有一條冗余指令。(例如對(duì)list先插入A/B/C,后刪除B/C,再插入D共6條指令,最終狀態(tài)為A/D,只需1條指令就可以)
實(shí)現(xiàn)原理就是讀現(xiàn)有數(shù)據(jù)庫(kù)的狀態(tài),根據(jù)狀態(tài)反推指令,跟之前的AOF無(wú)關(guān)。同樣,為了避免長(zhǎng)時(shí)間耗時(shí),重寫(xiě)工作放在子進(jìn)程進(jìn)行。
SAVE和BGSAVE兩個(gè)命令都是用于生成RDB文件,區(qū)別在于BGSAVE會(huì)fork出一個(gè)子進(jìn)程單獨(dú)進(jìn)行,不影響Redis處理正常請(qǐng)求。
定時(shí)和定次數(shù)后進(jìn)行持久化操作。
簡(jiǎn)而言之,RDB的過(guò)程其實(shí)是比較簡(jiǎn)單的,滿足條件后直接去寫(xiě)RDB文件就結(jié)束了。
避免單點(diǎn)是所有服務(wù)的通用問(wèn)題,Redis也不例外。解決單點(diǎn)就要有備機(jī),有備機(jī)就要解決固有的數(shù)據(jù)同步問(wèn)題。
Redis最初的同步做法是sync指令,通過(guò)sync每次都會(huì)全量數(shù)據(jù),顯然每次都全量復(fù)制的設(shè)計(jì)比較消耗資源。改進(jìn)思路也是常規(guī)邏輯,第一次全量,剩下的增量,這就是現(xiàn)在的psync指令的活。
部分重同步實(shí)現(xiàn)的技術(shù)手段是“偏移序號(hào)+積壓緩沖區(qū)”,具體做法如下:
(1)主從分別維護(hù)一個(gè)seq,主每次完成一個(gè)請(qǐng)求便seq+1,從每同步完后更新自己seq;
(2)從每次打算同步時(shí)都是攜帶著自己的seq到主,主將自身的seq與從做差結(jié)果與積壓緩沖區(qū)大小比較,如果小于積壓緩沖區(qū)大小,直接從積壓緩沖區(qū)取相應(yīng)的操作進(jìn)行部分重同步;
(3)否則說(shuō)明積壓緩沖區(qū)不能夠cover掉主從不一致的數(shù)據(jù),進(jìn)行全量同步。
本質(zhì)做法用空間換時(shí)間,顯然在這里犧牲部分空間換回高效的部分重同步,收益比很大。
本質(zhì):多主從服務(wù)器的Redis系統(tǒng),多臺(tái)主從上加了管理監(jiān)控,以保證系統(tǒng)高可用性。
Redis的官方版集群尚未在工業(yè)界普及起來(lái),下面主要介紹一下集群的管理體系和運(yùn)轉(zhuǎn)體系。
集群的數(shù)據(jù)區(qū)由slot組成,每個(gè)節(jié)點(diǎn)負(fù)責(zé)的slot是在集群?jiǎn)?dòng)時(shí)分配的。
客戶請(qǐng)求時(shí)如果相應(yīng)數(shù)據(jù)hash后不屬于請(qǐng)求節(jié)點(diǎn)所管理的slots,會(huì)給客戶返回MOVED錯(cuò)誤,并給出正確的slots。
從這個(gè)層面看,redis的集群還不夠友好,集群內(nèi)部的狀態(tài)必須由客戶感知。
主從服務(wù)器,從用于備份主,一旦主故障,從代替主。
通過(guò)Redis的研究,深刻體會(huì)到的一點(diǎn)就是:所有設(shè)計(jì)的過(guò)程都是權(quán)衡和割舍的過(guò)程。同樣放到日常的工作和開(kāi)發(fā)中也是如此,一句代碼寫(xiě)的好不好,一個(gè)模塊設(shè)計(jì)的是否科學(xué),就從速度和內(nèi)存的角度去衡量看是否需要優(yōu)化,并去評(píng)估每一種優(yōu)化會(huì)收益到什么,同時(shí)會(huì)損失什么,收益遠(yuǎn)大于損失的就是好的優(yōu)化,這樣往往對(duì)于開(kāi)發(fā)和提升更有針對(duì)性,更能提高效率。
到此,關(guān)于“Redis數(shù)據(jù)結(jié)構(gòu)和內(nèi)存管理方法是什么”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!