Redis快速入門和重點詳解
一、Redis概述
Redis是什麼?
Redis(Remote Dictionary Server ),即遠端字典服務,是一個開源的使用ANSI C語言編寫、支援網路、可基於記憶體亦可持久化的日誌型、Key-Value資料庫,並
提供多種語言的API,免費和開源,是當下最熱門的NoSql技術之一,也被人們稱之為機構化資料庫!
Redis能幹嘛?
- 記憶體儲存、持久化、記憶體中是斷電即失、所以說持久化很重要(RDB、AOF)
- 效率高,可以用於快取記憶體
- 釋出訂閱系統
- 地圖資訊分析
- 計數器、計時器(瀏覽量和計步器)
特性
- 多樣的資料型別
- 持久化、叢集、事務
1、Linux上安裝Redis
- 官網(https://redis.io)下載安裝包
- 解壓redis的安裝包,程式放/opt目錄下(解壓命令 tar -zxvf)
- 進入解壓後的資料夾,可以看到redis的配置檔案(redis.conf)
- 基本環境安裝
yum install gcc-c++ # 安裝gcc-c++
make # 安裝環境
make install # 安裝
- redis的預設安裝路徑
/user/local/bin
- 將redis配置檔案複製到當前目錄下
- redis預設不是後臺啟動的,修改配置檔案(daemonize的值改為yes)
- 啟動redis服務(在bin下輸入命令redis-server config/redis.conf)
- 使用redis客戶端(redis-cli -p 6379),連線redis服務
- 檢視redis服務是否開啟(ps -ef|grep redis)
- 如何關閉redis服務(shutdown)
2、測試效能(benchmark)
- 一個官方的壓力測試工具
- 測試
# 100個併發連線,對我們十萬個請求進行寫入測試
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
3、基礎知識
- redis預設有16個數據庫,預設使用的是第0個,可以是select進行切換
flushdb
:清除當前資料庫FLUSHALL
:清除全部資料庫- redis是單執行緒的,是基於記憶體操作的,cpu不是redis的效能瓶頸,是根據機器的記憶體和網路頻寬
二、五大資料型別
可用作資料庫,快取記憶體和訊息佇列代理,它支援字串、雜湊表、列表、集合、有序集合,點陣圖,hyperloglogs等資料型別
1、String(字串)
incr views # 自增1
decr views # 自減1
incrby views key # 指定增量
decr views key # 指定減量
getrange(key start end) # 擷取字串
setrange(key offset value) # 替換字串
setex(set with expire) # 設定過期時間
setnx(set if not exist) # 不存在再設定
mset # 批量設定值
mget # 批量獲取值
getset # 先get再設定值
2、List(列表)
在redis中,可以把list當成棧、佇列、阻塞佇列
所有的list命令都是L開頭的
127.0.0.1:6379> LPUSH list one # 將一個值或者多個值,插入列表的頭部(左)
(integer) 1
127.0.0.1:6379> LPUSH list two
(integer) 2
127.0.0.1:6379> LPUSH list three
(integer) 3
127.0.0.1:6379> LRANGE list 0 -1 # 獲取list中的值(可以選擇區間)
1) "three"
2) "two"
3) "one"
#########################################################################
127.0.0.1:6379> RPUSH list right # 將一個值或者多個值,插入列表的尾部(右)
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
#########################################################################
LPOP
RPOP
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
127.0.0.1:6379> LPOP list # 將一個值從頭部移除
"three"
127.0.0.1:6379> RPOP list # 將一個值從尾部移除
"right"
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
#########################################################################
LINDEX list 0 # 通過下標獲取值
127.0.0.1:6379> LINDEX list 0
"two"
127.0.0.1:6379> LINDEX list 1
"one"
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
#########################################################################
llen # 返回列表長度
127.0.0.1:6379> LLEN list
(integer) 2
#########################################################################
lrem # 移除指定個數的value
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
127.0.0.1:6379> LREM list 1 one
(integer) 1
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
#########################################################################
127.0.0.1:6379> LRANGE list 0 -1
1) "4"
2) "3"
3) "2"
4) "1"
127.0.0.1:6379> LTRIM list 1 2 # 擷取指定範圍的value,這個list已經被改變了
OK
127.0.0.1:6379> LRANGE list 0 -1
1) "3"
2) "2"
#########################################################################
rpoplpush # 移除列表的最後一個元素,將他移動到新的列表中
#########################################################################
linsert # 將某一個具體的值插入到列表中某個元素的前面或者後面
127.0.0.1:6379> rpush list one
(integer) 1
127.0.0.1:6379> rpush list two
(integer) 2
127.0.0.1:6379> LINSERT list after two three
(integer) 3
127.0.0.1:6379> LRANGE list 0 -1
1) "one"
2) "two"
3) "three"
小節
- 他實際上是一個連結串列,BEFORE NODE AFTER,left和right都可以插入值
- 如果key不存在,建立新的連結串列,如果存在,新增內容
- 可以做訊息佇列、訊息排隊Lpush、Rpop,棧(Lpush、Lpop)
3、Set(集合)
Set中的值不能重複,命令都是S開頭
127.0.0.1:6379> SADD set 1 # SET集合中新增元素
(integer) 1
127.0.0.1:6379> SADD set 2
(integer) 1
127.0.0.1:6379> SMEMBERS set # 檢視SET集合中所有的值
1) "1"
2) "2"
#########################################################################
127.0.0.1:6379> SMEMBERS set
1) "1"
2) "2"
127.0.0.1:6379> SISMEMBER set 1 # 判斷一個值是不是在set集合中
(integer) 1
127.0.0.1:6379> SISMEMBER set 3
(integer) 0
#########################################################################
127.0.0.1:6379> SCARD set # 檢視SET中的元素個數
(integer) 2
#########################################################################
127.0.0.1:6379> SREM set 1 # 移除SET中的指定元素
(integer) 1
127.0.0.1:6379> SMEMBERS set
1) "2"
#########################################################################
127.0.0.1:6379> SMEMBERS set # 隨機抽選出一個元素(後面可以加數量)
1) "2"
2) "3"
3) "4"
4) "5"
127.0.0.1:6379> SRANDMEMBER set
"3"
127.0.0.1:6379> SRANDMEMBER set
"2"
127.0.0.1:6379> SRANDMEMBER set
"5"
127.0.0.1:6379> SRANDMEMBER set
"4"
#########################################################################
127.0.0.1:6379> SADD k1 1
(integer) 1
127.0.0.1:6379> SADD k1 2
(integer) 1
127.0.0.1:6379> SADD k1 3
(integer) 1
127.0.0.1:6379> SADD k2 3
(integer) 1
127.0.0.1:6379> SADD k2 4
(integer) 1
127.0.0.1:6379> SADD k2 5
(integer) 1
127.0.0.1:6379> SDIFF k1 k2 # 檢視k1、k2的差集合
1) "1"
2) "2"
127.0.0.1:6379> SINTER k1 k2 # 檢視k1、k2的交集
1) "3"
127.0.0.1:6379> SUNION k1 k2 # 檢視k1、k2的並集
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
微博或者B站中,A使用者所有關注的人放在一個SET集合中,粉絲也放在一個集合中,可以實現共同關注,共同愛好,二度好友
4、Hash(雜湊)
map集合,key-,這時候就是一個map集合,本質和String型別沒有太大區別
127.0.0.1:6379> HSET hash k1 v1 # SET一個具體的key-value
(integer) 1
127.0.0.1:6379> HMSET hash k2 v2 k3 v3 # SET多個key-value
OK
127.0.0.1:6379> HGETALL hash # 獲取hash中的全部資料
1) "k1"
2) "v1"
3) "k2"
4) "v2"
5) "k3"
6) "v3"
5、Zset(有序集合)
在Set的基礎上,增加了一個值
127.0.0.1:6379> ZADD set 1 one # 新增一個值
(integer) 1
127.0.0.1:6379> ZADD set 2 two 3 three # 新增多個值
(integer) 2
127.0.0.1:6379> ZRANGE set 0 -1
1) "one"
2) "two"
3) "three"
#########################################################################
127.0.0.1:6379> ZADD salary 2500 zhangsan
(integer) 1
127.0.0.1:6379> ZADD salary 4000 lisi
(integer) 1
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf # 顯示所有使用者
1) "zhangsan"
2) "lisi"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores # 顯示所有使用者並且附帶成績
1) "zhangsan"
2) "2500"
3) "lisi"
4) "4000"
三、三種特殊資料型別
1、geospatial(地理位置)
朋友的定位、附近的人、打車距離
相關命令
- Redis GEOHASH
- Redis GEOPOS
- Redis GEODIST
- Redis GEORADIUS
- Redis GEOADD
- Redis GEORADIUSBYMEMBER
# GEOADD 新增地理位置
# 規則:兩級無法直接新增,我們一般會下載城市,直接通過JAVA程式一次性匯入
# GEOPOS 獲取指定城市的經度和緯度
# GEODIST 返回兩個給定位置之間的距離
# GEORADIUS 以給定的經緯度為中心, 找出某一半徑內的元素
2、Hyperloglog(基數統計)
基數統計的演算法、頁面統計
網頁的UV(一個人訪問一個網站多次,但是還是算作一個人)
- 傳統方式,SET儲存使用者ID,然後就可以統計SET中的元素數量作為標準判斷,這種方式儲存大量的ID就會比較麻煩,佔記憶體
- Hyperloglog優點:佔用的記憶體是固定,2^64不同的元素,只需要佔用12KB記憶體
127.0.0.1:6379> PFADD key a b c d e f # 建立第一組元素key
(integer) 1
127.0.0.1:6379> PFCOUNT key # 統計key元素的基數數量
(integer) 6
127.0.0.1:6379> PFADD key1 e f i j k # 建立第二組元素key
(integer) 1
127.0.0.1:6379> PFMERGE key2 key key1 # 合併兩組key、key1
OK
127.0.0.1:6379> PFCOUNT key2 # 檢視並集的數量
(integer) 9
3、Bitmaps(點陣圖)
應用場景:兩個狀態的資料都可以使用Bitmaps,都是操作二進位制位來進行記錄
- 疫情疫情感染人數(0/1)
- 統計使用者資訊活躍或者不活躍
- 登入或者未登入的人數
- 測試:使用bitmap來記錄週一到週日的打卡(0為沒打,1為打了)
127.0.0.1:6379> SETBIT sign 0 1
(integer) 0
127.0.0.1:6379> SETBIT sign 1 0
(integer) 0
127.0.0.1:6379> SETBIT sign 2 0
(integer) 0
127.0.0.1:6379> SETBIT sign 3 1
(integer) 0
127.0.0.1:6379> SETBIT sign 4 1
(integer) 0
127.0.0.1:6379> SETBIT sign 5 0
(integer) 0
127.0.0.1:6379> SETBIT sign 6 1
(integer) 0
#########################################################################
127.0.0.1:6379> GETBIT sign 1 # 檢視某一天是否開啟
(integer) 0
127.0.0.1:6379> GETBIT sign 4
(integer) 1
#########################################################################
127.0.0.1:6379> BITCOUNT sign # 統計打卡的天數,檢視是否全勤
(integer) 4
四、Redis事務
- redis事務本質:一組命令的集合,一個事務中的所有命令都會被序列化,在事務執行過程中,會按照佇列執行
- Redis單條命令要儲存原子性,但是事務不保證原子性,但是具有一次性、順序性、排他性
- redis的事務:
- 開啟事務(MULTI)
- 命令入隊(命令)
- 執行事務(EXEC)
正常執行事務
127.0.0.1:6379> MULTI # 開啟事務
OK
127.0.0.1:6379> set k1 v1 # 命令入隊
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> EXEC # 事務執行
1) OK
2) OK
3) "v2"
4) OK
#########################################################################
127.0.0.1:6379> MULTI # 開啟事務
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> DISCARD # 取消事務
OK
127.0.0.1:6379> get k4
(nil)
五、Jedis
-
使用JAVA來操作Redis,是官方推薦的Java開發連線工具,是操作Redis的中介軟體
-
測試:
- 匯入對應依賴
<dependencies> <!--匯入Jedis的依賴--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.2.0</version> </dependency> <!--Fastjson--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.72</version> </dependency> </dependencies>
-
編碼測試:
- 連線資料庫
package com.shdq; import redis.clients.jedis.Jedis; public class Ping { public static void main(String[] args) { // new一個Jedis物件 Jedis jedis = new Jedis("192.168.126.131",6379); System.out.println(jedis.ping()); } }
- 操作命令
- 斷開連線
六、SpringBoot整合
- springboot操作資料:都要和spring-data
- spring-data也是和spring-boot齊名的專案
- springboot2.X以後,原來使用的Jedis被替換成了Lettuce
- Jedis:採用直連,多個執行緒操作是不安全的,要避免就要使用JedisPool連線池(BIO模式)
- Lettuce:採用netty,例項可以在多個執行緒中共享,不存線上程不安全的情況,可以減少執行緒資料(NIO模式)
整合測試
- springboot所有的配置類,都有一個自動配置類(RedisAutoConfiguration)
- 自動配置類都會繫結一個properties配置檔案(RedisProperties)
- 配置連線、測試
spring:
redis:
host: 192.168.126.132
port: 6379
@SpringBootTest
class SpringRedisApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
// 測試連線物件
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
connection.flushAll();
}
- 真實開發一般都適用JSON來傳遞物件,所有物件都需要序列化
- 所有的redis操作,對Java開發人員來說,十分簡單,主要是理解redis每一種資料結構的應用場景
七、Redis.conf詳解
- 通過配置檔案啟動(redis-server config/redis.conf)
單位(unit單位對大小寫不敏感)
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes
#
# units are case insensitive so 1GB 1Gb 1gB are all the same.
包含
# include /path/to/local.conf
# include /path/to/other.conf
網路
bind 127.0.0.1 # 繫結IP
protected-mode no # 保護模式
port 6379 # 埠設定
通用設定
################################# GENERAL #####################################
daemonize yes # 以守護程序的方式執行,預設是no,需要手動開啟為yes
pidfile /var/run/redis_6379.pid # 如果以後臺方式執行,我們需要指定一個pid檔案
# 日誌
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
loglevel notice
logfile "" # 日誌的檔案位置名
databases 16 # 資料庫數量,預設16個
快照
- 持久化,在規定的時間內,執行了多少次操作,則會持久化到檔案.rdb、.aof
- redis是記憶體資料庫,沒有持久化,那麼資料斷電即
################################ SNAPSHOTTING ################################
# 如果900s內,如果至少有一個key進行了修改,我們就進行持久化操作
save 900 1
# 如果300s內,如果至少有十個key進行了修改,我們就進行持久化操作
save 300 10
# 如果60s內,如果至少有一萬個key進行了修改,我們就進行持久化操作
save 60 10000
SECURITY(安全)
################################## SECURITY ###################################
requirepass 123456 # 登入即需要密碼
CONFIG SET requirepass "123456" # 命令列設定密碼
八、Redis持久化
- 面試和工作,持久化都是重點!
- redis是記憶體資料庫,如果不將記憶體中的資料儲存到硬碟中,那麼一旦伺服器程序退出,伺服器中的資料庫狀態也會消失,所以redis提供了持久化功能
1、RDB(RedisDataBase)
Redis會單獨建立一個(fork)子程序來進行持久化,會先將資料寫入一個臨時檔案中,待持久化過程都結束了,再用這個臨時檔案替換上次持久化好的檔案。整個過程,主程序不進行任何IO操作,這也確保了極高的效能,如果需要進行大規模恢復,且對於資料恢復的完整性不是非常敏感,那RDB方式要比AOF方式更加高效,RDB的缺點是最後一次持久化的資料可能會丟失,預設的持久化方式就是RDB,rdb儲存的檔案是dump.rdb
sava 5 60 # 只要60內修改超過5次,就會觸發rdb操作
- 觸發機制
- sava的規則滿足的情況下,會自動觸發rdb操作
- 執行flushall命令,也會觸發rdb操作
- 退出redis,也會產生rdb檔案
- 恢復
- 只需要把rdb檔案放在我們的redis啟動目錄下就可以了,redis啟動會自動檢查dump.rdb,恢復其中的資料
2、AOF(Append Only File)
- 將我們所有的命令都記錄下來,history檔案,恢復的時候就把這個檔案的命令全部執行一遍
- 以日誌的形式記錄每一次寫操作,將Redis執行過的所有指令記錄下來(讀操作不記錄),只許追加檔案不許改寫檔案,redis啟動之初會讀該檔案並重新構建資料,redis重啟的話根據日誌檔案的內容將寫指令從前到後執行一次以完成資料恢復工作,AOF儲存的檔案是appendonly.aof
九、Redis釋出訂閱
- Redis釋出訂閱(pub/sub)是一種訊息通訊模式:傳送者釋出訊息、訂閱者接受訊息(微信、微博訂閱釋出)
127.0.0.1:6379> SUBSCRIBE zhou # 訂閱一個頻道
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "zhou"
3) (integer) 1
1) "message"
2) "zhou"
3) "Hello"
#########################################################################
127.0.0.1:6379> PUBLISH zhou Hello # 釋出訊息
(integer) 1
十、Redis主從複製
主從複製:是指將一臺Redis伺服器的資料,複製到其他的Redis伺服器。前者稱為主節點、後者稱為從節點,資料的複製是單向的,只能由主節點到從節點,Master以寫為主,Slave以讀為主
預設情況下,每臺Redis伺服器都是主節點,且一個主節點可以有多個從節點(或者沒有從節點),但一個從節點只能有一個主節點
- 資料冗餘:主從複製實現在資料的熱備份,是持久化之外的一種資料冗餘方式
- 故障恢復:當主節點出現問題時,可以由從節點提供服務,實現快速的恢復,實際上是一種服務的冗餘
- 負載均衡:在主從複製的基礎上,配合讀寫分離,可以由主節點提供寫服務,由從節點提供讀服務,分擔伺服器負載
- 高可用:主從複製是哨兵和叢集能夠實施的基礎,因此主從複製是Redis高可用的基礎
1、環境配置
- 只配置從庫,不配置主庫
127.0.0.1:6379> INFO replication # 檢視當前庫的資訊
# Replication
role:master # 角色
connected_slaves:0 # 從機數量
master_replid:f35ff87d0af7d90ce5c3609b56e3479a7435ae8f
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
- 配置多個伺服器,複製三個配置檔案,然後修改對應資訊
- 埠、pid名字
- log檔名字
- dump.rdb名字
- 從機配置
127.0.0.1:6380> SLAVEOF 127.0.0.1 6379 # 配置從機命令(SLAVEOF host port)
127.0.0.1:6380> INFO replication # 檢視當前庫的資訊
# Replication
role:slave # 角色
connected_slaves:0
複製原理
- Slave啟動成功連線到master後會傳送一個sync同步命令
- Master接到命令後,啟動後臺的存檔程序,同時收集所有接受到用於修改資料的命令,在後臺程序執行完畢之後,master將資料檔案發到slave,完成同步
- 全量複製:slave服務在接受到資料庫服務後,將其存檔並載入到記憶體中
- 增量複製:Master繼續將新的所有收集到的修改命令依次傳到slave,完成同步
十一、哨兵模式
自動選取主機模式
主從切換技術的方法是:當主伺服器宕機後,需要手動把一臺從伺服器切換成主伺服器,這需要人工干預,更多時候優先考慮哨兵模式,Redis從2.8以後開始使用Sentinel(哨兵)架構來解決這個問題
哨兵模式是一種特殊模式,redis提供了哨兵命令,哨兵是一個獨立的程序,作為程序會獨立執行,其原理是哨兵通過傳送命令,等待redis伺服器響應,從而監控執行多個redis例項
- 配置哨兵檔案(sentinel.conf)
sentinel monitor redis 127.0.0.1 6379 1
後面的數字1,代表主機掛了,slave投票看讓誰接替成為主機,票數最多的,就會成為主機!
- 啟動哨兵
[root@localhost bin]# redis-sentinel config/sentinel.conf
12021:X 10 Sep 2020 16:43:03.570 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
12021:X 10 Sep 2020 16:43:03.570 # Redis version=5.0.4, bits=64, commit=00000000, modified=0, pid=12021, just started
12021:X 10 Sep 2020 16:43:03.570 # Configuration loaded
12021:X 10 Sep 2020 16:43:03.571 * Increased maximum number of open files to 10032 (it was originally set to 1024).
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 5.0.4 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in sentinel mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 26379
| `-._ `._ / _.-' | PID: 12021
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
12021:X 10 Sep 2020 16:43:03.572 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
12021:X 10 Sep 2020 16:43:03.573 # Sentinel ID is 0fb4a488a02b611e5dd2eae5e60d7e9e5055e6e2
12021:X 10 Sep 2020 16:43:03.573 # +monitor master redis 127.0.0.1 6379 quorum 1
- 哨兵模式
- 哨兵叢集:基於主從複製的模式,所有的主從配置有點,它都有
- 主從可以切換,故障可以轉移,系統的可用性就會更好
- 哨兵模式就是主從模式的升級,從手動到自動
- redis不好線上擴容,實現哨兵模式的配置很麻煩,裡面有很多選擇
十二、Redis快取穿透和雪崩
Redis快取的作用,極大的提高了應用程式的效能和效率,特別是資料查詢方面,如果對資料一致性要求很高,就不能使用快取
1、快取穿透
概念:使用者想要查詢一個數據,發現redis記憶體資料庫沒有,也就是快取沒用命中,於是向持久層資料庫查詢,發現也沒有,一旦使用者很多的時候,快取都沒有命中(比如秒殺活動),於是都去請求持久層,給持久層資料庫造成很大的壓力,這時候就相當於出現了快取穿透