既然有Map了,為什麼還要有Redis?

語言: CN / TW / HK

一、同樣是緩存,用map不行嗎?

  1. Redis可以存儲幾十個G的數據,Map行嗎?
  2. Redis的緩存可以進行本地持久化,Map行嗎?
  3. Redis可以作為分佈式緩存,Map只能在同一個JVM中進行緩存;
  4. Redis支持每秒百萬級的併發,Map行嗎?
  5. Redis有過期機制,Map有嗎?
  6. Redis有豐富的API,支持非常多的應用場景,Map行嗎?

二、Redis為什麼是單線程的?

  1. 代碼更清晰,處理邏輯更簡單;
  2. 不用考慮各種鎖的問題,不存在加鎖和釋放鎖的操作,沒有因為可能出現死鎖而導致的性能問題;
  3. 不存在多線程切換而消耗CPU;
  4. 無法發揮多核CPU的優勢,但可以採用多開幾個Redis實例來完善;

三、Redis真的是單線程的嗎?

  1. Redis6.0之前是單線程的,Redis6.0之後開始支持多線程;
  2. Redis內部使用了基於epoll的多路服用,也可以多部署幾個Redis服務器解決單線程的問題;
  3. Redis主要的性能瓶頸是內存和網絡;
  4. 內存好説,加內存條就行了,而網絡才是大麻煩,所以Redis6內存好説,加內存條就行了;
  5. 而網絡才是大麻煩,所以Redis6.0引入了多線程的概念,
  6. Redis6.0在網絡IO處理方面引入了多線程,如網絡數據的讀寫和協議解析等,需要注意的是,執行命令的核心模塊還是單線程的。

四、Redis優缺點

1、優點

  1. Redis是KV數據庫,MySQL是關係型數據庫,Redis速度更快;
  2. Redis數據操作主要在內存中,MySQL主要將數據存儲在硬盤,Redis速度更快;
  3. Redis同樣支持持久化(RDB+AOF),Redis支持將數據異步將內存的數據持久化到硬盤上,避免Redis宕機出現數據丟失的問題;
  4. Redis性能極高,讀的速度是110000次/秒,寫的速度是81000次/秒;
  5. Redis數據類型豐富,不僅支持KV鍵值對,還支持list、set、zset、hash等數據結構的存儲;
  6. Redis支持數據的備份,即master-slave模式的數據備份;
  7. Redis支持簡單的事務,操作滿足原子性;
  8. Redis支持讀寫分離,分擔讀的壓力;
  9. Redis支持哨兵模式,實現故障的自動轉移;
  10. 單線程操作,避免了頻繁的上下文切換;
  11. 採用了非阻塞I/O多路複用機制,性能卓越;

2、缺點

  1. 數據存儲在內存,容易造成數據丟失;
  2. 存儲容量受內存的限制,只能存儲少量的常用數據;
  3. 緩存和數據庫雙寫一致性問題;
  4. 用於緩存時,容易出現內存穿透、緩存擊穿、緩存雪崩的問題;
  5. 修改配置文件後,需要進行重啟,將硬盤中的數據同步到內存中,消耗的時間較長,而且數據同步的時間裏Redis不能提供服務;

五、Redis常見業務場景

  1. Redis是基於內存的nosql數據庫,可以通過新建線程的形式進行持久化,不影響Redis單線程的讀寫操作
  2. 通過list取最新的N條數據
  3. 模擬類似於token這種需要設置過期時間的場景
  4. 發佈訂閲消息系統
  5. 定時器、計數器
  6. 緩存加速、分佈式會話、排行榜、分佈式計數器、分佈式鎖;
  7. Redis支持事務、持久化、LUA腳本、發佈/訂閲、緩存淘汰、流技術等特性;

六、Redis常見數據類型

1、String

(1)String簡介

String 是最基本的 key-value 結構,key 是唯一標識,value 是具體的值,value其實不僅是字符串, 也可以是數字(整數或浮點數),value 最多可以容納的數據長度是 512M。

(2)應用場景

① 作為緩存數據庫

在Java管理系統體系中,大多數都是用MySQL存儲數據,redis作為緩存,因為Redis具有支撐高併發的特性,通常能起到加速讀寫和降低數據庫服務器壓力的作用,大多數請求都會先請求Redis,如果Redis中沒有數據,再請求MySQL數據庫,然後再緩存到Redis中,以備下次使用。

② 計數器

Redis字符串中有一個命令INCR key,incr命令會對值進行自增操作,比如CSDN的文章閲讀,視頻的播放量,都可以通過Redis來計數,每閲讀一次就+1,同時將這些數據異步存儲到MySQL數據庫中,降低MySQL服務器的寫入壓力。

③ 共享session

在分佈式系統中,用户每次請求一般會訪問不同的服務器 ,這就會導致session不同步的問題,這時,一般會使用Redis來解決這個問題,將session存入Redis,使用的時候從Redis中取出就可以了。

④ 分佈式鎖

  1. setnx key value,加鎖
  2. del key,釋放鎖

(3)key操作命令

(4)set key value

SET key value [NX | XX] [GET] [EX seconds | PX milliseconds | EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL]

  1. EX seconds,設置過期時間,單位秒
  2. PX milliseconds,設置過期時間,單位毫秒
  3. EXAT timestamp-seconds,設置過期時間,以秒為單位的UNIX時間戳
  4. PXAT timestamp-milliseconds,設置過期時間,以毫秒為單位的UNIX時間戳
  5. NX,鍵不存在的時候設置鍵值
  6. XX,鍵存在的時候設置鍵值
  7. KEEPTTL,保留設置前指定鍵的生存時間
  8. GET,返回指定鍵原本的值,若鍵不存在返回nil

備註:

命令不區分大小寫,而key是區分大小寫的。

help @類型:查看當前類型相關的操作命令。

Since the SET command options can replace SETNX, SETEX, PSETEX, GETSET, it is possible that in future versions of Redis these commands will be deprecated and finally removed。

(5)同時設置多個鍵值

(6)獲取指定區間範圍內的值

getrange、setrange。

(7)數值增減

  1. INCR key,遞增數字
  2. INCRBY key increment,增加指定的數值遞增
  3. DECR key,遞減數值
  4. DECRBY key decrement,指定指定的數值遞減

(8)獲取字符串的長度,內容追加

  1. STRLEN key,獲取值的長度
  2. APPEND key value,內容追加

2、List

(1)List 列表簡介

List 列表是簡單的字符串列表,按照插入順序排序,可以從頭部或尾部向 List 列表添加元素。

列表的最大長度為 2^32 - 1,也即每個列表支持超過 40 億個元素。

主要功能有push/pop,一般用在棧、隊列、消息隊列等場景。

  1. left、right都可以插入添加;
  2. 如果鍵不存在,創建新的鏈表;
  3. 如果鍵存在,新增內容;
  4. 如果值全部移除,對應的鍵也會消失;

它的底層是雙向鏈表,對兩端的操作性能很高,通過索引下標操作中間的節點,性能會較差。

(2)應用場景

① 消息隊列

使用 lpush + rpop或者 rpush + lpop實現消息隊列,Redis還支持阻塞操作,在彈出元素的時候使用阻塞命令來實現阻塞隊列。

② 作為棧使用

使用 lpush+lpop或者 rpush+rpop實現棧。

③ 文章列表

(3)常用命令

3、Hash

(1)hash簡介

Hash 是一個鍵值對(key - value)集合,value也是一個hash,相當於 Map<String,Map<Object,Object>>

(2)常用場景

由於特殊的數據結構,hash一般作為存儲bean使用,String+JSON的數據結構存儲特定的應用場景。

(3)常用命令

4、Set

(1)Set類型簡介

Set 類型是一個無序並唯一的鍵值集合,它的存儲順序不會按照插入的先後順序進行存儲。

一個集合最多可以存儲 2^32-1 個元素。概念和數學中個的集合基本類似,可以交集,並集,差集等等,所以 Set 類型除了支持集合內的增刪改查,同時還支持多個集合取交集、並集、差集。

(2)應用場景

① 相同好友可見

在朋友圈場景中,對於點贊、評論的功能,通過交集實現相同還有可見的功能。

② 共同關注、共同喜好

③ 抽獎功能

(3)常用命令

5、Zset

(1)Zset 類型簡介

Zset 類型(有序集合類型)相比於 Set 類型多了一個排序屬性 score(分值),對於有序集合 ZSet 來説,每個存儲元素相當於有兩個值組成的,一個是有序結合的元素值,一個是排序值。

有序集合保留了集合不能有重複成員的特性(分值可以重複),但不同的是,有序集合中的元素可以排序。

zset k1 score1 v1 score2 v2

(2)應用場景

① 排行榜

通過score來記錄點贊數,然後根據score進行排序,實現排行榜的功能。

② 延遲消息隊列

訂單系統,下單後需要在15分鐘內進行支付操作,否則自動取消訂單。

將下單後15分鐘後的時間作為score,訂單作為value存入Redis,消費者輪詢去消費,如果消費的大於等於score,則取消該訂單。

(3)Zset常用命令

6、BitMap

(1)Bitmap簡介

Bitmap,即位圖,是一串連續的二進制數組(0和1),可以通過偏移量(offset)定位元素。BitMap通過最小的單位bit來進行0|1的設置,表示某個元素的值或者狀態,時間複雜度為O(1)。

(2)應用場景

由於 bit 是計算機中最小的單位,使用它進行儲存將非常節省空間,特別適合一些數據量大且使用二值統計的場景。

① 簽到統計

② 判斷用户是否登錄

③ 統計連續學習打卡的人

(3)BitMap常用命令

7、BitField

通過bitfield命令可以一次性操作多個比特位,它會執行一系列操作並返回一個響應數組,這個數組中的元素對參數列表中的相應操作的執行結果。

8、HyperLogLog

(1)HyperLogLog簡介

Redis HyperLogLog 是 Redis 2.8.9 版本新增的數據類型,是一種用於「統計基數」的數據集合類型,基數統計就是指統計一個集合中不重複的元素個數。但要注意,HyperLogLog 是統計規則是基於概率完成的,不是非常準確,標準誤算率是 0.81%。

所以,簡單來説 HyperLogLog 提供不精確的去重計數。

HyperLogLog 的優點是,在輸入元素的數量或者體積非常非常大時,計算基數所需的內存空間總是固定的、並且是很小的。

在 Redis 裏面,每個 HyperLogLog 鍵只需要花費 12 KB 內存,就可以計算接近 2^64 個不同元素的基數,和元素越多就越耗費內存的 Set 和 Hash 類型相比,HyperLogLog 就非常節省空間。

(2)應用場景

百萬級網頁 UV 計數

(3)常用命令

  1. pfadd key element,添加元素
  2. pfcount key,返回指定HyperLogLog的基數的估算值;
  3. pfmerge destkey sourcekey,將多個HyperLogLog合併成一個HyperLogLog;

9、GEO

(1)GEO簡介

Redis GEO 是 Redis 3.2 版本新增的數據類型,主要用於存儲地理位置信息,並對存儲的信息進行操作。

在日常生活中,我們越來越依賴搜索“附近的餐館”、在打車軟件上叫車,這些都離不開基於位置信息服務(Location-Based Service,LBS)的應用。LBS 應用訪問的數據是和人或物關聯的一組經緯度信息,而且要能查詢相鄰的經緯度範圍,GEO 就非常適合應用在 LBS 服務的場景中。

(2)應用場景

高德地圖、滴滴打車等定位軟件。

(3)常用命令

10、Stream

(1)Stream簡介

Redis Stream 是 Redis 5.0 版本新增加的數據類型,Redis 專門為消息隊列設計的數據類型。

在 Redis 5.0 Stream 沒出來之前,消息隊列的實現方式都有着各自的缺陷,例如:

  • 發佈訂閲模式,不能持久化也就無法可靠的保存消息,並且對於離線重連的客户端不能讀取歷史消息的缺陷;
  • List 實現消息隊列的方式不能重複消費,一個消息消費完就會被刪除,而且生產者需要自行實現全局唯一 ID。

基於以上問題,Redis 5.0 便推出了 Stream 類型也是此版本最重要的功能,用於完美地實現消息隊列,它支持消息的持久化、支持自動生成全局唯一 ID、支持 ack 確認消息的模式、支持消費組模式等,讓消息隊列更加的穩定和可靠。

(2)應用場景

消息隊列

(3)常用命令

七、總結

Redis是一個key-value存儲系統,支持10種數據類型,總結了為何要用Redis替代map作為程序緩存、Redis為什麼是單線程的、Redis的優缺點、Redis的常用場景,做了一次Redis的快速入門。