Redis
1 簡介
- 是開源免費、遵守BSD協議、高性能的
NOSQL的key-value資料庫, - 是簡單的、高效的、分布式的、基于記憶體的單執行緒快取工具,
- 簡單是Redis的突出特色,簡單可以保證核心功能的穩定和優異,
- 支持網路、可以持久化、支持資料備份、集群、訊息佇列等高可用功能,
- 在企業開發中,Redis常被作為分布式快取
- 熱點資料:常被查詢、不常被修改、洗掉的資料
- 臨時資料:不必持久化、有失效時間的資料(驗證碼等)
2 特點
2.1 關系型資料庫 VS 非關系型資料庫
(1)關系型
- 優點
- 容易理解,二維表比較符合邏輯世界的展示
- 單一關系模型,可以進行結構化存盤,有完整的約束
- 正是因為是結構化存盤的,所以可以很方便的使用結構化語言SQL,實作復雜的操作(表關聯JOIN等)
- 缺點
- SQL需要決議,效率低
- 要保證資料一致性,就需要加鎖,并發性能低
- 結構化存盤雖然方便,但是不靈活,擴展困難,
(2)非關系型
- 優點
- 并發性能高,可以處理大量的資料
- 效率高、速度快
- 缺點
- 不能支持復雜的操作(比如JOIN),每次只能執行簡單的命令
- 沒有完整的約束,事務處理能力差(比如Redis的事務就是偽事務)
2.2 Redis優點
性能高,速度快,簡單高效,可以處理萬級的QPS,使用集群后可達百萬級,遠超MySQL(幾千算好的),- 資料型別豐富(5個基本資料型別,還有其他的擴展型別,下面介紹)
- 因為是
單執行緒的,所以操作都是原子性的,天生執行緒安全, - 雖然是基于記憶體的,但是提供持久化功能,(兩種持久化方式,下面介紹)
2.3 Redis缺點
- 基于記憶體,所以對記憶體的要求高
3 安裝
還是去搜詳細教程吧,這部分之后有時間再整理,
- 使用docker安裝
docker pull redis
docker run -d --name redis -p 6379:6379 redis
docker exec -it redis redis-cli
- 直接壓縮包安裝
1、確保Linux已經安裝gcc
若沒安裝,則需要在root登錄下、可連接外網狀態下執行下面命令
yum -y install gcc automake autoconf libtool make
2、下載Redis
wget http://download.redis.io/releases/redis-5.0.5.tar.gz
3、解壓
tar -zxvf redis-5.0.5.tar.gz -C /opt (解壓到此目錄)
4、進入到解壓的目錄后編譯
cd redis-5.0.5
make MALLOC=libc
5、同樣在解壓后的目錄內進行安裝(安裝后會有一個bin目錄,目錄內就有redis-server檔案)
make PREFIX=/usr/local/redis install
6、啟動redis服務(默認不是后臺行程,所以當前視窗就會被占用,不能進行其他操作)
cd /usr/local/redis/bin
./redis-server
7、啟動redis客戶端(可以在xshell重開一個對話,啟動客戶端)
cd /usr/local/redis/bin
./redis-cli
啟動客戶端后檢測redis服務是否開啟:
redis 127.0.0.1:6379> PING(回傳PONG則表示ping通即服務已開啟)
4 配置
1、進入解壓的Redis目錄
cd /opt/redis-5.0.5
2、將redis.conf復制到安裝檔案的目錄下(與bin同級)
cp redis.conf /usr/local/redis
3、注意:要想讓組態檔生效,需要在啟動redis時帶上組態檔!!!
cd /usr/local/redis/bin
./bin/redis-server ./redis.conf
?
部分配置項
| 配置項 | 說明 |
|---|---|
| bind | 指定可以訪問Redis的主機IP,默認只能本機訪問Redis,如果要遠程訪問Redis,則需要將 bind 其注釋掉,或者系結地址==# bind 127.0.0.1== |
| daemonize | 指定是否為守護行程(后臺運行), 默認為no,即不是后臺運行,一運行就占用視窗,無法啟動其他程式,一般修改為 yes, |
| port | 指定埠號,默認為 6379 |
| requirepass | 設定密碼,默認沒有密碼 |
| pidfile | 指定piffile檔案位置,當Redis以守護行程運行時,會產生pid檔案默認把pid寫入到==/var/run/redis-6379.pid==檔案中, |
| timeout | 客戶端閑置多少(秒)后關閉連接, 默認為0,表示關閉此功能 |
| loglevel | 指定日志記錄級別,共四級:debug、verbose、notice、warning ,默認為notice |
| database | 設定資料庫數量,默認是16個,編號 0-15 |
| save | 指定多少時間、有多少次更新操作,就將資料持久化到dump.rdb檔案,這是RDB持久化方式的配置,redis默認配置有三個,滿足其中一個就會進行持久化: save 900 1 (900秒有1個更改) save 300 10 (300秒有10個更改) save 60 10000 (60秒有10000更改) |
| rdbcompression | 指定持久化是否壓縮資料,默認是yes, |
| dbfilename | 指定持久化后生成的檔案,默認是dump.rdb |
| dir | 指定上面檔案存放的路徑,默認是 ./ (當前目錄,與bin同級) |
| slaveof | 當本機設定為slave服務時,設定相應master服務的IP地址和埠號 ,redis啟動時,自動從master進行資料同步 |
| masterauth | 當master服務設定了密碼保護時,slave服務連接master的密碼 |
| maxclients | 設定同一時間最大客戶端連接數, 默認不限制, |
| maxmemory | 指定最大記憶體限制,(建議不要超過1G,在256-512M) 默認不限制, 單位是M |
5 啟動和關閉
5.1 啟動
(一)服務端啟動
[root@localhost ~]# cd /usr/local/redis
[root@localhost redis]# ./bin/redis-server ./redis.conf
(二)客戶端啟動
? ①本地訪問客戶端
[root@localhost redis]# ./bin/redis-cli -a [密碼(123456)]
默認: -h 127.0.0.1 -p 6379
? ②遠程訪問客戶端
[root@localhost redis]# ./bin/redis-cli -h [服務端IP] -p [埠號] -a [密碼]
5.2 關閉
- 正常關閉(通過客戶端,關閉服務端)
127.0.0.1:6379> shutdown [NOSAVE|SAVE]
- 非正常關閉(直接殺掉redis服務端行程)
查看當前與redis有關的行程: ps –ef | grep –i redis
得到redis-server的PID后,殺死相應的行程: kill -9 PID
6 基本資料型別和常用命令
key的命名建議
? 使用統一的命名模式,用冒號隔開,通過統一模式,來體現資料之間的關系
? 如:user:123:name 不用我解釋,也能看懂大概是什么
注意:
? 1、
key區分大小寫,命令不區分大小寫? 2、
基本資料型別針對的是value,key的保存都是通過字串形式保存的
6.1 公用命令
? 對所有型別資料都可以操作,或者說是資料庫層面的命令,
-
SELECT numb:切換資料庫(默認是0)
-
DEL key:洗掉key(回傳受影響的key數量)
-
DUMP key:序列化給定key(回傳被序列化的值)
-
EXISTS key:檢查key是否存在(存在回傳1,不存在回傳0)
-
EXPIRE key seconds:為key設定過期時間(秒)
- 沒有設定則默認為永久存在,除非delete掉
- 在驗證碼等有時效性的資料中常用
- PEXPIRE key milliseconds:功能相同,毫秒為單位
-
TTL key:回傳key剩余時間(秒)
-
- -1 表示永久有效
- -2表示已無效 / 不存在
- PTTL key:功能相同,毫秒為單位
-
PERSIST key:移除key的過期時間,key將持久保存
-
KEYS pattern:查詢所有符號給定模式的key
-
- pattern表示通配符:* 代表任意數量任意字符,? 代表任意一個字符
- KEYS * : 查詢所有的key
- KEYS *a: 查詢以a為結尾的key
- KEYS ?a*:查詢以任意一個字符拼接上a作為開頭的key
- KEYS ??: 查詢出兩個字符的key
- pattern表示通配符:* 代表任意數量任意字符,? 代表任意一個字符
-
RANDOMKEY:隨機回傳一個key
-
RANAME key newkey:修改key的名稱
-
MOVE key numb:移動key至numb序號所指定資料庫中(第一個庫為0)
-
FLUSHDB:清除當前資料庫的所有key
-
FLUSHALL:清除整個Redis所有資料庫的所有key
-
DBSIZE:查看當前資料庫的key的數量
-
TYPE key:回傳key所儲存的值的型別
6.2 基本資料型別
? Redis有5中基本資料型別:String、Hash、List、Set、ZSet
6.2.1 String
? String型別是二進制安全的,redis的String可以包含任何資料,如字串、影像音頻視頻的序列化物件(二進制),但是一個value最多能存盤512MB,
二進制安全是指,在傳輸資料的時候,能保證二進制資料的不會被篡改、破譯;如果被攻擊,能夠及時檢測出來
注意:String可以覆寫其他型別的資料,但是其他型別不能覆寫String型別的資料
? 如果一個key已經存在且是String型別,那么就不可以有其他型別使用賦值陳述句覆寫原來的key
? 如果一個key是其他型別的,可以用String型別的賦值陳述句將其覆寫成String型別的資料
使用場景
- 通常用于保存單個資料(字串或者數值)
- 也可用于保密要求高的圖片檔案,內容作為字串(二進制序列化)來存盤
命令
- SET key value [EX seconds|PX milliseconds] [ NX |XX ]:設定key-value
- EX表示過期時間設為幾秒,PX則是毫秒級
- NX → key不存在時才設定成功
- XX → key存在時才能設定成功(覆寫)
- 如果都不申明:不過期、key存不存在都可以設定
- SETNX key value:設定key-value
- 如果key已經存在,則不會執行任何操作(也就是只有不存在才設定)
- GET key:獲得key對應的value
- GETRANGE key start end:獲取key中字串的子字串,
- 下標從start開始,end結束(左閉右閉)(第一個字符的下標位0)
- SETRANGE key offset value:**置換(不是拼接)**指定的字串
- 從 key 對應的 value 中 第offset位 開始置換
- APPEND key value:字串拼接
- 追加至末尾,如果不存在,為其賦值
- MGET key1 [key2 …]:獲取多個key
- 每個key之間用空格隔開
- GETSET key value:設定key的值,并回傳key的舊值,
- 當key不存在,回傳nil
- 當key存在時,回傳舊值
- STRLEN key:回傳key所存盤的字串的長度
- INCR key: key對應的value(整數)的數字 +1
- 如果不存在key,則key中的值話先被初始化為 0 再加 1
- INCRBY key 增量:key 對應的value(整數)增加增量的大小
- 增量只能是整數
- INCRBYFLOAT key 增量:key 對應的value增加增量的大小
- value可以是整數,也可以是小數,增量同理
- DECR key: key中的值自 -1
- DECRBY key 減量:key 對應的value減少指定量的大小
- DECRBYFLOAT key 減量:同理
6.2.2 Hash
? 是一個String型別的 field-value 的映射表,hash特別適用于存盤物件(類似于Java中的bean,field為屬性,value為屬性值),每個Hash可以存盤 (2^32 - 1) 鍵值對,可以看成存放key-value對的HashMap容器,相比于JSON,hash占用很少的磁盤空間,
?
使用場景
? 常用來存盤一個物件的資料,Hash是最接近關系資料庫的資料型別,可以將資料庫的一條記錄或程式中的一個物件轉換成HashMap存放在Redis中,
為什么不用String存盤一個物件?/ 為什么Hash更適合存盤多屬性的物件?
如果使用String來存盤,主要有下面兩種方式:
1、將物件的主鍵作為key,其他的所有屬性封裝成JSON串,保存成value,
? 這種方式有下面幾個缺點:JSON串修改單個屬性需要將整個值取出來;增加了序列化/反序列化的開銷
? 2、物件的所有屬性都保存成一個個key-value,在key中進行物件的唯一性標識不同物件的不同屬性
? (物件型別:實體物件主鍵:屬性名) 這種方式的缺點很明顯:浪費記憶體
常用命令
-
HSET key field value:為設定的key設定 field 及其 value
-
HSETNX key field value:只有field不存在,才設定此欄位
-
HMSET key field value [field2 value2]:批量設定(空格隔開即可)
-
HGET key field:獲取Hash型別的key中對應的field的value
-
HMGET key field [field2]:批量獲取
-
HGETALL key:回傳hash表中key的所有field和value
-
HKEYS key:獲取hash表中key的所有field
-
HLEN key:獲取hash表中key對應的field的數量
-
HDEL key field [field2]:洗掉一個或多個hash表中key的field
- 注意:
洗掉時,如果key中所有的field都被洗掉,redis會自動清除這個key(redis不能存在沒有value的key,對于hash型別,其value就是field)
- 注意:
-
HESISTS key field:查看field是否存在
-
HINCRBY key field 增量:指定field(整數)增加增量
-
HINCRBYFLOAT key field 增量:指定field(整數或小數)增加增量
-
- 上面計數器的功能和效果,同String型別
6.2.3 List
? 類似于Java中LlinkedList,是簡單的字串串列,按照插入的順序排序,串列中的每個字串稱為元素(element),一個串列最多可以存盤2^32 -1個元素,
使用場景
- 保存 資料量大、型別一致的基本資料型別的集合
- 粉絲串列、留言評價、熱點新聞等
- Redis的
lpush + brpop命令組合即可實作阻塞佇列(不過一般不用redis作為訊息佇列)
常用命令
-
LPUSH key value1 [value2 …]:按順序從左側插入值
-
RPUSH key value1 [value2 …]:按順序從右側插入值
-
LPUSHX key value:從左側插入值,如果list不存在,則不操作
-
RPUSHX key value:從右側插入值,如果list不存在,則不操作
-
LLEN key:獲取串列長度(元素的個數)
-
LINDEX key index:獲取指定索引的元素
-
LRANGE key start stop:獲取串列指定范圍的元素
- LRANGE key 0 -1:表示獲取list的全部元素
- LRANGE key 1 1:表示獲取下標為1的一個元素
- -1 表示最后一個元素 -2 表示倒數第二個元素 以此類推
-
LPOP key:從左側移除一個元素
-
RPOP key:從右側移除一個元素
- 同樣的,
list內一定要有元素,如果所有元素都移除,則redis也會自動洗掉這個key
- 同樣的,
-
BLPOP key [key2 …] timeout:從左側移除并獲取key串列一個元素
- 可以多個串列一起
- 如果串列不存在,會阻塞到等待超時(秒)或發現可彈出元素為止
-
BRPOP key [key2 …] timeout:功能同上,只不過是從右側
-
LTRIM key start stop :對串列進行修剪,保留指定區間的元素
-
LSET key index value : 修改指定索引的值
-
LINSERT key before|after element value:在串列key的element元素前或則后插入元素value
6.2.4 Set
? 是String型別的無序、不可重復的集合,(唯一性、無序性是與List型別的區別)
使用場景
? 常用于兩個集合之間資料進行交、差、并運算
- 方便實作如共同關注、共同喜好等功能
- 利用唯一性,可以統計訪問網站的所有獨立 IP等
常見命令
- SADD key value1 [value2]:向key添加元素
- SCARD key:回傳集合元素數
- SMEMBERS key:回傳集合中所有元素
- SISMEMBER key element:判斷element元素是否在集合 key 中
- SRANDMEMBER key [count]:回傳集合中一個或多個(count)亂數
- SREM key element1 [element2]:移除集合中一個或多個元素
- SPOP key:移除并回傳集合中隨機一個元素
- SMOVE source destination element:將element元素從source集合移動到destination集合
- SINTER key [key …] : 回傳多個集合求交集
- SUNION key [key …] :回傳多個集合求并集(不會出現重復資料)
- SDIFF key [key …]: 回傳多個集合的差集
- SINTERSTORE destination key [key …]: 回傳給定所有key集合的交集并存盤在destination中
- SUNIONSTORE destination key [key …]: 回傳給定所有key集合的并集并存盤在destination中
- SDIFFSTORE destination key [key …]: 回傳給定所有key集合的差集并存盤在destination中
6.2.5 ZSet
? 不能重復、可以排序的String型別元素的集合,每個元素都會關聯一個double型別的分數(score),Redis通過分數進行從小到大的排序,(元素唯一,但是分數可以重復)
使用場景
? 排行榜
常用命令
-
ZADD key score1 memeber1 [score2 member2 ]:設定分數和元素
-
ZCARD key :獲取集合中的元素數量
-
ZCOUNT key min max: 計算指定區間分數的元素數量
-
ZRANK key member:回傳指定元素的索引(下標值)
-
ZSCORE key element:獲得指定元素的分數
-
ZINCRBY key 增量 element:為指定元素的分數增加增量
-
ZRANGE key start stop:回傳索引區間內的元素(分數從低到高排序)
-
ZREVRANGE key start stop :回傳索引區間內的元素(分數從高到低排序)
-
ZRANGEBYSCORE key min max:回傳分數區間內的元素
-
ZREM key member [member …]: 移除有序集合中的指定元素
-
ZREMRANGEBYRANK key start stop: 移除索引區間的所有元素
-
ZREMRANGEBYSCORE key min max: 移除分數區間的所有元素
7 持久化
? Redis有兩種持久化的機制:RDB 和 AOF,默認是RDB,
? 若兩種方式同時開啟,則重啟時以AOF的檔案恢復原始的Redis資料庫,(建議兩種都開啟)
7.1 RDB
? 這是Redis默認的持久化機制,是 定時快照 的方式,保存的是一種狀態,這種方式就是將記憶體中的資料以快照的方式寫入到二進制檔案中,默認檔案名為dump.rdb,(幾十G的資料 → 幾KB的快照檔案)
? 優點
-
保存速度、還原速度極快,適合大規模的資料恢復
-
適用于災難備份
缺點
-
小記憶體的機器不符合使用(快照的時候,redis消耗增加了一倍的記憶體)
-
有可能丟失最后一份修改
配置
配置是在組態檔中的save部分進行相關的配置(何時進行快照)
#redis默認配置有三個,滿足其中一個即可
save 900 1 #900秒有1個更改
save 300 10 #300秒有10個更改
save 60 10000 #60秒有10000更改
# 如果資料很重要,又不能等到時間到讓redis自動持久化,則可以使用命令來保存
save:只管保存,其他不管,全部阻塞
bgsave:后臺異步保存,快照的同時還可以相應客戶端請求
# (可以通過lastsave查看最后一次成功執行快照的時間)
7.2 AOF
? 由于RDB方式是在一定時間間隔做的,如果在間隔期間,修改了資料而redis斷電等意外導致服務端shutdown,就會丟失最后一次修改,如果要求不能丟失任何修改的話,就可以采用AOF的方式,
? 使用AOF,Redis會將每一個收到的寫命令都通過write函式 追加到檔案 中,默認是appendonly.aof,當redis重啟時會通過重新執行檔案中命令,重建整個資料庫內容,(保存的不是狀態,而是命令)
? 優點
-
可以保證修改資料不丟失
缺點
-
持久化檔案會越來越大,且檔案中可能存在許多多余的命令
-
比如不斷對統一資料的修改命令都會加入到檔案中,所有命令都會執行,才能得到最后真正有用的一個資料,
-
aof檔案遠大于rdb檔案,恢復速度較慢
? 配置
# 首先要開啟AOF:
appendonly yes
# 可以指定檔案:
appendfilename "appendonly.aof"
# 有三種AOF方式:
appendfsync always # 收到寫命令就立即寫入磁盤
appendfsync everysec # 每秒寫入磁盤一次(默認)
appendfsync no # 完全依賴OS,redis不主動進行AOF
重寫機制(REWRITE)
? 由于AOF采取檔案追加的方式,檔案會越來越大,為避免檔案過大的情況,新增了重寫機制:當檔案大小超過一定閾值的時候,Redis會啟動AOF檔案的內容壓縮,只保留可以恢復資料的最小指令集,
? 重寫原理和快照相似,是 fork一個新行程 ,將整個記憶體中的資料用命令的方式重寫了一個新的AOF檔案,并洗掉舊的AOF檔案,注意沒有讀取舊的AOF檔案,
? 觸發情景:Redis會記錄上次重寫時AOF的大小,默認配置是當AOF檔案大小是上次rewrite后大小的一倍且檔案大于64M時觸發,
auto-aof-rewrite-percenttage 100 # 重寫資料所占的百分比
auto-aof-rewrite-min-size 64mb # 重寫檔案最小大小
no-appendfsync-on-rewrite no # 重寫時是否可以運用Appendfsync,默認為no,保證資料安全
8 事務
? Redis事務可以一次執行多個命令,且有下面兩個保證:
- Redis會將一個事務中的所有命令序列化,然后按順序執行(串行執行)
- 執行中不會被其他命令插入,即不允許加塞行為
相關命令
- MULTI:標記一個事務塊的開始
- EXEC:執行上面事務塊內的命令
- DISCARD:取消事務,放棄事務塊中的命令
- WATCH key1 [ key2 ]:監視key,如果在事務執行前,這些key被其他命令改動,則打斷此事務,
- 有點像
樂觀鎖機制,watch相當于確定版本號,只要前后兩次所監視的key不同,則打斷 - 一旦執行了EXEC或DISCARD或UNWATCH,之前所加的watch監控鎖都會取消掉
- 有點像
- UNWATCH:取消對所有key的監視
正常事務命令塊例子
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> GET account:a
QUEUED
127.0.0.1:6379> GET account:b
QUEUED
127.0.0.1:6379> DECRBY account:a 50
QUEUED
127.0.0.1:6379> INCRBY account:b 50
QUEUED
127.0.0.1:6379> EXEC
1) "80"
2) "10"
3) (integer) 30
4) (integer) 60
事務的錯誤處理
-
如果是邏輯上的錯誤(比如:對一個字串“hello”執行加一命令),那么此事務塊中,只有這個報錯的命令不執行,其他的命令,無論在其前或后,只要是邏輯正確的,都會執行,
-
如果是語法上的錯誤(即命令語法不正確,也稱報告錯誤),那么此事務塊到最后執行exec時,會自動取消此事務,
-
- 在事務塊中,提交了一個錯誤的命令,會立即列印出報告錯誤,但是如果無視,依舊可以繼續在事務塊中添加其他命令,但是到最后執行exec也會取消此事務,所以一旦出現報告錯誤,則可以使用discard中斷事務,
所以說:Redis的事務,并不是真正的事務,是偽事務
9 主從復制 & 哨兵模式 & 集群
單臺Redis服務機有下面幾個缺點:
- 容易發生單點故障,不具備容錯性
- 一臺服務器負責所有的請求負載,壓力較大
- 且單臺服務器容量有限,一臺Redis服務器記憶體最好不要超過20G
如果使用多臺機器來保存Redis資料,不就可以解決上面問題,保證系統的高可用性了嗎?而這種方式,就需要進行機器之間的資料同步問題,這就是接下來要討論的,
9.1 主從復制
? 一個Redis服務可以有多個該服務的復制,這個Redis服務稱為Master,其他復制稱為Slave,因為大部分的應用都是“讀多寫少”,故:
Master負責處理寫請求(可寫可讀)Slave負責處理讀請求(只讀不可寫)
主機(Master)資料更新后,根據配置和策略,自動同步到備機(Slave)中,
- Slave啟動成功連接到Master后,會發送一個sync命令,進行全量復制(Master所有的資料進行同步)
- 之后的Master寫資料,slave進行的是增量復制(新增的部分進行同步)
? 要點:
- 一個Slave只能有一個Master
- 一個Redis可以既是Master,也是Slave
- 主從復制的時候不會阻塞Master,是異步的
? 配置實作
? 配從不配主,有下面三種方式可以變成從機:
- 在服務端登錄時,帶上
-slaveof、-p兩個屬性
注意是服務端登錄帶上上面兩個屬性,客戶端登錄只需帶上6380埠
(因為這里假設6379為主機的埠,從機的埠為6380)
//服務端登錄
./bin/redis-server ./redis6380-conf -p 6380 -slaveof MasterIP Master埠
//客戶端登錄
./bin/redis-cli -p 6380 -a 123456
- 在客戶端已經登錄的情況下,使用slaveof命令,可以變成從機
127.0.0.1:6380> slaveof MasterIP Master埠
上面兩種在與Master斷開連接(即從機shutdown等)時,需要重新進行salve連接,如果想要永久連接,則需要下面這種方式,將相關屬性配置在組態檔中
- 復制一份conf檔案作為從機的組態檔,從機配置開啟下面配置
# 在redis-6380.conf中配置
slaveof 127.0.0.1 6379
port 6380
# 還有其他類似 log檔案名、pid檔案名、dump檔案名 都進行修改
# 注意:這里修改是為了在一臺機器上模擬成多臺機器運行,實際上都是在不同機器上的redis中實作主從的,所以一般只需要配置slaveof即可,
# 服務端啟動
./bin/redis-server ./redis6380-conf
? 若要將從機變為獨立的主機Master,則執行命令slaveof on one即可,
9.2 哨兵模式(Sentinel)
? 主從復制弊端就是不具備高可用性,一旦Master掛了,Redis就不能提供寫操作處理,所以就出現了哨兵模式(若Master掛了自動從slave機中選出新的Master機)
? 哨兵模式是Redis高可用的一種解決方案,當Master主機遇到例外中斷服務后,Redis能夠在后臺自動檢測到主機是否故障,如果故障了就根據投票數自動將一個Slave從機轉換成新Master機,并發送訊息通知管理員,無需人工操作,實作高可用性,
? 每秒每個哨兵會向整個集群:Master主庫、Slave從庫、其他Sentinel(哨兵)行程,發送一次ping命令做一次心跳檢測,這個就是哨兵用來判斷節點是否正常的重要依據,涉及兩概念:
- 主觀下線:一個哨兵節點判定主機掛掉了
- 客觀下線:多個哨兵交換主觀判定結果,
只有半數以上的哨兵都認為主機掛了,才會判定主機下線,
? 基本上哪個哨兵節點最先判斷出這個主機客觀下線,就會在各個哨兵節點中發起投票機制Raft演算法(選舉演算法),最終被投為新Master機的哨兵節點完成主從自動化切換,
? 注意
? 如果原來掛了的Master在哨兵決定了新Master后,又恢復正常回來了,則這個舊Master機則會作為Slave機,掛在新的Master后,
? 和主從復制中手動修改新Master機不同,主從復制中手動修改,舊Master回來后,和其他的主從關系機無關系,是一個新的獨立的Master主機,
9.3 集群(Redis Cluster)
? 哨兵模式基本可以滿足一般生產的需求,具備高可用性,但是當資料量過大到一臺服務器存放不下的情況時,主從模式或sentinel模式就不能滿足需求了,這個時候需要對存盤的資料進行分片,將資料存盤到多個Redis實體中,
? 集群可以說是哨兵和主從模式的結合體,集群模式就是為了解決單機Redis容量有限的問題,將Redis的資料根據一定的規則分配到多臺機器,
cluster集群特點
-
所有redis節點網路互聯,資料共享
-
都是
一主N從,其中從庫不提供服務,僅作為備用 -
集群至少要三個主庫才能正常運行,即一個集群最少需要有6個Redis節點(三組,每組一主一從)
-
不支持同時處理多個key,redis把key均勻分布在各個節點上,分開保存
-
客戶端可以連接任何一個主節點進行讀寫,支持在線增加、洗掉節點
-
集群是去中心化的,每個Master節點都是平等的,都可以獲取和設定資料,而slave節點不提供服務,只是對應Master的一個備份,
-
在其中一個Master服務器上登錄的客戶端,寫入的資料會根據一定的演算法,將其存入到計算得到的 槽(slot) 中,由于槽平均分配給不同的Master,所以有可能在AMaster設定的資料,會保存在其他的BMaster服務器中,同時BSlave服務器也會同步備份,(這也是去中心化的部分體現)
真集群:每個Redis服務端都在不同的機器上(IP不同,port可能同)
假集群:在同一個機器上,通過不同的埠,模擬多個Redis服務端(IP同,port不同)
開啟集群(這部分留坑,之后深入使用后再填坑)
- 需要將redis組態檔中的cluster-enable配置打開,配置為yes
- 在redis沒有設定密碼并且bind IP注釋掉的情況下,redis會處于安全保護模式,禁止公網訪問redis cache,為了正常訪問需要取消保護模式:protected-mode no
10 快取常見問題 & 解決
10.1 快取穿透
? 當查詢一個資料庫不存在的資料,這個資料默認也不會寫入到快取Redis中,所以在高并發下,大量的用戶查詢此不存在的資料,會導致每次查詢都是直接訪問資料庫,穿透快取中間件,導致資料庫壓力極大,這就是快取穿透,
解決
- 當查詢一個不存在的資料時,在Redis快取中間件中寫于相對應key的空字串,表示空結果,這樣子之后繼續查詢此資料時,就會在快取中找到對應的key回傳空字串,避免再次穿透到資料庫,
- 進行引數過濾(對于大區間可以進行過濾,但是對于區間內的部分小無效的間隙,無法細致地過濾)
- 比如引數在1-100中有效,其中的3、8號無效,區間過濾只能過濾掉<1 、>100部分無效引數,
- 布隆過濾器(判斷所查資料是否存在資料庫中)
10.2 快取擊穿
? 在高并發下,對一個特定的值進行查詢,但是這個時候快取正好過期了,導致大量請求直接落到資料庫上,
解決
- 重要的快取資料不設定過期,若資料需要更新,就再同步更新快取,
- 或者限流,減低直接查詢資料庫的請求數量,查詢到后會快取在redis中,之后的請求就不會直接查詢資料庫了,因為在redis的時候就命中了,
- 甚至直接使用鎖(簡單利用redis的setnx以及Java的sleep即可),鎖住一小段時間,保證只有一個請求達到資料庫拿到資料后快取到redis中,sleep時間過后,大量的請求再去redis中拿,就不會打到資料庫上了,(如果請求量很多,個人感覺比限流好)
區分:
? 快取穿透:查詢的資料是資料庫不存在的資料
? 快取擊穿:查詢的資料是資料庫存在的資料,只是在快取中剛好過期了
10.3 快取雪崩
? 大量快取在同一時間內失效,導致大量的查詢直接訪問資料庫,即發生大量的快取擊穿,造成快取雪崩,
解決
- 讓快取的失效時間分布均勻(加上個隨機值,不要同一時間失效),然后就是處理快取擊穿的操作了,
10.4 快取預熱
? 在專案產品上線前,快取暫時為空,如果直接上線,同樣會導致大量的查詢直接訪問資料庫,所以在上線之前,可以先進行主要的查詢,將熱點資料加入到快取中在上線,這就是快取預熱,
11 其他面試題
部分參考敖丙大佬的:https://blog.csdn.net/qq_35190492/article/details/103041932
1、快取型別
-
本地快取:保存在本地,只有本地可以訪問,服務重啟就沒了,
-
分布式快取:保存在快取中間件,各服務都可以訪問,可以持久化,
2、redis的淘汰策略
- noeviction: 不洗掉策略, 達到最大記憶體限制時, 直接回傳錯誤資訊,
- allkeys-lru: 所有key通用, 優先洗掉最近最少使用的 key,
- volatile-lru: 只限于設定了 expire 的部分, 優先洗掉最近最少使用的 key,
- allkeys-random: 所有key通用;,隨機洗掉一部分 key,
- volatile-random: 只限于設定了 expire 的部分,隨機洗掉一部分 key,
- volatile-ttl: 只限于設定了 expire 的部分,優先洗掉剩余時間短的key,
3、Redis為什么是單執行緒的?單執行緒還那么快?
? Redis 核心就是 如果我的資料全都在記憶體里,我單執行緒的去操作 就是效率最高的,為什么呢,因為多執行緒的本質就是 CPU 模擬出來多個執行緒的情況,這種模擬出來的情況就有一個代價,就是背景關系的切換,對于一個記憶體的系統來說,它沒有背景關系的切換就是效率最高的,**redis 用 單個CPU 系結一塊記憶體的資料,然后次讀寫的時候針對這塊記憶體的資料進行多,都是在一個CPU上完成的,所以它是單執行緒處理這個事,**在記憶體的情況下,這個方案就是最佳方案 —— 阿里 沈詢
4、Redis為什么很快
- 是基于記憶體的單執行緒
- IO多路復用
- 底層的設計結構
5、RDB的原理是什么?
? 首先redis通過fork子行程來進行RDB檔案的寫入操作,然后進行copy on write,子行程創建后,父子行程共享資料段,父行程繼續提供讀寫服務,子行程負責寫RDB,隨者子行程的RDB寫入,父子行程共享的資料段會不斷減少,即寫臟的頁面資料會逐漸和子行程分離開來,(和拉鏈一樣,還沒拉開的就共享,拉開了的就分離了,如果父行程在已分離的區域有寫了資料,是不會寫入到本次RDB檔案中的)
6、除了5種基本資料型別,還有沒有其他的?
? pipeline、pub/sub、HyperLogLog等
Redis的同步機制了解么?
? slave 剛啟動時,會發送一個psync命令給master ,如果是這個slave第一次連接到master,他會觸發一個**全量復制:master就會啟動一個執行緒,生成RDB快照,還會把新的寫請求都快取在記憶體中,RDB檔案生成后,master會將這個RDB發送給slave的,slave拿到之后做的第一件事情就是寫進本地的磁盤,然后加載進記憶體,而在RDB快照之后新增的資料,則是通過AOF進行增量復制**保證同步的,
7、多個系統同時操作(并發)Redis帶來的資料問題?
? Redis是單執行緒的,并沒有鎖機制,各個客戶端之間的事務并不隔離,所以需要同步的話,要么就利用外部的分布式鎖來保證,要么就采取非鎖的方式來保證,
- 加上分布式鎖,可以利用zookeeper實作分布式鎖,
- 非鎖方式,那就是CAS,成本低、非阻塞、并發性能高,這個也是Redis提供的一個機制:watch,在redis事務中可以監聽某些key,如果key在這個事務未提交期間被其他客戶端修改過了,則本事務不會提交,會執行回滾,
8、如何保證快取與資料庫的雙寫一致性?
? 如果是嚴格要求資料一致性的,那么就是只能將讀請求和寫請求串行化了,資料絕對一致性,但是并發性嚴重拉跨,
? 如果不是嚴格要求資料一致性的,那么最經典的快取+資料庫讀寫的模式,就是 Cache Aside Pattern,
-
讀的時候,先讀快取,快取沒有的話,就讀資料庫,然后取出資料后放入快取,同時回傳回應,
-
更新的時候,先更新資料庫,然后再洗掉快取/更新快取,
-
- 洗掉快取還是更新快取,主要看是不是熱點資料,如果不是熱點資料,那么直接洗掉掉,等到之后有人訪問再存入快取即可,不然直接更新快取的話,這個資料又是經常修改但是不被經常用到,也就白白更新了,
- 注意這樣同樣也會出現資料不一致的情況,就比如更新資料庫后洗掉快取失敗,那么資料庫是新資料,快取是舊資料,不一致了,一般可以采用
延時雙刪,也就是多次洗掉確保洗掉成功,
9、為什么要用redis做快取
? 首先,為什么要快取?
- 如果沒有快取,請求會頻繁地達到資料庫上,對資料庫的壓力較大,使用了快取,相當于在資料庫前加了一層屏障,不僅可以降低資料庫的壓力,還可以較快地回傳擊中快取的資料,
? 其次,為什么要用redis做快取?
- redis是分布式快取,可以獨立部署,可以持久化,
- redis是基于記憶體、單執行緒、多路復用的快取,速度快,效率高,
10、redis實作分布式鎖
? 先拿setnx來爭搶鎖,搶到之后,再用expire給鎖加一個過期時間防止鎖忘記了釋放,最后如果是手動釋放鎖的話,使用delete,
? 而為了避免出現setnx之后執行expire之前行程意外crash或者要重啟維護了,導致鎖釋放不遼,可以直接在setnx命名后面帶上過期時間(即將setnx和expire結合成一條命令)
11、如果redis正在給線上的業務提供服務,那使用keys指令模糊查找會有什么問題?
? Redis的單執行緒的,keys指令會導致執行緒阻塞一段時間,線上服務會停頓,直到指令執行完畢,服務才能恢復,
? 這個時候可以使用scan指令,scan指令可以無阻塞的提取出指定模式的key串列,但是會有一定的重復概率,在客戶端做一次去重就可以了,但是整體所花費的時間會比直接用keys指令長,不過在scan程序中,鍵可能會被修改, 所以增量式迭代命令只能對被回傳的元素提供有限的保證 ,
12、使用過Redis做異步佇列么,你是怎么用的?
一般使用list結構作為佇列,rpush生產訊息,lpop消費訊息(或者相反也可以),當lpop沒有訊息的時候,要適當sleep一會再重試,
如果不用sleep,可以使用的是blpop,在沒有訊息的時候,它會阻塞住直到訊息到來,
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/168934.html
標籤:其他
上一篇:從歷史資料看阿里云和華為云?
下一篇:Centos7離線安裝MySQL
