1. 程式人生 > 資料庫 >Redis 個人學習筆記

Redis 個人學習筆記

Redis (Remote Dictionary Server)

概述

Redis 能幹嘛

  1. 記憶體儲存、持久化,記憶體中是斷電即失的,所以持久化很重要(rdb、aof)
  2. 效率高,可以用於快取記憶體
  3. 釋出訂閱系統
  4. 地圖資訊分析
  5. 計時器、計數器

Redis 特性

  1. 多樣的資料型別
  2. 持久化
  3. 叢集
  4. 事務

學習中需要用到的東西

官網:https://redis.io/

中文網:http://www.redis.cn/

Linux 安裝

  1. 下載安裝包

  2. 程式建議放在 /opt 目錄下

  3. 解壓 tar -zvxf <filename>
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-HqqVXaKR-1609299330682)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201201130844.png)]

  4. 基本的環境安裝

    yum install gcc-c++
    # 預設安裝gcc版本為4.8.5,而安裝Redis 6.0.9版本時會因為gcc版本過低(至少要gcc 8),make失敗,報錯:[server.o] Error 1
    # 解決辦法。升級gcc,升級過程如下:
    yum -y install centos-release-scl
    yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
    scl enable devtoolset-9 bash
    echo "source /opt/rh/devtoolset-9/enable" >> /etc/profile
    

    解決辦法引用自:https://blog.csdn.net/weixin_40836179/article/details/108245436

    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-H2eFLIhG-1609299330686)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201201130845.png)]

  5. Redis的預設安裝路徑:/usr/local/bin

  6. 將Redis配置檔案,複製到當前目錄下 /usr/local/bin/redis-config
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-YnWSZ29y-1609299330687)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201201130846.png)]

  7. Redis預設不是後臺啟動,修改配置檔案daemonize設定,將其設定為後臺啟動
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-8IM88hiZ-1609299330690)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201201130847.png)]
    將no修改為yes

  8. 通過指定的配置檔案啟動Redis服務
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-SfI0bBJ8-1609299330691)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201201130848.png)]

  9. 通過客戶端連線本機Redis
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-ph2ylck5-1609299330693)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201201130849.png)]

  10. 檢視Redis的程序是否開啟
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-R5l9zgm2-1609299330695)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201201130850.png)]

  11. 關閉Redis服務 shutdown
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-P2S6BzyV-1609299330696)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201201130851.png)]

效能測試

redis-benchmark是官方自帶的壓力測試工具。

圖片來自:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-JoG28axf-1609299330697)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201201130852.png)]

# 測試:100個併發連線,每個併發10w請求
redis-benchmark -h localhost -p 6379 -c 100 -n 10000

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-VPnygYOM-1609299330698)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201201130853.png)]

基礎知識

Redis預設有16個數據

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-CRtZbZVo-1609299330698)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201201130854.png)]

預設使用的是第0個,可以使用select進行切換

127.0.0.1:6379> ping  # 測試連通狀況
PONG
127.0.0.1:6379> select 3 # 切換到3號資料庫
OK
127.0.0.1:6379[3]> dbsize # 當前資料庫大小
(integer) 0

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-1hQrb4Wa-1609299330699)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201201130855.png)]

127.0.0.1:6379[7]> keys * # 檢視當前資料庫所有key值
1) "name"
127.0.0.1:6379[7]> flushdb # 清空當前資料庫
OK
127.0.0.1:6379[7]> keys *
(empty array)
127.0.0.1:6379[7]> FLUSHALL # 清空全部資料庫
OK

Redis是單執行緒的(6.0以下)

Redis是很快的,官方表示,Redis是基於記憶體操作,CPU不是Redis效能瓶頸,而是機器的記憶體和網路頻寬。

Redis是基於C語言,官方提供的資料位10w+的QPS,完全不比同樣的kv鍵值資料庫Memecache差。

Redis為什麼單執行緒還這麼快?

  1. 誤區1:高效能的伺服器一定是多執行緒的?
  2. 誤區2:多執行緒一定比單執行緒效率高?

核心:Redis是將所有的資料全部放在記憶體中,多執行緒的上下文切換會消耗時間,對於記憶體系統來說,如果沒有上下文切換,效率就是最高的,多次讀寫都是在一個CPU上的,所以在記憶體情況下,單執行緒是最佳方案。

基本命令

> set <key> <value>
> get <key>
> keys *
> flushdb
> flushall
> move <key> <db>	# 移動<key>至<db>
> del <key>		# 移除<key>
> exists <key>	# 判斷<key>是否存在
> expire <key> <seconds>	# 設定<key>過期時間,單位秒
> tll <key> # (time to live)檢視<key>還有多久過期(-1 表示不會過期,-2 表示當前庫中沒有/已經過期)
> type <key>	# 檢視<key>的型別

官網命令幫助文件:

五大資料型別

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-RJFSvN7w-1609299330700)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201201130856.png)]

String(字串)

127.0.0.1:6379> set key1 "v1"
OK
127.0.0.1:6379> append key1 "v2"	# 向已有的<key>中追加<value>,返回追加後的字串長度;若<key>不存在將等價於set
(integer) 4
127.0.0.1:6379> strlen key1		# 獲得字串長度
(integer) 4
127.0.0.1:6379> set views 0
OK
127.0.0.1:6379> incr views	# 整數原子+1
(integer) 1
127.0.0.1:6379> decr views	# 整數原子-1
(integer) 0
127.0.0.1:6379> get views
"0"
incrby(decrby) <key> <increment(decrement)> # 設定步長,增加(減少)<increment(decrement)>
# 字串範圍 range
127.0.0.1:6379> set key1 "hello, world"
OK
127.0.0.1:6379> getrange key1  5	# 擷取字串[0:5],可以使用負數定位,與Python類似
"hello,"
127.0.0.1:6379> setrange key1 2 "resetv3"	# 從指定位置開始用新的<value>覆蓋
(integer) 12
127.0.0.1:6379> get key1
"heresetv3rld"
> setex <key> <seconds> <value>	# (set with expire) 設定過期時間,如果<key>已存在則用<value>覆蓋原來的內容
> setnx <key> <value> 			# (set if not exist) 如果不存在就set,返回1;如果<key>已經存在就無作用,返回0
> mset <key> <value> [<key> <value> ...]	# 可以批量設定鍵值對
> mget <key> [<key> ...]					# 批量get
> msetnx <key> <value> [<key> <value> ...]	# 批量使用setnx,原子性操作,要麼全都成功,要麼全都失敗。

# 物件
> set user:1 {name:zhangsan,age:13}	# set一個json字串
> mset user:1:name zhangsan user:1:age 13	# 通過mset批量設定物件單個值
# user:{id}:{filed} 如此設計在Redis中是完全可以的
127.0.0.1:6379> mset user:1:name zhangsan user:1:age 13
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "zhangsan"
2) "13"

> getset <key> <value>	# 先get再set
127.0.0.1:6379> getset key2 3232
(nil)								# 如果原來不存在值則返回nil
127.0.0.1:6379> getset key2 323
"3232"								# 如果原來存在則返回舊值並設定新值
127.0.0.1:6379> get key2
"323"

String型別的使用場景

value除了是字串還可以是數字

  • 計數器
  • 統計多單位的數量
  • 物件快取儲存

List(列表)

可以做棧、佇列、阻塞佇列等。

幾乎所有的list命令都是用l開頭

> lpush <key> <value>	# 將一個或多個值插入列表頭部(左)
> rpush <key> <value>	# 尾部(右)
> lrange <key> <start> <end>	# 獲取列表指定區間的值
> lpop <key>					# 左移除
> rpop <key>					# 右移除
> lindex <key> <index(int)>		# 獲取列表的第<index>個值
> llen <key>					# 列表長度
> lrem <key> <count> <element>	# 移除列表中<count>個<element>
> ltrim <key> <start> <end>		# 擷取列表從<start>至<end>區間的元素
> rpoplpush <source> <destination>	# 移除<source>列表中隊尾元素新增至<destination>列表隊頭
> lset <key> <index> <value>		# 將已存在的<key>列表的第<index>值替換,<index>不能超過列表的範圍(更新操作)
> linsert <key> <BEFORE|AFTER> <pivot> <element>	# 將某個具體的<element>插入列表中某個元素的前面或者後面。
127.0.0.1:6379> linsert list1 BEFORE two insertItem
(integer) 3
127.0.0.1:6379> lrange list1 0 -1
1) "one"
2) "insertItem"
3) "two"

List 小結

  • list實際上是一個連結串列,before Node after,left,right都可以插入值
  • 如果key不存在,建立新的連結串列
  • 如果key存在,新增內容
  • 如果移除了列表中的所有值,那麼key值所表示的列表將不存在
  • 在列表兩邊插入或改動值,效率最高;中間元素效率相對較低

Set(集合)

set中的值不能重複

> sadd <key> <value>	# 新增元素,不可重複(return 0/1)
> smembers <key>			# 展示集合中的所有元素
> sismember <key> <member>	# 判斷某個元素<member>是否在集合中(return 0/1)
> scard <key>					# 集合中的元素個數
> srem <key> <member> [<member> ...]	# 移除
> srandmember <key> [<count>]	# 在集合中隨機抽選<count>個元素,預設1
> spop <key> [<count>]		# 隨機移除<count>個元素,預設1
> smove <source> <distination> <member>		# 將<source>集合中<member>移動到<distination>集合中
> sdiff <key> [<key> ...]		# 差集
> sinter <key> [<key> ...]		# 交集
> sunion <key> [<key> ...]		# 並集

Hash(雜湊)

Map集合,key-Map集合。(Java中的Map約等於Python中的字典)本質和String型別沒有太大區別,還是一個簡單的key-value

> hset <key> <field> <value> [<field> <value> ...]		# 存值,<field>和<value>是map的鍵值對,不會覆蓋
> hget <key> <field>		# 取值
> hmset		# 同時設定多個,會覆蓋
> hmget
> hgetall <key>		# 獲取雜湊中的所有鍵值對
> hdel <key> <field> [<field> ...]		# 刪除
> hlen <key>		# 長度
> hexists <key> <field>		# 判斷雜湊中的指定欄位是否存在
> hkeys / hvals	<key>		# 雜湊的全部鍵/值
> hincrby <key> <field> <increment(int)>	# 雜湊中<field>欄位的值按照步長<increment>自增
> hsetnx <key> <field> <value>		# 不存在則set,存在則不成功

127.0.0.1:6379> hset hash1 field1 satone
(integer) 1
127.0.0.1:6379> hset hash1 field1 satone
(integer) 0
127.0.0.1:6379> hmset hash1 field1 hello field2 world
OK
127.0.0.1:6379> hmget hash1 field2 field1
1) "world"
2) "hello"
127.0.0.1:6379> hgetall hash1
1) "field1"
2) "hello"
3) "field2"
4) "world"
127.0.0.1:6379> hdel hash1 field1
(integer) 1
127.0.0.1:6379> hlen hash1
(integer) 1
127.0.0.1:6379> hkeys hash1
1) "field2"

Hash小結

  • 存放一些經常變動的資料,如使用者資訊儲存
  • 是String型別中key-json對的更優的解決方案

Zset(有序集合)

在set的基礎上增加了一個值。

> zadd <key> [NX|XX] [CH] [INCR] <score> <member> [<score> <member> ...]
> zrange <key> <start> <stop>
> zrangebyscore <key> <min> <max>	[WITHSCORES]	# 從最小值到最大值排序.(-inf,+inf 負無窮,正無窮) 預設為閉區間,可用"(int"取開區間
> zrem <key> <member> [<member> ...]	# 移除
> zcard <key>		# 元素個數
> zrevrange <start> <stop>		# 可以倒序排列
127.0.0.1:6379> ZREVRANGE s 0 -1
1) "zhangsan"
2) "xiaohong"
3) "lisi"

> zcount <key> <min> <max>		# 指定區間內的元素個數

案例思路

  • 班級成績表、工資表排序
  • 普通訊息、重要訊息,帶權值進行排序
  • 排行榜排序

三種特殊資料型別

geospatial 地理位置

key - (經度、緯度、成員名稱)

經度的取值範圍:[-180, 180]

緯度的取值範圍:[-85.05112878, 85.05112878]

Redis的Geo在Redis3.2版本推出,這個功能可以推算地理位置資訊,比如兩地之間的距離等。

底層

Geo的底層實現是Zset資料型別,可以使用所有Zset命令

127.0.0.1:6379> type chain:city
zset

Geo專用的只有6個命令

> geoadd <key> <longitude> <latitude> <member> [<longitude> <latitude> <member> ...]		# 經度、維度、成員(南北極無法直接新增,一般會下載城市資料,通過程式一次性匯入)
> geodist <key> <member1> <member2> [m|km|ft|mi]	# 獲取兩點直線距離
> geohash <key> <member> [<member> ...]		# 獲取返回11個字元的GeoHash,縮短字元、損失精度
> geopos <key> <member> [<member> ...]	# 獲取成員的經緯度(座標值)
> georadius <key> <longitude> <latitude> <radius> <m|km|ft|mi> [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT <count>] [ASC|DESC] [STORE <key>]	# 某個位置半徑<radius>內的成員(WITHCOORD:輸出經緯度,WITHDIST:輸出距離,WITHHASH:輸出HASH,COUNT <count>:最多輸出<count>個結果,ASC|DESC:升序|降序)
> georadiusbymember <key> <member> <radius> <m|km|ft|mi> [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT <count>] [ASC|DESC] [STORE <key>]		# 某個成員半徑<radius>內的成員

127.0.0.1:6379> geoadd chain:city 116.40 39.90 Beijing
(integer) 1
127.0.0.1:6379> geoadd chain:city 121.47 31.23 Shanghai
(integer) 1
127.0.0.1:6379> geoadd chain:city 106.50 29.53 Chongqing
(integer) 1
127.0.0.1:6379> geoadd chain:city 114.05 22.52 Shenzhen 120.16 30.24 Hangzhou 108.96 34.26 xian
(integer) 3
127.0.0.1:6379> geodist chain:city Beijing Shanghai
"1067378.7564"
127.0.0.1:6379> geodist chain:city Beijing Shanghai km
"1067.3788"
127.0.0.1:6379> geopos chain:city Shanghai
1) 1) "121.47000163793563843"
   2) "31.22999903975783553"
127.0.0.1:6379> geohash chain:city Beijing
1) "wx4fbxxfke0"
127.0.0.1:6379> georadius chain:city 110 30 1000 km WITHDIST COUNT 2 ASC
1) 1) "Chongqing"
   2) "341.9374"
2) 1) "xian"
   2) "483.8340"
127.0.0.1:6379> georadius chain:city 110 30 1000 km WITHDIST COUNT 2 DESC
1) 1) "Hangzhou"
   2) "977.5143"
2) 1) "Shenzhen"
   2) "924.6408"

Hyperloglog 基數

基數:不重複的元素的數量,可以接受誤差

簡介

Redis2.8.9版本更新了Hyperloglog資料結構

Redis Hyperloglog 基數統計演算法

優點:佔用的記憶體是固定的,2^64不同的元素的基數,只需要使用12KB記憶體。如果從記憶體角度來比較,Hyperloglog為首選。

官方表示,Hyperloglog基數統計有0.81%錯誤率,但是在大部分應用場景可以忽略。如果不允許容錯,則不推薦使用Hyperloglog。

底層

127.0.0.1:6379> pfadd hyper 1 2 3
(integer) 1
127.0.0.1:6379> type hyper
string
127.0.0.1:6379> get hyper
"HYLL\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80]f\x80Mt\x80Q,\x8cC\xf3"

應用場景

網頁的UV(一個人訪問多次,但是還是算作一個人)

傳統方式:用set儲存使用者的ID,然後統計set中的元素數量作為標準判斷

這個方式如果儲存大量的使用者ID就會比較麻煩。但是最終的目的是為了計數,並不是為了儲存使用者ID。

專屬命令

> pfadd <key> <element> [<elememt> ...]		# 新增
> pfcount <key> [<key> ...]		# 統計
> pfmerge <destkey> <sourcekey> [<sourcekey> ...]		# 合併(會去重)

Bitmaps 點陣圖

應用場景

統計使用者資訊,區分活躍使用者和不活躍使用者,登入使用者和未登入使用者

設計365天打卡

只有兩個狀態的都可以使用Bitmaps

底層

127.0.0.1:6379> setbit bitmap1 0 0
(integer) 0
127.0.0.1:6379> type bitmap1
string
127.0.0.1:6379> get bitmap1
"\x00"

命令

> setbit <key> <offset> <value>		# 使用漂移量按位設定
> getbit <key> <offset>	
> bitcount <key> [<start> <end>]	# 統計

事務

MySQL中的事務

  • 原子性:要麼同時成功,要麼同時失敗

Redis中的事務

Redis事務的本質:一組命令的集合(佇列),在事務執行的過程中,會按照順序執行。

一次性、順序性、排他性(執行過程中不允許被幹擾)

------ 佇列 set set set ... 執行 ------

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> get k1
QUEUED
127.0.0.1:6379> exec				# 執行事務
1) OK
2) OK
3) "v2"
4) OK
5) "v1"

放棄事務

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> discard			  # 放棄事務
OK
127.0.0.1:6379> get k3				# 命令佇列裡的所有命令都不會被執行
(nil)

異常

  • 編譯型異常(程式碼/命令有錯):事務中所有命令都不會被執行

    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379> set k1 v1
    QUEUED
    127.0.0.1:6379> set k3 v3
    QUEUED
    127.0.0.1:6379> getset k3
    (error) ERR wrong number of arguments for 'getset' command
    127.0.0.1:6379> set k4 v4
    QUEUED
    127.0.0.1:6379> exec
    (error) EXECABORT Transaction discarded because of previous errors.
    127.0.0.1:6379> get k4
    (nil)
    127.0.0.1:6379> get k3
    (nil)
    127.0.0.1:6379> get k1
    (nil)
    
  • 執行時異常,如果事務佇列中存在語法性異常,那麼命令執行時,其他命令是可以正常執行的,錯誤命令單獨丟擲異常(Redis事務不存在原子性)

    127.0.0.1:6379> set k1 "v1"
    OK
    127.0.0.1:6379> get k1
    "v1"
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379> incr k1		# 執行的時候會失敗
    QUEUED
    127.0.0.1:6379> set k2 v2
    QUEUED
    127.0.0.1:6379> get k2
    QUEUED
    127.0.0.1:6379> exec
    1) (error) ERR value is not an integer or out of range		# 雖然第一條命令報錯了,但是事務依舊正常執行成功了
    2) OK
    3) "v2"
    127.0.0.1:6379> mget k1 k2
    1) "v1"
    2) "v2"
    

    監控:Watch

    悲觀鎖:

    • 很悲觀,認為任意時刻都可能出問題,無論做什麼都會加鎖(影響效能)

    樂觀鎖:

    • 很樂觀,認為任意時刻都不會出現問題,無論做什麼都不上鎖。更新資料的時候,判斷在此期間是否有人修改過資料。1. 獲取;2. 更新

    • 使用watch可以給加樂觀鎖,當事務執行過程中被其他客戶端修改,則會導致事務執行失敗。

      127.0.0.1:6379> watch money
      OK
      127.0.0.1:6379> decrby money 20		# 在watch加監視後修改了money
      (integer) 80
      127.0.0.1:6379> multi
      OK
      127.0.0.1:6379> decrby money 40
      QUEUED
      127.0.0.1:6379> incrby out 40
      QUEUED
      127.0.0.1:6379> exec		# money在事務外被修改,導致事務執行失敗
      (nil)
      127.0.0.1:6379> unwatch		# 解除監視
      OK
      

Jedis

使用Java操作Redis

Jedis是Redis官方推薦的Java連線開發工具,是使用Java操作Redis的中介軟體。

Redis.conf

配置檔案

單位

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-OpmUHyQ1-1609299330700)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201202183431.png)]

對大小寫不敏感

包含(Include)

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-IsyFuJx7-1609299330701)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201202183437.png)]

網路

bind 127.0.0.1 						# 繫結的IP
protected-mode yes					# 保護模式,預設開啟
port 6379									# 埠
tcp-backlog 511
timeout 0
tcp-keepalive 300

通用(General)

daemonize yes			# 以守護程序的方式執行,預設為no
supervised no			# 管理守護程序,預設no,一般不用動
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
always-show-logo yes			# 是否總是顯示logo

快照(Snapshotting)

持久化,在規定的時間內執行了多少次操作,則會持久化(快照)到檔案,.rdb,.aof

Redis 是記憶體資料庫,如果沒有持久化,資料斷電即失

save 900 1			# 900秒內如果至少有1個key進行了修改,就進行持久化操作
save 300 10			# 300秒內如果有至少10個key進行了修改,就進行持久化操作
save 60 10000		# 60秒內如果有至少1w個key進行了修改,就進行持久化操作

stop-writes-on-bgsave-error yes 	# 持久化如果出錯,知否還繼續工作,預設yes
rdbcompression yes		# 是否壓縮rdb檔案,預設yes
rdbchecksum yes				# 是否校驗rdb檔案
dbfilename dump.rdb			# rdb儲存時的檔名
rdb-del-sync-files no		# 
dir ./									# rdb儲存的目錄

複製(Replication)

安全 (Security)

requirepass foobared 		# 設定密碼,預設被註釋掉,無密碼

127.0.0.1:6379> config get requirepass		# 獲取Redis密碼
1) "requirepass"
2) ""
127.0.0.1:6379> config set requirepass "123123"		# 設定Redis密碼
OK
127.0.0.1:6379> exit
[root@VM-8-2-centos ~]# redis-cli
127.0.0.1:6379> ping		# 有密碼,無許可權
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth 123123		# 登入
OK
127.0.0.1:6379> ping		# 正常使用
PONG

限制(Clients)

maxclients 10000		# 最大連線客戶端數
maxmemory <bytes>		# 配置最大記憶體容量
maxmemory-policy noeviction			# 配置記憶體到達上限的處理策略

Redis中的六種垃圾回收策略:

volatile-lru:從已設定過期時間的記憶體資料集中挑選最近最少使用的資料 淘汰;

volatile-ttl: 從已設定過期時間的記憶體資料集中挑選即將過期的資料 淘汰;

volatile-random:從已設定過期時間的記憶體資料集中任意挑選資料 淘汰;

allkeys-lru:從記憶體資料集中挑選最近最少使用的資料 淘汰;

allkeys-random:從資料集中任意挑選資料 淘汰;

no-enviction(驅逐):禁止驅逐資料。(預設淘汰策略。當redis記憶體資料達到maxmemory,在該策略下,直接返回OOM錯誤);

aof配置(Append only mode)

Redis持久化

重點

RDB (Redis DataBase)

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-jie54y9W-1609299330702)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201203095604.png)]

在指定時間間隔內,將記憶體中的資料即快照寫入磁碟,恢復時是將快照檔案直接讀到記憶體裡。

Redis會單獨建立(fork)一個子程序來進行持久化,會先將資料寫入到一個臨時檔案中,待持久化過程都結束了,再用這個臨時檔案替換上次持久化好的檔案。整個過程中,主程序是不進行任何IO操作的,這就保證了極高的效能。如果需要進行大規模的資料恢復,切對於資料恢復的完整性不是非常敏感,那RDB方式要比AOF方式更加高效。RDB的缺點是最後一次持久化後的資料可能丟失。Redis預設使用RDB持久化,一般情況下不需要修改這個配置。

RDB儲存的檔案預設是dump.rdb,可以在Redis.conf檔案中進行修改。

觸發機制

  1. save的規則滿足的情況下,會自動觸發rdb規則
  2. 執行flushall命令,也會觸發rdb規則
  3. 退出Redis,也會產生rdb檔案

備份會自動生成dump.rdb檔案

如何恢復rdb檔案

  1. 只需要將rdb檔案放在Redis的啟動目錄下,Redis啟動的時候會自動檢查dump.rdb,恢復裡面的資料

  2. 檢視Redis的目錄

    127.0.0.1:6379> config get dir
    1) "dir"
    2) "/usr/local/bin"
    

優缺點

優點:

  1. 適合大規模的資料恢復,子執行緒備份,主執行緒繼續處理請求
  2. 對資料的完整性要求不高

缺點:

  1. 需要一定的時間間隔去操作,如果Redis意外宕機,最後一次rdb之後的資料就丟失了。
  2. fork程序的時候,會佔用一定的記憶體空間

AOF (Append Only File)

將所有修改資料的命令都記錄下來恢復的時候把這個檔案都執行一遍

AOF儲存的檔案是appendonly.aof檔案

在同時存在rdb和aof檔案的情況下,redis會優先選擇aof進行資料恢復

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-RoahqInf-1609299330702)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201203103927.png)]

AOF檔案修復方法

如果執行錯誤的指令,也會被記錄到aof檔案中,並且重啟Redis會因為aof檔案中的錯誤指令而啟動失敗。

那麼如果在實際生產中環境中,網路丟包、延遲、病毒、大檔案執行失敗等等因素導致aof檔案破損。aof檔案損壞了,該怎麼修復?

  1. 備份待修復的aof檔案
  2. 使用redis-check-aof --fix 進行修復
  3. 重啟Redis,重新載入aof

Redis還提供了 redis-check-rdb 工具,修復rdb檔案方法與aof類似。

AOF配置檔案

appendonly no 		# 預設不開啟aof模式,預設是使用rdb方式持久化,在幾乎所有的情況下,rdb完全夠用
appendfilename "appendonly.aof" 		# aof持久化檔案的名字

# appendfsync always			# 每次修改了值都同步,效能消耗大
appendfsync everysec			# 每秒執行一次同步,可能會丟失這1s的資料
# appendfsync no					# 不執行同步,這個時候作業系統自己同步資料,速度最快

no-appendfsync-on-rewrite no			# 重寫時是否可以運用Appendfsync,用預設no即可,保證資料安全性。
auto-aof-rewrite-percentage 100			# 設定觸發重寫的基準值
auto-aof-rewrite-min-size 64mb			# 設定觸發重寫的基準值

AOF的重寫策略

AOF採用檔案追加方式,檔案會越來越大。為避免出現此種情況,新增了重寫機制, 當AOF檔案的大小超過所設定的閾值時,Redis就會啟動AOF檔案的內容壓縮,只保留可以恢復資料的最小指令集,可以使用命令bgrewriteaof

重寫原理

AOF檔案持續增長而過大時,會fork出一條新程序來將檔案重寫(也是先寫臨時檔案最後再rename),遍歷新程序的記憶體中資料,每條記錄有一條的Set語句。重寫aof檔案的操作,並沒有讀取舊的aof檔案,而是將整個記憶體中的資料庫內容用命令的方式重寫了一個新的aof檔案,這點和快照有點類似

觸發機制

Redis會記錄上次重寫時的AOF大小,預設配置是當AOF檔案大小是上次rewrite後大小的一倍且檔案大於64M時觸發。請見配置檔案預設是,auto-aof-rewrite-percentage 100的意思是超過100%,也就是一倍;auto-aof-rewrite-min-size 64mb是超過64mb.

優缺點

優點:

  1. 可以每次修改都同步,檔案的完整性更好

缺點:

  1. 從資料檔案來說,aof遠遠大於rdb,修復的速度也比rdb慢
  2. aof的執行效率要慢於rdb(aof寫檔案需要IO操作)

小結

  1. RDB持久化方式能在指定的時間間隔內對資料進行快照儲存
  2. AOF持久化方式記錄每次對伺服器寫的操作,當伺服器重啟的時候會執行這些命令來恢復資料,AOF命令以Redis協議追加儲存每次寫的操作到檔案末尾,Redis還能對aof檔案進行後臺重寫,壓縮aof檔案的體積
  3. 如果Redis只做快取,只希望資料在伺服器執行時存在,也可以不是用任何持久化
  4. 同時開啟兩種持久化方式
    • Redis優先讀取aof檔案恢復資料,因為aof持久化方式的檔案完整性更好
    • RDB更適合用於備份資料庫,快速重啟,且不會有AOF可能潛在的bug,留作一個萬一的手段
  5. 效能建議
    • 因為RDB檔案只用作備份,建議只在Slave上持久化RDB檔案,而且只需要保留save 900 1這條規則即可
    • 如果Enable AOF,好處是在最惡劣的情況下,也只會丟失不超過2秒資料,啟動指令碼較簡單,只需要過載自己的aof檔案即可,代價一是需要持續IO,二是重寫過程的最後將新的aof寫入磁碟時不可避免的造成阻塞。在硬碟許可的情況下,應儘量減少重寫的頻率,可以將AOF的重寫基準設定到5GB以上。
    • 如果不Enable AOF,僅靠Master-Slave Repllcation實現高可用性也行,能省掉一大筆IO,也減少了重寫時帶來的系統波動。代價是如果主機從機同時宕機,會丟失十幾分鐘的資料,啟動指令碼也要比較主機從機中的RDB檔案,載入較新的那個。(微博就是這種架構)

Redis釋出訂閱

Redis釋出訂閱(pub/sub)是一種訊息通訊模式:傳送者(pub)傳送訊息,訂閱者(sub)接受訊息。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-voMezg79-1609299330703)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201203110402.png)]

命令

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-gQns3NiC-1609299330704)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201203110755.png)]

命令測試

訂閱者客戶端

127.0.0.1:6379> subscribe channel1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel1"
3) (integer) 1		# 開始監聽,等待資訊
1) "message"
2) "channel1"
3) "helloworld"

127.0.0.1:6379> psubscribe ch*		# 按照ch*模式進行訂閱,*為萬用字元,意為訂閱所有ch開頭的頻道
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"		# 返回值型別,顯示訂閱成功
2) "ch*"				# 訂閱模式
3) (integer) 1		# 訂閱的頻道數量
1) "pmessage"		# 接收值的型別,訊息
2) "ch*"				# 頻道匹配模式
3) "channl1"		# 訊息本身的頻道
4) "??"					# 訊息內容

傳送者客戶端:

127.0.0.1:6379> PUBLISH channel1 helloworld
(integer) 1			# 接收到訊息的客戶端數量
127.0.0.1:6379>

原理

Redis是通過C實現的,通過分析Redis原始碼裡的pubsub.c檔案,瞭解釋出和訂閱機制的底層實現,可以加深對Redis的理解

Redis通過publish、subscribe和psubscribe等命令實現釋出和訂閱功能

通過subscribe命令訂閱某頻道後,redis-server裡維護了一個字典,字典的鍵就是一個個channel,而字典的值是一個連結串列,連結串列中儲存了所有訂閱這個channel的客戶端。subscribe命令的關鍵就是將客戶端新增到給定的channel訂閱連結串列中。

使用場景

  1. 實時訊息系統(推送)
  2. 實時聊天(群聊)·

Redis主從複製(Master-Slave Replication)

概念

將一臺Redis伺服器的服務,複製到其他Redis伺服器。資料的複製是單向的,只能由主節點複製到從節點。

一個主節點可以有多個從節點,但一個從節點只能有一個主節點。

主從複製的作用主要包括:

  1. 資料冗餘:主從複製實現了資料的熱備份,是持久化之外的一種資料冗餘方式。
  2. 資料恢復:當主節點出問題時,可以由從節點提供服務,實現快速故障恢復,實際上是一種服務的冗餘。
  3. 負載均衡:在主從複製的基礎上,配合讀寫分離,可以由主節點提供寫服務,由從節點提供讀服務,分擔伺服器負載;由其是在寫少讀多的場景下,通過多個從節點分擔讀負載,可以大大提高Redis伺服器的併發量。
  4. 高可用(叢集)基石:主從複製還是哨兵和叢集能夠實施的基礎

一般來說,要將Redis運用於工程專案中,只使用一臺Redis是萬萬不能的,原因如下:

  1. 從結構上,單個Redis伺服器會發生單點故障,並且一臺伺服器需要處理所有的負載請求,壓力較大
  2. 從容量上,單個Redis伺服器記憶體容量有限,一般來說,單臺Redis最大使用記憶體不應該超過20G

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-tnITBhSM-1609299330705)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201203112850.png)]

主從複製,讀寫分離。80%的情況下都是在進行讀操作,減輕伺服器的壓力,最低一主二從。

環境配置

只需要配置從庫,不用配置主庫

127.0.0.1:6379> info replication		# 檢視當前庫的資訊
# Replication
role:master				# 角色 master
connected_slaves:0				# 沒有Slave
master_replid:8abb42c6c2219b7ca6a7ef83315757d94595750c
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

單機模擬主從機,修改配置的對應資訊:

  1. 埠號
  2. pid名字
  3. log檔名
  4. dump.rdb檔名

啟動三個Redis服務

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-qL62W8ki-1609299330706)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201203114946.png)]

> slaveof <host> <port>

127.0.0.1:6380> slaveof 127.0.0.1 6379
OK

127.0.0.1:6381> slaveof 127.0.0.1 6379
OK

127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2		# 有兩個Slave
slave0:ip=127.0.0.1,port=6380,state=online,offset=70,lag=1		# slave0的資訊
slave1:ip=127.0.0.1,port=6381,state=online,offset=70,lag=1		# slave1的資訊
master_replid:a02c004682dba83eb5d22d42c81dd89514387683
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:70
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:70

真實生產環境中,主從配置在conf檔案中進行設定,在Redis啟動時就會設定好主從角色

127.0.0.1:6379> set k1 v1 			# 在主機中寫入
OK

127.0.0.1:6380> keys *				# 從機中能讀到主機寫入的資訊
1) "k1"
127.0.0.1:6380> set k2 v2			# 但是從機不能寫入
(error) READONLY You can't write against a read only replica.

主機斷線重連,不影響主從複製關係

複製原理

Slave啟動成功連線到Master後會傳送一個sync同步命令

Master接到命令,啟動後臺的存檔程序,同時收集所有接收到的用於修改資料的命令,在後臺程序執行完畢後,Master將傳送整個資料檔案到Slave,並完成一次完全同步

全量複製:Slave服務在接收到資料庫檔案資料後,將其存檔並載入到記憶體中

增量複製:Master繼續將新的所有收集到的修改命令依次傳給Slave,完成同步

但是隻要是重新連線Master,就會自動執行一次完全同步(全量複製)。

鏈式連線

127.0.0.1:6380> slaveof 127.0.0.1 6379
OK

127.0.0.1:6381> slaveof 127.0.0.1 6380
OK

127.0.0.1:6380> info replication		# 查詢中間節點6380的資訊
# Replication
role:slave				# 角色依舊是 Slave , 不能寫入
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:4
master_sync_in_progress:0
slave_repl_offset:2175
slave_priority:100
slave_read_only:1
connected_slaves:1
slave0:ip=127.0.0.1,port=6381,state=online,offset=2175,lag=1
master_replid:a02c004682dba83eb5d22d42c81dd89514387683
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:2175
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:977
repl_backlog_histlen:1199

也可以完成主從複製,但是79主機節點斷線後,80節點使用slaveof no one命令將自己設定為主節點,擁有寫入許可權。

但是如果79節點重新上線,需要重新配置主從關係

哨兵模式

自動選舉主節點的模式

概述

哨兵(Sentinel)可以後臺監控主機是否故障,如果故障了根據投票數自動將從庫轉換為主庫。

哨兵模式是一種特殊的模式,Redis提供了哨兵的命令,哨兵是一個獨立的程序,它會獨立執行。其原理是哨兵通過傳送命令,等待Redis伺服器響應,從而監控多個Redis例項

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-kLnW1L0J-1609299330706)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201203135830.png)]

叢集多哨兵模式

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-jHqot0sb-1609299330707)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201203140028.png)]

假設主伺服器宕機,哨兵1先檢測到這個結果,系統不會立馬進行重新選舉的過程,僅僅哨兵1主觀的認為主伺服器不可用,這個現象稱為主觀下線。當後面的哨兵也檢測到主伺服器不可用,並且數量達到一定值時,那麼哨兵之間會進行一次投票,投票由一個哨兵發起,進行故障轉移操作。切換成功後,會通過釋出訂閱模式,讓各個哨兵把自己的監控的從伺服器切換為主機,這個過程稱為客觀下線

測試

  1. 配置哨兵配置檔案 sentinel.conf

    # sentinel monitor 被監控的名字 host port 1
    sentinel monitor mRedis 127.0.0.1 6379 1
    

    後面的數字1,代表如果主機宕機,Slave投票讓誰接替成為主機

  2. 使用配置檔案啟動哨兵,並且shutdown主機

    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-3I3R4GPc-1609299330708)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201203141648.png)]

    哨兵重新選擇了80節點作為新的主節點。

  3. 宕機的79節點重新上線,會被哨兵自動變為當前主節點的從節點

    # 哨兵輸出
    6743:X 03 Dec 2020 14:18:13.146 * +convert-to-slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mRedis 127.0.0.1 6380
    
    # 重新上線的79節點Info
    127.0.0.1:6379> info replication
    # Replication
    role:slave			# 變為了Slave
    master_host:127.0.0.1
    master_port:6380		# 主節點為80節點
    master_link_status:up
    master_last_io_seconds_ago:0
    master_sync_in_progress:0
    slave_repl_offset:8943
    slave_priority:100
    slave_read_only:1
    connected_slaves:0
    master_replid:8141663dbb7b0831c2cacb51ecff01d885976bbf
    master_replid2:0000000000000000000000000000000000000000
    master_repl_offset:8943
    second_repl_offset:-1
    repl_backlog_active:1
    repl_backlog_size:1048576
    repl_backlog_first_byte_offset:8659
    repl_backlog_histlen:285
    

優缺點

優點:

  1. 哨兵叢集,基於主從複製模式,所有的主從配置優點,它都有
  2. 主從可以切換,故障可以轉移,系統的可用性會更好
  3. 哨兵模式就是主從模式的升級,自動切換,更加健壯

缺點:

  1. Redis不方便線上擴容,叢集容量一旦到達上限,線上擴容非常麻煩
  2. 哨兵的配置麻煩

哨兵模式的配置

其他Redis叢集

cluster

目前大型專案最流行的叢集模式

Redis快取穿透、擊穿、雪崩

服務的高可用問題

快取穿透

概念

使用者想要查詢一個數據,發現Redis記憶體資料庫中沒有,於是向持久層資料庫查詢,發現也沒有,於是本次查詢失敗。當用戶很多的時候,快取都沒有命中,於是都去請求持久層資料庫,這回給持久層資料庫造成很大的壓力,這時候就相當於出現了快取穿透。

解決方案

布隆過濾器

布隆過濾器是一種資料結構,對所有可能查詢的引數以hash形式儲存,在控制層進行校驗,不符合則丟棄,從而避免了對底層儲存系統的查詢壓力

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-4xmiNQAh-1609299330709)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201203143903.png)]

快取空物件

當儲存層不命中後,及時返回的空物件也將其快取起來,同時設定一個過期時間,之後再反問這個資料將會從快取中獲取,保護了後端資料來源

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-q0d62C4U-1609299330710)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201203143911.png)]

但是這種方法會存在兩個問題:

  1. 如果空值能夠被快取起來,意味著將用更多的空間去設定無意義的鍵
  2. 即使對空值設定了過期時間,還是會存在快取層與持久層的資料會有一段時間視窗的不一致,這對於需要保持一致性的業務會有影響

快取擊穿

概述

快取擊穿,是指一個key非常熱點,在不停的扛著大併發,大併發集中對這一個點進行訪問,當這個key在失效的瞬間,持續的大併發就會穿破快取,直接請求資料庫,就像在屏障上鑿開了一個洞

當某個key在過期的瞬間,有大量的請求併發訪問,這類資料一般是熱點資料,由於快取過期,會同時訪問資料庫來查詢最新資料,並且回寫快取,會使資料庫瞬間壓力過大

解決方案

  1. 設定熱點資料永不過期
    從快取層面看,沒有設定過期時間,所以不會出現熱點key過期後產生的問題
  2. 加互斥鎖
    分散式鎖:使用分散式鎖,保證對於每個key同時只有一個執行緒去查詢後端服務,其他執行緒沒有獲得分散式鎖的許可權,因此只需要等待即可。這種方式將高併發的壓力轉移到了分散式鎖,因此對分散式鎖的考驗很大。

快取雪崩

概念

是指在某一個時間段,快取集中過期失效,Redis宕機

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-c0HrVImr-1609299330711)(https://raw.githubusercontent.com/Satone7/Typora_PicGo/master/image/20201203145628.png)]

解決方案

  1. Redis高可用。有可能因為Redis伺服器宕機造成雪崩,多增設幾臺Redis伺服器,搭建叢集。
  2. 限流降級。在快取失效後,通過加鎖或者佇列來控制資料寫快取的執行緒數量。
  3. 資料預熱。在正式部署前,先把可能的資料預先訪問一遍,設定不同的過期時間,讓快取失效的時間點儘量均勻。

Python中的Redis

安裝

pip install redis

使用

import redis

# 連線伺服器的Redis
r = redis.StrictRedis(host="tx.lesely.tk",port=6379,db=0)

# Redis命令 = Python方法,命令返回值 = 方法返回值