一篇文章瞭解Redis數據庫

文章原創於公衆號:程序猿周先森。本平臺不定時更新,喜歡個人文章,歡迎關注個人微信公衆號。 filehtml

redis是一個key-value存儲系統。它支持存儲的value類型相對更多,包括string(字符串)、list(鏈表)、set(集合)、zset(sorted set --有序集合)和hash(哈希類型)。這些數據類型都支持push/pop、add/remove及取交集並集和差集及更豐富的操做,並且這些操做都是原子性的。在此基礎上,redis支持各類不一樣方式的排序。爲了保證效率,數據都是緩存在內存中。區別的是redis會週期性的把更新的數據寫入磁盤或者把修改操做寫入追加的記錄文件,而且在此基礎上實現了主從同步。簡單來講 Redis 就是一個數據庫,不過與傳統數據庫不一樣的是 Redis 的數據是存在內存中的,因此存寫速度很是快,所以 Redis 被普遍應用於緩存方向。Redis 也常常用來作分佈式鎖。Redis 提供了多種數據類型來支持不一樣的業務場景。除此以外,Redis 支持事務 、持久化、LUA 腳本、LRU 驅動事件、多種集羣方案。redis

本篇文章將從下列幾個方向講解 Redis:數據庫

  • 爲何要用 Redis實現緩存?後端

  • 爲何要用 Redis 而不用 map/guava 作緩存緩存

  • Redis 和 Memcached 的區別安全

  • Redis 常見數據結構以及使用場景分析服務器

  • Redis 設置過時時間微信

  • Redis 內存淘汰機制網絡

  • Redis 持久化機制數據結構

  • Redis 事務

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

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

  • 如何保證緩存與數據庫數據一致性

  • 爲何要用 Redis 作緩存?

第一個問題先拋出來,既然選擇使用Redis做緩存,其實主要從「高性能」和「高併發」來進行理解。

高性能

  1. 用戶首次訪問數據,從數據庫讀取,效率較低

  2. 將用戶訪問數據保存緩存中,二次讀取則能夠直接從緩存中讀取,效率更高

  3. 數據庫若數據發生改變,則同步更新緩存中數據便可。

由於從數據庫讀取數據,是從硬盤中讀取數據,因此效率較低。若是將數據存入緩存中,二次讀取從緩存讀取,從緩存讀取數據是直接操做內存,因此效率很是之高。

高併發

直接操做緩存可以承受的請求是遠遠大於直接訪問數據庫的,因此咱們能夠考慮把數據庫中的部分數據轉移到緩存中去,這樣用戶的一部分請求能夠不用操做數據庫,提升高併發能力。

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

緩存分爲本地緩存和分佈式緩存。以 Java 爲例,使用自帶的 map /guava 實現的是本地緩存,最主要的特色是輕量以及快速,生命週期隨着 JVM 的銷燬而結束。並且在多實例狀態下緩存不具備惟一性。使用 Redis 做緩存稱爲分佈式緩存,在多實例狀態下共用一份緩存數據,緩存具備一致性。

Redis 和 Memcached 的區別

  • Redis支持常見數據類型:Redis 不只僅支持簡單的 key/value 類型的數據,同時還提供string(字符串)、list(鏈表)、set(集合)、zset(sorted set --有序集合)和hash(哈希類型)等數據結構的存儲。而Memcache 只支持簡單的數據類型 String。

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

  • 集羣模式:Memcached 沒有原生的集羣模式,須要依靠客戶端來實現往集羣中分片寫入數據;可是 Redis 目前是原生支持 Cluster 模式的。

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

貼一張對比圖可能看起來更加明顯: file

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

String

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

String 數據結構是簡單的 Key-Value 類型,Value 能夠是string或者數字。常規 Key-Value 緩存應用;常規計數:博客數,閱讀數等。

Hash

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

Hash 特別適合用於存儲對象。

List

  • 經常使用命令:lpush、rpush、lpop、rpop、lrange 等。

鏈表是 Redis 最重要的數據結構之一,Redis List 爲一個雙向鏈表,支持反向查找和遍歷,更方便操做,不過帶來了額外的內存開銷。

Set

  • 經常使用命令:sadd、spop、smembers、sunion 等。

Set 其實和List都是列表的選項,Set 是能夠自動去重的。當須要存儲一個不出現重複數據的列表數據,Set 是一個最好的選擇。你能夠基於 Set 輕易實現交集、並集、差集的操做。

Sorted Set

  • 經常使用命令:zadd、zrange、zrem、zcard 等。

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

Redis 設置過時時間

Redis能夠對存儲在緩存中的數據設置過時時間。做爲一個緩存數據庫,這是很是實用的功能。以前寫過一篇先後端交互的文章講過,Token 或者一些登陸信息,尤爲是短信驗證碼都是有時間限制的,按照傳統的數據庫處理方式,通常都是本身判斷過時,這樣無疑會嚴重影響項目性能。而有一個好的方案其實就是將這些驗證信息存入Redis設置過時時間,若是設置了存活時間30分鐘,那麼半小時以後這些數據就會從Redis中進行刪除。那說到刪除,Redis是若是作到對這些數據進行刪除的呢:

  • 按期刪除:Redis 默認是每隔 100ms 就隨機抽取一些設置了過時時間的 Key,檢查其是否過時,若是過時就刪除。爲何是隨機抽取而不是檢查全部key?由於你若是設置的key成千上萬,每100毫秒都將全部存在的key檢查一遍,會給CPU帶來比較大的壓力。

  • 惰性刪除 :按期刪除可能會致使不少過時 Key 到了時間並無被刪除掉。用戶在獲取key的時候,redis會檢查一下,這個key若是設置過時時間那麼是否過時了,若是過時就刪除這個key。

可是隻是使用按期刪除 + 惰性刪除的刪除機制仍是存在一個致命問題:若是按期刪除漏掉了不少過時 Key,並且用戶長時間也沒有使用到這些過時key,就會致使這些過時key堆積在內存裏,致使Redis內存塊被消耗殆盡。因此有了Redis內存淘汰機制的誕生。

Redis 內存淘汰機制

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

  • volatile-lru:從已設置過時時間的數據集中挑選最近最少使用的數據淘汰。

  • volatile-ttl:從已設置過時時間的數據集中挑選將要過時的數據淘汰。

  • volatile-random:從已設置過時時間的數據集中任意選擇數據淘汰。

  • allkeys-lru:當內存不足以容納新寫入數據時移除最近最少使用的key。

  • allkeys-random:從數據集中任意選擇數據淘汰。

  • no-enviction:當內存不足以容納新寫入數據時,新寫入操做會報錯。

Redis 持久化機制

怎麼保證 Redis 宕機以後再重啓Redis後數據能夠進行恢復?不少時候咱們須要持久化數據也就是將內存中的數據寫入到硬盤裏面。Redis持久化支持兩種不一樣的持久化操做。接下來,咱們來簡單聊聊Redis的兩種持久化機制RDB和AOF。 file

快照持久化(RDB)

RDB持久化是指在指定的時間間隔內將內存中的數據集快照寫入磁盤,實際操做過程是fork一個子進程,先將數據集寫入臨時文件,寫入成功後,再替換以前的文件,用二進制壓縮存儲。RDB是Redis默認的持久化方式,會在對應的目錄下生產一個dump.rdb文件,重啓會經過加載dump.rdb文件恢復數據。

優勢:

  • 只有一個文件dump.rdb,方便持久化;

  • 容災性好,一個文件能夠保存到安全的磁盤;

  • 性能最大化,fork子進程來完成寫操做,讓主進程繼續處理命令,因此是IO最大化(使用單獨子進程來進行持久化,主進程不會進行任何IO操做,保證了redis的高性能) ;

  • 若是數據集偏大,RDB的啓動效率會比AOF更高。

缺點:

  • 數據安全性低。

  • 若是當數據集較大時,可能會致使整個服務器中止服務幾百毫秒,甚至是1秒鐘。

快照持久化是 Redis 默認採用的持久化方式,在 redis.conf 配置文件中已經進行配置:

  • save 900 1:在15分鐘內,若是至少有1個key發生變化,Redis就會自動觸發BGSAVE命令建立快照。

  • save 300 10:在5分鐘內,若是至少有10個key發生變化,Redis就會自動觸發BGSAVE命令建立快照。

  • save 60 10000:在1分鐘以後,若是至少有10000個key發生變化,Redis就會自動觸發BGSAVE命令建立快照。

AOF持久化

AOF持久化是以日誌的形式記錄服務器所處理的每個寫、刪除操做,查詢操做不會記錄,以文本的方式記錄,文件中能夠看到詳細的操做記錄。她的出現是爲了彌補RDB的不足(數據的不一致性),因此它採用日誌的形式來記錄每一個寫操做,並追加到文件中。Redis 重啓的會根據日誌文件的內容將寫指令從前到後執行一次以完成數據的恢復工做。與快照持久化相比,AOF 持久化的實時性更好,所以已成爲主流的持久化方案。 默認狀況下 Redis 沒有開啓 AOF持久化,能夠經過設置 appendonly 參數開啓:

  • appendonly yes

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

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

  • appendfsync always:每次有數據修改發生時都會寫入AOF文件

  • appendfsync everysec:每秒鐘同步一次,將多個寫命令同步到硬盤

  • appendfsync no:讓操做系統決定什麼時候進行同步

用戶可使用appendfsync everysec選項 ,讓 Redis 每秒同步一次 AOF 文件,這樣Redis性能幾乎不會受到影響,並且這樣即便出現宕機,用戶最多隻會丟失一秒以內產生的數據。當硬盤忙於執行寫入操做的時候,Redis 還會優雅的放慢本身的速度以便適應硬盤的最大寫入速度。

優勢:

  • 數據安全性更高,AOF持久化能夠配置appendfsync屬性

  • 經過append模式寫文件,即便中途服務器宕機,能夠經過redis-check-aof工具解決數據一致性問題。

  • AOF機制的rewrite模式。

缺點:

  • AOF文件比RDB文件大,且恢復速度慢;數據集大的時候,比rdb啓動效率低。

  • 根據同步策略的不一樣,AOF在運行效率上每每會慢於RDB。

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

  • Redis 4.0支持 RDB 和 AOF 的混合持久化,不過默認是關閉狀態。

  • 開啓混合持久化,AOF 重寫的時候就直接把 RDB 的內容寫到 AOF 文件開頭。

  • AOF 裏面的 RDB 部分是壓縮格式再也不是 AOF 格式,可讀性較差。

Redis 事務

  • 命令:MULTI、EXEC、WATCH等。

事務提供了一種按順序地執行多個命令的機制。而且在事務執行期間,服務器會將事務中的全部命令都執行完畢,而後纔去處理其餘客戶端的命令請求。事務老是具備原子性、一致性和隔離性,而且當 Redis 運行在某種特定的持久化模式下時,事務也具備持久性。

緩存雪崩

**緩存處理過程:**接收到請求請求,先從緩存中取數據,取到直接返回結果,取不到時從數據庫中取,數據庫取到更新緩存,並返回結果,數據庫也沒取到,那直接返回空結果。

**緩存雪崩:**緩存雪崩是指緩存中數據大批量到過時時間,而查詢數據量巨大,引發數據庫壓力過大甚至down機。

解決辦法:

  • 緩存數據的過時時間設置隨機,防止同一時間大量數據過時現象發生。

  • 若是緩存數據庫是分佈式部署,將熱點數據均勻分佈在不一樣搞得緩存數據庫中。

  • 設置熱點數據永遠不過時。

緩存穿透

**簡介:**緩存穿透是指緩存和數據庫中都沒有的數據,而用戶不斷髮起請求,如發起爲id爲「-1」的數據或id爲特別大不存在的數據。這時的用戶極可能是攻擊者,攻擊會致使數據庫壓力過大。

解決辦法:

  • 接口層增長校驗,如用戶鑑權校驗,id作基礎校驗,id<=0的直接攔截;

  • 從緩存取不到的數據,在數據庫中也沒有取到,這時也能夠將key-value對寫爲key-null,緩存有效時間能夠設置30秒

緩存擊穿

**簡介: **緩存擊穿是指緩存中沒有但數據庫中有的數據,這時因爲併發用戶特別多,同時讀緩存沒讀到數據,又同時去數據庫去取數據,引發數據庫壓力瞬間增大

解決方法:

  • 設置熱點數據永遠不過時。

  • 加互斥鎖

解決 Redis 併發競爭 Key 問題

**問題描述:**多客戶端同時併發寫一個key,可能原本應該先到的數據後到了,致使數據版本錯了。或者是多客戶端同時獲取一個key,修改值以後再寫回去,只要順序錯了,數據就錯了。一個key的值是1,原本按順序修改成2,3,4,最後是4,可是順序變成了4,3,2,最後變成了2. file

我我的認爲比較好的方案是分佈式鎖+時間戳:

1.總體技術方案

這種狀況,主要是準備一個分佈式鎖,你們去搶鎖,加鎖的目的實際上就是把並行讀寫改爲串行讀寫的方式,從而來避免資源競爭。利用SETNX很是簡單地實現分佈式鎖。

2.時間戳

因爲key的操做須要順序執行,因此須要保存一個時間戳判斷順序。假設系統B先搶到鎖,將key1設置爲{ValueB 7:05}。接下來系統A搶到鎖,發現本身的key1的時間戳早於緩存中的時間戳(7:00<=7:05),那就不作set操做了。

3.什麼是分佈式鎖

分佈式鎖能夠基於不少種方式實現,好比zookeeper、redis等,無論哪一種方式實現,基本原理是不變的:用一個狀態值表示鎖,對鎖的佔用和釋放經過狀態值來標識。

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

可能對大部分來講最早想到的方案就是讀請求和寫請求串行化,串到一個內存隊列裏去。可是這個方案有着特別大的缺點:它也會致使系統的吞吐量大幅度下降,用比正常狀況下多幾倍的機器去支撐線上的一個請求。

最經典的緩存+數據庫讀寫的模式。

讀的時候,先讀緩存,緩存沒有的話,就讀數據庫,而後取出數據後放入緩存,同時返回響應。

更新的時候,先更新數據庫,而後再刪除緩存。

若是喜歡個人文章,歡迎關注個人我的公衆號:程序猿周先森。 file****

原文出處:https://www.cnblogs.com/niyueling/p/11583603.html

相關文章
相關標籤/搜索