Redis 緩存性能實(shí)踐及總結(jié)

一哗总、前言

在互聯(lián)網(wǎng)應(yīng)用中,緩存成為高并發(fā)架構(gòu)的關(guān)鍵組件脸候。這篇博客主要介紹緩存使用的典型場(chǎng)景穷娱、實(shí)操案例分析、Redis使用規(guī)范及常規(guī) Redis 監(jiān)控运沦。

二泵额、常見緩存對(duì)比

常見的緩存方案,有本地緩存携添,包括HashMap/ConcurrentHashMap嫁盲、Ehcache、Memcache烈掠、Guava Cache等羞秤,緩存中間件包括Redis、Tair等左敌。

image

三瘾蛋、Redis使用場(chǎng)景

1. 計(jì)數(shù)

Redis實(shí)現(xiàn)快速計(jì)數(shù)及緩存功能。

例如:視頻或直播在線觀看人數(shù)矫限,用戶每播放一次哺哼,就會(huì)自增1。

2. Session集中管理

Session可以存儲(chǔ)在應(yīng)用服務(wù)是JVM中叼风,但這一種方案會(huì)有一致性的問題取董,還有高并發(fā)下,會(huì)引發(fā)JVM內(nèi)存溢出咬扇。Redis將用戶的Session集中管理甲葬,這種情況下只要保證Redis的高可用和擴(kuò)展性,每次用戶更新或查詢登錄都直接從Redis中信息獲取懈贺。

3.限速

例如:業(yè)務(wù)要求用戶一分鐘內(nèi)经窖,只能獲取5次驗(yàn)證碼坡垫。

4.排行榜

關(guān)系型數(shù)據(jù)庫在排行榜方面查詢速度普遍偏慢,所以可以借助redis的SortedSet進(jìn)行熱點(diǎn)數(shù)據(jù)的排序画侣。

比如在項(xiàng)目中冰悠,如果需要統(tǒng)計(jì)主播的吸金排行榜,可以以主播的id作為member, 當(dāng)天打賞的活動(dòng)禮物對(duì)應(yīng)的熱度值作為 score, 通過zrangebyscore就可以獲取主播活動(dòng)日榜配乱。

5.分布式鎖

在實(shí)際的多進(jìn)程并發(fā)場(chǎng)景下溉卓,使用分布式鎖來限制程序的并發(fā)執(zhí)行。多用于防止高并發(fā)場(chǎng)景下搬泥,緩存被擊穿的可能桑寨。

分布式鎖的實(shí)際就是"占坑",當(dāng)另一個(gè)進(jìn)程來執(zhí)行setnx時(shí)忿檩,發(fā)現(xiàn)標(biāo)識(shí)位已經(jīng)為1尉尾,只好放棄或者等待。

四燥透、案例解析

1沙咏、【案例】過期設(shè)置- set命令會(huì)去掉過期時(shí)間

Redis所有的數(shù)據(jù)結(jié)構(gòu),都可以設(shè)置過期時(shí)間班套。如果一個(gè)字符串已經(jīng)設(shè)置了過期時(shí)間肢藐,然后重新設(shè)置它,會(huì)導(dǎo)致過期時(shí)間消失吱韭。所以在項(xiàng)目中需要合理評(píng)估Redis容量吆豹,避免因?yàn)轭l繁set導(dǎo)致沒有過期策略,間接導(dǎo)致內(nèi)存被占滿杉女。

如下是 Redis 源碼截圖:

image

2瞻讽、【案例】關(guān)于 Jedis 2.9.0 及以下版本過期設(shè)置 BUG

發(fā)現(xiàn)Jedis在進(jìn)行expiredAt命令調(diào)用時(shí)有bug,最終調(diào)用的是pexpire命令熏挎,這個(gè)bug會(huì)導(dǎo)致key過期時(shí)間很長(zhǎng)速勇,導(dǎo)致Redis內(nèi)存溢出等問題。建議升級(jí)到Jedis 2.9.1及以上版本坎拐。

BinaryJedisCluster.java源碼如下:

@Override
  public Long pexpireAt(final byte[] key, final long millisecondsTimestamp) {
    return new JedisClusterCommand<Long>(connectionHandler, maxAttempts) {
      @Override
      public Long execute(Jedis connection) {
        return connection.pexpire(key, millisecondsTimestamp); //此處pexpire應(yīng)該是pexpireAt
      }
    }.runBinary(key);
  }

對(duì)比pexpire和pexpireAt:

比如我們當(dāng)前使用的時(shí)間是2018-06-14 17:00:00烦磁,它的unix時(shí)間戳為1528966800000毫秒,當(dāng)我們使用PEXPIREAT命令時(shí)哼勇,由于是過去的時(shí)間都伪,相應(yīng)的key會(huì)立即過期。

而我們誤用了PEXPIRE命令時(shí)积担,key不會(huì)立即過期陨晶,而是等到1528966800000毫秒后才過期,key過期時(shí)間會(huì)相當(dāng)長(zhǎng),約幾W天后先誉,從而可能導(dǎo)致Redis內(nèi)存溢出湿刽、服務(wù)器崩潰等問題。

3褐耳、【案例】緩存被擊穿

緩存的key有過期策略诈闺,如果恰好在這個(gè)時(shí)間點(diǎn)對(duì)這個(gè)Key有大量的并發(fā)請(qǐng)求,這些請(qǐng)求發(fā)現(xiàn)緩存過期一般都會(huì)從后端DB回源數(shù)據(jù)并回設(shè)到緩存铃芦,這個(gè)時(shí)候大并發(fā)的請(qǐng)求可能會(huì)瞬間把后端DB壓掛雅镊。

業(yè)界常用優(yōu)化方案有兩種

第一種:使用分布式鎖,保證高并發(fā)下刃滓,僅有一個(gè)線程能回源后端DB仁烹。

第二種:保證高并發(fā)的請(qǐng)求到的Redis key始終是有效的,使用非用戶請(qǐng)求回源后端咧虎,改成主動(dòng)回源晃危。一般可以使用異步任務(wù)進(jìn)行緩存的主動(dòng)刷新。

4老客、【案例】Redis-standalone架構(gòu)禁止使用非0庫

Redis執(zhí)行命令select 0和select 1切換,造成性能損耗震叮。

RedisTemplate在執(zhí)行execute方法的時(shí)候會(huì)先獲取鏈接胧砰。

image

執(zhí)行到RedisConnectionUtils.java,會(huì)有一段獲取鏈接的方法苇瓣。

image

JedisConnectionFactory.java 會(huì)調(diào)用JedisConnection構(gòu)造器尉间,注意這邊的dbIndex就是數(shù)據(jù)庫編號(hào),如:1

image

繼續(xù)跟進(jìn)JedisConnection代碼击罪,當(dāng)選擇庫大于1時(shí)哲嘲,會(huì)有select db操作。如果一直使用0庫是不需要額外執(zhí)行切庫命令的媳禁。知道了第一個(gè)切庫select 1的地方眠副,那么select 0是哪來的呢?

image

其實(shí)客戶端使用Redis也會(huì)是要釋放鏈接的竣稽,只不過RedisTemplate已經(jīng)幫我們自動(dòng)釋放了囱怕,讓我們?cè)倩氐揭婚_始RedisTemplate執(zhí)行execute(...)方法的地方。

image

下面還是RedisConnectionUtils.java毫别,執(zhí)行鏈接關(guān)閉的代碼娃弓。

image

按代碼注釋的意思,如果選擇庫編號(hào)不為0岛宦,spring-data-redis框架每次都會(huì)執(zhí)行重置select 0台丛!

image

筆者在vivo商城業(yè)務(wù)中,商品詳情頁接口經(jīng)過上面的調(diào)優(yōu)砾肺,性能提高了3倍多挽霉。

進(jìn)一步驗(yàn)證數(shù)據(jù)庫切換至少影響性能3倍左右(視具體業(yè)務(wù)而定)防嗡。

Rediscluster集群數(shù)據(jù)庫,默認(rèn)0庫炼吴,無法選擇其他數(shù)據(jù)庫本鸣,也就避免了這個(gè)問題。

5硅蹦、【案例】當(dāng)心時(shí)間復(fù)雜度o(n)Redis命令

Redis是單線程的荣德,所以線程安全的。

Redis使用非阻塞IO童芹,并且大部分命令的時(shí)間復(fù)雜度O(1)涮瞻。

使用高耗時(shí)的命令是非常危險(xiǎn)的,會(huì)占用唯一的一個(gè)線程的大量處理時(shí)間假褪,導(dǎo)致所有的請(qǐng)求都被拖慢署咽。

例如:獲取所有set集合中的元素 smembers myset,返回指定Hash中所有的member生音,時(shí)間復(fù)雜度O(N)宁否。

緩存的Value集合變大,當(dāng)高并接口請(qǐng)求時(shí)缀遍,會(huì)從Redis讀取相關(guān)數(shù)據(jù)慕匠,每個(gè)請(qǐng)求讀取的時(shí)間變長(zhǎng),不斷的疊加域醇,導(dǎo)致出現(xiàn)熱點(diǎn)KEY情況台谊,Redis某個(gè)分片處于阻塞,CPU使用率達(dá)到100%譬挚。

6锅铅、【案例】緩存熱key

在Redis中,訪問頻率高的key稱為熱點(diǎn)key减宣,當(dāng)某一熱點(diǎn)key的請(qǐng)求到Server主機(jī)時(shí)盐须,由于請(qǐng)求量特別大,導(dǎo)致主機(jī)資源不足蚪腋,甚至宕機(jī)丰歌,影響正常的服務(wù)。

熱key問題的產(chǎn)生屉凯,有如下兩種原因:

  1. 用戶消費(fèi)的數(shù)據(jù)遠(yuǎn)大于生產(chǎn)的數(shù)據(jù)立帖,比如熱賣商品或秒殺商品、熱點(diǎn)新聞悠砚、熱點(diǎn)評(píng)論等晓勇,這些典型的讀多寫少的場(chǎng)景會(huì)產(chǎn)生熱點(diǎn)問題。

  2. 請(qǐng)求分片集中,超過單Server的性能極限绑咱,比如 固定名稱key绰筛,哈希落入一臺(tái)Server,訪問量極大的情況描融,超過Server極限時(shí)铝噩,就會(huì)導(dǎo)致熱點(diǎn)Key問題的產(chǎn)生。

那么在實(shí)際業(yè)務(wù)中窿克,如何識(shí)別到熱點(diǎn)key呢骏庸?

  1. 憑借業(yè)務(wù)經(jīng)驗(yàn),進(jìn)行預(yù)估哪些是熱key年叮;

  2. 客戶端統(tǒng)計(jì)收集具被,本地統(tǒng)計(jì)或者上報(bào);

  3. 如果服務(wù)端有代理層只损,可以在代理層進(jìn)行收集上報(bào)一姿;

當(dāng)我們識(shí)別到熱key,如何解決熱key問題跃惫?

  1. Redis集群擴(kuò)容:增加分片副本叮叹,均衡讀流量;

  2. 進(jìn)一步對(duì)熱key進(jìn)行散列爆存,比如將一個(gè)key備份為key1,key2……keyN衬横,同樣的數(shù)據(jù)N個(gè)備份,N個(gè)備份分布到不同分片终蒂,訪問時(shí)可隨機(jī)訪問N個(gè)備份中的一個(gè),進(jìn)一步分擔(dān)讀流量遥诉。

  3. 使用二級(jí)緩存拇泣,即本地緩存。

當(dāng)發(fā)現(xiàn)熱key后矮锈,將熱key對(duì)應(yīng)數(shù)據(jù)首先加載到應(yīng)用服務(wù)器本地緩存中霉翔,減少對(duì)Redis的讀請(qǐng)求。

五苞笨、Redis 規(guī)范

1债朵、禁止使用非database 0

說明:

Redis-standalone架構(gòu),禁止使用Redis中的其他database瀑凝。

原由:

  • 為以后業(yè)務(wù)遷移 Redis Cluster 保持兼容性.

  • 多個(gè) database 用 select 切換時(shí)序芦,更消耗CPU資源。

  • 更易自動(dòng)化運(yùn)維管理粤咪,如 scan/dbsize 命令只用于當(dāng)database谚中。

  • 部分 Redis Clients 因線程安全問題,不支持單實(shí)例多 database。

2宪塔、Key設(shè)計(jì)規(guī)范

按業(yè)務(wù)功能命名key前綴磁奖,防止key沖突覆蓋,推薦 用冒號(hào)分隔某筐,例如比搭,業(yè)務(wù)名:表名:id:,如 live:rank:user:weekly:1:202003南誊。

Key的長(zhǎng)度小于30個(gè)字符身诺,Key名字本身是String對(duì)象,Redis硬編碼限制最大長(zhǎng)度512MB弟疆。

在Redis緩存場(chǎng)景戚长,推薦Key都設(shè)置TTL值,保證不使用的Key能被及時(shí)清理或淘汰怠苔。

Key設(shè)計(jì)時(shí)禁止包含特殊字符同廉,如空格、換行柑司、單雙引號(hào)以及其他轉(zhuǎn)義字符迫肖。

3、Value設(shè)計(jì)規(guī)范

單個(gè)Value大小必須控制10KB以內(nèi)攒驰,單實(shí)例鍵個(gè)數(shù)過大蟆湖,可能導(dǎo)致過期鍵的回收不及時(shí)。

set玻粪、hash隅津、list等復(fù)雜數(shù)據(jù)類型,要盡量降低數(shù)據(jù)結(jié)構(gòu)中的元素個(gè)數(shù)劲室,建議個(gè)數(shù)不要超過1000伦仍。

4、關(guān)注命令時(shí)間復(fù)雜度

推薦使用O(1)命令很洋,如get scard等充蓝。

O(N)命令關(guān)注N的數(shù)量,如下命令需要對(duì)N值在業(yè)務(wù)層面做控制喉磁。

  • hgetall

  • lrange

  • smembers

  • zrange

例如:smember命令時(shí)間復(fù)雜度為O(n)谓苟,當(dāng)n持續(xù)增加時(shí),會(huì)導(dǎo)致 Redis CPU 持續(xù)飆高协怒,阻塞其他命令的執(zhí)行涝焙;

5、Pipeline使用

說明:

Pipeline是Redis批量提交的一種方式,也就是把多個(gè)命令操作建立一次連接發(fā)給Redis去執(zhí)行,會(huì)比循環(huán)的單次提交性能更優(yōu)孕暇。

Redis客戶端執(zhí)行一條命令分4個(gè)過程:發(fā)送命令 -> 命令排隊(duì) ->命令執(zhí)行 -> 返回結(jié)果纱皆。

image

常用的mget湾趾、mset命令,有效節(jié)約RTT(命令執(zhí)行往返時(shí)間)派草,但hgetall并沒有mhgetall搀缠,是不支持批量操作的。此時(shí)近迁,需要使用Pipeline命令

image

例如:直播中臺(tái)項(xiàng)目中艺普,需要同時(shí)查詢主播日、周鉴竭、月排行榜歧譬,使用PIPELINE一次提交多個(gè)命令,同時(shí)返回三個(gè)榜單數(shù)據(jù)搏存。

6瑰步、線上禁用命令

  • 禁止使用Monitor

禁止生產(chǎn)環(huán)境使用monitor命令,monitor命令在高并發(fā)條件下璧眠,會(huì)存在內(nèi)存暴增和影響Redis性能的隱患

  • 禁止使用Keys

keys操作是遍歷所有的key缩焦,如果key非常多的情況下導(dǎo)致慢查詢,會(huì)阻塞其他命令责静。所以禁止使用keys及keys pattern命令袁滥。

建議線上使用scan命令代替keys命令。

  • 禁止使用Flushall灾螃、Flushdb

刪除Redis中所有數(shù)據(jù)庫中的所有記錄题翻,并且該命令是原子性的,不會(huì)終止執(zhí)行腰鬼,一旦執(zhí)行嵌赠,將不會(huì)執(zhí)行失敗。

  • 禁止使用Save

阻塞當(dāng)前redis服務(wù)器熄赡,直到持久化操作完成為止猾普,對(duì)于內(nèi)存較大的實(shí)例會(huì)造成長(zhǎng)時(shí)間的阻塞。

  • BGREWRITEAOF

手動(dòng)AOF本谜,手動(dòng)持久化對(duì)于內(nèi)存較大的實(shí)例會(huì)造成長(zhǎng)時(shí)間的阻塞。

  • Config

Config是客戶端配置方式偎窘,不利于Redis運(yùn)維乌助。建議在Redis配置文件中設(shè)置。

六陌知、Redis 監(jiān)控

1他托、慢查詢

方法一:slowlog獲取慢查詢?nèi)罩?/p>

127.0.0.1:{port}> slowlog get 5

    1. (integer) 47

    2. (integer) 1533810300

    3. (integer) 175833

      1. "DEL"

      2. "spring:session:expirations:1533810300000"

    1. (integer) 46

    2. (integer) 1533810300

    3. (integer) 117400

      1. "SMEMBERS"

方法二:更全面的慢查詢可以通過CacheCloud工具監(jiān)控。

路徑:"應(yīng)用列表"-點(diǎn)擊相關(guān)應(yīng)用名-點(diǎn)擊"慢查詢"Tab頁仆葡。

點(diǎn)擊"慢查詢"赏参,重點(diǎn)關(guān)注慢查詢個(gè)數(shù)及相關(guān)命令志笼。

2、監(jiān)控Redis實(shí)例綁定的CPU核心使用率

由于Redis是單線程把篓,重點(diǎn)監(jiān)控Redis實(shí)例綁定的CPU核心使用率纫溃。

一般CPU資源使用率為10%左右,如果使用率高于20%時(shí)韧掩,考慮是否使用了RDB持久化紊浩。

3、Redis分片負(fù)載均衡

當(dāng)前redis-cluster架構(gòu)模式疗锐,3個(gè)master和3個(gè)Slave組成的集群坊谁,關(guān)注 Redis-cluster每個(gè)分片requests流量均衡情況;

通過命令獲然:redis-cli -p{port} -h{host} --stat

一般情況口芍,超過12W需要告警。

4雇卷、關(guān)注大key BigKey

通過Redis提供的工具鬓椭,redis-cli定時(shí)掃描相應(yīng)Redis大Key,進(jìn)行優(yōu)化聋庵。

具體命令如下:redis-cli -h 127.0.0.1 -p {port} --bigkeys或 redis-memory-for-key -s {IP} -p {port} XXX_KEY

一般超過10K為大key膘融,需要重點(diǎn)關(guān)注,建議從業(yè)務(wù)層面優(yōu)化祭玉。

5氧映、監(jiān)控Redis占用內(nèi)存大小

Info memory 命令查看,避免在高并發(fā)場(chǎng)景下脱货,由于分配的MaxMemory被耗盡岛都,帶來的性能問題。

重點(diǎn)關(guān)注 used_memory_human 配置項(xiàng)對(duì)應(yīng)的value值振峻,增量過高時(shí)臼疫,需要重點(diǎn)評(píng)估。

七扣孟、總結(jié)

結(jié)合具體業(yè)務(wù)特性烫堤,合理評(píng)估Redis所需內(nèi)存容量、選擇數(shù)據(jù)類型凤价、設(shè)置單key大小鸽斟,才能更好地服務(wù)于業(yè)務(wù),為業(yè)務(wù)提供高性能的保障利诺。


作者:Jessica Chen

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末富蓄,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子慢逾,更是在濱河造成了極大的恐慌立倍,老刑警劉巖灭红,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異口注,居然都是意外死亡变擒,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門疆导,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赁项,“玉大人,你說我怎么就攤上這事澈段∮撇耍” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵败富,是天一觀的道長(zhǎng)悔醋。 經(jīng)常有香客問我,道長(zhǎng)兽叮,這世上最難降的妖魔是什么芬骄? 我笑而不...
    開封第一講書人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮鹦聪,結(jié)果婚禮上账阻,老公的妹妹穿的比我還像新娘。我一直安慰自己泽本,他們只是感情好淘太,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著规丽,像睡著了一般蒲牧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上赌莺,一...
    開封第一講書人閱讀 51,727評(píng)論 1 305
  • 那天冰抢,我揣著相機(jī)與錄音,去河邊找鬼艘狭。 笑死挎扰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的巢音。 我是一名探鬼主播遵倦,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼港谊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起橙弱,我...
    開封第一講書人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤歧寺,失蹤者是張志新(化名)和其女友劉穎燥狰,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體斜筐,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡龙致,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了顷链。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片目代。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖嗤练,靈堂內(nèi)的尸體忽然破棺而出榛了,到底是詐尸還是另有隱情,我是刑警寧澤煞抬,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布霜大,位于F島的核電站,受9級(jí)特大地震影響革答,放射性物質(zhì)發(fā)生泄漏战坤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一残拐、第九天 我趴在偏房一處隱蔽的房頂上張望途茫。 院中可真熱鬧,春花似錦溪食、人聲如沸囊卜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽边败。三九已至,卻和暖如春捎废,著一層夾襖步出監(jiān)牢的瞬間笑窜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工登疗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留排截,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓辐益,卻偏偏與公主長(zhǎng)得像断傲,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子智政,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容