Redis總結

  • 2021 年 3 月 31 日
  • 筆記

Redis總結

這是我學習Redis網址,大家可以借鑒一下,僅供參考://www.bilibili.com/video/BV1S54y1R7SB?p=1

Nosql簡介

NoSQL(NoSQL = Not Only SQL ),意即”不僅僅是SQL”。

在現代的計算系統上每天網路上都會產生龐大的數據量。

NoSQL,泛指非關係型的資料庫。隨著互聯網web2.0網站的興起,傳統的關係資料庫在處理web2.0網站,特別是超大規模和高並發的SNS類型的web2.0純動態網站已經顯得力不從心,出現了很多難以克服的問題,而非關係型的資料庫則由於其本身的特點得到了非常迅速的發展。NoSQL資料庫的產生就是為了解決大規模數據集合多重數據種類帶來的挑戰,特別是大數據應用難題。

回顧關係型資料庫遵循ACID規則

事務在英文中是transaction,和現實世界中的交易很類似,它有如下四個特性:

1、A (Atomicity) 原子性

原子性很容易理解,也就是說事務里的所有操作要麼全部做完,要麼都不做,事務成功的條件是事務里的所有操作都成功,只要有一個操作失敗,整個事務就失敗,需要回滾。

2、C (Consistency) 一致性

一致性也比較容易理解,也就是說資料庫要一直處於一致的狀態,事務的運行不會改變資料庫原本的一致性約束。

3、I (Isolation) 獨立性

所謂的獨立性是指並發的事務之間不會互相影響,如果一個事務要訪問的數據正在被另外一個事務修改,只要另外一個事務未提交,它所訪問的數據就不受未提交事務的影響。

4、D (Durability) 持久性

持久性是指一旦事務提交後,它所做的修改將會永久的保存在資料庫上,即使出現宕機也不會丟失。

為什麼使用NoSQL ?

今天我們可以通過第三方平台(如:Google,Facebook等)可以很容易的訪問和抓取數據。用戶的個人資訊,社交網路,地理位置,用戶生成的數據和用戶操作日誌已經成倍的增加。我們如果要對這些用戶數據進行挖掘,那SQL資料庫已經不適合這些應用了, NoSQL 資料庫的發展卻能很好的處理這些大的數據。

RDBMS vs NoSQL

RDBMS
– 高度組織化結構化數據
– 結構化查詢語言(SQL) (SQL)
– 數據和關係都存儲在單獨的表中。
– 數據操縱語言,數據定義語言
– 嚴格的一致性
– 基礎事務

NoSQL
– 代表著不僅僅是SQL
– 沒有聲明性查詢語言
– 沒有預定義的模式
-鍵 – 值對存儲,列存儲,文檔存儲,圖形資料庫
– 最終一致性,而非ACID屬性
– 非結構化和不可預知的數據
– CAP定理
– 高性能,高可用性和可伸縮性

關於CAP定理的介紹

在電腦科學中, CAP定理(CAP theorem), 又被稱作 布魯爾定理(Brewer’s theorem), 它指出對於一個分散式計算系統來說,不可能同時滿足以下三點:

  • 一致性(Consistency) (所有節點在同一時間具有相同的數據)
  • 可用性(Availability) (保證每個請求不管成功或者失敗都有響應)
  • 分隔容忍(Partition tolerance) (系統中任意資訊的丟失或失敗不會影響系統的繼續運作)

CAP理論的核心是:一個分散式系統不可能同時很好的滿足一致性,可用性和分區容錯性這三個需求,最多只能同時較好的滿足兩個。

因此,根據 CAP 原理將 NoSQL 資料庫分成了滿足 CA 原則、滿足 CP 原則和滿足 AP 原則三 大類:

  • CA – 單點集群,滿足一致性,可用性的系統,通常在可擴展性上不太強大。
  • CP – 滿足一致性,分區容忍性的系統,通常性能不是特別高。
  • AP – 滿足可用性,分區容忍性的系統,通常可能對一致性要求低一些。

Redis

Redis概述

Redis(Remote Dictionary Server ),即遠程字典服務,是一個開源的使用ANSI C語言編寫、遵守 BSD 協議、支援網路、可基於記憶體、分散式、可選持久性的鍵值對(Key-Value)存儲資料庫,並提供多種語言的 API。

Redis 與其他 key – value 快取產品有以下三個特點:

  • Redis支援數據的持久化,可以將記憶體中的數據保存在磁碟中,重啟的時候可以再次載入進行使用。
  • Redis不僅僅支援簡單的key-value類型的數據,同時還提供list,set,zset,hash等數據結構的存儲。
  • Redis支援數據的備份,即master-slave模式的數據備份。

Redis 優勢

  • 性能極高 – Redis能讀的速度是110000次/s,寫的速度是81000次/s 。
  • 豐富的數據類型 – Redis支援二進位案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 數據類型操作。
  • 原子 – Redis的所有操作都是原子性的,意思就是要麼成功執行要麼失敗完全不執行。單個操作是原子性的。多個操作也支援事務,即原子性,通過MULTI和EXEC指令包起來。
  • 豐富的特性 – Redis還支援 publish/subscribe, 通知, key 過期等等特性。

redis的安裝

安裝gcc,yum install gcc-c++
img

解壓redis: tar -zxvf redis-5.0.7.tar.gz
img

安裝redis: make
img

如果是安裝的6.x版本的redis,可能會報錯,因為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
gcc -v
make
make install

修改config的啟動方式為後台啟動
img

啟動redis服務,指定配置文件啟動:./redis-server ../myredis/redis.conf
img
查看redis進程是否開啟 ps -ef|grep redis:
img

關閉redis: shutdown
img

Redis 數據類型

Redis支援五種數據類型:string(字元串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

類型 簡介 特性 場景
String(字元串) 二進位安全 可以包含任何數據,比如jpg圖片或者序列化的對象,一個鍵最大能存儲512M
Hash(字典) 鍵值對集合,即程式語言中的Map類型 適合存儲對象,並且可以像資料庫中update一個屬性一樣只修改某一項屬性值(Memcached中需要取出整個字元串反序列化成對象修改完再序列化存回去) 存儲、讀取、修改用戶屬性
List(列表) 鏈表(雙向鏈表) 增刪快,提供了操作某一段元素的API 1,最新消息排行等功能(比如朋友圈的時間線) 2,消息隊列
Set(集合) 哈希表實現,元素不重複 1、添加、刪除,查找的複雜度都是O(1) 2、為集合提供了求交集、並集、差集等操作 1、共同好友 2、利用唯一性,統計訪問網站的所有獨立ip 3、好友推薦時,根據tag求交集,大於某個閾值就可以推薦
Sorted Set(有序集合) 將Set中的元素增加一個權重參數score,元素按score有序排列 數據插入集合時,已經進行天然排序 1、排行榜 2、帶權重的消息隊列

Redis 鍵(key)

Redis 鍵命令用於管理 redis 的鍵。

select  [num]	    # 切換第num+1個資料庫
dbsize				# 當前資料庫大小
set name Antarcticc	# 意思是新增一欄位name,值為Antarcticc
get name			# 取得欄位name的值
keys *				# 查看所有鍵名
flushdb				# 清空當前庫
flushall			# 清空所有庫
exists name			# 判斷記憶體中鍵值對是否存在,存在返回1,不存在返回0
MOVE name 1			# 移動鍵為name的鍵值對到1號資料庫中
EXPIRE name 10		# 設置鍵為name的鍵值對10秒後自動過期
ttl name			# 查看鍵為name的鍵值對過期剩餘時間
type name			# 查看鍵name對應值的類型

Redis 字元串(String)

string 是 redis 最基本的類型,你可以理解成與 Memcached 一模一樣的類型,一個 key 對應一個 value。

string 類型是二進位安全的。意思是 redis 的 string 可以包含任何數據。比如jpg圖片或者序列化的對象。

string 類型是 Redis 最基本的數據類型,string 類型的值最大能存儲 512MB。

set key value    # 設置指定 key 的值
get key value    # 獲取指定 key 的值
append key value # 在key後面追加字元串value,如果key是不存在的,相當於set key value

INCR key       # 使key對應的值自增1
INCRBY key 10  # 使key對應的值自增10
DECR key       # 使key對應的值自減1
DECRBY key 10  # 使key對應的值自減10

getrange key 0 3    # 取得key值的字元串中下標為0到3的字元
getrange key 0 -1   # 取得key值的整個字元串
SETRANGE key 2 q    # 替換key值的下標為2位置的字元

SETEX key 30 "hello"  # 如果key存在,則設置其值為hello,並在30秒還過期,否則創建失敗
SETNX mykey "redis"   # 如果mykey不存在,則創建mykey並設置值為redis,否則創建失敗。在分散式鎖中經常使用

mset k1 v1 k2 v2 k3 v3 # 同時設置多個鍵值對,kl對v1,k2對v2,k3對v3
mget k1 k2 k3 		   # 使用多個鍵同時取得多個值,輸出結果如下
1) "v1"
2) "v2"
3) "v3" 

msetnx k4 v4 		# 如果k4不存在,則創建k4並賦值為v4
msetnx k1 v1 k4 v4  # 如果k1和k4都不存在,則創建k1和k4並賦值為v1和v4,如果有一個已經存在,則失敗。原子性操作

set user:1{name:zhangsan,age:3}		# 設置一個user:1對象,值為json字元串來保存對象

# 這裡的key是一個巧妙的設計:user:{id}:{filed}
mset user:1:name zhangsan user:1:age 20
# 即通過key加id加屬性的方式實現工整有序不重複的存儲。
getset db "redis"# 相當於get db得到原值並輸出後,set db "redis"。如果不存在則返回null並設置新值,如果存在先返回原值再設置新的值

String類型的使用場景:value除了是字元串還可以是數字!

  • 計數器
  • 瀏覽量
  • 統計多單位的數量,例:uid:999999:follow 0,設置uid為999999的用戶粉絲數為0
  • 對象快取存儲

Redis 哈希(Hash)

Redis hash 是一個鍵值(key=>value)對集合。

Redis hash 是一個 string 類型的 field 和 value 的映射表,hash 特別適合用於存儲對象。

127.0.0.1:6379> hset myhash field1 Antarcticc# 為myhash新增一個鍵值對,key:field1,value:Antarcticc
(integer) 1
127.0.0.1:6379> HGET myhash field1	# 查看myhash中鍵值對中key為field1的value值
"Antarcticc"
127.0.0.1:6379> HMSET myhash field1 hello field2 world# 批量對myhash新增鍵值對,如果包含已存在鍵值對,則覆蓋原有的
OK
127.0.0.1:6379> HMGET myhash field1 field2# 批量查詢myhash中的field1和field2對應的value值
1) "hello"
2) "world"
127.0.0.1:6379> HGETALL myhash# 列出myhash中所有的鍵值對,序號奇數為key,偶數為value
1) "field1"
2) "hello"
3) "field2"
4) "world"
127.0.0.1:6379> HDEL myhash field1# 刪除myhash中鍵名為field1的鍵值對
(integer) 1
127.0.0.1:6379> HLEN myhash# 查看myhash表長度
(integer) 3
127.0.0.1:6379> HEXISTS myhash field1# 判斷myhash表中是否存在field1欄位
(integer) 1
127.0.0.1:6379> HKEYS myhash# 獲取所有的key
1) "field2"
2) "field1"
3) "field3"
127.0.0.1:6379> HINCRBY myhash field4 1# 自增1
(integer) 2
127.0.0.1:6379> HSETNX myhash field hello# 如果myhash中不存在field欄位則創建並賦值為hello,否則執行失敗。
(integer) 0

Redis 列表(List)

Redis 列表是簡單的字元串列表,按照插入順序排序。可以添加一個元素到列表的頭部(左邊)或者尾部(右邊)。

LPUSH key value # 從左側插入,將一個值或者多個值插入到列表的頭部,寫入順是1,2,3存儲順序是3,2,1
LRANGE key 0 -1 # 取得key列表中所有內容
LRANGE key 0 1  # 取得key列表中下標為0到下標為1的內容(後進先出,最後進來的下標是0)
RPUSH key value # 從右側插入,將一個值或者多個值插入到列表的尾部,寫入順是1,2,3存儲順序就是1,2,3
LPOP key 		 # 移除名為key列表的第一個元素
RPOP key		 # 移除名為key列表的最後一個元素
LINDEX key index # 取得key列表中下標為index的值
LLEN key 		 # 取得key列表的長度
LREM key 4 3	 # 從頭部開始移除4個值為3的value
LTRIM key 1 2	 # 刪除列表中除下標為1、2的其他所有元素
RPOPLPUSH mylist myotherlist #移除mylist列表的最後一個元素,將其添加到myotherlist列表中
LSET mylist 0 value # 修改mylist中下標為0的值修改為value,如果列表或者下標不存在,則報錯。
LINSERT mylist before "1" "Antarcticc" # 將Antarcticc插入到列表中1的前面
LINSERT mylist after "2" "Antarcticc"  # 將Antarcticc插入到列表中2的後面

Redis 集合(Set)

Redis 的 Set 是 string 類型的無序集合。

集合是通過哈希表實現的,所以添加,刪除,查找的複雜度都是 O(1)。

127.0.0.1:6379> sadd myset hello	# 向set集合myset中添加元素hello
(integer) 1
127.0.0.1:6379> SADD myset world	# 向set集合myset中添加元素
(integer) 1
127.0.0.1:6379> SMEMBERS myset		# 查看myset中所有的值
1) "world"
2) "hello"
127.0.0.1:6379> SISMEMBER myset hello	# 查看hello是否在set中
(integer) 1
127.0.0.1:6379> SISMEMBER myset qq		# 查看hello是否在set中
(integer) 0
127.0.0.1:6379> scard myset				# 獲取myset集合中有多少個值
(integer) 2
127.0.0.1:6379> SREM myset hello		# 移除set中的值為hello的元素
(integer) 1
127.0.0.1:6379> SRANDMEMBER myset 1		# 隨機抽取myset集合中的1個元素
"hello"
127.0.0.1:6379> SRANDMEMBER myset 2		# 隨機抽取myset集合中的2個元素
"hello"
」world"
############################################################
127.0.0.1:6379> SMEMBERS myset
1) "hello"
2) "world"
3) "hello3"
4) "hello2"
127.0.0.1:6379> spop myset 1# 隨機移除myset集合中的1個元素
"hello2"
127.0.0.1:6379> spop myset 2# 隨機移除myset集合中的2個元素
"hello3"
"hello"
127.0.0.1:6379> SMEMBERS myset
1) "world"
############################################################
127.0.0.1:6379> SMEMBERS myset
1) "1"
2) "2"
3) "3"
4) "4"
127.0.0.1:6379> SMOVE myset myset2 3# 移動myset中的為3的值到myset2中,如果myset2不存在則先創建後再移動
(integer) 1
127.0.0.1:6379> SMEMBERS myset2
1) "3"
############################################################
# 交並差
127.0.0.1:6379> sadd key1 a
(integer) 1
127.0.0.1:6379> sadd key2 b
(integer) 1
127.0.0.1:6379> sadd key1 c
(integer) 1
127.0.0.1:6379> sadd key2 c
(integer) 1
127.0.0.1:6379> sadd key2 d
(integer) 1
127.0.0.1:6379> sadd key1 e
(integer) 1
127.0.0.1:6379> SDIFF key1 key2# 取得key1和key2的差集
1) "e"
2) "a"
127.0.0.1:6379> SINTER key1 key2# 取得key1和key2的交集
1) "c"
127.0.0.1:6379> SUNION key1 key2# 取得key1和key2的並集
1) "b"
2) "c"
3) "e"
4) "a"
5) "d"

實際應用:

用戶將所有關注的人放在一個set集合中。粉絲也放在一個set中。取得共同關注、共同好友。

Redis 有序集合(sorted set)

Redis zset 和 set 一樣也是string類型元素的集合,且不允許重複的成員。

不同的是每個元素都會關聯一個double類型的分數。redis正是通過分數來為集合中的成員進行從小到大的排序。

zset的成員是唯一的,但分數(score)卻可以重複。

127.0.0.1:6379> ZADD myset 1 one			# 添加一個值
(integer) 1
127.0.0.1:6379> ZADD myset 2 two 3 three	# 添加多個值
(integer) 2
127.0.0.1:6379> ZRANGE myset 0 -1			# 查詢所有
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> ZADD salary 2500 xiaohong
(integer) 1
127.0.0.1:6379> ZADD salary 5000 zhangsan
(integer) 1
127.0.0.1:6379> ZADD salary 199999 wangzheng
(integer) 1
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf # 根據score從小到大排列,範圍是負無窮到正無窮(從小到大不可改變)。
1) "xiaohong"
2) "zhangsan"
3) "wangzheng"
127.0.0.1:6379> ZREVRANGE salary 0 -1# 根據score從大到小排列,範圍是所有元素。
1) "wangzheng"
2) "zhangsan"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf 2500 withscores# 根據score從小到大排列,範圍是負無窮到2500(從小到大不可改變),並顯示具體值。
1) "xiaohong"
2) "2500"
127.0.0.1:6379> ZREM salary xiaohong	# 移除小紅
(integer) 1
127.0.0.1:6379> ZCARD salary			# 獲取集合中元素個數
(integer) 2
127.0.0.1:6379> ZCOUNT myset 0 1		# 獲取區間元素個數
(integer) 1

redis.conf配置文件

Redis 的配置文件位於 Redis 安裝目錄下,文件名為 redis.conf

CONFIG GET * #獲取所有配置項

redis.conf 配置項說明如下:

序號 配置項 說明
1 daemonize no Redis 默認不是以守護進程的方式運行,可以通過該配置項修改,使用 yes 啟用守護進程(Windows 不支援守護執行緒的配置為 no )
2 pidfile /var/run/redis.pid 當 Redis 以守護進程方式運行時,Redis 默認會把 pid 寫入 /var/run/redis.pid 文件,可以通過 pidfile 指定
3 port 6379 指定 Redis 監聽埠,默認埠為 6379,作者在自己的一篇博文中解釋了為什麼選用 6379 作為默認埠,因為 6379 在手機按鍵上 MERZ 對應的號碼,而 MERZ 取自義大利歌女 Alessia Merz 的名字
4 bind 127.0.0.1 綁定的主機地址
5 timeout 300 當客戶端閑置多長秒後關閉連接,如果指定為 0 ,表示關閉該功能
6 loglevel notice 指定日誌記錄級別,Redis 總共支援四個級別:debug、verbose、notice、warning,默認為 notice
7 logfile stdout 日誌記錄方式,默認為標準輸出,如果配置 Redis 為守護進程方式運行,而這裡又配置為日誌記錄方式為標準輸出,則日誌將會發送給 /dev/null
8 databases 16 設置資料庫的數量,默認資料庫為0,可以使用SELECT 命令在連接上指定資料庫id
9 save <seconds> <changes>Redis 默認配置文件中提供了三個條件:save 900 1save 300 10save 60 10000分別表示 900 秒(15 分鐘)內有 1 個更改,300 秒(5 分鐘)內有 10 個更改以及 60 秒內有 10000 個更改。 指定在多長時間內,有多少次更新操作,就將數據同步到數據文件,可以多個條件配合
10 rdbcompression yes 指定存儲至本地資料庫時是否壓縮數據,默認為 yes,Redis 採用 LZF 壓縮,如果為了節省 CPU 時間,可以關閉該選項,但會導致資料庫文件變的巨大
11 dbfilename dump.rdb 指定本地資料庫文件名,默認值為 dump.rdb
12 dir ./ 指定本地資料庫存放目錄
13 slaveof <masterip> <masterport> 設置當本機為 slave 服務時,設置 master 服務的 IP 地址及埠,在 Redis 啟動時,它會自動從 master 進行數據同步
14 masterauth <master-password> 當 master 服務設置了密碼保護時,slav 服務連接 master 的密碼
15 requirepass foobared 設置 Redis 連接密碼,如果配置了連接密碼,客戶端在連接 Redis 時需要通過 AUTH 命令提供密碼,默認關閉
16 maxclients 128 設置同一時間最大客戶端連接數,默認無限制,Redis 可以同時打開的客戶端連接數為 Redis 進程可以打開的最大文件描述符數,如果設置 maxclients 0,表示不作限制。當客戶端連接數到達限制時,Redis 會關閉新的連接並向客戶端返回 max number of clients reached 錯誤資訊
17 maxmemory <bytes> 指定 Redis 最大記憶體限制,Redis 在啟動時會把數據載入到記憶體中,達到最大記憶體後,Redis 會先嘗試清除已到期或即將到期的 Key,當此方法處理 後,仍然到達最大記憶體設置,將無法再進行寫入操作,但仍然可以進行讀取操作。Redis 新的 vm 機制,會把 Key 存放記憶體,Value 會存放在 swap 區
18 appendonly no 指定是否在每次更新操作後進行日誌記錄,Redis 在默認情況下是非同步的把數據寫入磁碟,如果不開啟,可能會在斷電時導致一段時間內的數據丟失。因為 redis 本身同步數據文件是按上面 save 條件來同步的,所以有的數據會在一段時間內只存在於記憶體中。默認為 no
19 appendfilename appendonly.aof 指定更新日誌文件名,默認為 appendonly.aof
20 appendfsync everysec 指定更新日誌條件,共有 3 個可選值:no:表示等作業系統進行數據快取同步到磁碟(快)always:表示每次更新操作後手動調用 fsync() 將數據寫到磁碟(慢,安全)everysec:表示每秒同步一次(折中,默認值)
21 vm-enabled no 指定是否啟用虛擬記憶體機制,默認值為 no,簡單的介紹一下,VM 機制將數據分頁存放,由 Redis 將訪問量較少的頁即冷數據 swap 到磁碟上,訪問多的頁面由磁碟自動換出到記憶體中(在後面的文章我會仔細分析 Redis 的 VM 機制)
22 vm-swap-file /tmp/redis.swap 虛擬記憶體文件路徑,默認值為 /tmp/redis.swap,不可多個 Redis 實例共享
23 vm-max-memory 0 將所有大於 vm-max-memory 的數據存入虛擬記憶體,無論 vm-max-memory 設置多小,所有索引數據都是記憶體存儲的(Redis 的索引數據 就是 keys),也就是說,當 vm-max-memory 設置為 0 的時候,其實是所有 value 都存在於磁碟。默認值為 0
24 vm-page-size 32 Redis swap 文件分成了很多的 page,一個對象可以保存在多個 page 上面,但一個 page 上不能被多個對象共享,vm-page-size 是要根據存儲的 數據大小來設定的,作者建議如果存儲很多小對象,page 大小最好設置為 32 或者 64bytes;如果存儲很大大對象,則可以使用更大的 page,如果不確定,就使用默認值
25 vm-pages 134217728 設置 swap 文件中的 page 數量,由於頁表(一種表示頁面空閑或使用的 bitmap)是在放在記憶體中的,,在磁碟上每 8 個 pages 將消耗 1byte 的記憶體。
26 vm-max-threads 4 設置訪問swap文件的執行緒數,最好不要超過機器的核數,如果設置為0,那麼所有對swap文件的操作都是串列的,可能會造成比較長時間的延遲。默認值為4
27 glueoutputbuf yes 設置在向客戶端應答時,是否把較小的包合併為一個包發送,默認為開啟
28 hash-max-zipmap-entries 64 hash-max-zipmap-value 512 指定在超過一定的數量或者最大的元素超過某一臨界值時,採用一種特殊的哈希演算法
29 activerehashing yes 指定是否激活重置哈希,默認為開啟(後面在介紹 Redis 的哈希演算法時具體介紹)
30 include /path/to/local.conf 指定包含其它的配置文件,可以在同一主機上多個Redis實例之間使用同一份配置文件,而同時各個實例又擁有自己的特定配置文件

三種特殊數據類型

Geospatial

地理位置

127.0.0.1:6379>  geoadd china:city 116.4 39.9 beijing# 添加地理位置
(integer) 1
127.0.0.1:6379> GEOPOS china:city beijing# 獲取指定城市的經緯度
1) 1) "116.39999896287918091" #經度
   2) "39.90000009167092543" #緯度
127.0.0.1:6379> GEOPOS china:city beijing shanghai aomen# 獲取多個城市經緯度
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
2) 1) "121.40000134706497192"
   2) "31.20000061483705878"
3) 1) "113.49999994039535522"
   2) "22.19999914574732003"
127.0.0.1:6379> GEODIST china:city beijing shanghai# beijing和shanghai之間的直線距離
"1067742.3622"# 默認單位米
127.0.0.1:6379> GEODIST china:city beijing shanghai km # beijing和shanghai之間的直線距離,設置單位為千米
"1067.7424"
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km# 以經緯度110 30為原心,1000km為半徑,尋找在china:city中滿足這個範圍內的城市。
1) "aomen"
2) "xianggang"
3) "shenzhen"
4) "guangzhou"
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withcoord# 加一個withcoord是顯示詳細經緯度
1) 1) "aomen"
   2) 1) "113.49999994039535522"
      2) "22.19999914574732003"
2) 1) "xianggang"
   2) 1) "114.19999748468399048"
      2) "22.29999896492555678"
3) 1) "shenzhen"
   2) 1) "114.09999936819076538"
      2) "22.50000113800319212"
4) 1) "guangzhou"
   2) 1) "113.29999834299087524"
      2) "23.10000005307264104"
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withdist# 加一個withdist顯示直線距離
1) 1) "aomen"
   2) "935.1758"
2) 1) "xianggang"
   2) "953.3433"
3) 1) "shenzhen"
   2) "928.8366"
4) 1) "guangzhou"
   2) "834.6077"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 10000 km# china:city中beijing的經緯度為中心,10000千米為半徑,尋找在china:city中滿足這個範圍內的城市
1) "aomen"
2) "xianggang"
3) "shenzhen"
4) "guangzhou"
5) "xiangshan"
6) "shanghai"
7) "beijing"
127.0.0.1:6379> GEOHASH china:city beijing shanghai# 將china:city集合中的beijing、shanghai二維的經緯度,轉換成一維的字元串
1) "wx4fbxxfke0"
2) "wtw36xbc1j0"
127.0.0.1:6379> ZRANGE china:city 0 -1# 使用Zset來查詢所有地理資訊
1) "aomen"
2) "xianggang"
3) "shenzhen"
4) "guangzhou"
5) "xiangshan"
6) "shanghai"
7) "beijing"
127.0.0.1:6379> ZREM china:city xianggang# 刪除指定地理位置
(integer) 1
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "aomen"
2) "shenzhen"
3) "guangzhou"
4) "xiangshan"
5) "shanghai"
6) "beijing"

Hyperloglog

HyperLogLog 是用來做基數統計的演算法,HyperLogLog 的優點是,在輸入元素的數量或者體積非常非常大時,計算基數所需的空間總是固定 的、並且是很小的。在 Redis 裡面,每個 HyperLogLog 鍵只需要花費 12 KB 記憶體,就可以計算接近 2^64 個不同元素的基 數。這和計算基數時,元素越多耗費記憶體就越多的集合形成鮮明對比。

127.0.0.1:6379> PFADD mykey a b c d e f g h i j k l m n	# 創建第一組元素
(integer) 1
127.0.0.1:6379> PFCOUNT mykey	# 顯示數據量
(integer) 14
127.0.0.1:6379> PFADD mykey2 o p q r s t u v w x y z	# 創建第二組元素
(integer) 1
127.0.0.1:6379> PFCOUNT mykey2	# 顯示數據量
(integer) 12
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2	# 將mykey3賦值為mykey和mykey2的不重複並集,如果出現重複,則只保留一個其餘的均忽略
OK
127.0.0.1:6379> PFCOUNT mykey32	# 顯示數據量
(integer) 25

Bitmaps點陣圖

都是操作二進位位來進行記錄,就只有1和0兩個狀態。

# 情景再現:員工打卡系統,sign代表打卡者,0代表星期一,1代表星期二,以此類推,記錄為1代表打卡成功,記錄為0代表打卡失敗
# 錄入
127.0.0.1:6379> setbit sign 0 1# 下標為0,記錄為1,打卡
(integer) 0
127.0.0.1:6379> setbit sign 1 0# 下標為1,記錄為0,未打卡
(integer) 0
# 查詢
127.0.0.1:6379> getbit sign 3# 查看星期四是否打卡
(integer) 0 # 否
127.0.0.1:6379> getbit sign 0# 查看星期一是否打卡
(integer) 1 # 是
# 統計
127.0.0.1:6379> BITCOUNT sign# 統計sign所有數據中為1的數據的總數,即統計sign本周打卡成功的情況
(integer) 3

Redis事務

Redis事務本質:一組命令的集合。

一次性:Redis中命令集合按照順序排列在一個隊列中一次執行完畢。

順序性:一個事務中所有的命令都會被序列化,在事務執行過程中,會按照順序執行。

排他性:事務執行中不允許其他干擾。

Redis事務步驟:

  • 開啟事務
  • 命令入隊
  • 執行事務
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 k3 v3# 語句入隊
QUEUED
127.0.0.1:6379> exec# 執行事務// 注意在執行完exec語句後,再需要使用事務需要再次multi開啟
1) OK
2) OK
3) 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# 取消事務// 注意在執行完DISCARD語句後,再需要使用事務需要再次multi開啟
OK
127.0.0.1:6379> get k4# k4存入失敗,事務取消成功
(nil)
# 如果語句出現問題,那麼整個事務不執行。比如下面入隊一個不存在的語句getset
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v3
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> set k5 v5
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k3
(nil)
# 如果是語句沒問題,是運行時異常,除了有問題的那一句報錯且不執行以外,其他語句照常執行。比如下面讓字元串類型自增
127.0.0.1:6379> set k1 v1
OK
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"

Jedis

使用Java來操作Redis,Jedis是Redis推薦的Java連接開發工具,是Java操作Redis的中間件

導入Jedis包:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.5.2</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.75</version>
</dependency>

資料庫的連接:

public class TestPing {
    public static void main(String[] args) {
        // 1、 new一個Jedis對象
        Jedis jedis = new Jedis("127.0.0.1",6379);
        // 如果 Redis 服務設置來密碼,需要下面這行,沒有就不需要
        // jedis.auth("123456"); 
        // Jedis所有的命令就是Redis命令
        System.out.println(jedis.ping());
        //連接成功輸出PONG
    }
}

Jedis事務

public static void main(String[] args) {
    Jedis jedis = new Jedis("47.99.179.145", 6379);
	// 清空資料庫
    jedis.flushAll();
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("hello","world");
    jsonObject.put("name","zhaohang");
    // 開啟事務
    Transaction multi = jedis.multi();
    String result = jsonObject.toJSONString();
    try {
        multi.set("user1",result);
        multi.set("user2",result);
        int  i=1/0;
        multi.exec();
    } catch (Exception e) {
        multi.discard();
        e.printStackTrace();
    } finally {
        System.out.println(jedis.get("user1"));
        System.out.println(jedis.get("user2"));
        jedis.close();
    }

}

Redis持久化

RDB

數據存入,保存的文件是dump.rdb,再次啟動時讀出。

只要dump.rdb文件生成,即使關機失去記憶體,也能在下一次啟動時成功讀取到持久化的資訊。

image-20210331200447777

rdb觸發機制

1、save的規則滿足的情況下會自動觸發.rdb文件的創建

2、執行了flushdb或者flushall也會生成 .rdb文件

3、退出redis,也會產生rdb文件

備份就會自動生成dump.rdb

只要dump.rdb文件生成,即使關機失去記憶體,也能在下一次啟動時成功讀取到持久化的資訊。

如何恢復rdb文件

只需要將rdb文件放在redis啟動目錄下即可,redis啟動時會自動檢查dump.rdb文件恢復其中的數據到記憶體中。

127.0.0.1:6379> config get dir
1) "dir"
2) "/usr/local/bin" # 如果在此目錄下存在dump.rdb文件,redis啟動就會自動恢復dump.rdb中的內容到記憶體中。

RDB是在某個時間點將數據寫入一個臨時文件,持久化結束後,用這個臨時文件替換上次持久化的文件,達到數據恢復。
優點:使用單獨子進程來進行持久化,主進程不會進行任何IO操作,保證了redis的高性能。適合打大規模的數據恢復。應用情景:伺服器宕機後,redis啟動目錄中的dump.rdb文件將數據恢復到宕機前的模樣。對數據完整性要求不高。

缺點:RDB是間隔一段時間進行持久化,如果持久化之間redis發生故障,會發生數據丟失。所以這種方式更適合數據要求不嚴謹的時候

AOF

將所有命令都記錄下來,有一個歷史文件,恢復時候將此文件全部再執行一遍。<幾乎不使用>

AOF保存的是appendonly.aof,也是在配置文件redis.conf中配置。

默認是不開啟aof的,我們需要主動進行配置

當aof文件有錯誤,此時redis是啟動不起來的,我們需要修復aof這個文件。redis-check-aof進行修復

重寫規則說明

redis會記錄上一次aof文件的大小,如果aof文件大於64MB,將fork一個新的進程來將為aof文件進行重寫。

aof默認的是文件的無限追加,所以這個文件只會越來越大。

優點和缺點

優點:

1、每一次修改都同步,文件的完整性比較好。

appendonly no # 默認是不開啟aof模式的,默認使用rdb方式持久化,在大部分情況下,rdb完全夠用
appendfilename "appendonly.aof  # 持久化文件的名字
# appendfsync always 			# 每次都會修改sync,消耗性能
appendfsync everysec			# 每1秒執行一次sync,可能會丟失者1秒的數據
# appendfsync no   		 #不執行sync,這個時候作業系統自己同步數據,速度最快

2、每秒同步一次,可能會丟失一秒的數據。

3、從不同步,效率最高。

缺點:

1、相對於數據文件來說,即aof文件遠遠大於rdb,修復速度也比rdb慢。

2、aop文件的讀寫效率慢,所以redis默認是rdb而不是aof

RDB和AOF的擴展

1、RDB 持久化方式能夠在指定的時間間隔內對你的數據進行快照存儲

2、AOF 持久化方式記錄每次對伺服器寫的操作,當伺服器重啟的時候會重新執行這些命令來恢復原始的數據。AOF命令以Redis 協議追加保存每次寫的操作到文件末尾,Redis還能對AOF文件進行後台重寫,使得AOF文件的體積不至於過大。

3、只做快取,如果你只希望你的數據在伺服器運行的時候存在,你也可以不使用任何持久化

4、同時開啟兩種持久化方式
在這種情況下,當redis重啟的時候會優先載入AOF文件來恢復原始的數據,因為在通常情況下AOF文件保存的數據集要比RDB文件保存的數據集要完整。RDB 的數據不實時,同時使用兩者時伺服器重啟也只會找AOF文件,那要不要只使用AOF呢?作者建議不要,因為RDB更適合用於備份資料庫(AOF在不斷變化不好備份),快速重啟,而且不會有AOF可能潛在的Bug,留著作為一個萬一的手段。

5、性能建議
因為RDB文件只用作後備用途,建議只在Slave上持久化RDB文件,而且只要15分鐘備份一次就夠了,只保留 save 900 1 這條規則。
如果啟用 AOF ,好處是在最惡劣情況下也只會丟失不超過兩秒數據,啟動腳本較簡單只load自己的AOF文件就可以了,代價一是帶來了持續的IO,二是AOF rewrite 的最後將 rewrite 過程中產生的新數據寫到新文件造成的阻塞幾乎是不可避免的。只要硬碟許可,應該盡量減少AOF rewrite的頻率,AOF重寫的基礎大小默認值64M太小了,可以設到5G以上,默認超過原大小100%大小重寫可以改到適當的數值。
如果不啟用 AOF ,僅靠 Master-Slave Repllcation 實現高可用性也可以,能省掉一大筆IO,也減少了rewrite時帶來的系統波動。代價是如果Master/Slave 同時倒掉,會丟失十幾分鐘的數據,啟動腳本也要比較兩個 Master/Slave 中的 RDB文件,載入較新的那個,微博就是這種架構。

Redis發布訂閱

Redis發布訂閱是一種消息模式:發送者發送消息,訂閱者接受消息。舉例:微信、微博、關注系統。

Redis客戶端可以訂閱任意數量的頻道。作為了解。

三個角色:消息發送者、頻道、消息訂閱者

訂閱並不是Redis特有的,只是Redis也可以實現。

image-20210331202132268

測試

訂閱端:

# 訂閱者對頻道Antarcticc發起訂閱
127.0.0.1:6379> SUBSCRIBE Antarcticc
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "Antarcticc"
3) (integer) 1

發布端:

# 發布者向頻道Antarcticc發起消息
127.0.0.1:6379> PUBLISH Antarcticc "hello"
(integer) 1

訂閱端得到發布端推送的資訊展示

# 訂閱者收到消息後實時更新
127.0.0.1:6379> SUBSCRIBE Antarcticc
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "Antarcticc"
3) (integer) 1
1) "message" 	# 消息
2) "Antarcticc" # 頻道
3) "hello" 		# 消息內容

實際應用:

1、實時消息系統。在註冊完網頁後,自動訂閱網站官方這個頻道,從而實現官方向用戶的推送。

2、實時聊天。將資訊回顯即可。

Redis主從複製

主從複製,是指將一台Redis伺服器的數據,複製到其他的Redis伺服器。前者稱為主節點(master/leader),後者稱為從節點(slave/follower);數據的複製是單向的,只能由主節點到從節點。默認情況下,每台Redis伺服器都是主節點;且一個主節點可以有多個從節點(或沒有從節點),但一個從節點只能有一個主節點。

image-20210331202752515

  1. 一個master可以有多個slave(最少是一主二從)
  2. 一個slave只能有一個master
  3. 數據流向是單向的,master到slave(主節點到從節點,主節點寫,從節點寫。)
  4. 主從複製,讀寫分離。80%情況下都是在讀。所以將寫的請求放在主節點,讀的請求放在從節點,減緩伺服器壓力。

主從複製的作用

數據冗餘:主從複製實現了數據的熱備份,是持久化之外的一種數據冗餘方式。

故障恢復:當主節點出現問題時,可以由從節點提供服務,實現快速的故障恢復;實際上是一種服務的冗餘。

負載均衡:在主從複製的基礎上,配合讀寫分離,可以由主節點提供寫服務,由從節點提供讀服務(即寫Redis數據時應用連接主節點,讀Redis數據時應用連接從節點),分擔伺服器負載;尤其是在寫少讀多的場景下,通過多個從節點分擔讀負載,可以大大提高Redis伺服器的並發量。

高可用基石:除了上述作用以外,主從複製還是哨兵和集群能夠實施的基礎,因此說主從複製是Redis高可用的基礎。

環境配置

只配置從機,不配置主機,因為每台redis伺服器本身就默認是主節點。

127.0.0.1:6379> info replication	 # 查看當前庫的角色
# Replication
role:master
connected_slaves:0
master_replid:fb1c53140ad7f8cacaf39d7e39fd64b8014deeea
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、日誌名

4、備份文件dump.rdb文件名

修改完畢後啟動三個redis服務

[root@antarctic ~]# cd /usr/local/bin
[root@antarctic bin]# ls
6379.log    cloud-init      easy_install-3.6  jsonpatch         mcrypt           redis-cli
6380.log    cloud-init-per  easy_install-3.8  jsonpointer       mdecrypt         redis-sentinel
6381.log    dump6380.rdb    jemalloc-config   jsonschema        myredisconf      redis-server
apt-get     dump6381.rdb    jemalloc.sh       libmcrypt-config  redis-benchmark  temp-1617193959.30034.rdb
chardetect  dump.rdb        jeprof            luajit            redis-check-aof
cloud-id    easy_install    jsondiff          luajit-2.0.4      redis-check-rdb
[root@antarctic bin]# cd myredisconf/
[root@antarctic myredisconf]# ls
redis79.conf  redis80.conf  redis81.conf  redis.conf  sentinel.conf

配置從機:

從機1:

127.0.0.1:6380> SLAVEOF 127.0.0.1 6379
127.0.0.1:6380> info replication
# Replication
role:slave# 身份是從機
master_host:127.0.0.1
master_port:6379 # 主機埠號
master_link_status:up
master_last_io_seconds_ago:9
master_sync_in_progress:0
slave_repl_offset:14
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:38d163d200d5c9b7c6e88773fe8564802f0e67a6
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:14
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:14

從機2:

127.0.0.1:6381> SLAVEOF 127.0.0.1 6379
OK
127.0.0.1:6381> info replication
# Replication
role:slave # 身份是從機
master_host:127.0.0.1
master_port:6379# 主機埠號
master_link_status:up
master_last_io_seconds_ago:7
master_sync_in_progress:0
slave_repl_offset:42
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:38d163d200d5c9b7c6e88773fe8564802f0e67a6
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:42
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:43
repl_backlog_histlen:0

查看主機資訊:

127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2 # 從機數
slave0:ip=127.0.0.1,port=6380,state=online,offset=84,lag=1# 從機1
slave1:ip=127.0.0.1,port=6381,state=online,offset=84,lag=1# 從機2
master_replid:38d163d200d5c9b7c6e88773fe8564802f0e67a6
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:84
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:84

真實的主從配置應該是在配置文件中配置,我們這裡使用的是命令,是暫時的。關閉服務將不會成為從機

主機可以寫,從機不能寫只能讀。主機中所有資訊和數據都會自動被從機保存。

測試

主機斷開連接:

主機斷開連接,從機依舊是連接到主機的,只是沒有寫操作了,從機依舊可以獲得主機寫入過的資訊,如果此時主機重新連接了,此時可以重新獲取到主機寫的資訊。

從機斷開連接:

前提是使用命令方式設置主從複製,而不是使用配置文件實現主從複製的情況下,從機斷開連接後,重新連接後此從機自動變回主機,不再能獲得原來是從機身份時主機中的內容,如果此時重新變回從機,就會重新獲得主機的內容。

Redis主從複製哨兵模式

主從切換技術的方法是:當主伺服器宕機後,需要手動把一台從伺服器切換為主伺服器,這就需要人工干預,費事費力,還會造成一段時間內服務不可用。這不是一種推薦的方式,更多時候,我們優先考慮哨兵模式。

哨兵模式概述

哨兵模式是一種特殊的模式,首先Redis提供了哨兵的命令,哨兵是一個獨立的進程,作為進程,它會獨立運行。其原理是哨兵通過發送命令,等待Redis伺服器響應,從而監控運行的多個Redis實例。

image-20210331204159889

這裡的哨兵有兩個作用:

  1. 通過發送命令,讓Redis伺服器返回監控其運行狀態,包括主伺服器和從伺服器。
  2. 當哨兵監測到master宕機,會自動將slave切換成master,然後通過發布訂閱模式通知其他的從伺服器,修改配置文件,讓它們切換主機。

然而一個哨兵進程對Redis伺服器進行監控,可能會出現問題,為此,我們可以使用多個哨兵進行監控。各個哨兵之間還會進行監控,這樣就形成了多哨兵模式。

image-20210331204436041

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

測試

1、創建哨兵配置文件sentinel.conf,固定名稱,不能拼錯

2、在哨兵配置文件中寫入

sentinel monitor myredis79 127.0.0.1 6381 1
# 自定義監控名稱myredis,監控目標127.0.0.1下的6379埠,1的意思是如果主機掛了就自動去已掛主機下的從機中票選一個票數最多的從機,成為新的主機。
sentinel auth-pass myredis79 123456

3、啟動哨兵

[root@antarctic bin]# redis-sentinel sentinel.conf # 哨兵模式啟動,配置文件是sentinel.conf
23516:X 08 Dec 2020 15:13:37.984 
23516:X 08 Dec 2020 15:13:37.984 # Redis version=5.0.7, bits=64, commit=00000000, modified=0, pid=23516, just started
23516:X 08 Dec 2020 15:13:37.984 # Configuration loaded
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 5.0.7 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in sentinel mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 26379
 |    `-._   `._    /     _.-'    |     PID: 23516
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           //redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               
23516:X 08 Dec 2020 15:13:37.985 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
23516:X 08 Dec 2020 15:13:37.987 # Sentinel ID is 334f503724eb834cfbbb2498f75fc2847e6b9496
23516:X 08 Dec 2020 15:13:37.987 # +monitor master myredis 127.0.0.1 6379 quorum 1

此時斷開6379主機連接,哨兵模式替換開始運作

哨兵日誌如下:

23531:X 08 Dec 2020 15:20:53.324 # +sdown master myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:53.324 # +odown master myredis 127.0.0.1 6379 #quorum 1/1
23531:X 08 Dec 2020 15:20:53.324 # +new-epoch 1
23531:X 08 Dec 2020 15:20:53.324 # +try-failover master myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:53.328 # +vote-for-leader 334f503724eb834cfbbb2498f75fc2847e6b9496 1
23531:X 08 Dec 2020 15:20:53.328 # +elected-leader master myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:53.328 # +failover-state-select-slave master myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:53.418 # +selected-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:53.418 * +failover-state-send-slaveof-noone slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:53.473 * +failover-state-wait-promotion slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:53.900 # +promoted-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:53.900 # +failover-state-reconf-slaves master myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:53.956 * +slave-reconf-sent slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:54.942 * +slave-reconf-inprog slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:54.942 * +slave-reconf-done slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:55.008 # +failover-end master myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:55.008 # +switch-master myredis 127.0.0.1 6379 127.0.0.1 6381
23531:X 08 Dec 2020 15:20:55.008 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6381
23531:X 08 Dec 2020 15:20:55.008 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ myredis 127.0.0.1 6381
23531:X 08 Dec 2020 15:21:25.053 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ myredis 127.0.0.1 6381 # 此時哨兵完成主機更換,新主機為127.0.0.1 6381

如果主機斷開連接,就會從從機中隨機票選出一個成為主機,具體是如何票選的是有具體演算法的,這裡不作贅述。(哨兵選主機是根據性能和數據最好的從機進行選擇)

如果此時原主機重新連接,會默認成為新主機的從機。

哨兵模式優點:

1、哨兵集群,基於主從複製模式,所有的主從配置優點,他全有。

2、主從可以切換,故障可以轉移,系統的可用性就會跟好。

3、哨兵模式就是主從模式的升級,手動到自動,更加健壯。

缺點:

1、Redis不好擴容,集群容量一旦到達上限,在線擴容十分麻煩(大量配置文件需要修改)。

2、實現哨兵模式的配置,其實是很麻煩的,裡面有很多選擇。

Sentinel.conf配置說明

# 哨兵sentinel監控的redis主節點的 ip port 
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 2
 
# 當在Redis實例中開啟了requirepass foobared 授權密碼 這樣所有連接Redis實例的客戶端都要提供密碼  
# 設置哨兵sentinel 連接主從的密碼 注意必須為主從設置一樣的驗證密碼  
# sentinel auth-pass <master-name> <password>  
sentinel auth-pass mymaster MySUPER--secret-0123passw0rd  
 
# 指定多少毫秒之後 主節點沒有應答哨兵sentinel 此時 哨兵主觀上認為主節點下線 默認30秒  
# sentinel down-after-milliseconds <master-name> <milliseconds>  
sentinel down-after-milliseconds mymaster 30000 
 
 
# 這個配置項指定了在發生failover主備切換時最多可以有多少個slave同時對新的master進行 同步,  
# 這個數字越小,完成failover所需的時間就越長,  
# 但是如果這個數字越大,就意味著越 多的slave因為replication而不可用。  
# 可以通過將這個值設為 1 來保證每次只有一個slave 處於不能處理命令請求的狀態。  
# sentinel parallel-syncs <master-name> <numslaves>  
sentinel parallel-syncs mymaster 1 
 
 
# 故障轉移的超時時間 failover-timeout 可以用在以下這些方面:   
# 1. 同一個sentinel對同一個master兩次failover之間的間隔時間。  
# 2. 當一個slave從一個錯誤的master那裡同步數據開始計算時間。直到slave被糾正為向正確的master那裡同步數據時。  
# 3.當想要取消一個正在進行的failover所需要的時間。    
# 4.當進行failover時,配置所有slaves指向新的master所需的最大時間。不過,即使過了這個超時,slaves依然會被正確配置為指向master,
# 但是就不按parallel-syncs所配置的規則來了  
# 默認三分鐘  
# sentinel failover-timeout <master-name> <milliseconds>  
sentinel failover-timeout mymaster 180000  
 
 
# 哨兵sentinel實例運行的埠 默認26379  
port 26379  
 
 
# SCRIPTS EXECUTION  
  
# 配置當某一事件發生時所需要執行的腳本,可以通過腳本來通知管理員,例如當系統運行不正常時發郵件通知相關人員。  
# 對於腳本的運行結果有以下規則:  
# 若腳本執行後返回1,那麼該腳本稍後將會被再次執行,重複次數目前默認為10  
# 若腳本執行後返回2,或者比2更高的一個返回值,腳本將不會重複執行。  
# 如果腳本在執行過程中由於收到系統中斷訊號被終止了,則同返回值為1時的行為相同。  
# 一個腳本的最大執行時間為60s,如果超過這個時間,腳本將會被一個SIGKILL訊號終止,之後重新執行。  
  
# 通知型腳本:當sentinel有任何警告級別的事件發生時(比如說redis實例的主觀失效和客觀失效等等),將會去調用這個腳本,  
# 這時這個腳本應該通過郵件,SMS等方式去通知系統管理員關於系統不正常運行的資訊。調用該腳本時,將傳給腳本兩個參數,  
# 一個是事件的類型,一個是事件的描述。  如果sentinel.conf配置文件中配置了這個腳本路徑,那麼必須保證這個腳本存在於這個
# 路徑,並且是可執行的,否則sentinel無法正常啟動成功。  
# 通知腳本  
# sentinel notification-script <master-name> <script-path>  
sentinel notification-script mymaster /var/redis/notify.sh  
  
# 客戶端重新配置主節點參數腳本  
# 當一個master由於failover而發生改變時,這個腳本將會被調用,通知相關的客戶端關於master地址已經發生改變的資訊。  
# 以下參數將會在調用腳本時傳給腳本:  
# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>  
# 目前<state>總是「failover」,  
# <role>是「leader」或者「observer」中的一個。   
# 參數 from-ip, from-port, to-ip, to-port是用來和舊的master和新的master(即舊的slave)通訊的  
# 這個腳本應該是通用的,能被多次調用,不是針對性的。  
# sentinel client-reconfig-script <master-name> <script-path>  
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh  

Redis快取穿透和雪崩

快取穿透

概念

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

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

為了避免快取穿透其實有很多種解決方案。下面介紹幾種。

解決方案

布隆過濾器

image-20210331205617119

布隆過濾器是一種數據結構,垃圾網站和正常網站加起來全世界據統計也有幾十億個。網警要過濾這些垃圾網站,總不能到資料庫裡面一個一個去比較吧,這就可以使用布隆過濾器。假設我們存儲一億個垃圾網站地址。

有一天網警查到了一個可疑的網站,想判斷一下是否是XX網站,首先將可疑網站通過哈希映射到1億個比特數組上的8個點。如果8個點的其中有一個點不為1,則可以判斷該元素一定不存在集合中。

那這個布隆過濾器是如何解決redis中的快取穿透呢?很簡單首先也是對所有可能查詢的參數以hash形式存儲,當用戶想要查詢的時候,使用布隆過濾器發現不在集合中,就直接丟棄,不再對持久層查詢。

這個形式很簡單。

快取空對象

image-20210331205600766

當存儲層不命中後,即使返回的空對象也將其快取起來,同時會設置一個過期時間,之後再訪問這個數據將會從快取中獲取,保護了後端數據源;

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

如果空值能夠被快取起來,這就意味著快取需要更多的空間存儲更多的鍵,因為這當中可能會有很多的空值的鍵;
即使對空值設置了過期時間,還是會存在快取層和存儲層的數據會有一段時間窗口的不一致,這對於需要保持一致性的業務會有影響。

快取雪崩

概念

快取雪崩是指,快取層出現了錯誤,不能正常工作了。於是所有的請求都會達到存儲層,存儲層的調用量會暴增,造成存儲層也會掛掉的情況。

image-20210331205631672

解決方案

(1)redis高可用

這個思想的含義是,既然redis有可能掛掉,那我多增設幾台redis,這樣一台掛掉之後其他的還可以繼續工作,其實就是搭建的集群。

(2)限流降級

這個解決方案的思想是,在快取失效後,通過加鎖或者隊列來控制讀資料庫寫快取的執行緒數量。比如對某個key只允許一個執行緒查詢數據和寫快取,其他執行緒等待。

(3)數據預熱

數據加熱的含義就是在正式部署之前,我先把可能的數據先預先訪問一遍,這樣部分可能大量訪問的數據就會載入到快取中。在即將發生大並發訪問前手動觸發載入快取不同的key,設置不同的過期時間,讓快取失效的時間點盡量均勻。

(4) 給快取的失效時間,加上一個隨機值,避免集體失效。