Redis的介紹及使用

redis 簡介

  簡單來講 redis 就是一個數據庫,不過與傳統數據庫不一樣的是 redis 的數據是存在內存中的,因此讀寫速度很是快,所以 redis 被普遍應用於緩存方向。另外,redis 也常常用來作分佈式鎖。redis 提供了多種數據類型來支持不一樣的業務場景。除此以外,redis 支持事務 、持久化、LUA腳本、LRU驅動事件、多種集羣方案。redis

爲何要用 redis?/爲何要用緩存?

  主要從「高性能」和「高併發」這兩點來看待這個問題。數據庫

  高性能:

  假如用戶第一次訪問數據庫中的某些數據。這個過程會比較慢,由於是從硬盤上讀取的。將該用戶訪問的數據存在緩存中,這樣下一次再訪問這些數據的時候就能夠直接從緩存中獲取了。操做緩存就是直接操做內存,因此速度至關快。若是數據庫中的對應數據改變的以後,同步改變緩存中相應的數據便可!緩存

 

   高併發:

  直接操做緩存可以承受的請求是遠遠大於直接訪問數據庫的,因此咱們能夠考慮把數據庫中的部分數據轉移到緩存中去,這樣用戶的一部分請求會直接到緩存這裏而不用通過數據庫。安全

 

 爲何要用 redis 而不用 map/guava 作緩存?

  緩存分爲本地緩存和分佈式緩存。以 Java 爲例,使用自帶的 map 或者 guava 實現的是本地緩存,最主要的特色是輕量以及快速,生命週期隨着 jvm 的銷燬而結束,而且在多實例的狀況下,每一個實例都須要各自保存一份緩存,緩存不具備一致性。  服務器

  使用 redis 或 memcached 之類的稱爲分佈式緩存,在多實例的狀況下,各實例共用一份緩存數據,緩存具備一致性。缺點是須要保持 redis 或 memcached服務的高可用,整個程序架構上較爲複雜。網絡

redis 的線程模型

  redis 內部使用文件事件處理器 file event handler,這個文件事件處理器是單線程的,因此 redis 才叫作單線程的模型。它採用 IO 多路複用機制同時監聽多個 socket,根據 socket 上的事件來選擇對應的事件處理器進行處理。  數據結構

  文件事件處理器的結構包含 4 個部分:  多線程

  1.   多個 socket
  2.   IO 多路複用程序
  3.   文件事件分派器
  4.   事件處理器(鏈接應答處理器、命令請求處理器、命令回覆處理器)

  多個 socket 可能會併發產生不一樣的操做,每一個操做對應不一樣的文件事件,可是 IO 多路複用程序會監聽多個 socket,會將 socket 產生的事件放入隊列中排隊,事件分派器每次從隊列中取出一個事件,把該事件交給對應的事件處理器進行處理。架構

redis 和 memcached 的區別

  對於 redis 和 memcached 我總結了下面四點。如今公司通常都是用 redis 來實現緩存,並且 redis 自身也愈來愈強大了!併發

  一、redis支持更豐富的數據類型(支持更復雜的應用場景):Redis不只僅支持簡單的k/v類型的數據,同時還提供list,set,zset,hash等數據結構的存儲。memcache支持簡單的數據類型,String。

  二、Redis支持數據的持久化,能夠將內存中的數據保持在磁盤中,重啓的時候能夠再次加載進行使用,而Memecache把數據所有存在內存之中。

  三、集羣模式:memcached沒有原生的集羣模式,須要依靠客戶端來實現往集羣中分片寫入數據;可是 redis 目前是原生支持 cluster 模式的.

  四、Memcached是多線程,非阻塞IO複用的網絡模型;Redis使用單線程的多路 IO 複用模型。

 

 redis 常見數據結構以及使用場景分析  

  1.String

  經常使用命令: set,get,decr,incr,mget 等。

  String數據結構是簡單的key-value類型,value其實不只能夠是String,也能夠是數字。 常規key-value緩存應用; 常規計數:微博數,粉絲數等。

  2.Hash

  經常使用命令: hget,hset,hgetall 等。

  hash 是一個 string 類型的 field 和 value 的映射表,hash 特別適合用於存儲對象,後續操做的時候,你能夠直接僅僅修改這個對象中的某個字段的值。 好比咱們能夠 hash 數據結構來存儲用戶信息,商品信息等等。好比下面我就用 hash 類型存放了我本人的一些信息:

key=JavaUser293847 
value={
「id」: 1,
「name」: 「SnailClimb」,
「age」: 22,
「location」: 「Wuhan, Hubei」
}

  3.List  

  經常使用命令: lpush,rpush,lpop,rpop,lrange等  

  list 就是鏈表,Redis list 的應用場景很是多,也是Redis最重要的數據結構之一,好比微博的關注列表,粉絲列表,消息列表等功能均可以用Redis的 list 結構來實現。  

  Redis list 的實現爲一個雙向鏈表,便可以支持反向查找和遍歷,更方便操做,不過帶來了部分額外的內存開銷。  

  另外能夠經過 lrange 命令,就是從某個元素開始讀取多少個元素,能夠基於 list 實現分頁查詢,這個很棒的一個功能,基於 redis 實現簡單的高性能分頁,能夠作相似微博那種下拉不斷分頁的東西(一頁一頁的往下走),性能高。

  4.Set  

  經常使用命令: sadd,spop,smembers,sunion 等  

  set 對外提供的功能與list相似是一個列表的功能,特殊之處在於 set 是能夠自動排重的。  

  當你須要存儲一個列表數據,又不但願出現重複數據時,set是一個很好的選擇,而且set提供了判斷某個成員是否在一個set集合內的重要接口,這個也是list所不能提供的。能夠基於 set 輕易實現交集、並集、差集的操做。  

  好比:在微博應用中,能夠將一個用戶全部的關注人存在一個集合中,將其全部粉絲存在一個集合。Redis能夠很是方便的實現如共同關注、共同粉絲、共同喜愛等功能。這個過程也就是求交集的過程,具體命令以下:

sinterstore key1 key2 key3     將交集存在key1內

  5.Sorted Set  

  經常使用命令: zadd,zrange,zrem,zcard等  

  和set相比,sorted set增長了一個權重參數score,使得集合中的元素可以按score進行有序排列。  

  舉例: 在直播系統中,實時排行信息包含直播間在線用戶列表,各類禮物排行榜,彈幕消息(能夠理解爲按消息維度的消息排行榜)等信息,適合使用 Redis 中的 Sorted Set 結構進行存儲。

redis 設置過時時間

  Redis中有個設置時間過時的功能,即對存儲在 redis 數據庫中的值能夠設置一個過時時間。做爲一個緩存數據庫,這是很是實用的。如咱們通常項目中的 token 或者一些登陸信息,尤爲是短信驗證碼都是有時間限制的,按照傳統的數據庫處理方式,通常都是本身判斷過時,這樣無疑會嚴重影響項目性能。  

  咱們 set key 的時候,均可以給一個 expire time,就是過時時間,經過過時時間咱們能夠指定這個 key 能夠存活的時間。  

  若是假設你設置了一批 key 只能存活1個小時,那麼接下來1小時後,redis是怎麼對這批key進行刪除的?

  按期刪除+惰性刪除。

  經過名字大概就能猜出這兩個刪除方式的意思了。

  • 按期刪除:redis默認是每隔 100ms 就隨機抽取一些設置了過時時間的key,檢查其是否過時,若是過時就刪除。注意這裏是隨機抽取的。爲何要隨機呢?你想想假如 redis 存了幾十萬個 key ,每隔100ms就遍歷全部的設置過時時間的 key 的話,就會給 CPU 帶來很大的負載!
  • 惰性刪除 :按期刪除可能會致使不少過時 key 到了時間並無被刪除掉。因此就有了惰性刪除。假如你的過時 key,靠按期刪除沒有被刪除掉,還停留在內存裏,除非你的系統去查一下那個 key,纔會被redis給刪除掉。這就是所謂的惰性刪除,也是夠懶的哈!

  可是僅僅經過設置過時時間仍是有問題的。咱們想一下:若是按期刪除漏掉了不少過時 key,而後你也沒及時去查,也就沒走惰性刪除,此時會怎麼樣?若是大量過時key堆積在內存裏,致使redis內存塊耗盡了。怎麼解決這個問題呢? redis 內存淘汰機制。

redis 內存淘汰機制(MySQL裏有2000w數據,Redis中只存20w的數據,如何保證Redis中的數據都是熱點數據?)

  redis 配置文件 redis.conf 中有相關注釋,我這裏就不貼了,你們能夠自行查閱或者經過這個網址查看:http://download.redis.io/redis-stable/redis.conf

  redis 提供 6種數據淘汰策略:

  1.   volatile-lru:從已設置過時時間的數據集(server.db[i].expires)中挑選最近最少使用的數據淘汰
  2.   volatile-ttl:從已設置過時時間的數據集(server.db[i].expires)中挑選將要過時的數據淘汰
  3.   volatile-random:從已設置過時時間的數據集(server.db[i].expires)中任意選擇數據淘汰
  4.   allkeys-lru:當內存不足以容納新寫入數據時,在鍵空間中,移除最近最少使用的key(這個是最經常使用的)
  5.   allkeys-random:從數據集(server.db[i].dict)中任意選擇數據淘汰
  6.        no-eviction:禁止驅逐數據,也就是說當內存不足以容納新寫入數據時,新寫入操做會報錯。這個應該沒人使用吧!

  4.0版本後增長如下兩種:

  7.         volatile-lfu:從已設置過時時間的數據集(server.db[i].expires)中挑選最不常用的數據淘汰

  8.         allkeys-lfu:當內存不足以容納新寫入數據時,在鍵空間中,移除最不常用的key

redis 持久化機制(怎麼保證 redis 掛掉以後再重啓數據能夠進行恢復)

  不少時候咱們須要持久化數據也就是將內存中的數據寫入到硬盤裏面,大部分緣由是爲了以後重用數據(好比重啓機器、機器故障以後恢復數據),或者是爲了防止系統故障而將數據備份到一個遠程位置。  

  Redis不一樣於Memcached的很重一點就是,Redis支持持久化,並且支持兩種不一樣的持久化操做。Redis的一種持久化方式叫快照(snapshotting,RDB),另外一種方式是隻追加文件(append-only file,AOF)。這兩種方法各有千秋,下面我會詳細這兩種持久化方法是什麼,怎麼用,如何選擇適合本身的持久化方法。  

  快照(snapshotting)持久化(RDB)  

  Redis能夠經過建立快照來得到存儲在內存裏面的數據在某個時間點上的副本。Redis建立快照以後,能夠對快照進行備份,能夠將快照複製到其餘服務器從而建立具備相同數據的服務器副本(Redis主從結構,主要用來提升Redis性能),還能夠將快照留在原地以便重啓服務器的時候使用。  

  快照持久化是Redis默認採用的持久化方式,在redis.conf配置文件中默認有此下配置:

save 900 1           #在900秒(15分鐘)以後,若是至少有1個key發生變化,Redis就會自動觸發BGSAVE命令建立快照。  
save 300 10 #在300秒(5分鐘)以後,若是至少有10個key發生變化,Redis就會自動觸發BGSAVE命令建立快照。
save 60 10000 #在60秒(1分鐘)以後,若是至少有10000個key發生變化,Redis就會自動觸發BGSAVE命令建立快照。

  AOF(append-only file)持久化

  與快照持久化相比,AOF持久化 的實時性更好,所以已成爲主流的持久化方案。默認狀況下Redis沒有開啓AOF(append only file)方式的持久化,能夠經過appendonly參數開啓:

appendonly yes

  開啓AOF持久化後每執行一條會更改Redis中的數據的命令,Redis就會將該命令寫入硬盤中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是經過dir參數設置的,默認的文件名是appendonly.aof。  

  在Redis的配置文件中存在三種不一樣的 AOF 持久化方式,它們分別是:

appendfsync always    #每次有數據修改發生時都會寫入AOF文件,這樣會嚴重下降Redis的速度 
appendfsync everysec #每秒鐘同步一次,顯示地將多個寫命令同步到硬盤
appendfsync no #讓操做系統決定什麼時候進行同步

  爲了兼顧數據和寫入性能,用戶能夠考慮 appendfsync everysec選項 ,讓Redis每秒同步一次AOF文件,Redis性能幾乎沒受到任何影響。並且這樣即便出現系統崩潰,用戶最多隻會丟失一秒以內產生的數據。當硬盤忙於執行寫入操做的時候,Redis還會優雅的放慢本身的速度以便適應硬盤的最大寫入速度。

  Redis 4.0 對於持久化機制的優化

  Redis 4.0 開始支持 RDB 和 AOF 的混合持久化(默認關閉,能夠經過配置項 aof-use-rdb-preamble 開啓)。  

  若是把混合持久化打開,AOF 重寫的時候就直接把 RDB 的內容寫到 AOF 文件開頭。這樣作的好處是能夠結合 RDB 和 AOF 的優勢, 快速加載同時避免丟失過多的數據。固然缺點也是有的, AOF 裏面的 RDB 部分是壓縮格式再也不是 AOF 格式,可讀性較差。

  補充內容:AOF 重寫

  AOF重寫能夠產生一個新的AOF文件,這個新的AOF文件和原有的AOF文件所保存的數據庫狀態同樣,但體積更小。

  AOF重寫是一個有歧義的名字,該功能是經過讀取數據庫中的鍵值對來實現的,程序無須對現有AOF文件進行任何讀入、分析或者寫入操做。  

  在執行 BGREWRITEAOF 命令時,Redis 服務器會維護一個 AOF 重寫緩衝區,該緩衝區會在子進程建立新AOF文件期間,記錄服務器執行的全部寫命令。當子進程完成建立新AOF文件的工做以後,服務器會將重寫緩衝區中的全部內容追加到新AOF文件的末尾,使得新舊兩個AOF文件所保存的數據庫狀態一致。最後,服務器用新的AOF文件替換舊的AOF文件,以此來完成AOF文件重寫操做

redis 事務

  Redis 經過 MULTI、EXEC、WATCH 等命令來實現事務(transaction)功能。事務提供了一種將多個命令請求打包,而後一次性、按順序地執行多個命令的機制,而且在事務執行期間,服務器不會中斷事務而改去執行其餘客戶端的命令請求,它會將事務中的全部命令都執行完畢,而後纔去處理其餘客戶端的命令請求。  

  在傳統的關係式數據庫中,經常用 ACID 性質來檢驗事務功能的可靠性和安全性。在 Redis 中,事務老是具備原子性(Atomicity)、一致性(Consistency)和隔離性(Isolation),而且當 Redis 運行在某種特定的持久化模式下時,事務也具備持久性(Durability)。

  注意:redis同一個事務中若是有一條命令執行失敗,其後的命令仍然會被執行,沒有回滾。

緩存雪崩和緩存穿透問題解決方案

  緩存雪崩  

  簡介:緩存同一時間大面積的失效,因此,後面的請求都會落到數據庫上,形成數據庫短期內承受大量請求而崩掉。  

  解決辦法:  

  •   事前:儘可能保證整個 redis 集羣的高可用性,發現機器宕機儘快補上。選擇合適的內存淘汰策略。
  •   事中:本地ehcache緩存 + hystrix限流&降級,避免MySQL崩掉
  •   過後:利用 redis 持久化機制保存的數據儘快恢復緩存

 

   緩存穿透  

  簡介:通常是黑客故意去請求緩存中不存在的數據,致使全部的請求都落到數據庫上,形成數據庫短期內承受大量請求而崩掉。  

  解決辦法:

  有不少種方法能夠有效地解決緩存穿透問題,最多見的則是採用布隆過濾器,將全部可能存在的數據哈希到一個足夠大的bitmap中,一個必定不存在的數據會被 這個bitmap攔截掉,從而避免了對底層存儲系統的查詢壓力。另外也有一個更爲簡單粗暴的方法(咱們採用的就是這種),若是一個查詢返回的數據爲空(無論是數 據不存在,仍是系統故障),咱們仍然把這個空結果進行緩存,但它的過時時間會很短,最長不超過五分鐘。

如何解決 Redis 的併發競爭 Key 問題

  所謂 Redis 的併發競爭 Key 的問題也就是多個系統同時對一個 key 進行操做,可是最後執行的順序和咱們指望的順序不一樣,這樣也就致使告終果的不一樣!  

  推薦一種方案:分佈式鎖(zookeeper 和 redis 均可以實現分佈式鎖)。(若是不存在 Redis 的併發競爭 Key 問題,不要使用分佈式鎖,這樣會影響性能)  

  基於zookeeper臨時有序節點能夠實現的分佈式鎖。大體思想爲:每一個客戶端對某個方法加鎖時,在zookeeper上的與該方法對應的指定節點的目錄下,生成一個惟一的瞬時有序節點。 判斷是否獲取鎖的方式很簡單,只須要判斷有序節點中序號最小的一個。 當釋放鎖的時候,只需將這個瞬時節點刪除便可。同時,其能夠避免服務宕機致使的鎖沒法釋放,而產生的死鎖問題。完成業務流程後,刪除對應的子節點釋放鎖。  

  在實踐中,固然是從以可靠性爲主。因此首推Zookeeper。

如何保證緩存與數據庫雙寫時的數據一致性?

  你只要用緩存,就可能會涉及到緩存與數據庫雙存儲雙寫,你只要是雙寫,就必定會有數據一致性的問題,那麼你如何解決一致性問題?  

  通常來講,就是若是你的系統不是嚴格要求緩存+數據庫必須一致性的話,緩存能夠稍微的跟數據庫偶爾有不一致的狀況,最好不要作這個方案,讀請求和寫請求串行化,串到一個內存隊列裏去,這樣就能夠保證必定不會出現不一致的狀況  

  串行化以後,就會致使系統的吞吐量會大幅度的下降,用比正常狀況下多幾倍的機器去支撐線上的一個請求。

相關文章
相關標籤/搜索