中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

手機(jī)企業(yè)網(wǎng)站制作企業(yè)網(wǎng)頁設(shè)計(jì)公司

手機(jī)企業(yè)網(wǎng)站制作,企業(yè)網(wǎng)頁設(shè)計(jì)公司,搜索引擎優(yōu)化搜索優(yōu)化,網(wǎng)站客服圖標(biāo)目錄 Redis 是單線程的!為什么 Redis-Key(操作redis的key命令) String 擴(kuò)展字符串操作命令 數(shù)字增長命令 字符串范圍range命令 設(shè)置過期時(shí)間命令 批量設(shè)置值 string設(shè)置對象,但最好使用hash來存儲對象 組合命令getset,先get然后在set Hash hash命令: h…

目錄

Redis 是單線程的!為什么

Redis-Key(操作redis的key命令)

String

擴(kuò)展字符串操作命令

數(shù)字增長命令

字符串范圍range命令

設(shè)置過期時(shí)間命令

批量設(shè)置值

string設(shè)置對象,但最好使用hash來存儲對象

組合命令getset,先get然后在set

Hash

hash命令:

hash基礎(chǔ)的增刪查

hash擴(kuò)展命令

總結(jié)

List

Set

ZSet

Redis事務(wù)

redis的事務(wù)命令:

正常執(zhí)行事務(wù) exec

放棄事務(wù) discard

命令異常,事務(wù)回滾

1.類似于java編譯型異常(語法錯(cuò)誤)

2.類似于java運(yùn)行時(shí)異常(如1/0,除數(shù)不能為0錯(cuò)誤) (邏輯錯(cuò)誤)

監(jiān)控 Watch(相當(dāng)于java加鎖) (面試: redis的watch命令監(jiān)控實(shí)現(xiàn)秒殺系統(tǒng))

悲觀鎖

樂觀鎖

Redis用watch做樂觀鎖實(shí)現(xiàn)步驟

redis監(jiān)視watch測試案例

取錢正常執(zhí)行成功的流程

取錢出現(xiàn)并發(fā)問題的流程

Redis的Java客戶端

測試jedis

Springboot整合Redis

自定義RedisTemplate

緩存

緩存穿透

解決方案一:緩存空數(shù)據(jù)

解決方案二:布隆過濾器

緩存擊穿

解決方案一:互斥鎖(分布式鎖)

解決方案二:邏輯過期

緩存雪崩

持久化

1.RDB

RDB執(zhí)行原理

2.AOF

RDB與AOF對比

雙寫一致性

1.一致性要求高

1.延遲雙刪

1.無論第一步是先刪除緩存還是先修改數(shù)據(jù)庫都會導(dǎo)致臟數(shù)據(jù)的出現(xiàn)

2.刪除兩次緩存的原因

3.延時(shí)刪除的原因

總結(jié)

2.加讀寫鎖

2.允許延遲一致(較為主流)

1.異步通知保證數(shù)據(jù)的最終一致性

2.基于Canal的異步通知

數(shù)據(jù)過期策略

方案一惰性刪除:

方案二定期刪除:

總結(jié)

數(shù)據(jù)淘汰策略

八種不同策略

數(shù)據(jù)淘汰策略--使用建議

分布式鎖

場景

引入與基本介紹

redis分布式鎖實(shí)現(xiàn)原理

基本介紹

設(shè)置超時(shí)失效時(shí)間的原因(避免死鎖):

總結(jié)

缺陷

Redisson實(shí)現(xiàn)的分布式鎖

1.redisson實(shí)現(xiàn)的分布式鎖的執(zhí)行流程/合理地控制鎖的有效時(shí)長(失效時(shí)間)

2.可重入

3.主從一致性

Redis發(fā)布訂閱

訂閱/發(fā)布消息圖

命令

測試

Redis發(fā)布訂閱原理

總結(jié)

使用場景

Redis消息隊(duì)列

概念

基于List結(jié)構(gòu)模擬消息隊(duì)列(可實(shí)現(xiàn)阻塞隊(duì)列的效果)

基于PubSub(發(fā)布訂閱)的消息隊(duì)列

基于Stream的消息隊(duì)列

基本知識

消費(fèi)者組

Redis集群(分布式緩存)

單點(diǎn)Redis的問題

主從復(fù)制

主從數(shù)據(jù)同步原理

1.主從全量同步

2.主從增量同步(slave重啟或后期數(shù)據(jù)變化)

3.總結(jié)

哨兵模式

哨兵模式的結(jié)構(gòu)與作用

服務(wù)狀態(tài)監(jiān)控

哨兵選主規(guī)則(主節(jié)點(diǎn)宕機(jī)后,選從節(jié)點(diǎn)為主節(jié)點(diǎn)的規(guī)則)

Redis集群(哨兵模式)的腦裂問題

分片集群

redis集群環(huán)境部署(環(huán)境配置)

一主二從集群搭建(命令或文件配置)(這種方式的redis集群實(shí)際工作用不到,僅供基礎(chǔ)學(xué)習(xí))

命令方式配置

文件方式配置(一主二從,持久化的,對于哨兵模式,不建議使用這種)

一主兩從的第二種搭建方式(層層鏈路)哨兵模式的手動版

I/O多路復(fù)用模型

redis為什么這么快

用戶空間和內(nèi)核空間

阻塞IO

非阻塞IO

IO多路復(fù)用

Redis網(wǎng)絡(luò)模型


Redis 是單線程的!為什么

Redis是基于內(nèi)存實(shí)現(xiàn)的,使用Redis時(shí),CPU幾乎不會成為Redis性能瓶頸,Redis的瓶頸是機(jī)器的內(nèi)存網(wǎng)絡(luò)帶寬(網(wǎng)絡(luò)),既然可以使用單線程來實(shí)現(xiàn),就使用單線程了!所有就使用了單線程了!

內(nèi)存訪問速度:由于Redis將數(shù)據(jù)存儲在內(nèi)存中,數(shù)據(jù)訪問速度非???/strong>,通常接近于CPU的緩存訪問速度。這意味著CPU在讀取或?qū)懭霐?shù)據(jù)時(shí)很少需要等待,從而減少了CPU的空閑時(shí)間。

計(jì)算密集度:Redis的操作通常是簡單的數(shù)據(jù)查找、插入、刪除和計(jì)算集合操作(如交集、并集等)。這些操作在CPU層面上的計(jì)算復(fù)雜度相對較低,因此不太可能使CPU成為瓶頸。

并發(fā)處理:Redis使用單線程模型來處理客戶端請求(盡管有IO多路復(fù)用技術(shù)來同時(shí)處理多個(gè)連接),但這并不意味著Redis不能利用多核CPU。通過部署多個(gè)Redis實(shí)例或使用Redis集群,可以水平擴(kuò)展以利用多核CPU的并行處理能力。

Redis 是C 語言寫的,官方提供的數(shù)據(jù)為 100000+ 的QPS,完全不比同樣是使用 key-vale的Memecache差!

Redis 為什么單線程還這么快?

1、高性能的服務(wù)器不一定是多線程的

2、多線程不一定比單線程效率高!(多線程CPU上下文會切換消耗資源!)

速度:CPU>內(nèi)存>硬盤

核心:redis 是將所有的數(shù)據(jù)全部放在內(nèi)存中的,所以說使用單線程去操作效率就是最高的,多線程(CPU上下文會切換:耗時(shí)的操作!

對于內(nèi)存系統(tǒng)來說,如果沒有上下文切換效率就是最高的!

多次讀寫都是在一個(gè)CPU上的,在內(nèi)存情況下,這個(gè)就是最佳的方案!

Redis-Key(操作redis的key命令)

keys * # 查看所有的key?

set name123 kuangshen # set key

EXISTS name123 # 判斷當(dāng)前的key是否存在

move name123 # 移除當(dāng)前的key

EXPIRE name123 10 # 設(shè)置key的過期時(shí)間,單位是秒

ttl name123 # 查看當(dāng)前key的剩余時(shí)間

type name123? ??# 查看當(dāng)前key的一個(gè)類型

String

擴(kuò)展字符串操作命令
  • APPEND key123 "hello" # 追加字符串,如果當(dāng)前key不存在,就相當(dāng)于setkey?
  • STRLEN key123 # 獲取字符串的長度!
數(shù)字增長命令
  • set views123 0 # 初始瀏覽量為0?
  • incr views123 # 自增1 瀏覽量變?yōu)??
  • decr views123 # 自減1 瀏覽量-1?
  • INCRBY views123 10 # 可以設(shè)置步長,指定增量10!
字符串范圍range命令
  • GETRANGE key123 0 3 # 截取字符串 [0,3]
  • GETRANGE key123 0 -1 # 獲取全部的字符串 和 get key是一樣的
  • SETRANGE key123 1 xx # 替換指定位置1開始的字符串!
設(shè)置過期時(shí)間命令

setex (set with expire) //設(shè)置過期時(shí)間

例:setex key123 30 "hello" //設(shè)置key123 的值為hello,30秒后過期

setnx (set if not exist) //key不存在在設(shè)置,key存在則回滾設(shè)置失敗(在分布式鎖中會常常使用)?

例:setnx mykey123 "MongoDB" //如果mykey不存在創(chuàng)建成功,存在,創(chuàng)建失敗不會替換值

批量設(shè)置值
  • ?mset k1 v1 k2 v2 k3 v3 # 同時(shí)設(shè)置多個(gè)key和value值(k1:v1, k2:v2, k3:v3)
  • mget k1 k2 k3 # 根據(jù)多個(gè)key同時(shí)獲取多個(gè)值
  • msetnx k1 v1 k4 v4 # msetnx 是一個(gè)原子性的操作,要么一起成功,要么一起失敗!
    • 由于k1已經(jīng)存在,所以setnx一定會失敗,由于是原子性操作k4也會跟著失敗
string設(shè)置對象,但最好使用hash來存儲對象
  • set user:1 {name:zhangsan,age:3} # 設(shè)置一個(gè)key為user:1的對象,值為 json字符來保存一個(gè)對象!?
    • key值為user:{id}, value值為json
  • mset user:1:name zhangsan user:1:age 2 #批量創(chuàng)建對象
    • 這里的key是一個(gè)巧妙的設(shè)計(jì): user:{id}:{filed} , 如此設(shè)計(jì)在Redis中是完全OK了!
  • mget user:1:name user:1:age #批量獲取對象中的值
組合命令getset,先get然后在set
  • getset db redis # 如果不存在值,則返回 nil?,但同時(shí)值被設(shè)置成了redis
  • getset db mongodb # 如果存在值,獲取原來的值,并設(shè)置新的值

Hash

相當(dāng)于Map集合,key-value!,只是value存的是map,也就是key-map,值是map集合

  • hash本質(zhì)和String類型沒有太大區(qū)別,還是一個(gè)簡單的key-value
  • hset myhash field codeyuaiiao
    • 命令含義:hset key mapkey mapvalue

hash命令:

hash基礎(chǔ)的增刪查
  • hset myhash field1 yuaiiao # 添加或修改一個(gè)具體的值
  • hget myhash field1 # 獲取一個(gè)字段值
  • hmset myhash field4 hello field5 byebye # 添加多個(gè)字段值進(jìn)map集合
  • hmget myhash field3 field4 # 獲取多個(gè)指定字段值
  • hgetall myhash # 獲取hash全部字段值(包含了key和value)
  • hdel myhash field1 field2 # 刪除一個(gè)或多個(gè)指定字段值
hash擴(kuò)展命令
  • hlen myhash # 獲取hash表的字段數(shù)量
  • hexists myhash field1 # 判斷hash表中指定字段是否存在
  • hkeys myhash # 獲取所有field(相當(dāng)于key)
  • hvals myhash # 獲取所有value
  • hincrby myhash field3 2 # 指定增量
  • hincrby myhash field3 -2 # 指定減量
  • hsetnx myhash field4 yuaiiao # 如果不存在可以設(shè)置,如果存在則不可設(shè)置

總結(jié)

hash變更的用戶數(shù)據(jù)user表,name,age字段 ,尤其是用戶信息之類的,經(jīng)常變動的信息!

hash更適合于對象的存儲,String更加適合字符串存儲

List

Set

Redis的Set結(jié)構(gòu)與java中的HashSet類似,可以看做是一個(gè)value為null的HashMap.因?yàn)橐彩且粋€(gè)hash表,因此具備與hashset類似的特征:
無序
元素不可重復(fù)
查找快
支持交集,并集,差集等功能

ZSet

Redis事務(wù)

mysql事務(wù)本質(zhì)

  • ACID特性,要么同時(shí)成功,要么同時(shí)失敗,保證原子性!

Redis事務(wù)本質(zhì)

  • 一組命令的集合! 一個(gè)事務(wù)中的所有命令都會被序列化,在事務(wù)執(zhí)行過程中,會按照順序執(zhí)行!
  • 一次性,順序性,排他性! 執(zhí)行一系列的命令!
------隊(duì)列 set get set 執(zhí)行-----
  • Redis 事務(wù)沒有隔離級別的概念!
    • 不會出現(xiàn)幻度,臟讀,不可重復(fù)讀等問題
  • 所有的命令在事務(wù)中,并沒有直接被執(zhí)行,只有發(fā)起執(zhí)行命令的時(shí)候才會執(zhí)行! Exec
  • Redis單條命令是保證原子性的,但是redis事務(wù)是一組命令的集合,所以不保證原子性!

redis的事務(wù)命令:

  • 開啟事務(wù)(multi)
  • 命令入隊(duì)(要執(zhí)行的命令) (事務(wù)隊(duì)列)
  • 執(zhí)行事務(wù)(exec)

正常執(zhí)行事務(wù) exec

127.0.0.1:6379> multi # 開啟事務(wù)
OK
127.0.0.1:6379> set key hello # 執(zhí)行命令
QUEUED
127.0.0.1:6379> set key1 yuaiiao
QUEUED
127.0.0.1:6379> get key
QUEUED
127.0.0.1:6379> get key1
QUEUED
127.0.0.1:6379> exec # 執(zhí)行事務(wù)
1) OK
2) OK
3) "hello"
4) "yuaiiao"
127.0.0.1:6379> 

放棄事務(wù) discard

127.0.0.1:6379> multi # 開啟事務(wù)
OK
127.0.0.1:6379> set key 1
QUEUED
127.0.0.1:6379> set key2 3
QUEUED
127.0.0.1:6379> discard # 放棄事務(wù)
OK
127.0.0.1:6379> get key2 #事務(wù)隊(duì)列中的命令都不會被執(zhí)行
(nil)
127.0.0.1:6379> 

命令異常,事務(wù)回滾

1.類似于java編譯型異常(語法錯(cuò)誤)

命令語法導(dǎo)致執(zhí)行錯(cuò)誤,事務(wù)中所有的命令都不會被執(zhí)行。

127.0.0.1:6379> multi # 開啟事務(wù)
OK
127.0.0.1:6379> set k1 v1 # 執(zhí)行命令
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> getset k3 v3
QUEUED
127.0.0.1:6379> getset k3 # 錯(cuò)誤的命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> exec # 執(zhí)行事務(wù)報(bào)錯(cuò)
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k2 # 事務(wù)執(zhí)行失敗,得不到值,所有的命令都不會被執(zhí)行
(nil)
127.0.0.1:6379> 
2.類似于java運(yùn)行時(shí)異常(如1/0,除數(shù)不能為0錯(cuò)誤) (邏輯錯(cuò)誤)

命令邏輯執(zhí)行錯(cuò)誤 , 那么執(zhí)行命令的時(shí)候,其他的命令是可以正常執(zhí)行的,只是錯(cuò)誤命令拋出異常!

證明事務(wù)不保證原子性

127.0.0.1:6379> set k1 "v1"  # 字符串
OK
127.0.0.1:6379> multi 
OK
127.0.0.1:6379> incr k1 # 對字符串進(jìn)行 自增1 運(yùn)行時(shí)異常錯(cuò)誤
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> exec # 錯(cuò)誤的命令報(bào)錯(cuò)但是其余命令都能執(zhí)行
1) (error) ERR value is not an integer or out of range 
2) OK
3) OK
4) "v2"
127.0.0.1:6379> get k2 # 其余命令正常執(zhí)行
"v2"
127.0.0.1:6379> get k3
"v3"
127.0.0.1:6379> 

監(jiān)控 Watch(相當(dāng)于java加鎖) (面試: redis的watch命令監(jiān)控實(shí)現(xiàn)秒殺系統(tǒng))

悲觀鎖

很悲觀,認(rèn)為什么時(shí)候都會出問題,無論做什么都會加鎖,效率低下

樂觀鎖

很樂觀,認(rèn)為什么時(shí)候都不會出現(xiàn)問題,所以不會上鎖,更新數(shù)據(jù)的時(shí)候去判斷一下,在此期間是否有人修改過這個(gè)數(shù)據(jù),使用version字段比較。

Redis用watch做樂觀鎖實(shí)現(xiàn)步驟

1.獲取最新version

2.更新的的時(shí)候比較version,version沒變更新成功,version改變進(jìn)入自旋。

redis的樂觀鎖watch

  • watch加鎖,記得用完需要unwatch解鎖
redis監(jiān)視watch測試案例
取錢正常執(zhí)行成功的流程
127.0.0.1:6379> set money 100 #存錢100
OK
127.0.0.1:6379> set out 0 #取錢0
OK
127.0.0.1:6379> watch money # 監(jiān)視money對象
OK
127.0.0.1:6379> multi 
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby out 20
QUEUED
127.0.0.1:6379> exec # 事務(wù)正常結(jié)束 , 期間數(shù)據(jù)沒有發(fā)生變動 ,這個(gè)時(shí)候就正常執(zhí)行成功了!
1) (integer) 80
2) (integer) 20
127.0.0.1:6379> 
取錢出現(xiàn)并發(fā)問題的流程
  • 測試多線程修改值,使用watch當(dāng)做redis 的樂觀鎖操作
    • 在開一個(gè)redis-client客戶端,一共有兩個(gè)客戶端
    • 客戶端1:開啟事務(wù),money取錢20
    • 客戶端2:這時(shí)候直接把money修改成1000
    • 客戶端1:繼續(xù)執(zhí)行,out存錢20,這時(shí)候執(zhí)行事務(wù)會
      • 執(zhí)行返回nil,修改失敗
# 客戶端1:開啟事務(wù),監(jiān)視money對象,money取錢20
127.0.0.1:6379> watch money # 監(jiān)視money對象
OK
127.0.1:6379> multi 
OK
127.0.0.1:6379> decrby money 20 
QUEUED# 客戶端1還未提交,客戶端2:這時(shí)候直接把money修改成1000
# set money 10000# 客戶端1:繼續(xù)執(zhí)行,out存錢20,然后執(zhí)行事務(wù),樂觀鎖對比money版本號改動了,執(zhí)行失敗
# 執(zhí)行之前,另一個(gè)線程,修改了我們的值,就會導(dǎo)致事務(wù)執(zhí)行失敗!#  127.0.0.1:6379> get money"80"#  127.0.0.1:6379> set money 1000#  OK
127.0.0.1:6379> incrby out 20 #out存錢20
QUEUED
127.0.0.1:6379> exec # 提交事務(wù),監(jiān)視money的version是否變化,有變化事務(wù)回滾,結(jié)果返回nil
(nil)
127.0.0.1:6379> 
  • redis事務(wù)執(zhí)行失敗后的自旋步驟
    • 釋放監(jiān)控鎖watch,在重新獲取鎖重復(fù)以上步驟,進(jìn)行自旋
127.0.0.1:6379> unwatch # 釋放鎖(監(jiān)控),如果發(fā)現(xiàn)事務(wù)執(zhí)行失敗,就先解鎖
OK
127.0.0.1:6379> watch money # 重新獲取鎖,獲取最新的值,再次監(jiān)視,select version
OK
127.0.0.1:6379> multi # 開啟事務(wù)執(zhí)行正常操作
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby out 20
QUEUED
127.0.0.1:6379> exec # 在比對監(jiān)視的值是否發(fā)生了變化,如果沒有變化,可以執(zhí)行成功呢,如果變化了就執(zhí)行失敗,在重新以上步驟
1) (integer) 980
2) (integer) 40
127.0.0.1:6379> 
  • 如果修改失敗,獲取最新的值就好

Redis的Java客戶端

我們要使用java來操作redis

有springboot整合了,我們也要學(xué)習(xí)jedis

什么是jedis?

  • jedis 是redis官方推薦的java連接開發(fā)工具! 使用java操作Redis的中間件 ! 如果你要使用java 操作redis, 那么一定要對jedis十分熟悉

測試jedis

  • 導(dǎo)入對應(yīng)的依賴
<!--導(dǎo)入jedis-->
<dependencies><!-- https://mvnrepository.com/artifact/redis.clients/jedis --><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.3.0</version></dependency><!--導(dǎo)入 fastjson--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.70</version></dependency>
</dependencies>

2.編碼測試

  • 連接redis數(shù)據(jù)庫
  • 操作命令
  • 斷開連接
package com.kuang;
import redis.clients.jedis.Jedis;public class TestPing {public static void main(String[] args) {// 1、 new Jedis 對象即可Jedis jedis = new Jedis("127.0.0.1", 6379);// jedis 所有的命令就是我們之前學(xué)習(xí)的所有指令!所以之前的指令學(xué)習(xí)很重要!System.out.println(jedis.ping());}
}

結(jié)果輸出:

jedis 所有的命令就是我們之前學(xué)習(xí)的所有指令!所以之前的指令學(xué)習(xí)很重要!

Springboot整合Redis

說明 :在 SpringBoot2.x之后, 原來使用的jedis 被替換為了 lettuce ?

    • jedis :底層采用的直連, 多個(gè)線程操作的話 ,是不安全的, 如果想要避免不安全, 使用jedis pool 連接池 ! 更像 BIO模式,阻塞的.
    • lettuce : 采用netty , 實(shí)例可以再多個(gè)線程中進(jìn)行共享,不存在線程不安全的情況 ! 可以減少線程數(shù)量,不需要開連接池, 更像NIO模式非阻塞的

自定義RedisTemplate

redis關(guān)于對象的保存,對象需要序列化。

  • 對象如果不序列化保存,則會報(bào)錯(cuò)

分析redisTemplate源碼為什么對象需要序列化

  • 分析源碼序列化配置

新建config/RedisConfig

  • 不使用JDK序列化,key使用哪個(gè)string序列化,value使用json序列化
package com.kuang.config;import com.fasterxml.jackson.annotation.JsonAutoDetect; 
import com.fasterxml.jackson.annotation.PropertyAccessor; 
import com.fasterxml.jackson.databind.ObjectMapper; 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory; 
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; 
import org.springframework.data.redis.serializer.StringRedisSerializer;@Configuration
public class RedisConfig {// 這是我給大家寫好的一個(gè)固定模板,大家在企業(yè)中,拿去就可以直接使用!// 自己定義了一個(gè) RedisTemplate @Bean @SuppressWarnings("all")public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {// 我們?yōu)榱俗约洪_發(fā)方便,一般直接使用 <String, Object>//源碼是<Object,Object>類型,可以自定義把Object轉(zhuǎn)換成String類型RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();template.setConnectionFactory(factory);// Json序列化配置Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); 	om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);// String 的序列化,解決redis存儲字符串是轉(zhuǎn)義字符,看著像亂碼StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();// key采用String的序列化方式template.setKeySerializer(stringRedisSerializer);// hash的key也采用String的序列化方式template.setHashKeySerializer(stringRedisSerializer);// value 序 列 化 方 式 采 用 jacksontemplate.setValueSerializer(jackson2JsonRedisSerializer);// hash 的 value 序 列 化 方 式 采 用 jacksontemplate.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet();return template;}}

StringRedisTemplate:

在使用String類型存儲自定義對象時(shí):

存入到Redis的數(shù)據(jù)會存儲一個(gè)該類對象的位置:

比如:

"@class": "com.sky.test.User",

這種方法更麻煩一點(diǎn),需要每次手動地序列化,反序列化。

緩存

緩存就是數(shù)據(jù)交換的緩沖區(qū)(稱作Cache),是存貯數(shù)據(jù)的臨時(shí)地方,一般讀寫性能較高。

緩存:緩存穿透,緩存擊穿,緩存雪崩

雙寫一致性,緩存的持久化

數(shù)據(jù)過期策略,數(shù)據(jù)淘汰策略

緩存穿透

緩存穿透:查詢一個(gè)不存在的數(shù)據(jù)(在緩存中和數(shù)據(jù)庫中都不存在),mysql查詢不到數(shù)據(jù)也不會直接寫入緩存,就會導(dǎo)致每次請求都查數(shù)據(jù)庫。

危害:如果有人惡意地讓很多線程并發(fā)地請求訪問這些不存在的數(shù)據(jù),那么這些所有的請求都會到達(dá)數(shù)據(jù)庫。而數(shù)據(jù)庫的并發(fā)不會很高,請求到達(dá)一定的量則會把數(shù)據(jù)庫搞垮,導(dǎo)致數(shù)據(jù)庫宕機(jī)。

解決方案一:緩存空數(shù)據(jù)

緩存空數(shù)據(jù),查詢返回的數(shù)據(jù)為空,仍把這個(gè)空結(jié)果進(jìn)行緩存 。即{key:1,value:null}
優(yōu)點(diǎn):簡單
缺點(diǎn):消耗內(nèi)存,可能會發(fā)生不一致的問題

解決方案二:布隆過濾器

優(yōu)點(diǎn):內(nèi)存占用較少,沒有多余key
缺點(diǎn):實(shí)現(xiàn)復(fù)雜存在誤判

首先是緩存預(yù)熱時(shí)往布隆過濾器中添加數(shù)據(jù)(存儲數(shù)據(jù)過程)。

布隆過濾器主要是用于檢索一個(gè)元素是否在一個(gè)集合中。

我們當(dāng)時(shí)使用的是redisson實(shí)現(xiàn)的布隆過濾器。


它的底層主要是先去初始化一個(gè)比較大數(shù)組(bitmap),里面存放的二進(jìn)制0或1。在一開始都是0,當(dāng)一個(gè)key來了之后經(jīng)過3次hash計(jì)算,模于數(shù)組長度找到數(shù)據(jù)的下標(biāo)然后把數(shù)組中原來的0改為1,這樣的話,三個(gè)數(shù)組的位置就能標(biāo)明一個(gè)key的存在。

查找的過程也是一樣的。

缺點(diǎn):
布隆過濾器有可能會產(chǎn)生一定的誤判,我們一般可以設(shè)置這個(gè)誤判率,大概不會超過5%,其實(shí)這個(gè)誤判是必然存在的,要不就得增加數(shù)組的長度,其實(shí)已經(jīng)算是很劃算了,5%以內(nèi)的誤判率一般的項(xiàng)目也能接受,不至于高并發(fā)下壓倒數(shù)據(jù)庫。

誤判示例如下圖:

誤判率:數(shù)組越小誤判率就越大,數(shù)組越大誤判率就越小,但是同時(shí)帶來了更多的內(nèi)存消耗。

緩存擊穿

緩存擊穿的意思是對于設(shè)置了過期時(shí)間的key,緩存在某個(gè)時(shí)間點(diǎn)過期的時(shí)候,恰好這時(shí)間點(diǎn)對這個(gè)key有大量的并發(fā)請求過來,這些請求發(fā)現(xiàn)緩存過期,一般都會從后端DB加載數(shù)據(jù)并回設(shè)到緩存,這個(gè)時(shí)候大并發(fā)的請求可能會瞬間把DB壓垮。

解釋:對于Redis中正好過期的數(shù)據(jù)(Redis不存在數(shù)據(jù)了),此時(shí)如果有請求來訪問這些數(shù)據(jù),正常來說是會去查DB,同時(shí)DB把數(shù)據(jù)更新到Redis,再把數(shù)據(jù)返回。那么Redis也就得到了刷新,后續(xù)redis也可以繼續(xù)為DB分擔(dān)壓力。

但是把DB數(shù)據(jù)更新到Redis的過程中,可能會花費(fèi)過多的時(shí)間(可能是因?yàn)镈B刷新到redis的數(shù)據(jù)是多表的,多表統(tǒng)計(jì)費(fèi)時(shí)間),在這個(gè)時(shí)間段內(nèi)redis的數(shù)據(jù)未重建完成,大量的并發(fā)請求過來的話則會全部走DB,會瞬間把DB壓垮。

如圖所示:

解決方案一:互斥鎖(分布式鎖)

使用互斥鎖:當(dāng)緩存過期失效時(shí),不立即去load db,先使用如redis的setnx去設(shè)置一個(gè)互斥鎖,當(dāng)操作成功返回時(shí)再進(jìn)行l(wèi)oad db的操作并回設(shè)緩存,否則重試get緩存的方法。

如圖所示:

互斥鎖保證了同時(shí)只能有一個(gè)線程獲得鎖去查詢數(shù)據(jù)庫并重建redis緩存數(shù)據(jù)。保證了數(shù)據(jù)的強(qiáng)一致性。

缺點(diǎn):性能較低。

解決方案二:邏輯過期

Redis中的熱點(diǎn)數(shù)據(jù)的key不設(shè)置過期時(shí)間設(shè)置邏輯過期字段。
1、在設(shè)置key的時(shí)候,設(shè)置一個(gè)過期時(shí)間字段一塊存入緩存中,不給當(dāng)前key設(shè)置過期時(shí)間
2、當(dāng)查詢的時(shí)候,從redis取出數(shù)據(jù)后判斷時(shí)間是否過期
3、如果過期則開通另外一個(gè)線程進(jìn)行數(shù)據(jù)同步,當(dāng)前線程正常返回?cái)?shù)據(jù),這個(gè)數(shù)據(jù)不是最新

如:

key:1 value:{"id":"123", "title":"張三", "expire":153213455}

這種方案也是在查詢DB和重置邏輯過期時(shí)間時(shí)加上互斥鎖,其它線程來查詢緩存時(shí)要不就是得到還未更新的過期數(shù)據(jù),要不就得到更新后的數(shù)據(jù)。保證了在多個(gè)線程并發(fā)訪問時(shí)不把其它線程全部攔截住(就是不會讓它們一遍又一遍地重試獲取數(shù)據(jù))。

相比方案一更為高可用、性能優(yōu)。但由于可能會得到邏輯過期數(shù)據(jù),導(dǎo)致數(shù)據(jù)并不是絕對一致的。

特點(diǎn):邏輯過期,更注重用戶體驗(yàn),高可用,性能優(yōu)。但不能保證數(shù)據(jù)絕對一致。

緩存雪崩

緩存雪崩是指設(shè)置緩存時(shí)采用了相同的過期時(shí)間,導(dǎo)致緩存在某一時(shí)刻同時(shí)失效,請求全部轉(zhuǎn)發(fā)到DB,DB瞬時(shí)壓力過重雪崩?;蛘呤?strong>Redis服務(wù)宏機(jī),導(dǎo)致大量請求到達(dá)數(shù)據(jù)庫,帶來巨大壓力。

緩存雪崩與緩存擊穿的區(qū)別:
雪崩是很多key,擊穿是某一個(gè)key緩存。


第一種情況的解決方案是將緩存失效時(shí)間分散開,比如可以在原有的失效時(shí)間(TTL)基礎(chǔ)上增加一個(gè)隨機(jī)值,比如1-5分鐘隨機(jī),這樣每一個(gè)緩存的過期時(shí)間的重復(fù)率就會降低,就很難引發(fā)集體失效的事件。

其余解決方案:
1.利用Redis集群提高服務(wù)的可用性(哨兵模式,集群模式)
2.給緩存業(yè)務(wù)添加降級限流策略(ngxin或spring cloud gateway) (降級限流策略可做為系統(tǒng)的保底策略,適用于穿透,擊穿,雪崩)
3.給業(yè)務(wù)添加多級緩存 (Guava或Caffeine)

持久化

Redis是內(nèi)存數(shù)據(jù)庫,如果不將內(nèi)存中的數(shù)據(jù)庫狀態(tài)保存到磁盤,那么一旦服務(wù)器進(jìn)程退出,服務(wù)器中的數(shù)據(jù)庫狀態(tài)也會消失(斷電即失),所以redis提供了持久化功能。

1.RDB

RDB執(zhí)行原理

這里存在一個(gè)問題:就是如果子進(jìn)程共享的內(nèi)存數(shù)據(jù)時(shí),主進(jìn)程正在對共享的內(nèi)存數(shù)據(jù)進(jìn)行更改。那么子進(jìn)程就可能會得到一些臟數(shù)據(jù)。

解決方法:

主進(jìn)程執(zhí)行寫操作時(shí),共享數(shù)據(jù)會改成只讀數(shù)據(jù)。且會拷貝一份數(shù)據(jù)去執(zhí)行主進(jìn)程的寫操作。

2.AOF

AOF全稱為Append Only File(追加文件)Redis處理的每一個(gè)寫命令都會記錄在AOF文件,可以看做是命令日志文件。

bgrewriteaof命令

因?yàn)槭怯涗?strong>命令,AOF文件會比RDB文件大的多。而且aof會記錄對同一個(gè)key的多次寫操作,但只有最后一次寫操作才有意義。通過執(zhí)行bgrewriteaof命令,可以讓AOF文件執(zhí)行重寫功能用最少的命令達(dá)到相同效果。

RDB與AOF對比

RDB和AOF各有自己的優(yōu)缺點(diǎn),如果對數(shù)據(jù)安全性要求較高,在實(shí)際開發(fā)中往往會結(jié)合兩者來使用。

雙寫一致性

redis做為緩存,mysql的數(shù)據(jù)如何與redis進(jìn)行同步呢? (雙寫一致性)

這個(gè)雙寫一致性最好要根據(jù)項(xiàng)目實(shí)際業(yè)務(wù)背景來說,一般分為兩種情況:
1.一致性要求高
2.允許延遲一致

1.一致性要求高

1.延遲雙刪

讀操作:緩存命中,直接返回;緩存未命中查詢數(shù)據(jù)庫,寫入緩存,設(shè)定超時(shí)時(shí)間
寫操作:延遲雙刪

1.無論第一步是先刪除緩存還是先修改數(shù)據(jù)庫都會導(dǎo)致臟數(shù)據(jù)的出現(xiàn)

所以重點(diǎn)是進(jìn)行二次刪除緩存以及第二次刪除時(shí)做到延時(shí)刪除。

2.刪除兩次緩存的原因

如果在第一次刪除緩存修改數(shù)據(jù)庫之間的時(shí)間里,另一個(gè)線程此時(shí)來查詢緩存了(未命中,查詢數(shù)據(jù)庫),那么此時(shí)寫入緩存的則是未更新的數(shù)據(jù)庫的數(shù)據(jù),為臟數(shù)據(jù)。如下圖所示:

所以在更新完數(shù)據(jù)庫后再次刪除緩存可以將這種情況下的臟數(shù)據(jù)盡量消除。

所以對緩存進(jìn)行兩次刪除可以降低臟數(shù)據(jù)的出現(xiàn),但是不能杜絕。

3.延時(shí)刪除的原因

因?yàn)閿?shù)據(jù)庫一般是主從模式的,讀寫分離了,主庫的數(shù)據(jù)同步到從庫需要一定的時(shí)間,故先要延遲一會。

問題:因?yàn)檠舆t的時(shí)間不好控制,所以還是可能會出現(xiàn)臟數(shù)據(jù)。

總結(jié)

延遲雙刪極大地控制了臟數(shù)據(jù)的風(fēng)險(xiǎn),但不可杜絕臟數(shù)據(jù)的風(fēng)險(xiǎn)。

2.加讀寫鎖

能保證強(qiáng)一致性,但性能低。

強(qiáng)一致性的,采用redisson提供的讀寫鎖
共享鎖:讀鎖readLock,加鎖之后,其他線程可以共享讀操作
排他鎖:獨(dú)占寫鎖writeLock,加鎖之后,阻塞其他線程讀寫操作

2.允許延遲一致(較為主流)

能保證最終一致性,會有短暫延遲。

1.異步通知保證數(shù)據(jù)的最終一致性

2.基于Canal的異步通知

數(shù)據(jù)過期策略

Redis對有些數(shù)據(jù)設(shè)置有效時(shí)間,數(shù)據(jù)過期以后,就需要將數(shù)據(jù)從內(nèi)存中刪除掉??梢园凑詹煌囊?guī)則進(jìn)行刪除,這種刪除規(guī)則就被稱之為數(shù)據(jù)的刪除策略(數(shù)據(jù)過期策略)

方案一惰性刪除

惰性刪除,在設(shè)置該key過期時(shí)間后,我們不去管它,當(dāng)需要該key時(shí),我們在檢查其是否過期,如果過期,我們就刪掉它,反之返回該key。


優(yōu)點(diǎn):對CPU友好,只會在使用該key時(shí)才會進(jìn)行過期檢查,對于很多用不到的key不用浪費(fèi)時(shí)間進(jìn)行過期檢查
缺點(diǎn):對內(nèi)存不友好,如果一個(gè)key已經(jīng)過期。但是一直沒有使用,那么該key就會一直存在內(nèi)存中,內(nèi)存永遠(yuǎn)不會釋放

方案二定期刪除:

定期刪除,就是說每隔一段時(shí)間,我們就對一些key進(jìn)行檢查,刪除里面過期的key。
?

定期清理的兩種模式
1.SLOW模式是定時(shí)任務(wù),執(zhí)行頻率默認(rèn)為10hz,每次不超過25ms,以通過修改配置文件redis.conf的hz選項(xiàng)來調(diào)整這個(gè)次數(shù)
2.FAST模式執(zhí)行頻率不固定,每次事件循環(huán)會嘗試執(zhí)行,但兩次間隔不低于2ms,每次耗時(shí)不超過1ms


優(yōu)點(diǎn):可以通過限制刪除操作執(zhí)行的時(shí)長和頻率來減少刪除操作對CPU的影響。另外定期劇除,也能有效釋放過期鍵占用的內(nèi)存。
缺點(diǎn):難以確定刪除操作執(zhí)行的時(shí)長和頻率。

定期清理控制時(shí)長和頻率--->盡量少占用主進(jìn)程的操作--->減少對CPU的影響

總結(jié)

Redis的過期刪除策略:情性刪除+定期刪除兩種策略進(jìn)行配合使用。

數(shù)據(jù)淘汰策略

數(shù)據(jù)的淘汰策略:當(dāng)Redis中的內(nèi)存不夠用時(shí),此時(shí)在向Redis中添加新的key,那么Redis就會按照某一種規(guī)則將內(nèi)存中的數(shù)據(jù)刪除掉,這種數(shù)據(jù)的刪除規(guī)則被稱之為內(nèi)存的淘汰策略。

Redis支持8種不同策略來選擇要刪除的key:

八種不同策略

noeviction:不淘汰任何key,但是內(nèi)存滿時(shí)不允許寫入新數(shù)據(jù),默認(rèn)就是這種策略。
volatile-ttl: 對設(shè)置了TTL的key,比較key的剩余TTL值,TTL越小越先被淘汰。 allkeys-random:對全體key,隨機(jī)進(jìn)行淘汰
volatile-random:對設(shè)置了TTL的key,隨機(jī)進(jìn)行淘汰
allkeys-lru:對全體key,基于LRU算法進(jìn)行淘汰
volatile-lru:對設(shè)置了TTL的key,基于LRU算法進(jìn)行淘汰
allkeys-lfu:對全體key,基于LFU算法進(jìn)行淘汰
volatile-lfu:對設(shè)置了TTL的key,基于LFU算法進(jìn)行淘汰


LRU(Least Recently Used)算法:最近最少使用。用當(dāng)前時(shí)間減去最
后一次訪問時(shí)間,這個(gè)值越大則淘汰優(yōu)先級越高。

例:key1是在6s之前訪問的,key2是在9s之前訪問的,刪除的就是key2


LFU(Least Frequently Used)算法:最少頻率使用。會統(tǒng)計(jì)每個(gè)key的
訪問頻率,值越小淘汰優(yōu)先級越高。

例:key1最近5s訪問了6次,key2最近5s訪問了9次,刪除的就是key1

數(shù)據(jù)淘汰策略--使用建議

1.優(yōu)先使用alkeys-lru策略。充分利用LRU算法的優(yōu)勢,把最近最常訪問的數(shù)據(jù)留在緩存中。如果業(yè)務(wù)有明顯的冷熱數(shù)據(jù)區(qū)分,建議使用這種策略。
2.如果業(yè)務(wù)中數(shù)據(jù)訪問頻率差別不大,沒有明顯冷熱數(shù)據(jù)區(qū)分,建議使用allkeys-random,隨機(jī)選擇淘汰。
3.如果業(yè)務(wù)中有置頂?shù)男枨?#xff0c;可以使用volatile-lru策略,同時(shí)置頂數(shù)據(jù)不設(shè)置過期時(shí)間,這些數(shù)據(jù)就一直不被刪除
會淘汰其他設(shè)置過期時(shí)間的數(shù)據(jù)。
4.如果業(yè)務(wù)中有短時(shí)高頻訪問的數(shù)據(jù),可以使用allkeys-lfu 或volatile-lfu 策略。

常見問題:
1.數(shù)據(jù)庫有1000萬數(shù)據(jù),Redis只能緩存2ow數(shù)據(jù),如何保證Redis中的數(shù)據(jù)都是熱點(diǎn)數(shù)據(jù)?
使用allkeys-lru(挑選最近最少使用的數(shù)據(jù)淘汰)淘汰策略,留下來的都是經(jīng)常訪問的熱點(diǎn)數(shù)據(jù)
2.Redis的內(nèi)存用完了會發(fā)生什么?
默認(rèn)的配置(noeviction):會直接報(bào)錯(cuò)

分布式鎖

場景

通常情況下,分布式鎖使用的場景:
集群情況下的定時(shí)任務(wù),搶單搶券,秒殺,冪等性場景

引入與基本介紹

如果項(xiàng)目是單體項(xiàng)目,只啟動了一臺服務(wù),那遇到這類搶單問題時(shí)(防止超賣),可以加synchronized鎖解決。(解決多線程并發(fā)環(huán)境下的問題)

但是項(xiàng)目服務(wù)是集群部署的話,那么synchronized鎖這種本地鎖(只能保證單個(gè)JVM內(nèi)部的多個(gè)線程之間互斥,不能讓集群下的多個(gè)JVM下的多個(gè)線程互斥)(只對本服務(wù)器有效)會失效,需要使用外部鎖,也就是分布式鎖。

例1(搶券場景):

例2:

分布式鎖:滿足分布式系統(tǒng)或集群模式下多進(jìn)程可見并且互斥的鎖。

實(shí)現(xiàn)分布式鎖的方式有很多,常見的有三種:

redis分布式鎖實(shí)現(xiàn)原理

基本介紹

設(shè)置超時(shí)失效時(shí)間的原因(避免死鎖):

如果某個(gè)線程拿到鎖在執(zhí)行業(yè)務(wù)時(shí),服務(wù)器突然宕機(jī),此時(shí)這個(gè)線程還沒來得及釋放鎖,而如果沒有設(shè)置過期時(shí)間的話,這個(gè)鎖就沒辦法得到釋放了,別的線程怎么也獲取不到這個(gè)鎖了,就造成了死鎖。而設(shè)置了過期時(shí)間的話,鎖到時(shí)間了就會自動釋放。

總結(jié)

缺陷

以上的問題是比較小的可能出現(xiàn)的,但是我們用Redis實(shí)現(xiàn)的分布式鎖去解決又顯得尤為困難,所以我們可以去使用Redisson框架,它底層提供了以上問題的解決方案,方便了我們?nèi)ソ鉀Q問題。

Redisson實(shí)現(xiàn)的分布式鎖

redisson是Redis的一個(gè)框架。

Redisson是一個(gè)在Redis的基礎(chǔ)上實(shí)現(xiàn)的Java駐內(nèi)存數(shù)據(jù)網(wǎng)格。它不僅提供了一系列的分布式的Java常用對象,還提供了許多分布式服務(wù),其中就包含了各種分布式鎖的實(shí)現(xiàn)。

快速入門:


在Redisson中需要手動加鎖,并且可以控制鎖的失效時(shí)間和等待時(shí)間,當(dāng)鎖住的一個(gè)業(yè)務(wù)還沒有執(zhí)行完成的時(shí)候,在redisson中引入了一個(gè)看門狗機(jī)制,就是說每隔一段時(shí)間就檢查當(dāng)前業(yè)務(wù)是否還持有鎖,如果持有就增加鎖的持有時(shí)間,當(dāng)業(yè)務(wù)執(zhí)行完成之后需要釋放鎖。
在高并發(fā)下,一個(gè)業(yè)務(wù)有可能會執(zhí)行很快,先客戶1持有鎖的時(shí)候,客戶2來了以后并不會馬上拒絕,它會自旋不斷嘗試獲取鎖(while循環(huán)獲取),如果客戶1釋放之后,客戶2就可以馬上持有鎖,性能也得到了提升。

1.redisson實(shí)現(xiàn)的分布式鎖的執(zhí)行流程/合理地控制鎖的有效時(shí)長(失效時(shí)間)

原因:如果鎖的有效時(shí)間設(shè)置的不合理,可能業(yè)務(wù)還沒執(zhí)行完鎖就釋放了,那此時(shí)其它線程來也可以獲取到鎖,就破壞了業(yè)務(wù)執(zhí)行的原子性,業(yè)務(wù)數(shù)據(jù)會受到影響。


方法:根據(jù)業(yè)務(wù)所需時(shí)間實(shí)時(shí)給鎖續(xù)期。

//可重試:利用信號量PubSub功能實(shí)現(xiàn)等待,喚醒,獲取鎖失敗的重試機(jī)制。

releaseTime默認(rèn)是30s。

另外開一個(gè)線程“看門狗”來監(jiān)視持有鎖的線程并做續(xù)期任務(wù)(每隔releaseTime/3的時(shí)間做一次續(xù)期)。

public void redislock() thr throws interruptedexception {//獲取鎖(重入鎖),執(zhí)行鎖的名稱RLock lock = redissonClient.getLock("lock");//嘗試獲取鎖,//參數(shù)分別是:獲取鎖的最大等待時(shí)間(期間會重試),鎖自動釋放時(shí)間(鎖失效時(shí)間),時(shí)間單位//boolean islock  = lock.tryLock(10,30,TimeUnit.SECONDS);boolean isLock = lock.tryLock(10,TimeUnit.SECONDS);//參數(shù):1.鎖的最大等待時(shí)間:鎖通過while循環(huán)來不斷嘗試獲取鎖的最大等待時(shí)間,如果這個(gè)時(shí)間內(nèi)沒有獲取到鎖則放棄獲取鎖。//      2.鎖自動釋放時(shí)間:最好不要設(shè)置或者設(shè)置為-1,否則不會啟動看門狗線程進(jìn)行續(xù)期任務(wù)。//		3.時(shí)間單位//加鎖,釋放鎖,設(shè)置過期時(shí)間,給鎖續(xù)期等操作都是基于lua腳本完成。//Lua腳本可以調(diào)用Redis命令來保證多條命令執(zhí)行的原子性。//判斷是否獲取成功             if(isLock){try{System.out.println("執(zhí)行業(yè)務(wù)");} finally {//釋放鎖lock.unlock();}}
}

原子性問題:

Redis提供了Lua腳本功能,在一個(gè)腳本中編寫多條Redis命令,確保多條命令執(zhí)行時(shí)的原子性。

2.可重入

Redis實(shí)現(xiàn)的鎖是不可重入的,但redisson實(shí)現(xiàn)的鎖是可重入的。

作用:避免死鎖的產(chǎn)生。

這個(gè)重入其實(shí)在內(nèi)部就是判斷是否是當(dāng)前線程持有的鎖。如果是當(dāng)前線程持有的鎖就會計(jì)數(shù),如果釋放鎖就會在計(jì)算上減一。

存儲數(shù)據(jù)的時(shí)候采用的hash結(jié)構(gòu),大key可以按照自己的業(yè)務(wù)進(jìn)行定制,其中小key是當(dāng)前線程的唯一標(biāo)識線程id),value是當(dāng)前線程重入的次數(shù)。

public void add1(){RLock lock = redissonClient.getLock("heimalock");boolean islock = lock.tryLock();//執(zhí)行業(yè)務(wù)add2();//釋放鎖lock.unlock();
}
public void add2(){RLock lock = redissonClient.getLock("heimalock");boolean islock = lock.trylock();//執(zhí)行業(yè)務(wù)...//釋放鎖lock.unlock();
}

底層獲取鎖釋放鎖等操作都很復(fù)雜,都是有多個(gè)步驟,所以是用Lua腳本寫確保各個(gè)操作的原子性。

3.主從一致性

redisson實(shí)現(xiàn)的分布式鎖不能解決主從一致性問題。

比如,當(dāng)線程1加鎖成功后,Master節(jié)點(diǎn)數(shù)據(jù)會異步復(fù)制到Slave節(jié)點(diǎn),當(dāng)數(shù)據(jù)還沒來得及同步到Slave節(jié)點(diǎn)時(shí),當(dāng)前持有Redis鎖的Master節(jié)點(diǎn)宕機(jī),Slave節(jié)點(diǎn)被提升為新的Master節(jié)點(diǎn)。(按道理主節(jié)點(diǎn)和從節(jié)點(diǎn)的數(shù)據(jù)應(yīng)該要是一模一樣的,加鎖的信息也要一模一樣(其實(shí)就是一個(gè)setnx數(shù)據(jù)而已))

假如現(xiàn)在來了一個(gè)線程2,再次加鎖,因?yàn)镸aster節(jié)點(diǎn)數(shù)據(jù)還沒來得及同步過來(從節(jié)點(diǎn)已經(jīng)被這把鎖鎖住且線程一已經(jīng)拿到了這把鎖的信息還未更新過來),所以會在新的Master節(jié)點(diǎn)上加鎖成功,這個(gè)時(shí)候就會出現(xiàn)兩個(gè)線程同時(shí)持有一把鎖的問題。

兩個(gè)線程同時(shí)獲取一把鎖--->違背了鎖的互斥性(鎖失效了)。

紅鎖:

紅鎖算法的基本思想是,當(dāng)需要鎖定多個(gè)資源時(shí),可以在多個(gè)Redis節(jié)點(diǎn)上分別獲取鎖,只有當(dāng)大多數(shù)節(jié)點(diǎn)上
的鎖都被成功獲取時(shí),整個(gè)鎖才算獲取成功。這樣可以提高系統(tǒng)的容錯(cuò)性和可用性。


我們可以利用Redisson提供的紅鎖來解決這個(gè)問題,它的主要作用是,不能只在一個(gè)redis實(shí)例上創(chuàng)建鎖,應(yīng)該是在多個(gè)redis實(shí)例上創(chuàng)建鎖,并且要求在大多數(shù)Redis節(jié)點(diǎn)上都成功創(chuàng)建鎖,紅鎖中要求是Redis的節(jié)點(diǎn)數(shù)量要過半。這樣就能避免線程1加鎖成功后Master節(jié)點(diǎn)宕機(jī)導(dǎo)致線程2成功加鎖到新的Master節(jié)點(diǎn)上的問題了。

意思就是線程來的時(shí)候要獲取多個(gè)Redis節(jié)點(diǎn)的鎖才算成功,才可以執(zhí)行代碼。

如果一個(gè)主節(jié)點(diǎn)宕機(jī)(主節(jié)點(diǎn)的數(shù)據(jù)還沒來得及同步到從節(jié)點(diǎn),與以上同理),它的從節(jié)點(diǎn)變成主節(jié)點(diǎn),那么此時(shí)另一個(gè)線程來是不可以獲取到鎖的,因?yàn)檫@個(gè)線程必須要獲取到所有的節(jié)點(diǎn)的鎖才能成功獲取到鎖,它只能拿到宕機(jī)的那個(gè)主節(jié)點(diǎn)的從節(jié)點(diǎn)的鎖(因?yàn)橹鞴?jié)點(diǎn)的數(shù)據(jù)還沒來得及同步到從節(jié)點(diǎn)),所以會獲取鎖失敗。

只要有一個(gè)節(jié)點(diǎn)是存活的,其它線程就不可以拿到鎖,鎖就不會失效。


缺點(diǎn)

如果使用了紅鎖,因?yàn)樾枰瑫r(shí)在多個(gè)節(jié)點(diǎn)上都添加鎖,性能就變的很低了,并且運(yùn)維維護(hù)成本也非常高,所以,我們一般在項(xiàng)目中也不會直接使用紅鎖,并且官方也暫時(shí)廢棄了這個(gè)紅鎖。
所以強(qiáng)一致性要求高的業(yè)務(wù),建議使用zookeeper實(shí)現(xiàn)的分布式鎖,它是可以保證強(qiáng)一致性的。

Redis發(fā)布訂閱

  • Redis 發(fā)布訂閱(pub/sub)是一種消息通信模式: 發(fā)送者(pub)發(fā)送消息,訂閱者(sub)接受消息.微博,微信,關(guān)注系統(tǒng)
  • Redis 客戶端可以訂閱任意數(shù)量的頻道

訂閱/發(fā)布消息圖

  • 第一個(gè): 消息發(fā)送者, 第二個(gè) :頻道 第三個(gè) :消息訂閱者!

  • 下圖展示了頻道 channel1 , 以及訂閱這個(gè)頻道的三個(gè)客戶端 —— client2 、 client5 和 client1 之間的關(guān)系:

  • 當(dāng)有新消息通過 PUBLISH 命令發(fā)送給頻道 channel1 時(shí), 這個(gè)消息就會被發(fā)送給訂閱它的三個(gè)客戶端:

命令

  • 這些命令被廣泛用于構(gòu)建即時(shí)通信應(yīng)用,比如網(wǎng)絡(luò)聊天室和實(shí)時(shí)廣播,實(shí)時(shí)提醒

測試

  • 訂閱端(消費(fèi)者)
    • 開啟客戶端1
127.0.0.1:6379> subscribe codeyuaiiao //訂閱一個(gè)頻道,頻道名稱:codeyuaiiao  訂閱的時(shí)候頻道就建立了
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "codeyuaiiao"
3) (integer) 1# 等待讀取推送(客戶端2發(fā)送消息,客戶端1這邊接收消息)
1) "message" # 消息
2) "codeyuaiiao" # 消息來自哪個(gè)頻道
3) "hello world" # 消息的具體內(nèi)容1) "message"
2) "codeyuaiiao"
3) "hello yuhaijiao"
  • 發(fā)送端:(生產(chǎn)者)
    • 再開啟一個(gè)客戶端2
127.0.0.1:6379> publish codeyuaiiao "hello world" # 發(fā)布者發(fā)布消息到頻道
(integer) 1
127.0.0.1:6379> publish codeyuaiiao "hello yuhaijiao" # 發(fā)布者發(fā)布消息到頻道
(integer) 1
127.0.0.1:6379> 

Redis發(fā)布訂閱原理

  • Redis是使用C實(shí)現(xiàn)的,通過分析 Redis 源碼里的 pubsub.c 文件,了解發(fā)布和訂閱機(jī)制的底層實(shí)現(xiàn),籍此加深對 Redis 的理解。
  • Redis 通過 PUBLISH(發(fā)送消息) 、SUBSCRIBE(訂閱頻道) 和 PSUBSCRIBE(訂閱多個(gè)頻道) 等命令實(shí)現(xiàn)發(fā)布和訂閱功能。

例如微信訂閱公眾號:

  • 通過 SUBSCRIBE 命令訂閱某頻道后
    • Redis-server 里維護(hù)了一個(gè)字典
    • 字典的鍵就是一個(gè)個(gè)頻道
    • 字典的值則是一個(gè)鏈表,鏈表中保存了所有訂閱這個(gè) channel 的客戶端
    • SUBSCRIBE 命令的關(guān)鍵, 就是將客戶端添加到給定 channel 的訂閱鏈表中
  • 通過publish命令向訂閱者發(fā)送消息,redis-server會使用給定的頻道作為鍵,在它所維護(hù)的channel字典中查找記錄了訂閱這個(gè)頻道的所有客戶端的鏈表,遍歷這個(gè)鏈表,將消息發(fā)布給所有訂閱者.

總結(jié)

Pub/Sub 從字面上理解就是發(fā)布(Publish)與訂閱(Subscribe),在Redis中,你可以設(shè)定對某一個(gè)key值進(jìn)行消息發(fā)布及消息訂閱,當(dāng)一個(gè)key值上進(jìn)行了消息發(fā)布后,所有訂閱它的客戶端都會收到相應(yīng)的消息。這一功能最明顯的用法就是用作實(shí)時(shí)消息系統(tǒng),比如普通的即時(shí)聊天,群聊等功能。

使用場景

  1. 實(shí)時(shí)消息系統(tǒng)
  2. 實(shí)時(shí)聊天! (頻道當(dāng)做聊天室,將信息回顯給所有人即可! )
  3. 訂閱,關(guān)注系統(tǒng)都是可以的

稍微復(fù)雜的場景我們就會使用 消息中間件MQ

Redis消息隊(duì)列

概念

消息隊(duì)列(Message Queue),字面意思就是存放消息的隊(duì)列。最簡單的消息隊(duì)列模型包括3個(gè)角色:
消息隊(duì)列:存儲和管理消息,也被稱為消息代理(message broker)
生產(chǎn)者:發(fā)送消息到消息隊(duì)列
消費(fèi)者:從消息隊(duì)列獲取消息并處理消息

例(秒殺搶券業(yè)務(wù)):

生產(chǎn)者:判斷是否有資格搶券(券的剩余數(shù)量大于0且當(dāng)前用戶之前未搶到券),如果有資格則將訂單相關(guān)信息寫入消息隊(duì)列。

消費(fèi)者:開啟一個(gè)獨(dú)立的線程去接收消息,完成下單(把訂單信息寫入Mysql數(shù)據(jù)庫)

這樣秒殺搶單的業(yè)務(wù)真正寫數(shù)據(jù)庫的業(yè)務(wù)就實(shí)現(xiàn)了分離,變成了異步操作,解耦合了。

秒殺搶單的業(yè)務(wù):秒殺這里因?yàn)椴挥脤憯?shù)據(jù)庫(比較耗時(shí)),并發(fā)能力大大提高
寫數(shù)據(jù)庫的業(yè)務(wù):可以根據(jù)自己的節(jié)奏慢慢地去取訂單寫數(shù)據(jù)庫,不會讓數(shù)據(jù)庫有太大的壓力,保證數(shù)據(jù)庫抗得住。

Redis提供了三種不同的方式來實(shí)現(xiàn)消息隊(duì)列:
list結(jié)構(gòu):基于List結(jié)構(gòu)模擬消息隊(duì)列
PubSub:基本的點(diǎn)對點(diǎn)消息模型
Stream:比較完善的消息隊(duì)列模型

基于List結(jié)構(gòu)模擬消息隊(duì)列(可實(shí)現(xiàn)阻塞隊(duì)列的效果)

支持持久化:因?yàn)閘ist類型redis本身是用鏈表做存儲數(shù)據(jù)的,只是我們把它當(dāng)成消息隊(duì)列來用,故對數(shù)據(jù)可以持久化

基于PubSub(發(fā)布訂閱)的消息隊(duì)列

主要內(nèi)容就是上面學(xué)習(xí)的Redis發(fā)布訂閱


優(yōu)點(diǎn):
采用發(fā)布訂閱模型,支持多生產(chǎn),多消費(fèi)
缺點(diǎn):
不支持?jǐn)?shù)據(jù)持久化
無法避免消息丟失
消息堆積有上限,超出時(shí)數(shù)據(jù)丟失

不支持持久化:因?yàn)镻ubSub本身就只是用來做發(fā)布訂閱功能的,如果沒有人訂閱某個(gè)頻道,那么往這個(gè)頻道發(fā)布數(shù)據(jù)后,數(shù)據(jù)會丟失,Redis不會保存這個(gè)數(shù)據(jù)。

基于Stream的消息隊(duì)列

基本知識

Stream是Redis 5.0引入的一種新數(shù)據(jù)類型,可以實(shí)現(xiàn)一個(gè)功能非常完善的消息隊(duì)列。

例:

注意
當(dāng)我們指定起始id為$時(shí),代表讀取最新的消息,如果我們處理一條消息的過程中,又有超過一條以上的消息到達(dá)隊(duì)列,則下次獲取時(shí)也只能獲取到最新的一條,會出現(xiàn)漏讀消息的問題。

Stream類型消息隊(duì)列的XREAD命令特點(diǎn):
1.消息可回溯
2.一個(gè)消息可以被多個(gè)消費(fèi)者讀取
3.可以阻塞讀取
4.有消息漏讀的風(fēng)險(xiǎn)

消費(fèi)者組

消費(fèi)者組:將多個(gè)消費(fèi)者劃分到一個(gè)組中,監(jiān)聽同一個(gè)隊(duì)列。

具備下列特點(diǎn):

Stream類型消息隊(duì)列的XREADGROUP命令特點(diǎn):
消息可回溯
可以多消費(fèi)者爭搶消息,加快消費(fèi)速度
可以阻塞讀取
沒有消息漏讀的風(fēng)險(xiǎn)
有消息確認(rèn)機(jī)制,保證消息至少被消費(fèi)一次

Redis集群(分布式緩存)

單點(diǎn)Redis的問題

1.并發(fā)能力問題

解決方法:搭建主從集群,實(shí)現(xiàn)讀寫分離。實(shí)現(xiàn)高并發(fā)。

2.故障恢復(fù)問題

解決方法:利用Redis哨兵,實(shí)現(xiàn)健康檢測和自動恢復(fù)。保障高可用。

3.存儲能力問題

解決方法:搭建分片集群,利用插槽機(jī)制實(shí)現(xiàn)動態(tài)擴(kuò)容。

主從復(fù)制

單節(jié)點(diǎn)Redis的并發(fā)能力是有上限的,要進(jìn)一步提高Redis的并發(fā)能力,就需要搭建主從集群,實(shí)現(xiàn)讀寫分離。

主從數(shù)據(jù)同步原理
1.主從全量同步

注:

1.判斷是否第一次同步:

從節(jié)點(diǎn)的replid與主節(jié)點(diǎn)的不一樣則說明這個(gè)從節(jié)點(diǎn)是第一次同步。

2.只有第一次同步的時(shí)候主節(jié)點(diǎn)才會生成RDB文件,第一次之后的同步會根據(jù)偏移量利用repl_baklog日志文件進(jìn)行同步數(shù)據(jù)。

2.主從增量同步(slave重啟或后期數(shù)據(jù)變化)

3.總結(jié)

全量同步:
1.從節(jié)點(diǎn)請求主節(jié)點(diǎn)同步數(shù)據(jù)(replication id, offset)
2.主節(jié)點(diǎn)判斷是否是第一次請求,是第一次就與從節(jié)點(diǎn)同步版本信息(replicationid和offset)
3.主節(jié)點(diǎn)執(zhí)行bgsave,生成rdb文件后,發(fā)送給從節(jié)點(diǎn)去執(zhí)行
4.在rdb生成執(zhí)行期間,主節(jié)點(diǎn)會以命令的方式記錄到緩沖區(qū)(一個(gè)日志文件)
5.把生成之后的命令日志文件發(fā)送給從節(jié)點(diǎn)進(jìn)行同步

增量同步:
1.從節(jié)點(diǎn)請求主節(jié)點(diǎn)同步數(shù)據(jù),主節(jié)點(diǎn)判斷不是第一次請求,不是第一次就獲取從節(jié)點(diǎn)的offset值
2.主節(jié)點(diǎn)從命令日志中獲取offset值之后的數(shù)據(jù),發(fā)送給從節(jié)點(diǎn)進(jìn)行數(shù)據(jù)同步

哨兵模式

redis提供了哨兵模式來實(shí)現(xiàn)主從集群的自動故障恢復(fù),從而極大地保障了Redis主從高可用。

哨兵模式的結(jié)構(gòu)與作用

redis提供了哨兵 (Sentinel)機(jī)制來實(shí)現(xiàn)主從集群的自動故障恢復(fù)。

結(jié)構(gòu):

作用:

監(jiān)控:Sentinel會不斷檢查您的master和slave是否按預(yù)期工作
自動故障恢復(fù):如果master故障, Sentinel會將一個(gè)slave提升為master。當(dāng)故障實(shí)例恢復(fù)后也以新的master為主
通知:Sentinel充當(dāng)redis客戶端的服務(wù)發(fā)現(xiàn)來源,當(dāng)集群發(fā)生故障轉(zhuǎn)移時(shí),會將最新信息推送給redis的客戶端

服務(wù)狀態(tài)監(jiān)控

Sentinel基于心跳機(jī)制監(jiān)測服務(wù)狀態(tài),每隔1秒向集群的每個(gè)實(shí)例發(fā)送ping命令:
主觀下線:如果某sentinel節(jié)點(diǎn)發(fā)現(xiàn)某實(shí)例未在規(guī)定時(shí)間響應(yīng),則認(rèn)為該實(shí)例主觀下線。
客觀下線:若超過指定數(shù)量(quorum)的sentinel都認(rèn)為該實(shí)例主觀下線,則該實(shí)例客觀下線。quorum值最好
超過sentinel實(shí)例數(shù)量的一半。

哨兵選主規(guī)則(主節(jié)點(diǎn)宕機(jī)后,選從節(jié)點(diǎn)為主節(jié)點(diǎn)的規(guī)則)

1.首先判斷主與從節(jié)點(diǎn)斷開時(shí)間長短,如超過指定值就排除該從節(jié)點(diǎn)
2.然后判斷從節(jié)點(diǎn)的slave-priority值,越小優(yōu)先級越高
3.如果slave-prority一樣,則判斷slave節(jié)點(diǎn)的offset值,越大優(yōu)先級越高.
4.最后是判斷slave節(jié)點(diǎn)的運(yùn)行id大小,越小優(yōu)先級越高。

第三條最重要!!!

Redis集群(哨兵模式)的腦裂問題

有的時(shí)候由于網(wǎng)絡(luò)等原因可能會出現(xiàn)腦裂的情況,就是說,由于redis的master節(jié)點(diǎn)和redis的salve節(jié)點(diǎn)和sentinel處于不同的網(wǎng)絡(luò)分區(qū),使得sentinel沒有能夠心跳感知到主節(jié)點(diǎn),所以通過選舉的方式提升了一個(gè)salve為master,這樣就存在了兩個(gè)master,就像大腦分裂了一樣,這樣會導(dǎo)致客戶端還在old master那里寫入數(shù)據(jù)新節(jié)點(diǎn)無法同步數(shù)據(jù),當(dāng)網(wǎng)絡(luò)恢復(fù)后,sentinel會將old master降為salve,這時(shí)再從新master同步數(shù)據(jù),就會導(dǎo)致old master中的大量數(shù)據(jù)丟失。

----------->

------------>

解決方法

在redis的配置中設(shè)置兩個(gè)配置參數(shù)

1.(min-replicas-to-write 1)設(shè)置最少的salve節(jié)點(diǎn)個(gè)數(shù)為1,設(shè)置至少要有一個(gè)從節(jié)點(diǎn)才能同步數(shù)據(jù)

2.(min-replicas-max-lag 5)設(shè)置主從數(shù)據(jù)復(fù)制和同步的延遲時(shí)間不能超過5秒

達(dá)不到要求就拒絕請求,就可以避免大量的數(shù)據(jù)丟失。

總結(jié):
我們可以修改redis的配置,可以設(shè)置最少的從節(jié)點(diǎn)數(shù)量至少為一個(gè)以及縮短主從數(shù)據(jù)同步的延遲時(shí)間(不能超過5秒),達(dá)不到要求就拒絕Redis客戶端的請求(不讓客戶端寫入數(shù)據(jù)到老的主節(jié)點(diǎn)),這樣就可以避免大量的數(shù)據(jù)丟失。

分片集群

主從和哨兵可以解決高可用,高并發(fā)讀的問題。但是依然有兩個(gè)問題沒有解決;
1.海量數(shù)據(jù)存儲問題
2.高并發(fā)寫的問題

使用分片集群可以解決上述問題,分片集群特征
1.集群中有多個(gè)master,每個(gè)master保存不同數(shù)據(jù)
2.每個(gè)master都可以有多個(gè)slave節(jié)點(diǎn)
3.master之間通過ping監(jiān)測彼此健康狀態(tài) (這點(diǎn)類似于之前的哨兵模式)
4.客戶端請求可以訪問集群任意節(jié)點(diǎn),最終都會被轉(zhuǎn)發(fā)到正確節(jié)點(diǎn) (路由:客戶端請求可以訪問集群任意節(jié)點(diǎn),最終都會被轉(zhuǎn)發(fā)到正確節(jié)點(diǎn)。)

具體的路由規(guī)則:

Redis分片集群引入了哈希槽的概念,Redis集群有16384個(gè)哈希槽,每個(gè)key通過 CRC16 校驗(yàn)后對 16384 取模來
決定放置哪個(gè)槽,集群的每個(gè)節(jié)點(diǎn)負(fù)責(zé)一部分hash槽。

Redis分片集群中數(shù)據(jù)的存儲和讀取:
redis分片集群引入了哈希槽的概念,redis集群有16384個(gè)哈希槽,將16384個(gè)插槽分配到不同的實(shí)例
讀寫數(shù)據(jù):根據(jù)key的有效部分計(jì)算哈希值。對16384取余(有效部分,如果key前面有大括號的
內(nèi)容就是有效部分,如果沒有,則以key本身做為有效部分)余數(shù)做為插槽,尋找插槽所在的實(shí)例

redis集群環(huán)境部署(環(huán)境配置)

只配置從庫,不用配置主庫!

  • 原因:redis默認(rèn)都是主庫

查看當(dāng)前redis庫的信息,分析是否是主庫

  • 命令:info replication
127.0.0.1:6379> info replication	# 查看當(dāng)前庫的信息
# Replication
role:master	# 角色 master	
master connected_slaves:0 # 沒有從機(jī)
master_replid:b63c90e6c501143759cb0e7f450bd1eb0c70882a 
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

搭建redis集群準(zhǔn)備工作

  • 復(fù)制3個(gè)redis.conf配置文件,然后修改對應(yīng)的集群信息
  • 分別修改3個(gè)redis.conf對應(yīng)的以下4個(gè)屬性配置
  1. port端口修改
  1. pid名字
  1. log文件名字
  1. dump.rdb名字
  • 修改完畢之后,啟動我們的3個(gè)redis服務(wù)器
    • 分別啟動3個(gè)redis服務(wù)命令
  • 啟動完畢,通過進(jìn)程查看信息

一主二從集群搭建(命令或文件配置)(這種方式的redis集群實(shí)際工作用不到,僅供基礎(chǔ)學(xué)習(xí))

命令方式配置

默認(rèn)情況下, 每臺Redis 服務(wù)器都是主節(jié)點(diǎn)?; 我們一般情況下只用配置從機(jī)就好了!

  • 認(rèn)老大! 一主 (6379)二從(6380,6381)
  • 配置從機(jī),去6380和6381配置,命令:
127.0.0.1:6380> SLAVEOF 127.0.0.1 6379	#	SLAVEOF host 6379	登錄6380找6379主機(jī)當(dāng)自己的老大!
OK
127.0.0.1:6380> info replication # 查詢信息redis6380的服務(wù)信息
role:slave	# 查看當(dāng)前角色是從機(jī)
master_host:127.0.0.1	# 可以的看到主機(jī)的信息和端口號
master_port:6379
  • 配置結(jié)果查詢:6380,6381的角色role變成了slave從機(jī)

  • 如果兩個(gè)都配置完了,就有兩個(gè)從機(jī)了
    • 登錄6379主機(jī),查看主機(jī)下的兩個(gè)從節(jié)點(diǎn)

  • 真實(shí)的主從配置應(yīng)該在配置文件中配置,這樣的話是永久的, 我們這里使用的是命令,暫時(shí)的!
文件方式配置(一主二從,持久化的,對于哨兵模式,不建議使用這種)
  • 登錄redis從機(jī),進(jìn)入redis.conf配置文件配置replicaof
  • 如果主機(jī)有密碼,則配置主機(jī)密碼

一主二從細(xì)節(jié)

  • 主機(jī)可以寫, 從機(jī)不能寫只能讀! 主機(jī)中的所有信息和數(shù)據(jù),都會自動被從機(jī)保存.

測試主機(jī)寫,從機(jī)讀

  • 主機(jī)寫:

  • 從機(jī)讀:

  • 從機(jī)寫,會報(bào)錯(cuò)

測試: 主機(jī)宕機(jī)斷開連接,從機(jī)會有什么變化

  • 從機(jī)沒有變化,依然指向主機(jī),并且只能讀不能寫
  • 如果想把從機(jī)改為主機(jī),只能手動去設(shè)置,或者配置哨兵通過選舉,將從機(jī)變?yōu)橹鳈C(jī)

測試2:這個(gè)時(shí)候, 主機(jī)如果回來了,從機(jī)有什么變化

  • 從機(jī)依舊可以直接獲取到主機(jī)寫的信息!保證高可用性

測試3:如果從機(jī)斷了,會有什么后果

  • 由于是使用命令行來配置的從機(jī),這個(gè)時(shí)候如果從機(jī)重啟了,就會變成主機(jī) (所以建議在redis.conf配置文件中配置從機(jī))!
  • 但只要重新將主機(jī)變?yōu)閺臋C(jī), 立馬就會從主機(jī)中獲取值!

主從復(fù)制原理

  • Slave啟動成功連接到master后會發(fā)送一個(gè)sync同步命令
  • Master接到命令,啟動后臺的存盤進(jìn)程,同時(shí)收集所有接收到的用于修改數(shù)據(jù)集命令, 在后臺進(jìn)程完畢之后,master將傳送整個(gè)數(shù)據(jù)文件到slave,并完成一次完全同步.
  • 全量復(fù)制: 而slave服務(wù)在接收到數(shù)據(jù)庫文件數(shù)據(jù)后, 將其存盤并加載到內(nèi)存中.
  • 增量復(fù)制: Master繼續(xù)將新的所有收集到的修改命令依次傳給slave,完成同步.
  • 但是只要是重新連接master, 一次完全同步(全量復(fù)制)將被自動執(zhí)行! 我們的數(shù)據(jù)一定可以在從機(jī)中看到!
一主兩從的第二種搭建方式(層層鏈路)哨兵模式的手動版

層層鏈路

  • 79是主節(jié)點(diǎn)
  • 80是79的從節(jié)點(diǎn)
  • 81是79的從節(jié)點(diǎn)
  • 上一個(gè)M連接下一個(gè)S!

  • 這時(shí)候也可以完成我們的主從復(fù)制!

如果沒有老大了,這個(gè)時(shí)候能不能選擇一個(gè)老大出來呢? 手動!

  • 謀朝篡位
    • 如果主機(jī)斷開了連接, 我們可以使用slaveof no one?讓自己變成主機(jī)! 其他的節(jié)點(diǎn)就可以手動連接到最新的這個(gè)主節(jié)點(diǎn)(手動)! 如果這個(gè)時(shí)候老大修復(fù)了, 那就只能重新配置連接!
    • 所以建議使用命令配置集群,方便將從節(jié)點(diǎn)改為主節(jié)點(diǎn)后,不用在去改配置文件

I/O多路復(fù)用模型

redis為什么這么快

用戶空間和內(nèi)核空間

Linux系統(tǒng)中一個(gè)進(jìn)程使用的內(nèi)存情況劃分兩部分:內(nèi)核空間,用戶空間。

用戶空間只能執(zhí)行受限的命令(Ring3),而且不能直接調(diào)用系統(tǒng)資源(比如網(wǎng)卡數(shù)據(jù)),必須通過內(nèi)核提供的接口來訪問。
內(nèi)核空間可以執(zhí)行特權(quán)命令(Ring0),調(diào)用一切系統(tǒng)資源。

Linux系統(tǒng)為了提高IO效率,會在用戶空間和內(nèi)核空間加入緩沖區(qū)
寫數(shù)據(jù)時(shí),要把用戶緩沖數(shù)據(jù)拷貝到內(nèi)核緩沖區(qū),然后寫入設(shè)備
讀數(shù)據(jù)時(shí),要從設(shè)備讀取數(shù)據(jù)到內(nèi)核緩沖區(qū),然后拷貝到用戶緩沖區(qū)

如圖所示

阻塞IO

顧名思義,阻塞IO就是兩個(gè)階段都必須阻塞等待
階段一:
1.用戶進(jìn)程嘗試讀取數(shù)據(jù)(比如網(wǎng)卡數(shù)據(jù))
2.此時(shí)數(shù)據(jù)尚未到達(dá),內(nèi)核需要等待數(shù)據(jù)
3.此時(shí)用戶進(jìn)程也處于阻塞狀態(tài)

階段二:
1.數(shù)據(jù)到達(dá)并拷貝到內(nèi)核緩沖區(qū),代表已就緒
2.將內(nèi)核數(shù)據(jù)拷貝到用戶緩沖區(qū)
3.拷貝過程中,用戶進(jìn)程依然阻塞等待
4.拷貝完成,用戶進(jìn)程解除阻塞,處理數(shù)據(jù)

可以看到,阻塞IO模型中,用戶進(jìn)程在兩個(gè)階段都是阻塞狀態(tài)。

非阻塞IO

顧名思義,非阻塞IO的recvfrom操作立即返回結(jié)果而不是阻塞用戶進(jìn)程。

階段一:
1.用戶進(jìn)程嘗試讀取數(shù)據(jù)(比如網(wǎng)卡數(shù)據(jù))
2.此時(shí)數(shù)據(jù)尚未到達(dá),內(nèi)核需要等待數(shù)據(jù)
3.返回異常給用戶進(jìn)程
4.用戶進(jìn)程拿到error后,再次嘗試讀取
5.循環(huán)往復(fù),直到數(shù)據(jù)就緒
階段二:
將內(nèi)核數(shù)據(jù)拷貝到用戶緩沖區(qū)
拷貝過程中,用戶進(jìn)程依然阻塞等待
拷貝完成,用戶進(jìn)程解除阻塞,處理數(shù)據(jù)

可以看到,非阻塞IO模型中,用戶進(jìn)程在第一個(gè)階段是非阻露,第二個(gè)階段是阻塞狀態(tài)。雖然是非阻塞,但性能并沒有得到提高。而且忙等機(jī)制導(dǎo)致CPU空轉(zhuǎn),CPU使用率暴增。

IO多路復(fù)用

Redis網(wǎng)絡(luò)模型

Redis通過IO多路復(fù)用來提高網(wǎng)絡(luò)性能,并且支持各種不同的多路復(fù)用實(shí)現(xiàn),并且將這些實(shí)現(xiàn)進(jìn)行封裝,提供了統(tǒng)一的高性能事件庫。

主要是IO多路復(fù)用+事件派發(fā)機(jī)制:

Redis 6.0之后,為了提升性能,引入了多線程處理:

http://www.risenshineclean.com/news/32864.html

相關(guān)文章:

  • 網(wǎng)站建設(shè)夢幻創(chuàng)意百度文庫官網(wǎng)
  • php做的網(wǎng)站安全嗎今天的新聞?lì)^條
  • 什么公司在百度做網(wǎng)站常州seo關(guān)鍵詞排名
  • 做網(wǎng)站實(shí)習(xí)日志寧波seo怎么做引流推廣
  • 陽泉購物網(wǎng)站開發(fā)設(shè)計(jì)市場營銷策劃
  • 網(wǎng)站網(wǎng)絡(luò)廣告如何建設(shè)自助建站免費(fèi)搭建個(gè)人網(wǎng)站
  • 織夢網(wǎng)站后臺關(guān)鍵詞推廣優(yōu)化app
  • 婚禮顧問網(wǎng)站介紹模版有哪些營銷推廣方式
  • 用php做動態(tài)網(wǎng)站嗎企業(yè)中層管理人員培訓(xùn)課程
  • 手機(jī)網(wǎng)站內(nèi)容模塊如何進(jìn)行網(wǎng)站宣傳推廣
  • 58網(wǎng)站怎么做優(yōu)化迅雷磁力鏈bt磁力種子
  • 合肥疫情風(fēng)險(xiǎn)等級思億歐seo靠譜嗎
  • 天津企業(yè)網(wǎng)站建站做一個(gè)公司網(wǎng)站大概要多少錢
  • 用自己電腦做網(wǎng)站的空間百度自媒體注冊入口
  • 南寧seo費(fèi)用服務(wù)短視頻seo系統(tǒng)
  • 黨政機(jī)關(guān)如何建設(shè)網(wǎng)站企業(yè)推廣網(wǎng)絡(luò)營銷外包服務(wù)
  • 做網(wǎng)站如何與美工配合日本比分預(yù)測
  • 網(wǎng)站開發(fā)速成班網(wǎng)絡(luò)軟文怎么寫
  • 天津建設(shè)工程信息網(wǎng) 官網(wǎng)首頁seo排名點(diǎn)擊器曝光行者seo
  • 鄂州做網(wǎng)站公司推廣營銷軟件app
  • 建設(shè)銀行梅州分行網(wǎng)站廈門seo怎么做
  • 武漢 網(wǎng)站制作案例北京建站
  • 平頂山網(wǎng)站建設(shè)費(fèi)用競價(jià)排名是什么
  • 網(wǎng)站建設(shè)流程有東莞網(wǎng)站推廣方案
  • 成都模板網(wǎng)站建設(shè)網(wǎng)絡(luò)推廣優(yōu)化平臺
  • 下沙開發(fā)區(qū)建設(shè)局網(wǎng)站廣州軟文推廣公司
  • 威海做網(wǎng)站多少錢百度助手app下載
  • 網(wǎng)站建設(shè)計(jì)劃網(wǎng)絡(luò)營銷有什么崗位
  • 做網(wǎng)站論文網(wǎng)絡(luò)營銷的模式有哪些?
  • 做二手車網(wǎng)站怎么做的外貿(mào)軟件排行榜