為什么使用Redis
-
高性能:
假如用戶第一次訪問數(shù)據(jù)庫(kù)中的某些數(shù)據(jù)。這個(gè)過程會(huì)比較慢,因?yàn)槭菑挠脖P上讀取的。將該用戶訪問的數(shù)據(jù)存在數(shù)緩存中,這樣下一次再訪問這些數(shù)據(jù)的時(shí)候就可以直接從緩存中獲取了。操作緩存就是直接操作內(nèi)存,所以速度相當(dāng)快。如果數(shù)據(jù)庫(kù)中的對(duì)應(yīng)數(shù)據(jù)改變的之后,同步改變緩存中相應(yīng)的數(shù)據(jù)即可!
-
高并發(fā):
直接操作緩存能夠承受的請(qǐng)求是遠(yuǎn)遠(yuǎn)大于直接訪問數(shù)據(jù)庫(kù)的,所以我們可以考慮把數(shù)據(jù)庫(kù)中的部分?jǐn)?shù)據(jù)轉(zhuǎn)移到緩存中去,這樣用戶的一部分請(qǐng)求會(huì)直接到緩存這里而不用經(jīng)過數(shù)據(jù)庫(kù)。
使用redis的好處?
速度快,因?yàn)閿?shù)據(jù)存在內(nèi)存中,類似于HashMap,HashMap的優(yōu)勢(shì)就是查找和操作的時(shí)間復(fù)雜度都是O(1)
支持豐富數(shù)據(jù)類型,支持string,list,set,sorted set,hash
支持事務(wù) :redis對(duì)事務(wù)是部分支持的,如果是在入隊(duì)時(shí)報(bào)錯(cuò),那么都不會(huì)執(zhí)行;在非入隊(duì)時(shí)報(bào)錯(cuò),那么成功的就會(huì)成功執(zhí)行。 redis監(jiān)控:鎖的介紹
豐富的特性:可用于緩存,消息,按key設(shè)置過期時(shí)間,過期后將會(huì)自動(dòng)刪除。
分布式緩存和本地緩存有啥區(qū)別?讓你自己設(shè)計(jì)本地緩存怎么設(shè)計(jì)?如何解決緩存過期問題?如何解決內(nèi)存溢出問題?
分布式緩存和本地緩存的區(qū)別
分布式緩存一致性更好一點(diǎn),本地緩存 每個(gè)實(shí)例都有自己的緩存,可能會(huì)存在不一致的情況。
本地緩存會(huì)占用堆內(nèi)存,影響垃圾回收、影響系統(tǒng)性能。分布式緩存兩大開銷會(huì)導(dǎo)致其慢于本地緩存,網(wǎng)絡(luò)延遲和對(duì)象序列化
進(jìn)程內(nèi)緩存適用于較小且頻率可見的訪問場(chǎng)景,尤其適用于不變對(duì)象,對(duì)于較大且不可預(yù)見的訪問,最好采用分布式緩存。
如何解決緩存過期問題
緩存失效:
引起這個(gè)原因的主要因素是高并發(fā)下,我們一般設(shè)定一個(gè)緩存的過期時(shí)間時(shí),可能有一些會(huì)設(shè)置5分鐘啊,10分鐘這些;并發(fā)很高時(shí)可能會(huì)出在某一個(gè)時(shí)間同時(shí)生成了很多的緩存,并且過期時(shí)間在同一時(shí)刻,這個(gè)時(shí)候就可能引發(fā)——當(dāng)過期時(shí)間到后,這些緩存同時(shí)失效,請(qǐng)求全部轉(zhuǎn)發(fā)到DB,DB可能會(huì)壓力過重。
處理方法:
一個(gè)簡(jiǎn)單方案就是將緩存失效時(shí)間分散開,不要所以緩存時(shí)間長(zhǎng)度都設(shè)置成5分鐘或者10分鐘;比如我們可以在原有的失效時(shí)間基礎(chǔ)上增加一個(gè)隨機(jī)值,比如1-5分鐘隨機(jī),這樣每一個(gè)緩存的過期時(shí)間的重復(fù)率就會(huì)降低,就很難引發(fā)集體失效的事件。
緩存失效時(shí)產(chǎn)生的雪崩效應(yīng),將所有請(qǐng)求全部放在數(shù)據(jù)庫(kù)上,這樣很容易就達(dá)到數(shù)據(jù)庫(kù)的瓶頸,導(dǎo)致服務(wù)無(wú)法正常提供。盡量避免這種場(chǎng)景的發(fā)生。
redis和memcached的區(qū)別
Redis和Memcache都是將數(shù)據(jù)存放在內(nèi)存中,都是內(nèi)存數(shù)據(jù)庫(kù)。不過memcache還可用于緩存其他東西,例如圖片、視頻等等;
Memcached僅支持簡(jiǎn)單的key-value結(jié)構(gòu)的數(shù)據(jù)記錄。Redis不僅僅支持簡(jiǎn)單的k/v類型的數(shù)據(jù),同時(shí)還提供list,set,hash等數(shù)據(jù)結(jié)構(gòu)的存儲(chǔ);
虛擬內(nèi)存--Redis當(dāng)物理內(nèi)存用完時(shí),可以將一些很久沒用到的value 交換到磁盤;
過期策略--memcache在set時(shí)就指定,例如set key1 0 0 8,即永不過期。Redis可以通過例如expire 設(shè)定,例如expire name 10;
分布式--設(shè)定memcache集群,利用magent做一主多從;redis可以做一主多從。都可以一主一從;
存儲(chǔ)數(shù)據(jù)安全--memcache掛掉后,數(shù)據(jù)沒了;redis可以定期保存到磁盤(持久化);
災(zāi)難恢復(fù)--memcache掛掉后,數(shù)據(jù)不可恢復(fù); redis數(shù)據(jù)丟失后可以通過aof恢復(fù);
Redis支持?jǐn)?shù)據(jù)的備份,即master-slave模式的數(shù)據(jù)備份;
redis常用數(shù)據(jù)結(jié)構(gòu)和使用場(chǎng)景
-
String(字符串)
set 鍵 值 (設(shè)置鍵值對(duì))
get 鍵 (根據(jù)鍵取值)
String結(jié)構(gòu)又存在3種類型,分別是字符串、數(shù)值、bitmap。
使用場(chǎng)景:
字符串:分布式鎖
數(shù)值:在一些系統(tǒng)中看似不是很重要的統(tǒng)計(jì),搶購(gòu),秒殺,詳情頁(yè)中數(shù)據(jù)統(tǒng)計(jì),點(diǎn)贊,評(píng)論等。可以規(guī)避并發(fā)情況下對(duì)數(shù)據(jù)庫(kù)的事務(wù)操作,完全由redis內(nèi)存操作代替。
bitmap:用戶簽到、在線用戶統(tǒng)計(jì)、統(tǒng)計(jì)活躍用戶。
-
Hash(哈希)
hset 鍵 字段 值
hmset 鍵 字段1 值1 字段2 值2 字段3 值3 字段n 值n
hger 鍵 字段
hmget 鍵 字段1 字段2 字段3 字段n
-
hgetall 鍵
容量:每個(gè)hash可以存儲(chǔ)4294967295個(gè)鍵值對(duì)(2的32次方-1)
使用場(chǎng)景:點(diǎn)贊、收藏、詳情頁(yè)等
-
Set(集合)
sadd 鍵 值1 值2 值3 值n
smembers 鍵
使用場(chǎng)景:
關(guān)注集合:共同關(guān)注、二度好友
點(diǎn)贊集合
抽獎(jiǎng)集合
用戶標(biāo)簽
-
List(列表)
lpush 鍵 值1 值2 值3 值n
-
lrange 鍵 開始下標(biāo) 結(jié)束下標(biāo)
容量:每個(gè)hash可以存儲(chǔ)4294967295個(gè)鍵值對(duì)(2的32次方-1)
使用場(chǎng)景:
棧
隊(duì)列,比如:關(guān)注隊(duì)列、粉絲隊(duì)列
消息隊(duì)列
-
Zset(Sorted Set: 有序集合)
zadd 鍵 分?jǐn)?shù)1 值1 分?jǐn)?shù)2 值2 分?jǐn)?shù)n 值n
zrange 鍵 開始下標(biāo) 結(jié)束下標(biāo)
zrangebyscore 鍵 開始分值 結(jié)束分值
使用場(chǎng)景:
排行榜
時(shí)間軸
優(yōu)先級(jí)隊(duì)列
Zset底層實(shí)現(xiàn)?跳表搜索插入刪除過程?
zset的編碼有ziplist和skiplist兩種。 底層分別使用ziplist(壓縮鏈表)和skiplist(跳表)實(shí)現(xiàn)。
當(dāng)zset滿足以下兩個(gè)條件的時(shí)候,使用ziplist:
- 保存的元素少于128個(gè)
- 保存的所有元素大小都小于64字節(jié)
不滿足這兩個(gè)條件則使用skiplist (注意:這兩個(gè)數(shù)值是可以通過redis.conf的zset-max-ziplist-entries 和 zset-max-ziplist-value選項(xiàng) 進(jìn)行修改。)
-
ziplist編碼
ziplist 編碼的有序集合對(duì)象使用壓縮列表作為底層實(shí)現(xiàn),每個(gè)集合元素使用兩個(gè)緊挨在一起的壓縮列表節(jié)點(diǎn)來(lái)保存,第一個(gè)節(jié)點(diǎn)保存元素的成員,第二個(gè)節(jié)點(diǎn)保存元素的分值。并且壓縮列表內(nèi)的集合元素按分值從小到大的順序進(jìn)行排列,小的放置在靠近表頭的位置,大的放置在靠近表尾的位置。
-
skiplist編碼
skiplist 編碼的有序集合對(duì)象使用 zet 結(jié)構(gòu)作為底層實(shí)現(xiàn),一個(gè) zset 結(jié)構(gòu)同時(shí)包含一個(gè)字典和一個(gè)跳表:
字典的鍵保存元素的值,字典的值則保存元素的分值;跳躍表節(jié)點(diǎn)的 object 屬性保存元素的成員,跳躍表節(jié)點(diǎn)的 score 屬性保存元素的分值。
這兩種數(shù)據(jù)結(jié)構(gòu)會(huì)通過指針來(lái)共享相同元素的成員和分值,所以不會(huì)產(chǎn)生重復(fù)成員和分值,造成內(nèi)存的浪費(fèi)。
注意:?jiǎn)为?dú)使用字典,時(shí)間復(fù)雜度很低(O(1)),但字典是以無(wú)序的方式保存集合元素的,需要重新進(jìn)行排序;單獨(dú)使用跳表,雖然能執(zhí)行范圍操作,但是查找操作從O(1)的復(fù)雜度變?yōu)榱薕(logN)。所以使用兩種數(shù)據(jù)結(jié)構(gòu)共同實(shí)現(xiàn)
redis過期淘汰策略
淘汰策略
定期刪除+惰性刪除
所謂定期刪除,指的是redis默認(rèn)是每隔100ms就隨機(jī)抽取一些設(shè)置了過期時(shí)間的key,檢查其是否過期,如果過期就刪除。(注意:是隨機(jī))
但是,定期刪除可能會(huì)導(dǎo)致很多過期key到了時(shí)間并沒有被刪除掉,所以就得靠惰性刪除了。
惰性刪除就是在你獲取某個(gè)key的時(shí)候,redis會(huì)檢查一下 ,這個(gè)key如果設(shè)置了過期時(shí)間那么是否過期了?如果過期了此時(shí)就會(huì)刪除,不會(huì)給你返回任何東西。并不是key到時(shí)間就被刪除掉,而是你查詢這個(gè)key的時(shí)候,redis再懶惰的檢查一下
通過上述兩種手段結(jié)合起來(lái),保證過期的key一定會(huì)被淘汰
內(nèi)存淘汰機(jī)制
如果redis的內(nèi)存占用過多的時(shí)候,此時(shí)會(huì)進(jìn)行內(nèi)存淘汰,有如下一些策略:
noeviction:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),新寫入操作會(huì)報(bào)錯(cuò)(一般沒人用)
allkeys-lru:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在鍵空間中,移除最近最少使用的key(這個(gè)是最常用的)
allkeys-random:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在鍵空間中,隨機(jī)移除某個(gè)key(一般沒人用)
volatile-lru:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在設(shè)置了過期時(shí)間的鍵空間中,移除最近最少使用的key(這個(gè)一般不太合適)
volatile-random:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在設(shè)置了過期時(shí)間的鍵空間中,隨機(jī)移除某個(gè)key
volatile-ttl:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在設(shè)置了過期時(shí)間的鍵空間中,有更早過期時(shí)間的key優(yōu)先移除
redis持久化機(jī)制?都有什么優(yōu)缺點(diǎn)?持久化的時(shí)候還能接受請(qǐng)求嗎?
RDB機(jī)制和AOF機(jī)制
RDB:
RDB其實(shí)就是把數(shù)據(jù)以快照的形式保存在磁盤上。什么是快照呢,你可以理解成把當(dāng)前時(shí)刻的數(shù)據(jù)拍成一張照片保存下來(lái)。
RDB持久化是指在指定的時(shí)間間隔內(nèi)將內(nèi)存中的數(shù)據(jù)集快照寫入磁盤。也是默認(rèn)的持久化方式,這種方式是就是將內(nèi)存中數(shù)據(jù)以快照的方式寫入到二進(jìn)制文件中,默認(rèn)的文件名為dump.rdb。
觸發(fā)機(jī)制
-
save觸發(fā)方式(同步)
該命令會(huì)阻塞當(dāng)前Redis服務(wù)器,執(zhí)行save命令期間,Redis不能處理其他命令,直到RDB過程完成為止。執(zhí)行完成時(shí)候如果存在老的RDB文件,就把新的替代掉舊的。我們的客戶端可能都是幾萬(wàn)或者是幾十萬(wàn),這種方式顯然不可取。
-
bgsave觸發(fā)方式(異步)
執(zhí)行該命令時(shí),Redis會(huì)在后臺(tái)異步進(jìn)行快照操作,快照同時(shí)還可以響應(yīng)客戶端請(qǐng)求。
具體操作是Redis進(jìn)程執(zhí)行fork操作創(chuàng)建子進(jìn)程,RDB持久化過程由子進(jìn)程負(fù)責(zé),完成后自動(dòng)結(jié)束。阻塞只發(fā)生在fork階段,一般時(shí)間很短。基本上 Redis 內(nèi)部所有的RDB操作都是采用 bgsave 命令。
-
自動(dòng)觸發(fā)
自動(dòng)觸發(fā)是由我們的配置文件來(lái)完成的。
RDB 的優(yōu)勢(shì)和劣勢(shì)
①優(yōu)勢(shì)
(1)RDB文件緊湊,全量備份,非常適合用于進(jìn)行備份和災(zāi)難恢復(fù)。
(2)生成RDB文件的時(shí)候,redis主進(jìn)程會(huì)fork()一個(gè)子進(jìn)程來(lái)處理所有保存工作,主進(jìn)程不需要進(jìn)行任何磁盤IO操作。
(3)RDB 在恢復(fù)大數(shù)據(jù)集時(shí)的速度比 AOF 的恢復(fù)速度要快。
②劣勢(shì)
RDB快照是一次全量備份,存儲(chǔ)的是內(nèi)存數(shù)據(jù)的二進(jìn)制序列化形式,存儲(chǔ)上非常緊湊。當(dāng)進(jìn)行快照持久化時(shí),會(huì)開啟一個(gè)子進(jìn)程專門負(fù)責(zé)快照持久化,子進(jìn)程會(huì)擁有父進(jìn)程的內(nèi)存數(shù)據(jù),父進(jìn)程修改內(nèi)存子進(jìn)程不會(huì)反應(yīng)出來(lái),所以在快照持久化期間修改的數(shù)據(jù)不會(huì)被保存,可能丟失數(shù)據(jù)。
AOF:
全量備份總是耗時(shí)的,有時(shí)候我們提供一種更加高效的方式AOF,工作機(jī)制很簡(jiǎn)單,redis會(huì)將每一個(gè)收到的寫命令都通過write函數(shù)追加到文件中。通俗的理解就是日志記錄。
AOF也有三種觸發(fā)機(jī)制
(1)每修改同步always:同步持久化 每次發(fā)生數(shù)據(jù)變更會(huì)被立即記錄到磁盤 性能較差但數(shù)據(jù)完整性比較好
(2)每秒同步everysec:異步操作,每秒記錄 如果一秒內(nèi)宕機(jī),有數(shù)據(jù)丟失
(3)不同no:從不同步
AOF的優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
(1)AOF可以更好的保護(hù)數(shù)據(jù)不丟失,一般AOF會(huì)每隔1秒,通過一個(gè)后臺(tái)線程執(zhí)行一次fsync操作,最多丟失1秒鐘的數(shù)據(jù)。(2)AOF日志文件沒有任何磁盤尋址的開銷,寫入性能非常高,文件不容易破損。
(3)AOF日志文件即使過大的時(shí)候,出現(xiàn)后臺(tái)重寫操作,也不會(huì)影響客戶端的讀寫。
(4)AOF日志文件的命令通過非??勺x的方式進(jìn)行記錄,這個(gè)特性非常適合做災(zāi)難性的誤刪除的緊急恢復(fù)。比如某人不小心用flushall命令清空了所有數(shù)據(jù),只要這個(gè)時(shí)候后臺(tái)rewrite還沒有發(fā)生,那么就可以立即拷貝AOF文件,將最后一條flushall命令給刪了,然后再將該AOF文件放回去,就可以通過恢復(fù)機(jī)制,自動(dòng)恢復(fù)所有數(shù)據(jù)
缺點(diǎn)
(1)對(duì)于同一份數(shù)據(jù)來(lái)說(shuō),AOF日志文件通常比RDB數(shù)據(jù)快照文件更大
(2)AOF開啟后,支持的寫QPS會(huì)比RDB支持的寫QPS低,因?yàn)锳OF一般會(huì)配置成每秒fsync一次日志文件,當(dāng)然,每秒一次fsync,性能也還是很高的
(3)以前AOF發(fā)生過bug,就是通過AOF記錄的日志,進(jìn)行數(shù)據(jù)恢復(fù)的時(shí)候,沒有恢復(fù)一模一樣的數(shù)據(jù)出來(lái)。
持久化的時(shí)候還能接受請(qǐng)求嗎?
RDB就是生成某個(gè)時(shí)間點(diǎn)快照,有異步bgsave和同步save,同步的話肯定不能對(duì)外服務(wù)了,異步是通過fork子進(jìn)程完成的,需要注意的是,在異步的時(shí)候,數(shù)據(jù)可能發(fā)生變化,redis并不是直接復(fù)制一份進(jìn)行復(fù)制,redis運(yùn)用寫時(shí)復(fù)制cow思想,即一開始redis和子進(jìn)程都指向同一個(gè)數(shù)據(jù),當(dāng)某個(gè)key改變時(shí)redis主進(jìn)程指向新的,子進(jìn)程不變。
AOF也是同步不行,異步可以
redis事務(wù)
概念:
Redis 事務(wù)的本質(zhì)是一組命令的集合。事務(wù)支持一次執(zhí)行多個(gè)命令,一個(gè)事務(wù)中所有命令都會(huì)被序列化。在事務(wù)執(zhí)行過程,會(huì)按照順序串行化執(zhí)行隊(duì)列中的命令,其他客戶端提交的命令請(qǐng)求不會(huì)插入到事務(wù)執(zhí)行命令序列中。
總結(jié)說(shuō):redis事務(wù)就是一次性、順序性、排他性的執(zhí)行一個(gè)隊(duì)列中的一系列命令。
Redis事務(wù)沒有隔離級(jí)別的概念:
批量操作在發(fā)送 EXEC 命令前被放入隊(duì)列緩存,并不會(huì)被實(shí)際執(zhí)行,也就不存在事務(wù)內(nèi)的查詢要看到事務(wù)里的更新,事務(wù)外查詢不能看到。
Redis事務(wù)不保證原子性:
Redis中,單條命令是原子性執(zhí)行的,但事務(wù)不保證原子性,且沒有回滾。事務(wù)中任意命令執(zhí)行失敗,其余的命令仍會(huì)被執(zhí)行。
Redis事務(wù)的三個(gè)階段:
開始事務(wù)
命令入隊(duì)
執(zhí)行事務(wù)
Redis事務(wù)相關(guān)命令:
watch key1 key2 ... : 監(jiān)視一或多個(gè)key,如果在事務(wù)執(zhí)行之前,被監(jiān)視的key被其他命令改動(dòng),則事務(wù)被打斷 ( 類似樂觀鎖 )
multi : 標(biāo)記一個(gè)事務(wù)塊的開始( queued )
exec : 執(zhí)行所有事務(wù)塊的命令 ( 一旦執(zhí)行exec后,之前加的監(jiān)控鎖都會(huì)被取消掉 )
discard : 取消事務(wù),放棄事務(wù)塊中的所有命令
unwatch : 取消watch對(duì)所有key的監(jiān)控
緩存雪崩和緩存穿透,以及解決方法
-
緩存穿透
緩存穿透是指緩存和數(shù)據(jù)庫(kù)中沒有的數(shù)據(jù),而用戶不斷發(fā)起請(qǐng)求,若發(fā)起 id為“-1”的數(shù)據(jù)或id為特別大而不存在的數(shù)據(jù),此時(shí)的用戶很可能是攻擊者,攻擊會(huì)導(dǎo)致數(shù)據(jù)庫(kù)壓力過大,而影響數(shù)據(jù)庫(kù)的性能
-
解決方案
接口層增加校驗(yàn),如用戶鑒權(quán)校驗(yàn),id做基礎(chǔ)校驗(yàn),id<=0的直接攔截
從緩存取不到的數(shù)據(jù),在數(shù)據(jù)庫(kù)中也沒有取到,這時(shí)可以設(shè)置該id對(duì)應(yīng)的value為null,設(shè)置一定的有效時(shí)間,可以防止攻擊用戶在一定時(shí)間內(nèi)反復(fù)使用同一id進(jìn)行暴力攻擊
-
緩存擊穿
緩存擊穿是指緩存中沒有但是數(shù)據(jù)庫(kù)中有的數(shù)據(jù)(一般是由于緩存時(shí)間到期銷毀),這時(shí)由于并發(fā)用戶特別多,同時(shí)讀緩存沒有讀到數(shù)據(jù),又同時(shí)去數(shù)據(jù)庫(kù)中取數(shù)據(jù),使數(shù)據(jù)庫(kù)壓力瞬間增大,造成數(shù)據(jù)庫(kù)性能大幅度降低
-
解決方案
設(shè)置熱點(diǎn)數(shù)據(jù)永不過期
加互斥鎖
定時(shí)任務(wù)更新緩存數(shù)據(jù)
-
緩存雪崩
緩存雪崩是指緩存中數(shù)據(jù)大批量到過期時(shí)間,而查詢數(shù)量巨大,引起數(shù)據(jù)庫(kù)壓力巨大甚至down機(jī)。和緩存擊穿不同的是,緩存擊穿是指大量用戶并發(fā)查同一條數(shù)據(jù),緩存雪崩是指大量不同數(shù)據(jù)都在同一時(shí)間過期了,大量用戶不能從緩存中得到這些數(shù)據(jù),而進(jìn)入數(shù)據(jù)庫(kù)查詢
-
解決方案
緩存數(shù)據(jù)的過期時(shí)間設(shè)置為隨機(jī),防止大量數(shù)據(jù)在同一時(shí)間過期的現(xiàn)象出現(xiàn)
如果緩存數(shù)據(jù)庫(kù)是分布式部署,將熱點(diǎn)數(shù)據(jù)均勻分布在不同緩存數(shù)據(jù)庫(kù)中
設(shè)置熱點(diǎn)數(shù)據(jù)永不過期
如何保證緩存和數(shù)據(jù)庫(kù)的數(shù)據(jù)一致性?
選擇淘汰緩存
原因:數(shù)據(jù)可能為簡(jiǎn)單數(shù)據(jù),也可能為較復(fù)雜的數(shù)據(jù),復(fù)雜數(shù)據(jù)進(jìn)行緩存的更新操作,成本較高,因此一般推薦淘汰緩存
方案:
先刪除緩存,后更新數(shù)據(jù)庫(kù)。
更新的時(shí)候,先刪除緩存,然后再更新數(shù)據(jù)庫(kù)。
讀的時(shí)候,先讀緩存;如果沒有的話,就讀數(shù)據(jù)庫(kù),同時(shí)將數(shù)據(jù)放入緩存,并返回響應(yīng)。
原因:因?yàn)榧词购竺娓聰?shù)據(jù)庫(kù)失敗了,緩存是空的,讀的時(shí)候會(huì)從數(shù)據(jù)庫(kù)中重新拉,雖然都是舊數(shù)據(jù),但數(shù)據(jù)是一致的。
- 有如下場(chǎng)景:同時(shí)有一個(gè)請(qǐng)求A進(jìn)行更新操作,另一個(gè)請(qǐng)求B進(jìn)行查詢操作。 (1)請(qǐng)求A進(jìn)行寫操作,刪除緩存 (2)請(qǐng)求B查詢發(fā)現(xiàn)緩存不存在 (3)請(qǐng)求B去數(shù)據(jù)庫(kù)查詢得到舊值 (4)請(qǐng)求B將舊值寫入緩存 (5)請(qǐng)求A將新值寫入數(shù)據(jù)庫(kù)
次數(shù)便出現(xiàn)了數(shù)據(jù)不一致問題。采用延時(shí)雙刪策略得以解決
- 數(shù)據(jù)庫(kù)讀寫分離的場(chǎng)景:
兩個(gè)請(qǐng)求,一個(gè)請(qǐng)求A進(jìn)行更新操作,另一個(gè)請(qǐng)求B進(jìn)行查詢操作。
(1)請(qǐng)求A進(jìn)行寫操作,刪除緩存 (2)請(qǐng)求A將數(shù)據(jù)寫入數(shù)據(jù)庫(kù)了, (3)請(qǐng)求B查詢緩存發(fā)現(xiàn),緩存沒有值 (4)請(qǐng)求B去從庫(kù)查詢,這時(shí),還沒有完成主從同步,因此查詢到的是舊值 (5)請(qǐng)求B將舊值寫入緩存 (6)數(shù)據(jù)庫(kù)完成主從同步,從庫(kù)變?yōu)樾轮?/p>
依舊采用延時(shí)雙刪策略解決此問題
延時(shí)雙刪是將1秒內(nèi)所造成的緩存臟數(shù)據(jù),再次刪除。
redis是單線程還是多線程?為什么那么快?
Redis是單線程的
為什么快?
Redis完全基于內(nèi)存,絕大部分請(qǐng)求是純粹的內(nèi)存操作,非??焖?。數(shù)據(jù)存在內(nèi)存中,類似于HashMap,HashMap的優(yōu)勢(shì)就是查找和操作的時(shí)間復(fù)雜度都是O(1)
數(shù)據(jù)結(jié)構(gòu)簡(jiǎn)單,對(duì)數(shù)據(jù)操作也簡(jiǎn)單,Redis中的數(shù)據(jù)結(jié)構(gòu)是專門進(jìn)行設(shè)計(jì)的;
使用多路I/O復(fù)用模型,非阻塞IO;
采用單線程,避免了不必要的上下文切換和競(jìng)爭(zhēng)條件,也不存在多進(jìn)程或者多線程導(dǎo)致的切換而消耗 CPU,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作,沒有因?yàn)榭赡艹霈F(xiàn)死鎖而導(dǎo)致的性能消耗;
五種IO模型的區(qū)別
- 阻塞式IO
使用系統(tǒng)調(diào)用,并一直阻塞直到內(nèi)核將數(shù)據(jù)準(zhǔn)備好,之后再由內(nèi)核緩沖區(qū)復(fù)制到用戶態(tài),在等待內(nèi)核準(zhǔn)備的這段時(shí)間什么也干不了
下圖函數(shù)調(diào)用期間,一直被阻塞,直到數(shù)據(jù)準(zhǔn)備好且從內(nèi)核復(fù)制到用戶程序才返回,這種IO模型為阻塞式IO
阻塞式IO式最流行的IO模型
- 非阻塞式IO
內(nèi)核在沒有準(zhǔn)備好數(shù)據(jù)的時(shí)候會(huì)返回錯(cuò)誤碼,而調(diào)用程序不會(huì)休眠,而是不斷輪詢?cè)儐杻?nèi)核數(shù)據(jù)是否準(zhǔn)備好
下圖函數(shù)調(diào)用時(shí),如果數(shù)據(jù)沒有準(zhǔn)備好,不像阻塞式IO那樣一直被阻塞,而是返回一個(gè)錯(cuò)誤碼。數(shù)據(jù)準(zhǔn)備好時(shí),函數(shù)成功返回。
應(yīng)用程序?qū)@樣一個(gè)非阻塞描述符循環(huán)調(diào)用成為輪詢。
非阻塞式IO的輪詢會(huì)耗費(fèi)大量cpu,通常在專門提供某一功能的系統(tǒng)中才會(huì)使用。通過為套接字的描述符屬性設(shè)置非阻塞式,可使用該功能
- IO多路復(fù)用
類似與非阻塞,只不過輪詢不是由用戶線程去執(zhí)行,而是由內(nèi)核去輪詢,內(nèi)核監(jiān)聽程序監(jiān)聽到數(shù)據(jù)準(zhǔn)備好后,調(diào)用內(nèi)核函數(shù)復(fù)制數(shù)據(jù)到用戶態(tài)
下圖中select這個(gè)系統(tǒng)調(diào)用,充當(dāng)代理類的角色,不斷輪詢注冊(cè)到它這里的所有需要IO的文件描述符,有結(jié)果時(shí),把結(jié)果告訴被代理的recvfrom函數(shù),它本尊再親自出馬去拿數(shù)據(jù)
IO多路復(fù)用至少有兩次系統(tǒng)調(diào)用,如果只有一個(gè)代理對(duì)象,性能上是不如前面的IO模型的,但是由于它可以同時(shí)監(jiān)聽很多套接字,所以性能比前兩者高
-
多路復(fù)用包括:
select:線性掃描所有監(jiān)聽的文件描述符,不管他們是不是活躍的。有最大數(shù)量限制(32位系統(tǒng)1024,64位系統(tǒng)2048)
poll:同select,不過數(shù)據(jù)結(jié)構(gòu)不同,需要分配一個(gè)pollfd結(jié)構(gòu)數(shù)組,維護(hù)在內(nèi)核中。它沒有大小限制,不過需要很多復(fù)制操作
epoll:用于代替poll和select,沒有大小限制。使用一個(gè)文件描述符管理多個(gè)文件描述符,使用紅黑樹存儲(chǔ)。同時(shí)用事件驅(qū)動(dòng)代替了輪詢。epoll_ctl中注冊(cè)的文件描述符在事件觸發(fā)的時(shí)候會(huì)通過回調(diào)機(jī)制激活該文件描述符。epoll_wait便會(huì)收到通知。最后,epoll還采用了mmap虛擬內(nèi)存映射技術(shù)減少用戶態(tài)和內(nèi)核態(tài)數(shù)據(jù)傳輸?shù)拈_銷
- 信號(hào)驅(qū)動(dòng)式IO
使用信號(hào),內(nèi)核在數(shù)據(jù)準(zhǔn)備就緒時(shí)通過信號(hào)來(lái)進(jìn)行通知
首先開啟信號(hào)驅(qū)動(dòng)io套接字,并使用sigaction系統(tǒng)調(diào)用來(lái)安裝信號(hào)處理程序,內(nèi)核直接返回,不會(huì)阻塞用戶態(tài)
數(shù)據(jù)準(zhǔn)備好時(shí),內(nèi)核會(huì)發(fā)送SIGIO信號(hào),收到信號(hào)后開始進(jìn)行io操作
- 異步IO
異步IO依賴信號(hào)處理程序來(lái)進(jìn)行通知
不過異步IO與前面IO模型不同的是:前面的都是數(shù)據(jù)準(zhǔn)備階段的阻塞與非阻塞,異步IO模型通知的是IO操作已經(jīng)完成,而不是數(shù)據(jù)準(zhǔn)備完成
異步IO才是真正的非阻塞,主進(jìn)程只負(fù)責(zé)做自己的事情,等IO操作完成(數(shù)據(jù)成功從內(nèi)核緩存區(qū)復(fù)制到應(yīng)用程序緩沖區(qū))時(shí)通過回調(diào)函數(shù)對(duì)數(shù)據(jù)進(jìn)行處理
unix中異步io函數(shù)以aio_或 lio打頭
各種IO模型對(duì)比
前面四種IO模型的主要區(qū)別在第一階段,他們第二階段是一樣的:數(shù)據(jù)從內(nèi)核緩沖區(qū)復(fù)制到調(diào)用者緩沖區(qū)期間都被阻塞?。?/p>
前面四種IO都是同步IO:IO操作導(dǎo)致請(qǐng)求進(jìn)程阻塞,直到IO操作完成
異步IO:IO操作不導(dǎo)致請(qǐng)求進(jìn)程阻塞
select、poll、epoll的區(qū)別?
-
支持一個(gè)線程所能打開的最大連接數(shù)
image -
FD劇增后帶來(lái)的IO效率問題
image.png
-
消息傳遞方式
image
綜上,在選擇select,poll,epoll時(shí)要根據(jù)具體的使用場(chǎng)合以及這三種方式的自身特點(diǎn):
表面上看epoll的性能最好,但是在連接數(shù)少并且連接都十分活躍的情況下,select和poll的性能可能比epoll好,畢竟epoll的通知機(jī)制需要很多函數(shù)回調(diào)。
select低效是因?yàn)槊看嗡夹枰喸?。但低效也是相?duì)的,視情況而定,也可通過良好的設(shè)計(jì)改善。
redis熱key問題?如何發(fā)現(xiàn)以及如何解決?
熱點(diǎn)問題概述
熱點(diǎn)問題一般出現(xiàn)在讀多寫少的場(chǎng)景
在日常工作生活中一些突發(fā)的的事件,諸如:“雙11”期間某些熱門商品的降價(jià)促銷,當(dāng)這其中的某一件商品被數(shù)萬(wàn)次點(diǎn)擊、購(gòu)買時(shí),會(huì)形成一個(gè)較大的需求量,這種情況下就會(huì)產(chǎn)生一個(gè)單一的Key,這樣就會(huì)引起一個(gè)熱點(diǎn);同理,當(dāng)被大量刊發(fā)、瀏覽的熱點(diǎn)新聞,熱點(diǎn)評(píng)論等也會(huì)產(chǎn)生熱點(diǎn);另外,在服務(wù)端讀數(shù)據(jù)進(jìn)行訪問時(shí),往往會(huì)對(duì)數(shù)據(jù)進(jìn)行分片切分,此類過程中會(huì)在某一主機(jī)Server上對(duì)相應(yīng)的Key進(jìn)行訪問,當(dāng)訪問超過主機(jī)Server極限時(shí),就會(huì)導(dǎo)致熱點(diǎn)Key問題的產(chǎn)生。
當(dāng)某一熱點(diǎn)的Key在某一主機(jī)上超過該主機(jī)網(wǎng)卡上線時(shí),由于流量的過度集中,會(huì)導(dǎo)致服務(wù)器中其它服務(wù)無(wú)法進(jìn)行。
此外,熱點(diǎn)Key的緩存過多,超過目前的緩存容量時(shí),就會(huì)導(dǎo)致緩存分片服務(wù)被打垮現(xiàn)象的產(chǎn)生。當(dāng)緩存服務(wù)崩潰后,此時(shí)再有請(qǐng)求產(chǎn)生,會(huì)緩存到后臺(tái)DB上,由于其本身性能較弱,在面臨大請(qǐng)求時(shí)很容易發(fā)生請(qǐng)求穿透現(xiàn)象,會(huì)進(jìn)一步導(dǎo)致“雪崩”現(xiàn)象,嚴(yán)重影響設(shè)備的性能。
如何發(fā)現(xiàn)
對(duì)于db上熱點(diǎn)數(shù)據(jù)的發(fā)現(xiàn),首先會(huì)在一個(gè)周期內(nèi)對(duì)Key進(jìn)行請(qǐng)求統(tǒng)計(jì),在達(dá)到請(qǐng)求量級(jí)后會(huì)對(duì)熱點(diǎn)Key進(jìn)行熱點(diǎn)定位,并將所有的熱點(diǎn)Key放入一個(gè)小的LRU鏈表內(nèi),在通過Proxy請(qǐng)求進(jìn)行訪問時(shí),若Redis發(fā)現(xiàn)待訪點(diǎn)是一個(gè)熱點(diǎn),就會(huì)進(jìn)入一個(gè)反饋階段,同時(shí)對(duì)該數(shù)據(jù)進(jìn)行標(biāo)記。
如何解決
首先Client會(huì)將請(qǐng)求發(fā)送至Server上,而Server又是一個(gè)多線程的服務(wù),本地就具有一個(gè)小的緩存空間。當(dāng)Server本身就擁堵時(shí),Server不會(huì)將請(qǐng)求進(jìn)一步發(fā)送給DB而是直接返回,只有當(dāng)Server本身暢通時(shí)才會(huì)將Client請(qǐng)求發(fā)送至DB,并且將該數(shù)據(jù)重新寫入到Cache中。
https://blog.csdn.net/qq_35956041/article/details/81195826
redis數(shù)據(jù)分布方式?有什么優(yōu)點(diǎn)?一致性hash呢?
分布方式:
-
節(jié)點(diǎn)取余
客戶端分片:哈希+取余
節(jié)點(diǎn)伸縮:數(shù)據(jù)節(jié)點(diǎn)關(guān)系變化,導(dǎo)致數(shù)據(jù)遷移
遷移數(shù)量和添加節(jié)點(diǎn)數(shù)量有關(guān):建議翻倍擴(kuò)容
-
一致性哈希
客戶端分片:哈希+順時(shí)針(優(yōu)化取余)
節(jié)點(diǎn)伸縮:只影響鄰近節(jié)點(diǎn),但是還是有數(shù)據(jù)遷移
翻倍伸縮:保證最小遷移數(shù)據(jù)和負(fù)載均衡
-
虛擬槽分區(qū)
預(yù)設(shè)虛擬槽:每個(gè)槽映射一個(gè)數(shù)據(jù)子集,一般比節(jié)點(diǎn)數(shù)大
良好的哈希函數(shù):例如CRC16
服務(wù)端管理節(jié)點(diǎn)、槽、數(shù)據(jù):例如Redis Cluster


