Redis

為什么使用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的好處?
  1. 速度快,因?yàn)閿?shù)據(jù)存在內(nèi)存中,類似于HashMap,HashMap的優(yōu)勢(shì)就是查找和操作的時(shí)間復(fù)雜度都是O(1)

  2. 支持豐富數(shù)據(jù)類型,支持string,list,set,sorted set,hash

  3. 支持事務(wù) :redis對(duì)事務(wù)是部分支持的,如果是在入隊(duì)時(shí)報(bào)錯(cuò),那么都不會(huì)執(zhí)行;在非入隊(duì)時(shí)報(bào)錯(cuò),那么成功的就會(huì)成功執(zhí)行。 redis監(jiān)控:鎖的介紹

  4. 豐富的特性:可用于緩存,消息,按key設(shè)置過期時(shí)間,過期后將會(huì)自動(dòng)刪除。

分布式緩存和本地緩存有啥區(qū)別?讓你自己設(shè)計(jì)本地緩存怎么設(shè)計(jì)?如何解決緩存過期問題?如何解決內(nèi)存溢出問題?
分布式緩存和本地緩存的區(qū)別
  1. 分布式緩存一致性更好一點(diǎn),本地緩存 每個(gè)實(shí)例都有自己的緩存,可能會(huì)存在不一致的情況。

  2. 本地緩存會(huì)占用堆內(nèi)存,影響垃圾回收、影響系統(tǒng)性能。分布式緩存兩大開銷會(huì)導(dǎo)致其慢于本地緩存,網(wǎng)絡(luò)延遲和對(duì)象序列化

  3. 進(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ū)別
  1. Redis和Memcache都是將數(shù)據(jù)存放在內(nèi)存中,都是內(nèi)存數(shù)據(jù)庫(kù)。不過memcache還可用于緩存其他東西,例如圖片、視頻等等;

  2. 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ǔ);

  3. 虛擬內(nèi)存--Redis當(dāng)物理內(nèi)存用完時(shí),可以將一些很久沒用到的value 交換到磁盤;

  4. 過期策略--memcache在set時(shí)就指定,例如set key1 0 0 8,即永不過期。Redis可以通過例如expire 設(shè)定,例如expire name 10;

  5. 分布式--設(shè)定memcache集群,利用magent做一主多從;redis可以做一主多從。都可以一主一從;

  6. 存儲(chǔ)數(shù)據(jù)安全--memcache掛掉后,數(shù)據(jù)沒了;redis可以定期保存到磁盤(持久化);

  7. 災(zāi)難恢復(fù)--memcache掛掉后,數(shù)據(jù)不可恢復(fù); redis數(shù)據(jù)丟失后可以通過aof恢復(fù);

  8. 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)景:

    1. 關(guān)注集合:共同關(guān)注、二度好友

    2. 點(diǎn)贊集合

    3. 抽獎(jiǎng)集合

    4. 用戶標(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)景:

    1. 隊(duì)列,比如:關(guān)注隊(duì)列、粉絲隊(duì)列

    2. 消息隊(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)景:

    1. 排行榜

    2. 時(shí)間軸

    3. 優(yōu)先級(jí)隊(duì)列

Zset底層實(shí)現(xiàn)?跳表搜索插入刪除過程?

zset的編碼有ziplistskiplist兩種。 底層分別使用ziplist(壓縮鏈表)skiplist(跳表)實(shí)現(xiàn)。

當(dāng)zset滿足以下兩個(gè)條件的時(shí)候,使用ziplist:

  1. 保存的元素少于128個(gè)
  1. 保存的所有元素大小都小于64字節(jié)

不滿足這兩個(gè)條件則使用skiplist (注意:這兩個(gè)數(shù)值是可以通過redis.confzset-max-ziplist-entrieszset-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ī)制

  1. save觸發(fā)方式(同步)

    該命令會(huì)阻塞當(dāng)前Redis服務(wù)器,執(zhí)行save命令期間,Redis不能處理其他命令,直到RDB過程完成為止。執(zhí)行完成時(shí)候如果存在老的RDB文件,就把新的替代掉舊的。我們的客戶端可能都是幾萬(wàn)或者是幾十萬(wàn),這種方式顯然不可取。

  2. 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 命令。

  3. 自動(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ù)的性能

    • 解決方案

      1. 接口層增加校驗(yàn),如用戶鑒權(quán)校驗(yàn),id做基礎(chǔ)校驗(yàn),id<=0的直接攔截

      2. 從緩存取不到的數(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ù)性能大幅度降低

    • 解決方案

      1. 設(shè)置熱點(diǎn)數(shù)據(jù)永不過期

      2. 加互斥鎖

      3. 定時(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ù)查詢

    • 解決方案

      1. 緩存數(shù)據(jù)的過期時(shí)間設(shè)置為隨機(jī),防止大量數(shù)據(jù)在同一時(shí)間過期的現(xiàn)象出現(xiàn)

      2. 如果緩存數(shù)據(jù)庫(kù)是分布式部署,將熱點(diǎn)數(shù)據(jù)均勻分布在不同緩存數(shù)據(jù)庫(kù)中

      3. 設(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ù)是一致的。

  1. 有如下場(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í)雙刪策略得以解決

  1. 數(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是單線程的

為什么快?

  1. Redis完全基于內(nèi)存,絕大部分請(qǐng)求是純粹的內(nèi)存操作,非??焖?。數(shù)據(jù)存在內(nèi)存中,類似于HashMap,HashMap的優(yōu)勢(shì)就是查找和操作的時(shí)間復(fù)雜度都是O(1)

  2. 數(shù)據(jù)結(jié)構(gòu)簡(jiǎn)單,對(duì)數(shù)據(jù)操作也簡(jiǎn)單,Redis中的數(shù)據(jù)結(jié)構(gòu)是專門進(jìn)行設(shè)計(jì)的;

  3. 使用多路I/O復(fù)用模型,非阻塞IO;

  4. 采用單線程,避免了不必要的上下文切換和競(jìng)爭(zhēng)條件,也不存在多進(jìn)程或者多線程導(dǎo)致的切換而消耗 CPU,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作,沒有因?yàn)榭赡艹霈F(xiàn)死鎖而導(dǎo)致的性能消耗;

五種IO模型的區(qū)別
  1. 阻塞式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模型

  1. 非阻塞式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è)置非阻塞式,可使用該功能

  1. 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ù)拈_銷

  1. 信號(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操作

  1. 異步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ū)別?
  1. 支持一個(gè)線程所能打開的最大連接數(shù)

    image
  2. FD劇增后帶來(lái)的IO效率問題

    image.png
  1. 消息傳遞方式

    image

綜上,在選擇select,poll,epoll時(shí)要根據(jù)具體的使用場(chǎng)合以及這三種方式的自身特點(diǎn):

  1. 表面上看epoll的性能最好,但是在連接數(shù)少并且連接都十分活躍的情況下,select和poll的性能可能比epoll好,畢竟epoll的通知機(jī)制需要很多函數(shù)回調(diào)。

  2. 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

redis主從復(fù)制,主從切換,集群
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容