redis的基本數據類型
字符串String、字典Hash、列表List、集合Set、有序集合SortedSet。
所有數據類型的一些具體方法使用教程
String:
這是最簡單的類型,就是普通的 set 和 get,做簡單的 KV 緩存。
實際應用場景:
- 緩存功能:String字符串是最常用的數據類型,不僅僅是Redis,各個語言都是最基本類型,因此,利用Redis作爲緩存,配合其它數據庫作爲存儲層,利用Redis支持高併發的特點,可以大大加快系統的讀寫速度、以及降低後端數據庫的壓力。
- 計數器:許多系統都會使用Redis作爲系統的實時計數器,可以快速實現計數和查詢的功能。而且最終的數據結果可以按照特定的時間落地到數據庫或者其它存儲介質當中進行永久保存。
- 共享用戶Session:用戶重新刷新一次界面,可能需要訪問一下數據進行重新登錄,或者訪問頁面緩存Cookie,但是可以利用Redis將用戶的Session集中管理,在這種模式只需要保證Redis的高可用,每次用戶Session的更新和獲取都可以快速完成。大大提高效率。
Hash:
這個是類似 Map 的一種結構,這個一般就是可以將結構化的數據,比如一個對象(前提是這個對象沒嵌套其他的對象)給緩存在 Redis 裏,然後每次讀寫緩存的時候,可以就操作 Hash 裏的某個字段。
但是這個的場景其實還是多少單一了一些,因爲現在很多對象都是比較複雜的,比如你的商品對象可能裏面就包含了很多屬性,其中也有對象。我自己使用的場景用得不是那麼多。
List:
有序列表
比如可以通過 List 存儲一些列表型的數據結構,類似粉絲列表、文章的評論列表之類的東西。
比如可以通過 lrange 命令,讀取某個閉區間內的元素,可以基於 List 實現分頁查詢,這個是很棒的一個功能,基於 Redis 實現簡單的高性能分頁,可以做類似微博那種下拉不斷分頁的東西,性能高,就一頁一頁走。
比如可以搞個簡單的消息隊列,從 List 頭懟進去,從 List 屁股那裏弄出來。
List本身就是我們在開發過程中比較常用的數據結構了,熱點數據更不用說了。
- 消息隊列:Redis的鏈表結構,可以輕鬆實現阻塞隊列,可以使用左進右出的命令組成來完成隊列的設計。比如:數據的生產者可以通過Lpush命令從左邊插入數據,多個數據消費者,可以使用BRpop命令阻塞的“搶”列表尾部的數據。
- 文章列表或者數據分頁展示的應用。
比如,我們常用的博客網站的文章列表,當用戶量越來越多時,而且每一個用戶都有自己的文章列表,而且當文章多時,都需要分頁展示,這時可以考慮使用Redis的列表,列表不但有序同時還支持按照範圍內獲取元素,可以完美解決分頁查詢功能。大大提高查詢效率。
Set:
Set 是無序集合,會自動去重的那種。
直接基於 Set 將系統裏需要去重的數據扔進去,自動就給去重了,如果你需要對一些數據進行快速的全局去重,你當然也可以基於 JVM 內存裏的 HashSet 進行去重,但是如果你的某個系統部署在多臺機器上呢?得基於Redis進行全局的 Set 去重。
可以基於 Set 玩兒交集、並集、差集的操作,比如交集吧,我們可以把兩個人的好友列表整一個交集,看看倆人的共同好友是誰?對吧。
反正這些場景比較多,因爲對比很快,操作也簡單,兩個查詢一個Set搞定。
Sorted Set:
Sorted set 是排序的 Set,去重但可以排序,寫進去的時候給一個分數,自動根據分數排序。
有序集合的使用場景與集合類似,但是set集合不是自動有序的,而Sorted set可以利用分數進行成員間的排序,而且是插入時就排序好。所以當你需要一個有序且不重複的集合列表時,就可以選擇Sorted set數據結構作爲選擇方案。
-
排行榜:有序集合經典使用場景。例如視頻網站需要對用戶上傳的視頻做排行榜,榜單維護可能是多方面:按照時間、按照播放量、按照獲得的贊數等。
-
用Sorted Sets來做帶權重的隊列,比如普通消息的score爲1,重要消息的score爲2,然後工作線程可以選擇按score的倒序來獲取工作任務。讓重要的任務優先執行。
微博熱搜榜,就是有個後面的熱度值,前面就是名稱
多個系統同時操作(併發)Redis帶來的數據問題
- 某個時刻,多個系統實例都去更新某個 key。可以基於 Zookeeper 實現分佈式鎖。每個系統通過 Zookeeper 獲取分佈式鎖,確保同一時間,只能有一個系統實例在操作某個 Key,別人都不允許讀和寫。
- 先拿setnx來爭搶鎖,搶到之後,再用expire給鎖加一個過期時間防止鎖忘記了釋放。
- 如果在setnx之後執行expire之前進程意外crash或者要重啓維護了,set指令有非常複雜的參數,這個應該是可以同時把setnx和expire合成一條指令來用的!
最經典的緩存+數據庫讀寫的模式,就是 Cache Aside Pattern
- 讀的時候,先讀緩存,緩存沒有的話,就讀數據庫,然後取出數據後放入緩存,同時返回響應。
- 更新的時候,先更新數據庫,然後再刪除緩存。 等待下去讀取的時候再去將數據寫入redis 這樣可以保證更新次數過多 而讀取次數很少造成不必要的redis消耗 特殊情況下更新緩存的代價有時候是很高的 比如:多表聯合計算數據
緩存穿透
緩存穿透。產生這個問題的原因可能是外部的惡意攻擊,例如,對用戶信息進行了緩存,但惡意攻擊者使用不存在的用戶id頻繁請求接口,導致查詢緩存不命中,然後穿透 DB 查詢依然不命中。這時會有大量請求穿透緩存訪問到 DB。
解決的辦法如下。
- 對不存在的用戶,在緩存中保存一個空對象進行標記,防止相同 ID 再次訪問 DB。不過有時這個方法並不能很好解決問題,可能導致緩存中存儲大量無用數據。
- 使用 BloomFilter 過濾器,BloomFilter 的特點是存在性檢測,如果 BloomFilter 中不存在,那麼數據一定不存在;如果 BloomFilter 中存在,實際數據也有可能會不存在。非常適合解決這類的問題。
緩存擊穿
緩存擊穿,就是某個熱點數據失效時,大量針對這個數據的請求會穿透到數據源。
解決這個問題有如下辦法。
- 可以使用互斥鎖更新,保證同一個進程中針對同一個數據不會併發請求到 DB,減小 DB 壓力。
- 使用隨機退避方式,失效時隨機 sleep 一個很短的時間,再次查詢,如果失敗再執行更新。
- 針對多個熱點 key 同時失效的問題,可以在緩存時使用固定時間加上一個小的隨機數,避免大量熱點 key 同一時刻失效。
緩存雪崩
緩存雪崩,產生的原因是緩存掛掉,這時所有的請求都會穿透到 DB。
解決方法:
使用快速失敗的熔斷策略,減少 DB 瞬間壓力;
使用主從模式和集羣模式來儘量保證緩存服務的高可用。
實際場景中,這兩種方法會結合使用。
redis的持久化
持久化的話是Redis高可用中比較重要的一個環節,因爲Redis數據在內存的特性,持久化必須得有,我瞭解到的持久化是有兩種方式的。
- RDB:RDB 持久化機制,是對 Redis 中的數據執行週期性的持久化。
- AOF:AOF 機制對每條寫入命令作爲日誌,以 append-only的模式寫入一個日誌文件中,因爲這個模式是隻追加的方式,所以沒有任何磁盤尋址的開銷,所以很快,有點像Mysql中的binlog。
tip:兩種機制全部開啓的時候,Redis在重啓的時候會默認使用AOF去重新構建數據,因爲AOF的數據是比RDB更完整的。
RDB
優點:
他會生成多個數據文件,每個數據文件分別都代表了某一時刻Redis裏面的數據,這種方式,有沒有覺得很適合做冷備,完整的數據運維設置定時任務,定時同步到遠端的服務器,比如阿里的雲服務,這樣一旦線上掛了,你想恢復多少分鐘之前的數據,就去遠端拷貝一份之前的數據就好了。
RDB對Redis的性能影響非常小,是因爲在同步數據的時候他只是fork了一個子進程去做持久化的,而且他在數據恢復的時候速度比AOF來的快。
缺點:
RDB都是快照文件,都是默認五分鐘甚至更久的時間纔會生成一次,這意味着你這次同步到下次同步這中間五分鐘的數據都很可能全部丟失掉。AOF則最多丟一秒的數據,數據完整性上高下立判。
還有就是RDB在生成數據快照的時候,如果文件很大,客戶端可能會暫停幾毫秒甚至幾秒,你公司在做秒殺的時候他剛好在這個時候fork了一個子進程去生成一個大快照,哦豁,出大問題。
AOF
優點:
上面提到了,RDB五分鐘一次生成快照,但是AOF是一秒一次去通過一個後臺的線程fsync操作,那最多丟這一秒的數據。
AOF在對日誌文件進行操作的時候是以append-only的方式去寫的,他只是追加的方式寫數據,自然就少了很多磁盤尋址的開銷了,寫入性能驚人,文件也不容易破損。
AOF的日誌是通過一個叫非常可讀的方式記錄的,這樣的特性就適合做災難性數據誤刪除的緊急恢復了,比如公司的實習生通過flushall清空了所有的數據,只要這個時候後臺重寫還沒發生,你馬上拷貝一份AOF日誌文件,把最後一條flushall命令刪了就完事了。
tip:我說的命令你們別真去線上系統操作啊,想試去自己買的服務器上裝個Redis試,別到時候來說,敖丙真是個渣男,害我把服務器搞崩了,Redis官網上的命令都去看看,不要亂試!!!
缺點:
一樣的數據,AOF文件比RDB還要大。
AOF開啓後,Redis支持寫的QPS會比RDB支持寫的要低,他不是每秒都要去異步刷新一次日誌嘛fsync,當然即使這樣性能還是很高
redis 提供 8種數據淘汰策略:
一般的剔除策略有 FIFO 淘汰最早數據、LRU 剔除最近最少使用、和 LFU 剔除最近使用頻率最低的數據幾種策略。
- noeviction:返回錯誤當內存限制達到並且客戶端嘗試執行會讓更多內存被使用的命令(大部分的寫入指令,但DEL和幾個例外)
- allkeys-lru: 嘗試回收最少使用的鍵(LRU),使得新添加的數據有空間存放。
- volatile-lru: 嘗試回收最少使用的鍵(LRU),但僅限於在過期集合的鍵,使得新添加的數據有空間存放。
- allkeys-random:回收隨機的鍵使得新添加的數據有空間存放。
- volatile-random: 回收隨機的鍵使得新添加的數據有空間存放,但僅限於在過期集合的鍵。
- volatile-ttl:回收在過期集合的鍵,並且優先回收存活時間(TTL)較短的鍵,使得新添加的數據有空間存放。
如果沒有鍵滿足回收的前提條件的話,策略volatile-lru, volatile-random以及volatile-ttl就和noeviction 差不多了。
4.0版本後增加以下兩種:
7.volatile-lfu:從已設置過期時間的數據集(server.db[i].expires)中挑選最不經常使用的數據淘汰
8.allkeys-lfu:當內存不足以容納新寫入數據時,在鍵空間中,移除最不經常使用的key