Redis数据结构以及对应存储策略
redis數(shù)據(jù)結(jié)構(gòu):
- String 可以是字符串也可以是數(shù)字,以及浮點(diǎn)數(shù)
- List,一個(gè)鏈表,鏈表上每一個(gè)節(jié)點(diǎn)都包含一個(gè)字符串
- set 包含字符串的無(wú)序手機(jī)其,特點(diǎn)是每一個(gè)字符都是唯一的
- hash,包含鍵值對(duì)的無(wú)序散列,類(lèi)似map
- ZSet,字符串成員,在set的基礎(chǔ)上是順序的,元素的順序由分值來(lái)決定
- redis的發(fā)布與訂閱的特性(重點(diǎn)):
- subscribe channel命令 訂閱給定的一個(gè)或者多個(gè)頻道
- pushlish channel message 給指定頻道發(fā)送消息,
- 通過(guò)這兩個(gè)命令redis可以實(shí)現(xiàn)發(fā)布與訂閱功能
- redis的基本事務(wù)功能
- redis通過(guò)multi和exec命令來(lái)實(shí)現(xiàn)事務(wù)的特性,在執(zhí)行multi命令之后,redis會(huì)將這個(gè)客戶(hù)端之后發(fā)送的所有命令存儲(chǔ)在一個(gè)隊(duì)列中,直到接受到exec命令為止。然后會(huì)在不被打斷的情況下執(zhí)行這些操作
- redis鍵過(guò)期時(shí)間:使用expiration設(shè)置key存活的時(shí)間,之后自動(dòng)刪除
重要的數(shù)據(jù)結(jié)構(gòu)
- hyperLogLog:基數(shù)計(jì)算概念:通常用來(lái)統(tǒng)計(jì)一個(gè)集合中不重復(fù)元素個(gè)數(shù),比如用來(lái)統(tǒng)計(jì)網(wǎng)站的UV,或者網(wǎng)站搜索關(guān)鍵字的數(shù)量。最簡(jiǎn)單的辦法是用給一個(gè)集合做統(tǒng)計(jì),判斷新詞是否存在,然后添加存在兩個(gè)問(wèn)題,數(shù)據(jù)量會(huì)變很大,判斷成本會(huì)變得很高。bitMap存儲(chǔ)1 億個(gè)數(shù)據(jù)大概要12M內(nèi)存,redis中HyperLogLog(HLL)中只需要1K內(nèi)存就能做到
- hyperLogLog原理(簡(jiǎn)析):將需要統(tǒng)計(jì)的數(shù)據(jù)進(jìn)行HASH,得到每個(gè)數(shù)據(jù)對(duì)應(yīng)二進(jìn)制串,統(tǒng)計(jì)得到的二進(jìn)制串中從低位開(kāi)始第一次出現(xiàn)1的情況并且進(jìn)行統(tǒng)計(jì),統(tǒng)計(jì)每個(gè)key的這種1 的位置,讓后用概率和統(tǒng)計(jì)的方法推到出統(tǒng)計(jì)和數(shù)據(jù)量之間的關(guān)系,HyperLogLog核心在于集合中每個(gè)實(shí)體對(duì)應(yīng)的Hash串,通過(guò)這種統(tǒng)計(jì)來(lái)預(yù)估整體的量,可以大大減少內(nèi)存的耗費(fèi),所以這種方式并沒(méi)有存儲(chǔ)所有的樣本,而是通過(guò)樣本估算的方式推算出總體大小。(數(shù)學(xué)原理 伯努力分布)
- GEO redis3.2中的新特性用來(lái)存儲(chǔ)和獲取經(jīng)緯度,以及地理位置,還有位置距離計(jì)算等內(nèi)容 如下操作
- redis modules:redis4.0 曾加了自定義模塊的開(kāi)發(fā),類(lèi)似于3.X版本中l(wèi)ua腳本的兼容,但是lua有一定學(xué)習(xí)量,在4.0中提供了一系列模塊源碼: https://github.com/RedisLabsModules/redex ,可以通過(guò)命令將對(duì)應(yīng)的模塊代碼導(dǎo)入到redis中,就可以使用對(duì)應(yīng)自定義命令
- BloomFilter:主要功能是用來(lái)做去重,我們可以用來(lái)對(duì)訪問(wèn)UV和購(gòu)買(mǎi)UV的實(shí)時(shí)去重,具體原理如下:
- BloomFilter過(guò)濾器會(huì)將key做一次Hash,將hash后得到的int分布到一個(gè)大的Bit數(shù)組中對(duì)應(yīng)的位,判斷一個(gè)Key是否存在,只要看Hash對(duì)應(yīng)的bit數(shù)組位置是否是1,1則被占用,0不被占用
- 而且redis2.0 以后就支持bigMap的操作,以及Bit操作中的AND,OR,NOT,XOR操作,bitMap支持232 大小,也就是512M,下標(biāo)最大232-1,可以存2億左右數(shù)據(jù)
- Redis Search:基于redis的搜索插件,可以用來(lái)做全局key值的模糊查詢(xún),并且可以兼容一部分通配符
- Redis-ML :用來(lái)做機(jī)器學(xué)習(xí)的
redis數(shù)據(jù)安全與性能保障
-
redis提供兩種不同持久化方式來(lái)將數(shù)據(jù)存儲(chǔ)到硬盤(pán):
-
快照模式(RDB)可以將存在某一個(gè)時(shí)間點(diǎn)上所有數(shù)據(jù)寫(xiě)入硬盤(pán)
- RDB模式通過(guò)配置信息來(lái)啟動(dòng),并且設(shè)置觸發(fā)的配置,比如save 60 10000 在最近一次快照算起,60秒以?xún)?nèi)有10000次寫(xiě)操作,自動(dòng)執(zhí)行一次BGSAVE命令做備份
- 快照生成一個(gè)rdb文件,可以拷貝,但是在心快照創(chuàng)建完畢之前,redis,系統(tǒng),硬件有一個(gè)出現(xiàn)問(wèn)題,那么redis將會(huì)丟失最近一次快照之后的所有寫(xiě)入數(shù)據(jù)。
- 可以手動(dòng)創(chuàng)建快照:通過(guò)BGSAVE命令,redis會(huì)調(diào)用fork來(lái)創(chuàng)建一個(gè)子進(jìn)程,他來(lái)完成寫(xiě)入,父子進(jìn)程共享內(nèi)存,直到父進(jìn)程有寫(xiě)入操作的時(shí)候共享結(jié)束
- 只適合哪些可以容忍少部分?jǐn)?shù)據(jù)丟失的業(yè)務(wù)場(chǎng)景
- rdb方式需要找到save配置的點(diǎn),因?yàn)槿绻^(guò)于頻繁會(huì)浪費(fèi)資源,過(guò)于稀少可能丟失大量數(shù)據(jù),我們一般做法是在測(cè)試機(jī)器上盡量模擬線上環(huán)境
- 執(zhí)行BGSAVE會(huì)使redis停頓一定時(shí)間取決于系統(tǒng)以及硬件
-
追加文件模式(AOF),在執(zhí)行寫(xiě)命令時(shí)候?qū)⑦@條命令復(fù)制到硬盤(pán)
- AOF方式也可以通過(guò)配置方式打開(kāi),他會(huì)將所有寫(xiě)命令追加到aof文件末尾,redis只需要執(zhí)行一次aof文件就可以恢復(fù)所有記錄。
- 配置寫(xiě)入頻率:always:每個(gè)寫(xiě)入都同步aof文件到硬盤(pán),效率極低。everysec:每一秒同步一次 no:讓操作系統(tǒng)來(lái)決定何時(shí)同步。
- 一般使用everysec,每秒執(zhí)行一次和不適用任何持久化的性能比差不多,這樣可以保證做多丟失一秒的數(shù)據(jù)
- aof文件可能會(huì)變得很大甚至沾滿(mǎn)硬盤(pán),這時(shí)候redis重啟需要執(zhí)行AOF文件中所有命令這個(gè)執(zhí)行時(shí)間會(huì)很長(zhǎng)
- 可以通過(guò)向redis發(fā)送BGREWRITEAOF命令來(lái)移除榮譽(yù)的命令,來(lái)重寫(xiě)aof文件,但是如果文件太大,刪除一個(gè)十幾G的文件redis可能會(huì)處于掛起狀態(tài)數(shù)秒
-
方式可以同時(shí)使用也可以單獨(dú),也可以不用
-
redis過(guò)期策略
- volatile-lru:從已經(jīng)設(shè)置過(guò)期時(shí)間的數(shù)據(jù)集合中通過(guò)LRU算法淘汰數(shù)據(jù),3.0以前的默認(rèn)策略:
- volatile-ttl:從過(guò)期數(shù)據(jù)集合中挑選過(guò)期時(shí)間最小的數(shù)據(jù)刪掉
- volatile-random:從已過(guò)期集合中所見(jiàn)刪除數(shù)據(jù)
- allkey-lru:從所有數(shù)據(jù)聚合中通過(guò)lru刪掉
- allkey-random:從所有數(shù)據(jù)集合中隨機(jī)刪除
- noenviction:禁止從內(nèi)存中刪除數(shù)據(jù) 3.0默認(rèn)策略
- redis過(guò)期策略只在主庫(kù)中進(jìn)行所有很容易在從庫(kù)中讀到臟數(shù)據(jù):以下方式避免
- 通過(guò)sca命令掃庫(kù),相當(dāng)于訪問(wèn)這個(gè)key,充分發(fā)揮惰性刪除,但是確定在于會(huì)在掃庫(kù)的時(shí)候給數(shù)據(jù)庫(kù)帶來(lái)一定壓力,所有需要找好時(shí)間點(diǎn)去執(zhí)行。
- 升級(jí)redis到3.0版本,在3.0 以后的版本中修復(fù)了這個(gè)問(wèn)題,可以在源碼中看到,曾加了對(duì)key值的主從庫(kù)的判斷,然后key為空,擇返回null
redis緩存遇到的問(wèn)題
-
緩存穿透:一般緩存系統(tǒng)按照key去緩存查詢(xún),value不存在,就去后端系統(tǒng)查找,如果key對(duì)應(yīng)的value不存在,并且這個(gè)key訪問(wèn)量巨大,這樣會(huì)給后端DB造成很大的壓力,這就叫緩存穿透:
- 解決方案一:對(duì)查詢(xún)結(jié)果為空的情況也進(jìn)行緩存一個(gè)null,之后如果insert了之后在清理這個(gè)null的緩存
- 對(duì)一定不存在的key進(jìn)行過(guò)濾,放到一個(gè)足夠大的bitMap中,不存在數(shù)據(jù)會(huì)被這個(gè)bitMap攔截
-
緩存雪崩:如果我們?cè)O(shè)置緩存時(shí)候在同一個(gè)時(shí)刻過(guò)期,導(dǎo)致某一個(gè)時(shí)刻緩存幾乎失效,請(qǐng)求都裝法DB,DB瞬時(shí)壓力過(guò)大雪崩
- 解決方案:如果需要集中社會(huì)緩存過(guò)期時(shí)間,那么我們一般會(huì)在緩存失效時(shí)間的計(jì)算上加一個(gè)隨機(jī)數(shù),這樣緩存到期時(shí)間就可以分散開(kāi)
-
緩存擊穿:對(duì)于設(shè)置了過(guò)期時(shí)間的key,如果這些key在某些時(shí)間點(diǎn)被高超并發(fā)訪問(wèn),這個(gè)時(shí)候必須考慮唄擊穿的問(wèn)題,和雪崩區(qū)別在于這是某一個(gè)key,正好過(guò)期的時(shí)候高并發(fā)訪問(wèn)一個(gè)key
- 解決方案一:使用互斥鎖,mutex key,就是在緩存失效的時(shí)候并不立馬去load db,而是先使用緩存工具中某一些帶成功操作返回值的操作,比如 redis的setNx實(shí)現(xiàn)的互斥鎖,他去set一個(gè)mutex key,當(dāng)操作返回成功之后在去load db并且設(shè)置到緩存中,如果返回失敗,重試get方法,這樣就可以只有一個(gè)請(qǐng)求到DB層,代碼如下
- 解決方案二:可以設(shè)置無(wú)過(guò)期時(shí)間,做邏輯過(guò)期,在redis上不做過(guò)期的設(shè)置,就不會(huì)出現(xiàn)這種情況,但是在value中設(shè)置一個(gè)過(guò)期的時(shí)間,在獲取value之后判斷是否過(guò)期,過(guò)期擇重新load到cache,這樣可能會(huì)有其他線程讀到過(guò)期的數(shù)據(jù),但是應(yīng)該影響不大
redis 分布式鎖
- redis中有一個(gè)命令 setNx(key存在時(shí)候設(shè)置值),成功返回1,可以用setNx來(lái)爭(zhēng)奪鎖,在通過(guò)expire來(lái)設(shè)置過(guò)期時(shí)間防止忘記釋放,也可以直接通過(guò)redis.setnx(key_mutex, 1, 3 * 60),同時(shí)設(shè)置值與時(shí)間,如下jedisclient源碼:
redis可能有的問(wèn)題
- redis中有1億個(gè)key,其中10萬(wàn)個(gè)是一樣的前綴開(kāi)頭,如何全部找出來(lái):
- 可以用keys命令,因?yàn)閞edis是單線程的,如果用keys會(huì)造成服務(wù)停頓
- 用scan遍歷所有key,可以無(wú)阻塞的提取指定模式的列表,但是可能會(huì)有重復(fù),時(shí)間更長(zhǎng)
- Redis做異步消息隊(duì)列:
- 用list結(jié)構(gòu)可以,通過(guò)repush生產(chǎn)消息,lpop消費(fèi)消息,沒(méi)有消息可以sleep之后再做,或者用blpop命令,他會(huì)阻塞直到獲取消息。
- 可以用pub/sub命令做消息隊(duì)列并且可以實(shí)現(xiàn)一次消息消費(fèi)多次 1:N
- 延遲隊(duì)列可以用sort命令對(duì)一個(gè)set進(jìn)行排序,排期的對(duì)象是時(shí)間戳,這樣我們可以通過(guò)邏輯去匹配一定時(shí)間之前的消息。
- 大量的key需要設(shè)置同一時(shí)間過(guò)期一般需要注意緩存雪崩效應(yīng):
- 所有值統(tǒng)一時(shí)刻過(guò)期,導(dǎo)致直接到DB查詢(xún),可以通過(guò)隨機(jī)值,分散過(guò)期的時(shí)間
- redis如何做數(shù)據(jù)持久化:
- rdb模式我們可以通過(guò)bgsave方式主動(dòng)去觸發(fā),同時(shí)也做aof做持久化,bgsave會(huì)持續(xù)一段時(shí)間,不夠?qū)崟r(shí)性,如果機(jī)器有問(wèn)題可能會(huì)丟比較多的數(shù)據(jù),aof是記錄寫(xiě)命令,這個(gè)丟失的數(shù)據(jù)比較少,在重啟的時(shí)候,會(huì)用bgsave的文件進(jìn)行數(shù)據(jù)恢復(fù),然后在用aof來(lái)恢復(fù)沒(méi)有備份到文件中的數(shù)據(jù),這樣做到最小丟失。
- rdb原理如上:redis通過(guò)fork出一個(gè)子進(jìn)程來(lái)做bgsave操作,父子進(jìn)程共享內(nèi)存直到父進(jìn)程有寫(xiě)操作,共享結(jié)束。
- redis同步機(jī)制:
- redis一般是主從同步,從從同步,master在同步時(shí)候,先bgsave一次,期間所有寫(xiě)入都在緩存buffer,然后將rdb文件同步到從節(jié)點(diǎn),從節(jié)點(diǎn)loader到內(nèi)存,通知主節(jié)點(diǎn)將緩存中的寫(xiě)入同步。
redis集群
- redis3.0 的新特性就是自帶集群功能,3.0 下的集群沒(méi)有中心節(jié)點(diǎn),RedisCluster 將所有的key映射到16385個(gè)slot中,每一個(gè)redis實(shí)例負(fù)責(zé)一部分最少三個(gè)實(shí)例一個(gè)集群,業(yè)務(wù)程序通過(guò)任務(wù)一個(gè)redisCluster都可以訪問(wèn)所有的數(shù)據(jù),如果數(shù)據(jù)不再這個(gè)節(jié)點(diǎn),那么這個(gè)節(jié)點(diǎn)會(huì)引導(dǎo)客戶(hù)端去對(duì)應(yīng)節(jié)點(diǎn)獲取信息,redisCluster的管理實(shí)通過(guò)節(jié)點(diǎn)之間的通訊完成,定期交互,定期更新
- 每一個(gè)節(jié)點(diǎn)都可以配置一個(gè)從節(jié)點(diǎn),集群中三個(gè)redisCluster某個(gè)掛了,從節(jié)點(diǎn)將頂替他的位置,,主從復(fù)制的功能 和單機(jī)的時(shí)候一直,所以叫他基于主從配置的redis集群。
總結(jié)
以上是生活随笔為你收集整理的Redis数据结构以及对应存储策略的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 吃蘑菇可以减肥吗
- 下一篇: Redis和DB数据一致性解决方案