1. 程式人生 > >redis從入門到放棄 -> 簡介&概念

redis從入門到放棄 -> 簡介&概念

一、redis簡介

Redis是一款開源的、高效能的鍵-值儲存。它常被稱作是一款資料結構伺服器。

當值支援的主要資料型別為:字串(strings)型別,括雜湊(hashes)、列表(lists)、集合(sets)和 有序集合(sorted sets)等資料型別。

同時Redis可以進行持久化(將資料存到硬碟),意味著不僅僅可以作為快取記憶體伺服器,也可以作為資料庫使用。

資料來源:https://db-engines.com/en/ranking

redis學習網站連結

菜鳥教程 -> http://www.runoob.com/redis/redis-tutorial.html

redis中文網站 -> http://doc.redisfans.com/index.html

redis特點

Redis支援多種資料型別,適應更多的場景需求。
支援釋出訂閱,管道
設定TTL存活時間,到期自動刪除
可以執行lua指令碼
提供了簡單的事務功能, 能在一定程度上保證事務特性。
提供了流水線(Pipeline) 功能, 這樣客戶端能將一批命令一次性傳到Redis, 減少了網路的開銷。
可以使用記憶體做持久化
可以將資料複製到任意數量的從伺服器。  

二、快取解決方案對比

Memcached:
    優點:高效能讀寫、單一資料型別、支援客戶端式分散式叢集、一致性hash多核結構、多執行緒讀寫效能高。
    缺點:無持久化、節點故障可能出現快取穿透、分散式需要客戶端實現、跨機房資料同步困難、架構擴容複雜度高
Redis:
    優點:高效能讀寫、多資料型別支援、資料持久化、高可用架構、支援自定義虛擬記憶體、支援分散式分片叢集、單執行緒讀寫效能極高
    缺點:多執行緒讀寫較Memcached慢
Tair:
    優點:高效能讀寫、支援三種儲存引擎(ddb、rdb、ldb)、支援高可用、支援分散式分片叢集、支撐了幾乎所有淘寶業務的快取。
    缺點:單機情況下,讀寫效能較其他兩種產品較慢

三、redis快取原理

命令執行結構:

                                             一條客戶端命令的生命週期

 

客戶端傳送命令後,Redis伺服器將為這個客戶端連結創造一個’輸入快取’,將命令放到裡面。之後再由Redis伺服器進行分配挨個執行,順序是隨機的,這將不會產生併發衝突問題,也就不需要事物了。最後再將結果返回到客戶端的’輸出快取’中,客戶端再獲得資訊結果。

如果資料是寫入命令,例如set name:1 zhangsan方式新增一個字串
redis將根據策略,將這對 key:value 來用內部編碼格式儲存。好處是改變內部編碼不會對外有影響,正常操作即可,同時不同情況下儲存格式不一樣,發揮優勢。

Redis高效能原因:
基於記憶體的訪問,非阻塞I/O,Redis使用事件驅動模型epoll多路複用實現,連線、讀寫、關閉都轉換為事件,不在網路I/O上浪費過多的時間。單執行緒避免的高併發的時候,多執行緒有鎖的問題和執行緒切換的CPU開銷的問題。
雖然是單執行緒的,我們還可以通過多例項來彌補。

四、Redis支援的鍵值型別

String 字串型別
Hash雜湊型別
List列表型別
Set集合型別
Zset有序集合型別

redis的資料型別,以及每種資料型別的使用場景

分析:是不是覺得這個問題很基礎,其實我也這麼覺得。然而根據面試經驗發現,至少百分八十的人答不上這個問題。建議,在專案中用到後,再類比記憶,體會更深,不要硬記。基本上,一個合格的程式設計師,五種型別都會用到。
回答:一共五種
(一)String
這個其實沒啥好說的,最常規的set/get操作,value可以是String也可以是數字。一般做一些複雜的計數功能的快取。
(二)hash
這裡value存放的是結構化的物件,比較方便的就是操作其中的某個欄位。博主在做單點登入的時候,就是用這種資料結構儲存使用者資訊,以cookieId作為key,設定30分鐘為快取過期時間,能很好的模擬出類似session的效果。
(三)list
使用List的資料結構,可以做簡單的訊息佇列的功能。另外還有一個就是,可以利用lrange命令,做基於redis的分頁功能,效能極佳,使用者體驗好。
(四)set
因為set堆放的是一堆不重複值的集合。所以可以做全域性去重的功能。為什麼不用JVM自帶的Set進行去重?因為我們的系統一般都是叢集部署,使用JVM自帶的Set,比較麻煩,難道為了一個做一個全域性去重,再起一個公共服務,太麻煩了。
另外,就是利用交集、並集、差集等操作,可以計算共同喜好,全部的喜好,自己獨有的喜好等功能
(五)sorted set
sorted set多了一個權重引數score,集合中的元素能夠按score進行排列。可以做排行榜應用,取TOP N操作。另外,參照另一篇《分散式之延時任務方案解析》,該文指出了sorted set可以用來做延時任務。最後一個應用就是可以做範圍查詢

五、應用場景

快取:
Redis提供了鍵值過期時間設定, 並且也提供了靈活控制最大記憶體和記憶體溢位後的淘汰策略。可以這麼說,一個合理的快取設計能夠為一個網站的穩定保駕護航。

社交網路:
贊/踩、 粉絲、 共同好友/喜好、 推送、 下拉重新整理等是社交網站的必備功能,由於社交網站訪問量通常比較大, 而且傳統的關係型資料不太適合儲存這種型別的資料, Redis提供的資料結構可以相對比較容易地實現這些功能。

1、為什麼使用redis?

分析:博主覺得在專案中使用redis,主要是從兩個角度去考慮:效能併發。當然,redis還具備可以做分散式鎖等其他功能,但是如果只是為了分散式鎖這些其他功能,完全還有其他中介軟體(如zookpeer等)代替,並不是非要使用redis。因此,這個問題主要從效能和併發兩個角度去答。
回答:如下所示,分為兩點
(一)效能
如下圖所示,我們在碰到需要執行耗時特別久,且結果不頻繁變動的SQL,就特別適合將執行結果放入快取。這樣,後面的請求就去快取中讀取,使得請求能夠迅速響應

題外話忽然想聊一下這個迅速響應的標準。其實根據互動效果的不同,這個響應時間沒有固定標準。不過曾經有人這麼告訴我:"在理想狀態下,我們的頁面跳轉需要在瞬間解決,對於頁內操作則需要在剎那間解決。另外,超過一彈指的耗時操作要有進度提示,並且可以隨時中止或取消,這樣才能給使用者最好的體驗。"
那麼瞬間、剎那、一彈指具體是多少時間呢?
根據《摩訶僧祗律》記載

一剎那者為一念,二十念為一瞬,二十瞬為一彈指,二十彈指為一羅預,二十羅預為一須臾,一日一夜有三十須臾。

那麼,經過周密的計算,一瞬間為0.36 秒,一剎那有 0.018 秒.一彈指長達 7.2 秒。

(二)併發
如下圖所示,在大併發的情況下,所有的請求直接訪問資料庫,資料庫會出現連線異常。這個時候,就需要使用redis做一個緩衝操作,讓請求先訪問到redis,而不是直接訪問資料庫。  

 

2、使用redis有什麼缺點

分析:大家用redis這麼久,這個問題是必須要了解的,基本上使用redis都會碰到一些問題,常見的也就幾個。
回答:主要是四個問題

(一)快取和資料庫雙寫一致性問題
(二)快取雪崩問題
(三)快取擊穿問題
(四)快取的併發競爭問題

這四個問題,我個人是覺得在專案中,比較常遇見的

1、redis和資料庫雙寫一致性問題

分析:一致性問題是分散式常見問題,還可以再分為最終一致性和強一致性。資料庫和快取雙寫,就必然會存在不一致的問題。答這個問題,先明白一個前提。就是如果對資料有強一致性要求,不能放快取。我們所做的一切,只能保證最終一致性。另外,我們所做的方案其實從根本上來說,只能說降低不一致發生的概率,無法完全避免。因此,有強一致性要求的資料,不能放快取。

回答:《分散式之資料庫和快取雙寫一致性方案解析》給出了詳細的分析,在這裡簡單的說一說。首先,採取正確更新策略,先更新資料庫,再刪快取。其次,因為可能存在刪除快取失敗的問題,提供一個補償措施即可,例如利用訊息佇列。 

2、如何應對快取穿透和快取雪崩問題

分析:這兩個問題,說句實在話,一般中小型傳統軟體企業,很難碰到這個問題。如果有大併發的專案,流量有幾百萬左右。這兩個問題一定要深刻考慮。
回答:如下所示
快取穿透,即黑客故意去請求快取中不存在的資料,導致所有的請求都懟到資料庫上,從而資料庫連線異常。
解決方案:
(一)利用互斥鎖,快取失效的時候,先去獲得鎖,得到鎖了,再去請求資料庫。沒得到鎖,則休眠一段時間重試
(二)採用非同步更新策略,無論key是否取到值,都直接返回。value值中維護一個快取失效時間,快取如果過期,非同步起一個執行緒去讀資料庫,更新快取。需要做快取預熱(專案啟動前,先載入快取)操作。
(三)提供一個能迅速判斷請求是否有效的攔截機制,比如,利用布隆過濾器,內部維護一系列合法有效的key。迅速判斷出,請求所攜帶的Key是否合法有效。如果不合法,則直接返回。
快取雪崩,即快取同一時間大面積的失效,這個時候又來了一波請求,結果請求都懟到資料庫上,從而導致資料庫連線異常。
解決方案:
(一)給快取的失效時間,加上一個隨機值,避免集體失效。
(二)使用互斥鎖,但是該方案吞吐量明顯下降了。
(三)雙快取。我們有兩個快取,快取A和快取B。快取A的失效時間為20分鐘,快取B不設失效時間。自己做快取預熱操作。然後細分以下幾個小點

    • I 從快取A讀資料庫,有則直接返回
    • II A沒有資料,直接從B讀資料,直接返回,並且非同步啟動一個更新執行緒。
    • III 更新執行緒同時更新快取A和快取B。

3、如何解決redis的併發競爭key問題

分析:這個問題大致就是,同時有多個子系統去set一個key。這個時候要注意什麼呢?大家思考過麼。需要說明一下,博主提前百度了一下,發現答案基本都是推薦用redis事務機制。博主不推薦使用redis的事務機制。因為我們的生產環境,基本都是redis叢集環境,做了資料分片操作。你一個事務中有涉及到多個key操作的時候,這多個key不一定都儲存在同一個redis-server上。因此,redis的事務機制,十分雞肋。
回答:如下所示
(1)如果對這個key操作,不要求順序
這種情況下,準備一個分散式鎖,大家去搶鎖,搶到鎖就做set操作即可,比較簡單。
(2)如果對這個key操作,要求順序
假設有一個key1,系統A需要將key1設定為valueA,系統B需要將key1設定為valueB,系統C需要將key1設定為valueC.
期望按照key1的value值按照 valueA-->valueB-->valueC的順序變化。這種時候我們在資料寫入資料庫的時候,需要儲存一個時間戳。假設時間戳如下

系統A key 1 {valueA  3:00} 系統B key 1 {valueB 3:05} 系統C key 1 {valueC 3:10}

那麼,假設這會系統B先搶到鎖,將key1設定為{valueB 3:05}。接下來系統A搶到鎖,發現自己的valueA的時間戳早於快取中的時間戳,那就不做set操作了。以此類推。

其他方法,比如利用佇列,將set方法變成序列訪問也可以。總之,靈活變通。

注:文章中redis部分內容轉載出處:https://www.cnblogs.com/rjzheng/ 

                 https://www.cnblogs.com/clsn/p/8409458.html#auto_id_0

                https://www.abcdocker.com