redis數據結構及其使用場景、持久化、緩存淘汰策略

Redis 單線程高性能,它全部的數據都在內存中,全部的運算都是內存級別的運算,並且單線程避免了多線程的切換性能損耗問題。redis利用epoll來實現IO多路複用,將鏈接信息和事件放到隊列中,依次放到文件事件分派器,事件分派器將事件分發給事件處理器。html

 

1.Redis數據結構及簡單操做指令、應用場景
java

String、list、set、hash、zset(有序set)redis

整體來講redis都是經過Key-Value的形式來存儲數據的。只是不用數據類型Value的形式不一樣。算法

 

String:最簡單數據結構,好比咱們將一個對象轉成json串來存儲數據庫

  set key value 存放數據json

  get key 獲取數據緩存

  exists key 查看數據是否存在,存在返回1不然0安全

  del key 刪除數據 返回操做成功的條數數據結構

 

  mset key1 value1 key2 value2 key3 value3...存放多組數據多線程

  mget key1 ke2y key3... 獲取多個key的數據,返回一個集合,相似Map的values方法

 

  expire key second 設置key 過時時間,單位秒

  expire key second 設置key 過時時間,單位秒

  setex key second value設置key 過時時間,單位秒(等價於先set,再expire)

  setnx key value 若是key不存在就set 返回1.若是存在返回0(能夠基於此實現分佈式鎖)

應用場景

  1.普通的單值存儲key-value

  2.分佈式鎖 setnx key value 

  3.計數器 incr key   原子加,好比統計文章的閱讀量

  

 

   4.分佈式seesion

  

List:並非java裏面的list,redis的list更像一個鏈表或者說隊列/棧的結構。這就意味着它的刪除插入快,可是經過索引定位就比較慢了。當列表彈出了最後一個元素以後,該數據結構自動被刪除,內存被回收。

Redis 的列表結構能夠來作異步隊列使用。將須要延後處理的任務塞進 Redis 的列表,另外一個線程從這個列表中輪詢數據進行處理。list的索引有正向索引和反向因此,正向索引以0開始,反向索引以-1結束。

  rpush key value1 value2 value3...  插入list數據

  llen key     查看長度

  lpop key    按加入順序獲取(先進先出,相似隊列)

  rpop key   後進先出,有點相似棧

  lrange key start stop   查看此索引訪問內的數據,可使用正向索引或者反向索引,這個操做不會彈出數據。

  blpop key timeout   從左邊彈出一個元素若是沒有則阻塞等待timeout秒,若是timeout=0表示一直阻塞等待

  brpop key timeout  同上從右邊彈出一個元素

  列表取數據,使用pop取完後整個列表都被回收了,就是說只能取一次數據。

應用場景

  1.經常使用數據結構模擬:

    棧:lpush + lpop 後進先出

    隊列:lpush + rpop 先進先出

    阻塞隊列:lpush + brpop 

  2.訂閱消息推送:A用戶關注B公衆號,B發佈文章的時候就往一個關於A的key裏面寫入文章關鍵信息,lpush B:A:msg  文章ID,每次A進入訂閱信息的時候:lrange B:A:msg 0 4 就能夠看到最新的5條消息

  3.

 

Hash:相似java的hashMap,和字符串相比,咱們存儲數據的時候能夠只存儲對象的部分屬性,而字符串則須要完整將整個對象轉換。固然hash存儲結構的消耗確定是高於字符串的,缺點只能對rediskey進行一些屬性設置操做,不能操做設置裏面hashkey,好比設置過時時間這些,只能是針對rediskey。

  hset redisKey hashKey1 value1

    hset redisKey hashKey2 value2  插入數據

  hgetall redisKey  獲取數據,key value間隔出現

  hlen redisKey  查看hash長度

  hget redisKey hashKey 獲取hashKey 對應的value

  hmset redisKey hashKey1 value1 hashKey2 value2 hashKey2 value3 批量插入值

應用場景

  1.購物車實現:

    添加商品:hset cart:用戶ID  商品ID  數量

    增長數量:hincrby cart:用戶ID  商品ID  數量

    商品總數:hlen cart:用戶ID

    刪除商品:hdel cart:用戶ID  商品ID

    獲取購物車全部商品:hgetall cart:用戶ID

 

Set : 相似HashSet無序,

  sadd key value         

  sadd key value1 value2   批量添加

  smembers key    查看全部

  sismember key value  查詢某個值是否存在,存在返回1

  scard  key   查看大小

  spop key    獲取一個元素 最後一個數據取完以後,該結構會被清理,沒法再次獲取數據

  srandmember key count  從集合中選出幾個元素,此操做不會刪除數據

 

  sinter key...   交集運算,求出多個集合的交集

  sinterstore newkey  key...   將交集結果存入newkey中

  sunion key...  求並集

  sunionstore newkey  key...   將並集結果存入新key中

  sdiff  key...   求差集

  sdiffstore newkey  key...   將差集結果存入新key中

應用場景

  1.隨機抽獎活動:將參與者放入set中,抽獎時若是能夠重複參與一二三等獎,就用srandmember key count ,若是不能重複參與就世界pop出去。

  2.紅包瓜分,參考隨機瓜分百萬紅包,這裏面其實應該用set,文章中用的list,由於是在代碼層面作了一次隨機性。

  3.微博的關注模型:可使用set的交,並,差操做來實現。

    

Zset:帶一個分值大小的set集合,基於大小排序的集合。

  zadd key score value    往有序集合key中加入帶分值score的元素
  zrem key value        從有序集合key中刪除元素
  zscore key value             返回有序集合key中元素value的分值
  zincrby key increment value        爲有序集合key中元素value的分值加上increment
  zcard key                返回有序集合key中元素個數
  zrange key start stop      正序獲取有序集合key從start下標到stop下標的元素(從小到大)
  zrevrange key start stop    倒序獲取有序集合key從start下標到stop下標的元素(從大到小)

 

  zunionstore newkey numkeys key...    並集計算將多個key求並集 若是value同樣的則會將score相加 獲得一個新的zet,numkeys是後面集合數量
  zinterstore newkey  numkeys key...     交集計算,一樣會將其score相加

應用場景:

  1.排行榜,投票排名等須要排序的場景

  2.一週排行數據彙總,zunionstore或者zinterstore

 

原子計數操做

若是value是整數的話還能夠實現自增操做(也能夠用於實現分佈式鎖,該增加有限,最大到long max,超過該值會直接報錯)

incr key 自增 若是key不存在默認從0自增1

incrby key step 設置增長步長step

 

2.redis持久化

雖說redis都是內存級別的操做,其實也是有持久化的。

一種是基於RDB快照

Redis 將內存數據庫快照保存在名字爲 dump.rdb 的二進制文件中。

 

 

 

能夠對 Redis 進行設置, 讓它在N 秒內數據集至少有 M 個改動這一條件被知足時, 自動保存一次數據集。

 也能夠執行sava或者bgsave指令手動生成快照。save是同步的會阻塞其餘指令執行,bgsave是另起線程去生成快照,不會阻塞客戶端指令。

 

 

另外一種是AOF(append-only file)

快照並不可靠,上次快照以後,還未到達下一次快照條件時,這時候服務出現了問題,那麼這期間的數據是沒法保存到快照版本中的。這個時候就須要AOF了,它將每一條指令都記錄進文件,當redis重啓的時候,從新執行這個文件裏面指令,就能夠恢復全部的數據到內存中了。

能夠經過配置appendonly yes 來開啓AOF,默認是關閉的

 

 

AOF也有三種同步數據的策略,

每次有操做都去刷新文件,很慢,但安全

每秒同步刷新一次:可能會丟失一秒內的數據

從不一樣步同步刷新:讓操做系統在須要的時候刷新數據,不安全

默認的是每秒刷一次

 

 

這樣一來可能AOF中有不少沒用的記錄,好比屢次set 其實只有最後一次的數據纔是有效的,前面全是無效的,保存下來,既佔用磁盤,執行的時候也耗費時間,這個時候就有AOF重寫。AOF重寫是啥呢,其實就是整理咱們的執行指令,前面那些原本會被覆蓋的指令就不須要再記錄了,只保留最後一次修改的指令就能夠了,這樣一來文件就能夠小不少,既省了磁盤,後續恢復數據執行起來也快。

有兩個參數:

  auto-aof-rewrite-min-size 64mb    AOF文件至少要達到64M纔會自動重寫
  auto-aof-rewrite-percentage 100  AOF文件自上一次重寫後文件大小增加了100%則再次觸發重寫
也能夠手動執行 bgrewriteaof 指令去進行AOF重寫。這個指令是異步起線程去執行,不會阻塞客戶端的指令。

 

 

混合持久化

RDB快照數據恢復速度快,可是可能會有大量數據丟失,因此一般恢復數據仍是用的AOF日誌重放,可是AOF相對來講速度會很慢,尤爲是在數據量大的時候。所以在4.0的時候帶來了混合持久化,也就是AOF在刷新的時候,先記錄上次的快照版本,而後記錄上次快照版本到如今的增量操做,而後合併成一個文件,覆蓋原來的appendonly.aof文件。Redis重啓的時候,先加載RDB快照的內容,在重放AOF日誌中增量操做的內容就能夠了。

開啓混合持久化:aof-use-rdb-preamble yes

混合持久化中appendonly.aof內容格式,一部分是RDB文件內容格式,另外的纔是AOF文件的內容格式。

 

3.緩存淘汰策略:

當 Redis 內存超出物理內存限制時,內存的數據會開始和磁盤產生頻繁的交換 。會讓 Redis 的性能急劇降低,對於訪問量比較頻繁的 Redis 來講,這樣存取效率基本上等於不可用。

在生產環境中咱們是不容許 Redis 出現交換行爲的,爲了限制最大使用內存,Redis 提供了配置參數 maxmemory 來限制內存超出指望大小。

當實際內存超出 maxmemory 時,Redis 提供了幾種可選策略 (maxmemory-policy) 來讓用戶本身決定該如何騰出新的空間以繼續提供讀寫服務。

 

maxmemory <bytes> # MAXMEMORY POLICY: how Redis will select what to remove when maxmemory # is reached. You can select among five behaviors: # # volatile-lru -> Evict using approximated LRU among the keys with an expire set. # allkeys-lru -> Evict any key using approximated LRU. # volatile-lfu -> Evict using approximated LFU among the keys with an expire set. # allkeys-lfu -> Evict any key using approximated LFU. # volatile-random -> Remove a random key among the ones with an expire set. # allkeys-random -> Remove a random key, any key. # volatile-ttl -> Remove the key with the nearest expire time (minor TTL) # noeviction -> Don't evict anything, just return an error on write operations. # # LRU means Least Recently Used # LFU means Least Frequently Used # # Both LRU, LFU and volatile-ttl are implemented using approximated # randomized algorithms. # # Note: with any of the above policies, Redis will return an error on write # operations, when there are no suitable keys for eviction. # # At the date of writing these commands are: set setnx setex append # incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd # sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby # zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby # getset mset msetnx exec sort # # The default is: # # maxmemory-policy noeviction

noeviction 不會繼續處理寫請求 (del,read請求能夠繼續進行)。這樣能夠保證不會丟失數據,可是會讓線上的寫相關的業務不能持續進行。這是默認的淘汰策略。

volatile-lru 嘗試淘汰設置了過時時間的 key,最少使用的 key 優先被淘汰。沒有設置過時時間的 key 不會被淘汰,這樣能夠保證須要持久化的數據不會忽然丟失。

volatile-ttl 跟上面同樣,除了淘汰的策略不是 LRU,而是 key 的剩餘壽命 ttl 的值,ttl 越小越優先被淘汰。

volatile-random 跟上面同樣,不過淘汰的 key 是過時 key 集合中隨機的 key。

allkeys-lru 區別於 volatile-lru,這個策略要淘汰的 key 對象是全體的 key 集合,而不僅是過時的 key 集合。這意味着沒有設置過時時間的 key 也會被淘汰。

allkeys-random 跟上面同樣,不過淘汰的策略是隨機的 key。

volatile-xxx 策略只會針對帶過時時間的 key 進行淘汰,allkeys-xxx 策略會對全部的 key 進行淘汰。若是你只是拿 Redis 作緩存,那應該使用 allkeys-xxx,客戶端寫緩存時沒必要攜帶過時時間。若是你還想同時使用 Redis 的持久化功能,那就使用 volatile-xxx 策略,這樣能夠保留沒有設置過時時間的 key,它們是永久的 key 不會被 LRU 算法淘汰。

原文出處:https://www.cnblogs.com/nijunyang/p/11443001.html

相關文章
相關標籤/搜索