国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Redis從基礎(chǔ)到進(jìn)階篇(一)

這篇具有很好參考價值的文章主要介紹了Redis從基礎(chǔ)到進(jìn)階篇(一)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

目錄

一、了解NoSql

1.1?什么是Nosql?

1.2?為什么要使用NoSql?

1.3?NoSql數(shù)據(jù)庫的優(yōu)勢

1.4 常見的NoSql產(chǎn)品?

1.5 各產(chǎn)品的區(qū)別

二、Redis介紹

2.1什么是Redis?

2.2 Redis優(yōu)勢?

2.3 Redis應(yīng)用場景

2.4 Redis下載

三、Linux下安裝Redis

3.1 環(huán)境準(zhǔn)備?

3.2 Redis的安裝

3.2.1 Redis的編譯環(huán)境

3.2.2 Redis的安裝

3.3 Redis的啟動

3.3.1 Redis的前端模式啟動(了解)

3.3.2 Redis的后端啟動?

3.3.3 客戶端訪問redis

3.3.4 向Redis服務(wù)器發(fā)送命令

3.3.5 退出客戶端

3.3.6 Redis的停止

3.3.7 第三方工具(redis-desktop-manager)操作redis

四、Redis數(shù)據(jù)結(jié)構(gòu)

五、Redis常用指令?

5.1?string類型

5.1.1 常用指令?

5.1.2?應(yīng)用場景之自增主鍵

5.2?hash類型

5.2.1 常用指令

5.2.2?string類型和hash類型的區(qū)別

5.2.3?應(yīng)用之存儲商品信息

5.3?list類型

5.3.1?ArrayList與LinkedList的區(qū)別

5.3.2 常用命令

5.3.3?應(yīng)用之商品評論列表

5.4 set類型

5.4.1?set類型介紹

5.4.2 常用命令

5.4.3?集合運算命令

5.4.4 其他命令

5.5?zset類型 (sortedset)

5.5.1?zset介紹

5.5.2 常用命令

5.5.3?其它命令

5..5.4?應(yīng)用之商品銷售排行榜

5.6?HyperLogLog命令

5.6.1??HyperLogLog命令介紹

5.6.2??HyperLogLog的優(yōu)點

5.6.3?HyperLogLog 相關(guān)的一些基本命令。

六、Redis的通用命令

七、Redis的事務(wù)

7.1?Redis事務(wù)介紹

7.2?Redis事務(wù)命令

7.3?事務(wù)演示

7.4?事務(wù)失敗處理

八、Redis發(fā)布訂閱模式

九、Jedis連接Redis

9.1?創(chuàng)建項目,導(dǎo)入依賴

9.2?鏈接服務(wù)器

9.2.1?方案一 :單實例鏈接

9.2.2?方案二:連接池

十、Redis持久化方式

10.1?什么是Redis持久化

10.2?Redis 持久化存儲方式

10.2.1?RDB持久化

10.2.2?AOF持久化

10.2.3?AOF與RDB區(qū)別

十一、Redis主從復(fù)制

11.1?主從搭建步驟:

十二、Redis哨兵模式

12.1?第一步:配置哨兵:

12.2?第二步:啟動哨兵:

12.3?第三步:主機宕機

十三、Redis集群方案

13.1?redis-cluster架構(gòu)圖

13.2?redis-cluster投票:容錯

13.3?集群搭建步驟

13.4?連接集群

13.5?查看集群信息

13.6?查看集群中節(jié)點信息

13.7?Jedis連接集群

13.7.1?關(guān)閉防火墻

13.7.2?代碼實現(xiàn)

十四、Redis高端面試-緩存穿透,緩存擊穿,緩存雪崩問題

14.1?緩存的概念

14.2?緩存雪崩

14.3?緩存穿透

14.4?緩存擊穿

十五、Redis高端面試-分布式鎖

15.1?使用分布式鎖要滿足的幾個條件:

15.2?什么是分布式鎖?

15.3?應(yīng)用的場景

15.4?使用redis的setNX命令實現(xiàn)分布式鎖

15.4.1 實現(xiàn)的原理:

15.4.2?基本命令解析


一、了解NoSql

1.1?什么是Nosql?

NoSQL,泛指非關(guān)系型的數(shù)據(jù)庫。隨著互聯(lián)網(wǎng)web2.0網(wǎng)站的興起,傳統(tǒng)的關(guān)系數(shù)據(jù)庫在處理web2.0網(wǎng)站,特別是超大規(guī)模和高并發(fā)的SNS類型的web2.0純動態(tài)網(wǎng)站已經(jīng)顯得力不從心,出現(xiàn)了很多難以克服的問題,而非關(guān)系型的數(shù)據(jù)庫則由于其本身的特點得到了非常迅速的發(fā)展。NoSQL數(shù)據(jù)庫的產(chǎn)生就是為了解決大規(guī)模數(shù)據(jù)集合,多重數(shù)據(jù)種類帶來的挑戰(zhàn),尤其是大數(shù)據(jù)應(yīng)用難題。

NoSQL最常見的解釋是“non-relational”, “Not Only SQL”也被很多人接受。NoSQL僅僅是一個概念,泛指非關(guān)系型的數(shù)據(jù)庫,區(qū)別于關(guān)系數(shù)據(jù)庫,它們不保證關(guān)系數(shù)據(jù)的ACID特性。?

1.2?為什么要使用NoSql?

傳統(tǒng)的數(shù)據(jù)庫遇到的瓶頸:

傳統(tǒng)的關(guān)系數(shù)據(jù)庫具有不錯的性能,高穩(wěn)定型,久經(jīng)歷史考驗,而且使用簡單,功能強大,同時也積累 了大量的成功案例。在互聯(lián)網(wǎng)領(lǐng)域,MySQL成為了絕對靠前的王者,毫不夸張的說,MySQL為互聯(lián)網(wǎng) 的發(fā)展做出了卓越的貢獻(xiàn)。?

在90年代,一個網(wǎng)站的訪問量一般都不大,用單個數(shù)據(jù)庫完全可以輕松應(yīng)付。在那個時候,更多的都是 靜態(tài)網(wǎng)頁,動態(tài)交互類型的網(wǎng)站不多。
到了最近10年,網(wǎng)站開始快速發(fā)展?;鸨恼搲⒉┛?、sns、微博逐漸引領(lǐng)web領(lǐng)域的潮流。在初 期,論壇的流量其實也不大,如果你接觸網(wǎng)絡(luò)比較早,你可能還記得那個時候還有文本型存儲的論壇程 序,可以想象一般的論壇的流量有多大。

現(xiàn)在網(wǎng)站的特點:
(1) 高并發(fā)讀寫
????????Web2.0網(wǎng)站,數(shù)據(jù)庫并發(fā)負(fù)載非常高,往往達(dá)到每秒上萬次的讀寫請求
(2) 高容量存儲和高效存儲
????????Web2.0網(wǎng)站通常需要在后臺數(shù)據(jù)庫中存儲海量數(shù)據(jù),如何存儲海量數(shù)據(jù)并進(jìn)行高效的查詢往往是一個 挑戰(zhàn)
(3) 高擴展性和高可用性
????????隨著系統(tǒng)的用戶量和訪問量與日俱增,需要數(shù)據(jù)庫能夠很方便的進(jìn)行擴展、維護(hù)?

1.3?NoSql數(shù)據(jù)庫的優(yōu)勢

(1) 易擴展

????????NoSQL數(shù)據(jù)庫種類繁多,但是一個共同的特點都是去掉關(guān)系數(shù)據(jù)庫的關(guān)系型特性。數(shù)據(jù)之間無關(guān)系,這 樣就非常容易擴展。也無形之間,在架構(gòu)的層面上帶來了可擴展的能力。

(2)大數(shù)據(jù)量,高性能

????????NoSQL數(shù)據(jù)庫都具有非常高的讀寫性能,尤其在大數(shù)據(jù)量下,同樣表現(xiàn)優(yōu)秀。這得益于它的無關(guān)系性, 數(shù)據(jù)庫的結(jié)構(gòu)簡單。一般MySQL使用Query Cache,每次表的更新Cache就失效,是一種大粒度的 Cache,在針對web2.0的交互頻繁的應(yīng)用,Cache性能不高。而NoSQL的Cache是記錄級的,是一種細(xì) 粒度的Cache,所以NoSQL在這個層面上來說就要性能高很多了。

?(3)靈活的數(shù)據(jù)模型

????????NoSQL無需事先為要存儲的數(shù)據(jù)建立字段,隨時可以存儲自定義的數(shù)據(jù)格式。而在關(guān)系數(shù)據(jù)庫里,增刪 字段是一件非常麻煩的事情。如果是非常大數(shù)據(jù)量的表,增加字段簡直就是一個噩夢。這點在大數(shù)據(jù)量 的web2.0時代尤其明顯。?

(4) 高可用?

????????NoSQL在不太影響性能的情況,就可以方便的實現(xiàn)高可用的架構(gòu)。比如Cassandra,HBase模型,通過 復(fù)制模型也能實現(xiàn)高可用。?

1.4 常見的NoSql產(chǎn)品?

?1.5 各產(chǎn)品的區(qū)別

Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

二、Redis介紹

2.1什么是Redis?

全稱:REmote DIctionary Server(遠(yuǎn)程字典服務(wù)器)。是完全開源免費的,用C語言編寫的, 遵守BCD協(xié)議。是一個高性能的(key/value)分布式內(nèi)存數(shù)據(jù)庫,?基于內(nèi)存運行并支持持久化的NoSQL數(shù)據(jù)庫,是當(dāng)前最熱門的NoSql數(shù)據(jù)庫之一,也被人們稱為數(shù)據(jù)結(jié)構(gòu)服務(wù)器。

Redis 與其他 key - value 緩存產(chǎn)品有以下三個特點:
????????(1) Redis支持?jǐn)?shù)據(jù)的持久化,可以將內(nèi)存中的數(shù)據(jù)保持在磁盤中,重啟的時候可以再次加載進(jìn)行使用
????????(2) Redis不僅僅支持簡單的key-value類型的數(shù)據(jù),同時還提供list,set,zset,hash等數(shù)據(jù)結(jié)構(gòu)的存儲
????????(3) Redis支持?jǐn)?shù)據(jù)的備份,即master-slave(主從)模式的數(shù)據(jù)備份

2.2 Redis優(yōu)勢?

? (1) 性能極高 – Redis能讀的速度是110000次/s,寫的速度是81000次/s 。
? (2) 豐富的數(shù)據(jù)類型 – Redis支持二進(jìn)制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 數(shù)據(jù)類型操作。
? (3) 原子 – Redis的所有操作都是原子性的,同時Redis還支持對幾個操作全并后的原子性執(zhí)行。
? (4) 豐富的特性 – Redis還支持 publish/subscribe, 通知, key 過期等等特性
? (5) 采用單線程,避免了不必要的上下文切換和競爭條件,也不存在多進(jìn)程或者多線程導(dǎo)致的切換而消耗 CPU,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作,沒有因為可能出現(xiàn)死鎖而導(dǎo)致的性能消耗;
? (6) 使用多路I/O復(fù)用模型,非阻塞IO;?

2.3 Redis應(yīng)用場景

(1) 緩存(數(shù)據(jù)查詢,短連接,新聞內(nèi)容,商品內(nèi)容等),使用最多
(2) 聊天室在線好友列表
(3) 任務(wù)隊列(秒殺,搶購,12306等)
(4) 應(yīng)用排行榜
(5) 網(wǎng)站訪問統(tǒng)計
(6) 數(shù)據(jù)過期處理(可以精確到毫秒)
(7) 分布式集群架構(gòu)中的session問題?

2.4 Redis下載

(1)Http://redis.io/ 英文地址
(2)Http://www.redis.cn/ 中文地址?

三、Linux下安裝Redis

3.1 環(huán)境準(zhǔn)備?

(1)虛擬機版本:VMware? Workstation 12 Pro
(2) Linux系統(tǒng):Centos Release 6.5
(3) 遠(yuǎn)程命令端:xshell
(4)文件傳輸工具:SecureFXPortable

3.2 Redis的安裝

3.2.1 Redis的編譯環(huán)境

Redis是C語言開發(fā)的,安裝redis需要先去官網(wǎng)下載源碼進(jìn)行編譯,編譯需要依賴于GCC編譯環(huán)境,如果CentOS上沒有安裝gcc編譯環(huán)境,需要提前安裝,安裝命令如下:(這里我們使用root用戶處理這些操作)?

[root@localhost ~]# yum install gcc-c++

如果提示是否下載,選擇: y

Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

?如果提示是否安裝,選擇: y

Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

3.2.2 Redis的安裝

(1) 使用SecureFXPortable上傳Redis安裝文件到Linux目錄

Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

?(2)上傳Redis安裝文件,這里我上傳自建文件夾: /home/yhp/local

Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

(3)解壓redis文件

[root@localhost local]# tar -zxvf redis-5.0.5.tar.gz

Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存(4)編譯Redis(編譯,將.c文件編譯為.o文件)

? ? ?進(jìn)入解壓文件夾,執(zhí)行 make

[root@localhost local]# cd redis-5.0.5
[root@localhost redis-5.0.5]# make

Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

?編譯成功!如果編譯過程中出錯,先刪除安裝文件目錄,后解壓重新編譯。

(5) 安裝

[root@localhost redis-5.0.5]# make PREFIX=/home/admin/myapps/redis install

說明:這里的/home/myapps/redis 是自定義的redis安裝路徑

Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

(6)安裝之后的bin目錄

Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存?bin文件夾下的命令:

Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

(7) Copy文件

Redis啟動需要一個配置文件,可以修改端口號信息。將redis解壓的文件夾中的redis.conf文件復(fù)制到安裝目錄

[root@localhost redis-5.0.5]# cp redis.conf /home/admin/myapps/redis

3.3 Redis的啟動

3.3.1 Redis的前端模式啟動(了解)

直接運行bin/redis-server將使永前端模式啟動,前端模式啟動的缺點是啟動完成后,不能再進(jìn)行其他操作,如果要操作必須使用ctrl+c,同時redis-server程序結(jié)束,不推薦此方法。?

[root@localhost bin]# ./redis-server

?下面是啟動界面(這個界面只能啟動,啟動后不能進(jìn)行其他操作)

Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

?使用ctrl+c退出前端啟動。

3.3.2 Redis的后端啟動?

修改redis.conf配置文件,設(shè)置:daemonize yes,然后可以使用后端模式啟動。

[root@localhost redis]# vi redis.conf

Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

啟動時,指定配置文件(這里所在文件夾是redis)

[root@localhost redis]# ./bin/redis-server ./redis.conf

?Redis默認(rèn)端口:6379,通過當(dāng)前服務(wù)進(jìn)行查看

[root@localhost redis]# ps -ef | grep -i redis

Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

?3.3.3 客戶端訪問redis

如果想要通過指令來操作redis,可以使用redis的客戶端進(jìn)行操作,在bin文件夾下運行redis-cli

該指令默認(rèn)連接的127.0.0.1 ,端口號是6379

[root@localhost bin]# ./redis-cli
127.0.0.1:6379>

?如果想要連接指定的ip地址以及端口號,則需要按照

redis-cli -h ip地址 -p 端口號

3.3.4 向Redis服務(wù)器發(fā)送命令

Ping,測試客戶端與Redis的連接是否正常,如果連接正常,回收到pong?

127.0.0.1:6379> ping
PONG

?3.3.5 退出客戶端

127.0.0.1:6379> quit

?3.3.6 Redis的停止

(1) 強制結(jié)束程序。強制終止Redis進(jìn)程可能會導(dǎo)致redis持久化數(shù)據(jù)丟失。

語法:kill -9 pidRedis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存?(2) 正確停止Redis的方式應(yīng)該是向Redis發(fā)送SHUTDOWN命令,方法為(關(guān)閉默認(rèn)的端口)

Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

3.3.7 第三方工具(redis-desktop-manager)操作redis

?Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

?注意:需要關(guān)閉linux防火墻并且修改redis.conf文件中的bind參數(shù)

bind linux的ip地址

此時如果通過redis客戶端訪問的時候,代碼如下:

./redis-cli -h 192.168.197.132 -p 6379

四、Redis數(shù)據(jù)結(jié)構(gòu)

Redis 是一種基于內(nèi)存的數(shù)據(jù)庫,并且提供一定的持久化功能,它是一種鍵值(key-value)數(shù)據(jù)庫,使用 key 作為 索引找到當(dāng)前緩存的數(shù)據(jù),并且返回給程序調(diào)用者。
當(dāng)前的 Redis 支持 6 種數(shù)據(jù)類型,它們分別是字符串(String)、列表(List)、集合(set)、哈希結(jié)構(gòu) (hash)、有序集合(zset)和基數(shù)( HyperLogLog

Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

五、Redis常用指令?

5.1?string類型

5.1.1 常用指令?

(1)?賦值?

語法:
SET key value

示例:

127.0.0.1:6379> set test 123
OK

(2)?取值

語法:
GET key
示例:
127.0.0.1:6379> get test 
"123"

(3)?取值并賦值

語法:

GETSET key value

?示例:

127.0.0.1:6379> getset s2 222 
"111" 
127.0.0.1:6379> get s2 
"222"

(4)?數(shù)值增減

注意事項 :

1 、 當(dāng) value 為整數(shù)數(shù)據(jù)時,才能使用以下命令操作數(shù)值的增減。
2 、 數(shù)值遞增都是【原子】操作。
3 redis 中的每一個單獨的命令都是原子性操作。當(dāng)多個命令一起執(zhí)行的時候,就不能保證? ? ? ? 原子性,不過我們可以使 用事務(wù)和lua 腳本來保證這一點。

非原子性操作示例:

int i = 1; 
i++; 
System.out.println(i)

(5)?遞增數(shù)字

語法(increment):

INCR key
示例:
127.0.0.1:6379> incr num (integer) 
1 
127.0.0.1:6379> incr num (integer) 
2 
127.0.0.1:6379> incr num (integer) 
3

(6)?增加指定的整數(shù)

語法:
INCRBY key increment 
示例:
127.0.0.1:6379> incrby num 2 
(integer) 5 
127.0.0.1:6379> incrby num 2 
(integer) 7 
127.0.0.1:6379> incrby num 2 
(integer) 9

(7)?遞減數(shù)值

語法:
DECR key
示例:
127.0.0.1:6379> incr num 
(integer) 1 
127.0.0.1:6379> incr num 
(integer) 2 
127.0.0.1:6379> incr num 
(integer) 3

(8)?減少指定的整數(shù)

語法:
DECRBY key decrement
示例:
127.0.0.1:6379> decr num 
(integer) 6 
127.0.0.1:6379> decr num 
(integer) 5 
127.0.0.1:6379> decrby num 3 
(integer) 2 
127.0.0.1:6379> decrby num 3 
(integer) -1

(9)?僅當(dāng)不存在時賦值

使用該命令可以實現(xiàn)【分布式鎖】的功能,后續(xù)講解?。?!
語法:
setnx key value
示例:
redis> EXISTS job               # job 不存在 
(integer) 0 
redis> SETNX job "programmer"   # job 設(shè)置成功 
(integer) 1 
redis> SETNX job "code-farmer"  # 嘗試覆蓋 job ,失敗 
(integer) 0 
redis> GET job                  # 沒有被覆蓋 
"programmer"

(10)?其它命令

1.向尾部追加值 ?

APPEND 命令,向鍵值的末尾追加 value 。
如果鍵不存在則將該鍵的值設(shè)置為 value ,即相當(dāng)于 SET key value 。返回值是追加后字符串的總長度。
語法:
APPEND key value
示例:
127.0.0.1:6379> set str hello 
OK
127.0.0.1:6379> append str " world!" 
(integer) 12 
127.0.0.1:6379> get str 
"hello world!"

2.獲取字符串長度

STRLEN 命令,返回鍵值的長度,如果鍵不存在則返回0。
語法:
STRLEN key
示例:
127.0.0.1:6379> strlen str 
(integer) 0 
127.0.0.1:6379> set str hello 
OK
127.0.0.1:6379> strlen str 
(integer) 5

3.同時設(shè)置/獲取多個鍵值

語法:
MSET key value [key value …]
MGET key [key …]
示例:
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 
OK
127.0.0.1:6379> get k1 "v1" 
127.0.0.1:6379> mget k1 k3 
1) "v1" 
2) "v3"

5.1.2?應(yīng)用場景之自增主鍵

需求:商品編號、訂單號采用 INCR 命令生成。
設(shè)計: key 命名要有一定的設(shè)計
實現(xiàn):定義商品編號 key items:id
192.168.101.3:7003> INCR items:id (integer) 
2 
192.168.101.3:7003> INCR items:id (integer) 
3

5.2?hash類型

hash 類型也叫 散列類型,它提供了字段和字段值的映射。字段值只能是字符串類型,不支持散列類型、集合類型等 其它類型。如下:
Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

5.2.1 常用指令

(1)?賦值

HSET 命令不區(qū)分插入和更新操作,當(dāng)執(zhí)行插入操作時 HSET 命令返回 1 ,當(dāng)執(zhí)行更新操作時返回 0 。
1.設(shè)置一個字段值:
語法:
HSET key field value
示例:
127.0.0.1:6379> hset user username zhangsan 
(integer) 1
2.設(shè)置多個字段值
語法:
HMSET key field value [field value ...]
示例:
127.0.0.1:6379> hmset user age 20 username lisi 
OK
3.當(dāng)字段不存在時賦值
類似 HSET ,區(qū)別在于如果字段存在,該命令不執(zhí)行任何操作
語法:
HSETNX key field value
示例:
127.0.0.1:6379> hsetnx user age 30 # 如果user中沒有age字段則設(shè)置age值為30,否則不做任何操作 
(integer) 0

(2)?取值

1.獲取一個字段值

語法:
HGET key field
示例:
127.0.0.1:6379> hget user username 
"zhangsan“

2.獲取多個字段值

語法:
HMGET key field [field ...] 1
示例:
127.0.0.1:6379> hmget user age username 
1) "20" 
2) "lisi"

3.獲取所有字段值

語法:
HGETALL key
示例:
127.0.0.1:6379> hgetall user 
1) "age" 
2) "20" 
3) "username" 
4) "lisi"

(3)?刪除字段

可以刪除一個或多個字段,返回值是被刪除的字段個數(shù)
語法:
HDEL key field [field ...]
示例:
127.0.0.1:6379> hdel user age 
(integer) 1 
127.0.0.1:6379> hdel user age name 
(integer) 0 
127.0.0.1:6379> hdel user age username 
(integer) 1

(4)?增加數(shù)字

語法:
HINCRBY key field increment 
示例:
127.0.0.1:6379> hincrby user age 2 # 將用戶的年齡加2 
(integer) 22 
127.0.0.1:6379> hget user age # 獲取用戶的年齡 
"22“

(5)?其它命令

1.判斷字段是否存在

語法:
HEXISTS key field
示例:
127.0.0.1:6379> hexists user age 查看user中是否有age字段 
(integer) 1 
127.0.0.1:6379> hexists user name 查看user中是否有name字段 
(integer) 0

2.只獲取字段名或字段值

語法:
HKEYS key 
HVALS key
示例:
127.0.0.1:6379> hmset user age 20 name lisi 
OK
127.0.0.1:6379> hkeys user 
1) "age" 
2) "name" 
127.0.0.1:6379> hvals user 
1) "20" 
2) "lisi"

3.獲取字段數(shù)量

語法:
HLEN key
示例:
127.0.0.1:6379> hlen user 
(integer) 2

4.獲取所有字段

獲得 hash 的所有信息,包括 key 和 value
語法:
hgetall key

5.2.2?string類型和hash類型的區(qū)別

hash類型適合存儲那些對象數(shù)據(jù),特別是對象屬性經(jīng)常發(fā)生【增刪改】操作的數(shù)據(jù)。 string類型也可以存儲對象數(shù) 據(jù),將java對象轉(zhuǎn)成json字符串進(jìn)行存儲,這種存儲適合【查詢】操作。

5.2.3?應(yīng)用之存儲商品信息

?商品信息字段

【商品id、商品名稱、商品描述、商品庫存、商品好評】
定義商品信息的key
商品ID為1001的信息在 Redis中的key為:[items:1001]
存儲商品信息
192.168.101.3:7003> HMSET items:1001 id 3 name apple price 999.9 
OK
獲取商品信息
192.168.101.3:7003> HGET items:1001 id 
"3" 
192.168.101.3:7003> HGETALL items:1001 
1) "id" 
2) "3" 
3) "name"
4) "apple" 
5) "price" 
6) "999.9"

5.3?list類型

Redis 的列表類型( list 類型)可以 存儲一個有序的字符串列表 ,常用的操作是向列表兩端添加元素,或者獲得列表 的某一個片段。
列表類型內(nèi)部是使用 雙向鏈表( double linked list 實現(xiàn)的,所以向列表兩端添加元素的時間復(fù)雜度為 o(1) ,獲取越接近兩端的元素速度就越快。這意味著即使是一個有幾千萬個元素的列表,獲取頭部或尾部的10條記 錄也是極快的。

5.3.1?ArrayList與LinkedList的區(qū)別

ArrayList 使用數(shù)組方式存儲數(shù)據(jù),所以根據(jù)索引查詢數(shù)據(jù)速度快,而新增或者刪除元素時需要設(shè)計到位移操作, 所以比較慢。
LinkedList 使用雙向鏈表方式存儲數(shù)據(jù),每個元素都記錄前后元素的指針,所以插入、刪除數(shù)據(jù)時只是更改前后元 素的指針指向即可,速度非??臁H缓笸ㄟ^下標(biāo)查詢元素時需要從頭開始索引,所以比較慢,但是如果查詢前幾個元 素或后幾個元素速度比較快。

Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

5.3.2 常用命令

(1)?LPUSH/RPUSH

語法:
LPUSH key value [value ...] 
RPUSH key value [value ...]
示例:
127.0.0.1:6379> lpush list:1 1 2 3 
(integer) 3 
127.0.0.1:6379> rpush list:1 4 5 6 
(integer) 3

(2)?LRANGE

獲取列表中的某一片段。將返回 `start` `stop` 之間的所有元素(包含兩端的元素),索引從`0` 開始。索引可以 是負(fù)數(shù),如:“`-1`” 代表最后邊的一個元素。
語法:
LRANGE key start stop
示例:
127.0.0.1:6379> lrange list:1 0 2 
1) "2" 
2) "1" 
3) "4"

(3)?LPOP/RPOP

從列表兩端彈出元素
從列表左邊彈出一個元素,會分兩步完成:
第一步是將列表左邊的元素從列表中移除
第二步是返回被移除的元素值。
語法:
LPOP key 
RPOP key
示例 :
127.0.0.1:6379>lpop list:1 
"3"
127.0.0.1:6379>rpop list:1 
"6"

(4)?LLEN

獲取列表中元素的個數(shù)
語法:
llen key
示例:
127.0.0.1:6379> llen list:1 
(integer) 2

(5)?其它命令

1.?LREM

刪除列表中指定個數(shù)的值
LREM 命令會刪除列表中前 count 個值為 value 的元素,返回實際刪除的元素個數(shù)。根據(jù) count 值的不同,該命令的 執(zhí)行方式會有所不同:
- 當(dāng) count>0 時, LREM 會從列表左邊開始刪除。
- 當(dāng) count<0 時, LREM 會從列表后邊開始刪除。
- 當(dāng) count=0 時, LREM 刪除所有值為 value 的元素。
語法:
LREM key count value

2.?LINDEX

獲得指定索引的元素值
語法:
LINDEX key index
示例:
127.0.0.1:6379>lindex l:list 2 
"1"

3.?設(shè)置指定索引的元素值

語法:
LSET key index value

示例:

127.0.0.1:6379> lset l:list 2 2 
OK 
127.0.0.1:6379> lrange l:list 0 -1 
1) "6" 
2) "5" 
3) "2" 
4) "2"

4.?LTRIM

只保留列表指定片段 , 指定范圍和 LRANGE 一致
語法:
LTRIM key start stop
示例:
127.0.0.1:6379> lrange l:list 0 -1 
1) "6" 
2) "5" 
3) "0" 
4) "2" 
127.0.0.1:6379> ltrim l:list 0 2 
OK
127.0.0.1:6379> lrange l:list 0 -1 
1) "6" 
2) "5" 
3) "0"

5.?LINSERT

向列表中插入元素。
該命令首先會在列表中從左到右查找值為 pivot 的元素,然后根據(jù)第二個參數(shù)是BEFORE 還是 AFTER 來決定將 value 插 入到該元素的前面還是后面。
語法:
LINSERT key BEFORE|AFTER pivot value
示例:
127.0.0.1:6379> lrange list 0 -1 
1) "3" 
2) "2" 
3) "1" 
127.0.0.1:6379> linsert list after 3 4 
(integer) 4 
127.0.0.1:6379> lrange list 0 -1 
1) "3" 
2) "4" 
3) "2" 
4) "1"

6.?RPOPLPUSH

將元素從一個列表轉(zhuǎn)移到另一個列表中
語法:
RPOPLPUSH source destination

示例:

127.0.0.1:6379> rpoplpush list newlist 
"1" 
127.0.0.1:6379> lrange newlist 0 -1 
1) "1" 
127.0.0.1:6379> lrange list 0 -1 
1) "3" 
2) "4" 
3) "2"

5.3.3?應(yīng)用之商品評論列表

需求:
用戶針對某一商品發(fā)布評論,一個商品會被不同的用戶進(jìn)行評論,存儲商品評論時,要按時間順序排序。
用戶在前端頁面查詢該商品的評論,需要按照時間順序降序排序。
分析:
使用 list 存儲商品評論信息, KEY 是該商品的 ID VALUE 是商品評論信息列表
實現(xiàn):商品編號為 1001 的商品評論 key items: comment:1001
192.168.101.3:7001> LPUSH items:comment:1001 '{"id":1,"name":"商品不錯,很 好!!","date":1430295077289}'

5.4 set類型

5.4.1?set類型介紹

set 類型即集合類型,其中的數(shù)據(jù)是不重復(fù)且沒有順序。 ?

集合類型和列表類型的對比:
Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存
集合類型的常用操作是向集合中加入或刪除元素、判斷某個元素是否存在等,由于集合類型的 Redis 內(nèi)部是使用值 為空的散列表實現(xiàn),所有這些操作的時間復(fù)雜度都為 0(1)
Redis 還提供了多個集合之間的 交集、并集、差集 的運算。

5.4.2 常用命令

(1)?SADD/SREM? ??添加元素/刪除元素

語法:
SADD key member [member ...]
SREM key member [member ...]
示例:
127.0.0.1:6379> sadd set a b c
(integer) 3
127.0.0.1:6379> sadd set a
(integer) 0
127.0.0.1:6379> srem set c d
(integer) 1

(2)?SMEMBERS??獲得集合中的所有元素

語法:
SMEMBERS key
示例:
127.0.0.1:6379> smembers set
1) "b"
2) "a”

(3)?SISMEMBER??判斷元素是否在集合中

語法:
SISMEMBER key member
示例:
127.0.0.1:6379>sismember set a
(integer) 1
127.0.0.1:6379>sismember set h
(integer) 0

5.4.3?集合運算命令

(1)?SDIFF??集合的差集運算 A-B:屬于A并且不屬于B的元素構(gòu)成的集合。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

語法:
SDIFF key [key ...]
示例:
127.0.0.1:6379> sadd setA 1 2 3
(integer) 3
127.0.0.1:6379> sadd setB 2 3 4
(integer) 3
127.0.0.1:6379> sdiff setA setB
1) "1"
127.0.0.1:6379> sdiff setB setA
1) "4"

(2)?SINTER??集合的交集運算 A ∩ B:屬于A且屬于B的元素構(gòu)成的集合。?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

語法:

SINTER key [key ...]
示例:
127.0.0.1:6379> sinter setA setB
1) "2"
2) "3"

(3)?SUNION??集合的并集運算 A B:屬于A或者屬于B的元素構(gòu)成的集合

語法:
SUNION key [key ...]
示例:
127.0.0.1:6379> sunion setA setB
1) "1"
2) "2"
3) "3"
4) "4"

5.4.4 其他命令

(1)?SCARD??獲得集合中元素的個數(shù)

語法:
SCARD key
示例:
127.0.0.1:6379> smembers setA
1) "1"
2) "2"
3) "3"
127.0.0.1:6379> scard setA
(integer) 3

(2)?SPOP? ?

從集合中彈出一個元素。
注意:由于集合是無序的,所有 SPOP 命令會從集合中隨機選擇一個元素彈出
語法:
SPOP key
示例:
127.0.0.1:6379> spop setA
"1"

5.5?zset類型 (sortedset)

5.5.1?zset介紹

set 集合類型的基礎(chǔ)上,有序集合類型為集合中的每個元素都 關(guān)聯(lián)一個分?jǐn)?shù) ,這使得我們不僅可以完成插入、刪除 和判斷元素是否存在在集合中,還能夠獲得分?jǐn)?shù)最高或最低的前N個元素、獲取指定分?jǐn)?shù)范圍內(nèi)的元素等與分?jǐn)?shù)有關(guān) 的操作。
在某些方面有序集合和列表類型有些相似:
1 、二者都是有序的。
2 、二者都可以獲得某一范圍的元素。

但是,二者有著很大區(qū)別:

1 、列表類型是通過鏈表實現(xiàn)的,獲取靠近兩端的數(shù)據(jù)速度極快,而當(dāng)元素增多后,訪問中間數(shù)據(jù)的速度會變慢。
2 、有序集合類型使用散列表實現(xiàn),所有即使讀取位于中間部分的數(shù)據(jù)也很快。
3 、列表中不能簡單的調(diào)整某個元素的位置,但是有序集合可以(通過更改分?jǐn)?shù)實現(xiàn))
4 、有序集合要比列表類型更耗內(nèi)存。

5.5.2 常用命令

(1)?ZADD

增加元素。
向有序集合中加入一個元素和該元素的分?jǐn)?shù),如果該元素已經(jīng)存在則會用新的分?jǐn)?shù)替換原有的分?jǐn)?shù)。返回值是新加入到 集合中的元素個數(shù),不包含之前已經(jīng)存在的元素。

語法:

ZADD key score member [score member ...]

?示例:

127.0.0.1:6379> zadd scoreboard 80 zhangsan 89 lisi 94 wangwu
(integer) 3
127.0.0.1:6379> zadd scoreboard 97 lisi
(integer) 0

(2)?ZRANGE/ZREVRANGE

獲得排名在某個范圍的元素列表。
- ZRANGE:按照元素分?jǐn)?shù)從小到大的順序返回索引從start到stop之間的所有元素(包含兩端的元素)
- ZREVRANGE:按照元素分?jǐn)?shù)從大到小的順序返回索引從start到stop之間的所有元素(包含兩端的元素)
語法:
ZRANGE key start stop [WITHSCORES]
ZREVRANGE key start stop [WITHSCORES]
示例:
127.0.0.1:6379> zrange scoreboard 0 2
1) "zhangsan"
2) "wangwu"
3) "lisi“
127.0.0.1:6379> zrevrange scoreboard 0 2
1) " lisi "
2) "wangwu"
3) " zhangsan “
如果需要獲得元素的分?jǐn)?shù)的可以在命令尾部加上 WITHSCORES 參數(shù)
127.0.0.1:6379> zrange scoreboard 0 1 WITHSCORES
1) "zhangsan"
2) "80"
3) "wangwu"
4) "94"
(3) ZSCORE?? 獲取元素的分?jǐn)?shù)
語法:
ZSCORE key member 
示例:
127.0.0.1:6379> zscore scoreboard lisi
"97"

(4)?ZREM?

刪除元素。
移除有序集合 key 中的一個或多個成員,不存在的成員將被忽略。
當(dāng) key 存在但不是有序集類型時,返回一個錯誤。
語法:
ZREM key member [member ...]
示例:
127.0.0.1:6379> zrem scoreboard lisi
(integer) 1

5.5.3?其它命令

(1) ZRANGEBYSCORE? ? 獲得指定分?jǐn)?shù)范圍的元素。
語法:
ZRANGEBYSCORE key min max [WITHSCORES]
示例:
127.0.0.1:6379> ZRANGEBYSCORE scoreboard 90 97 WITHSCORES
1) "wangwu"
2) "94"
3) "lisi"
4) "97"
127.0.0.1:6379> ZRANGEBYSCORE scoreboard 70 100 limit 1 2
1) "wangwu"
2) "lisi"

(2)?ZINCRBY? ?

增加某個元素的分?jǐn)?shù)。
返回值是更改后的分?jǐn)?shù)
語法:
ZINCRBY key increment member
示例:
127.0.0.1:6379> ZINCRBY scoreboard 4 lisi
"101"

(3)?ZCARD? ??獲得集合中元素的數(shù)量。

語法:
ZCARD key
示例:
127.0.0.1:6379> ZCARD scoreboard
(integer) 3

(4)?ZCOUNT??獲得指定分?jǐn)?shù)范圍內(nèi)的元素個數(shù)

語法:
ZCOUNT key min max
示例:
127.0.0.1:6379> ZCOUNT scoreboard 80 90
(integer) 1

(5)?ZREMRANGEBYRANK? ??按照排名范圍刪除元素

語法:
ZREMRANGEBYRANK key start stop
示例:
127.0.0.1:6379> ZREMRANGEBYRANK scoreboard 0 1
(integer) 2
127.0.0.1:6379> ZRANGE scoreboard 0 -1
1) "lisi"

(6)?ZREMRANGEBYSCORE? ?按照分?jǐn)?shù)范圍刪除元素

語法:
ZREMRANGEBYSCORE key min max
示例:
127.0.0.1:6379> zadd scoreboard 84 zhangsan
(integer) 1
127.0.0.1:6379> ZREMRANGEBYSCORE scoreboard 80 100
(integer) 1

(7)?ZRANK/ZREVRANK

獲取元素的排名。
- ZRANK :從小到大
- ZREVRANK :從大到小
語法:
ZRANK key member
ZREVRANK key member
示例:
127.0.0.1:6379> ZRANK scoreboard lisi
(integer) 0
127.0.0.1:6379> ZREVRANK scoreboard zhangsan
(integer) 1

5..5.4?應(yīng)用之商品銷售排行榜

需求:
根據(jù)商品銷售量對商品進(jìn)行排行顯示
設(shè)計:
定義商品銷售排行榜( sorted set 集合), Key items:sellsort ,分?jǐn)?shù)為商品銷售量。
寫入商品銷售量: 商品編號 1001 的銷量是 9 ,商品編號 1002 的銷量是 10:
192.168.101.3:7007> ZADD items:sellsort 9 1001 10 1002
商品編號 1001 的銷量加 1:
192.168.101.3:7001> ZINCRBY items:sellsort 1 1001
商品銷量前 10 名:
192.168.101.3:7001> ZREVRANGE items:sellsort 0 9 withscores

5.6?HyperLogLog命令

5.6.1??HyperLogLog命令介紹

HyperLogLog 是一種使用隨機化的算法,以少量內(nèi)存提供集合中唯一元素數(shù)量的近似值。
HyperLogLog 可以接受多個元素作為輸入,并給出輸入元素的基數(shù)估算值:
基數(shù):集合中不同元素的數(shù)量。比如 {‘a(chǎn)pple’, ‘banana’, ‘cherry’, ‘banana’, ‘a(chǎn)pple’} 的基數(shù)就是3。
估算值:算法給出的基數(shù)并不是精確的,可能會比實際稍微多一些或者稍微少一些,但會控制在合理的范圍之內(nèi)。

5.6.2??HyperLogLog的優(yōu)點

HyperLogLog 的優(yōu)點是,即使輸入元素的數(shù)量或者體積非常非常大,計算基數(shù)所需的空間總是固定的、并且是很 小的。
Redis 里面,每個 HyperLogLog 鍵只需要花費 12 KB 內(nèi)存,就可以計算接近 2^64 個不同元素的基數(shù)。這和計 算基數(shù)時,元素越多耗費內(nèi)存就越多的集合形成鮮明對比。
但是,因為 HyperLogLog 只會根據(jù)輸入元素來計算基數(shù),而不會儲存輸入元素本身,所以 HyperLogLog 不能像 集合那樣,返回輸入的各個元素。

5.6.3?HyperLogLog 相關(guān)的一些基本命令。

命令
說明
PFADD key element [element …]
將指定的元素添加到指定的 HyperLogLog
PFCOUNT key [key …]
返回給定 HyperLogLog 的基數(shù)估算值
PFMERGE destkey sourcekey [sourcekey …]
將多個 HyperLogLog 合并為一個 HyperLogLog

?示例:

redis 127.0.0.1:6379> PFADD mykey "redis"
1) (integer) 1
redis 127.0.0.1:6379> PFADD mykey "java"
1) (integer) 1
redis 127.0.0.1:6379> PFADD mykey "mysql"
1) (integer) 1
redis 127.0.0.1:6379> PFCOUNT mykey
(integer) 3

六、Redis的通用命令

(1)?keys

返回滿足給定 pattern 的所有 key
語法:
keys pattern
示例:
redis 127.0.0.1:6379> keys mylist*
1) "mylist"
2) "mylist5"
3) "mylist6"
4) "mylist7"
5) "mylist8"

(2)?del

語法:
DEL key
示例:
127.0.0.1:6379> del test
(integer) 1

(3)?exists? ?確認(rèn)一個key 是否存在

語法:
exists key
示例:從結(jié)果來看,數(shù)據(jù)庫中不存在 HongWan 這個 key ,但是 age 這個 key 是存在的
redis 127.0.0.1:6379> exists HongWan
(integer) 0
redis 127.0.0.1:6379> exists age
(integer) 1

(4)?expire(重點)

Redis 在實際使用過程中更多的用作緩存,然而緩存的數(shù)據(jù)一般都是需要設(shè)置生存時間的,即:到期后數(shù)據(jù)銷毀。
語法:
EXPIRE key seconds 設(shè)置key的生存時間(單位:秒)key在多少秒后會自動刪除
TTL key 查看key生于的生存時間
PERSIST key 清除生存時間
PEXPIRE key milliseconds 生存時間設(shè)置單位為:毫秒
示例:
192.168.101.3:7002> set test 1 設(shè)置test的值為1
OK
192.168.101.3:7002> get test 獲取test的值
"1"
192.168.101.3:7002> EXPIRE test 5 設(shè)置test的生存時間為5秒
(integer) 1
192.168.101.3:7002> TTL test 查看test的生于生成時間還有1秒刪除
(integer) 1
192.168.101.3:7002> TTL test
(integer) -2
192.168.101.3:7002> get test 獲取test的值,已經(jīng)刪除
(nil)

(5)?rename??重命名key

語法:
rename oldkey newkey
示例: age 成功的被我們改名為 age_new
redis 127.0.0.1:6379[1]> keys *
1) "age"
redis 127.0.0.1:6379[1]> rename age age_new
OK
redis 127.0.0.1:6379[1]> keys *
1) "age_new"

(6)?type??顯示指定key的數(shù)據(jù)類型

語法:
type key
示例:這個方法可以非常簡單的判斷出值的類型
redis 127.0.0.1:6379> type addr
string
redis 127.0.0.1:6379> type myzset2
zset
redis 127.0.0.1:6379> type mylist
list

七、Redis的事務(wù)

7.1?Redis事務(wù)介紹

Redis 的事務(wù)是通過 MULTI 、 EXEC 、 DISCARD 和 WATCH 、UNWATCH這五個命令來完成的。
Redis 的單個命令都是原子性的,所以這里需要確保事務(wù)性的對象是命令集合。
Redis 將命令集合序列化并確保處于同一事務(wù)的命令集合連續(xù)且不被打斷的執(zhí)行
Redis 不支持回滾操作。

7.2?Redis事務(wù)命令

(1)?MULTI

用于標(biāo)記事務(wù)塊的開始。
Redis 會將后續(xù)的命令逐個放入隊列中,然后使用 EXEC 命令原子化地執(zhí)行這個命令序列。
語法: multi

(2)?EXEC

在一個事務(wù)中執(zhí)行所有先前放入隊列的命令,然后恢復(fù)正常的連接狀態(tài)
語法: exec

(3)?DISCARD

清除所有先前在一個事務(wù)中放入隊列的命令,然后恢復(fù)正常的連接狀態(tài)。
語法: discard

(4)?WATCH

當(dāng)某個 [ 事務(wù)需要按條件執(zhí)行 ] 時,就要使用這個命令將給定的 [ 鍵設(shè)置為受監(jiān)控 ] 的狀態(tài)。
語法:watch key [key…]

(5)?UNWATCH

清除所有先前為一個事務(wù)監(jiān)控的鍵。
語法: unwatch

7.3?事務(wù)演示

?示例一:?

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set s1 111
QUEUED
127.0.0.1:6379> hset set1 name zhangsan
QUEUED
127.0.0.1:6379> exec
1) OK
2) (integer) 1
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set s2 222
QUEUED
127.0.0.1:6379> hset set2 age 20
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> exec
(error) ERR EXEC without MULTI
127.0.0.1:6379> watch s1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set s1 555
QUEUED
127.0.0.1:6379> exec # 此時在沒有exec之前,通過另一個命令窗口對監(jiān)控的s1字段進(jìn)行修改
(nil)
127.0.0.1:6379> get s1
111

示例二:

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set u1 user1
QUEUED
127.0.0.1:6379> get u1
QUEUED
127.0.0.1:6379> sadd tag c++ html java
QUEUED
127.0.0.1:6379> smembers tag
QUEUED
127.0.0.1:6379> exec
1) OK
2) "user1"
3) (integer) 3
4) 1) "java"
   2) "html"
   3) "c++"

7.4?事務(wù)失敗處理

(1)?Redis 語法錯誤(編譯期)

Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

?(2)?Redis 運行錯誤

Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

?(3)?Redis 不支持事務(wù)回滾(為什么呢)

1、大多數(shù)事務(wù)失敗是因為 語法錯誤或者類型錯誤 ,這兩種錯誤,在開發(fā)階段都是可以預(yù)見的
2、 Redis 為了 性能方面 就忽略了事務(wù)回滾。

八、Redis發(fā)布訂閱模式

Redis 發(fā)布訂閱 (pub/sub) 是一種消息通信模式:發(fā)送者 (pub) 發(fā)送消息,訂閱者 (sub) 接收消息。
Redis 客戶端可以訂閱任意數(shù)量的頻道。
下圖展示了頻道 channel1 , 以及訂閱這個頻道的三個客戶端 —— client2 、 client5 client1 之間的關(guān)系:
? ? ? ? ? ? ? ? ? ? ?? Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存
當(dāng)有新消息通過 PUBLISH 命令發(fā)送給頻道 channel1 時, 這個消息就會被發(fā)送給訂閱它的三個客戶端:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存
在我們實例中我們創(chuàng)建了訂閱頻道名為 redisMessage:
127.0.0.1:6379> subscribe redisMessage
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisMessage"
3) (integer) 1
現(xiàn)在,我們先重新開啟個 redis 客戶端,然后在同一個頻道 redisMessage 發(fā)布三次消息,訂閱者就能接收到消 息。
127.0.0.1:6379> publish redisMessage "demo1 test"
(integer) 1
127.0.0.1:6379> publish redisMessage "demo2 test"
(integer) 1
127.0.0.1:6379> publish redisMessage "demo3 test"
(integer) 1
# 訂閱者的客戶端會顯示如下消息
127.0.0.1:6379> subscribe redisMessage
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisMessage"
3) (integer) 1
1) "message"
2) "redisMessage"
3) "demo1 test"
1) "message"
2) "redisMessage"
3) "demo2 test"
1) "message"
2) "redisMessage"
3) "demo3 test"

九、Jedis連接Redis

9.1?創(chuàng)建項目,導(dǎo)入依賴

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.7.2</version>
</dependency>
注意:
1 )確認(rèn)遠(yuǎn)程服務(wù)器是否可以 ping : ping vm ip 地址:
Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存2) 確認(rèn)防火墻是否關(guān)閉或放行
service iptables stop
service iptables status

9.2?鏈接服務(wù)器

9.2.1?方案一 :單實例鏈接

Jedis jedis = new Jedis(“ip地址”, 端口號);//建立鏈接
核心代碼:
public static void main(String[] args) {
    Jedis jedis=new Jedis("192.168.197.129",6379);
    //設(shè)置值
    jedis.set("java001","java工程師");
    String java001 = jedis.get("java001");
    System.out.println(java001);
}
常見異常 :
Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

解決方案:

虛擬機客戶端連接的 ip 127.0.0.1, 意思是連接的本機 , 其他機器無法連接 , 這里需要修改配置文件 , 將連接地址改為虛擬機的地址, 就可以了
修改 redis.conf 文件里面的 bind 連接地址 , 將連接地址改為自己虛擬機的 ip
bind 192.168.197.129
重新啟動服務(wù) ,Jedis 就可以正常連上了
Idea中控制臺打?。?/span> Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

服務(wù)器上存儲:

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

9.2.2?方案二:連接池

jedis 連接池連接 , 后面會使用 Spring 的配置文件來整合。
// 1.獲取連接池配置對象,設(shè)置配置項
JedisPoolConfig config = new JedisPoolConfig();
// 1.1最大的連接數(shù)
config.setMaxTotal(30);
// 1.2最大的空閑
config.setMaxIdle(10);
// 2.獲取連接池
JedisPool jedisPool = new JedisPool(config, "192.168.197.129", 6379);
Jedis jedis = null;
try {
    jedis = jedisPool.getResource();
    // 3.設(shè)置數(shù)據(jù)
    jedis.set("name", "張三");
    String name = jedis.get("name");
    System.out.println("name=" + name);
} catch (Exception e) {
    e.printStackTrace();
} finally {
    if (jedis != null) {
        jedis.close();
    }
    // 4.虛擬機關(guān)閉的時候,釋放資源
    if (jedisPool != null) {
        jedisPool.close();
    }
}
服務(wù)端存儲確認(rèn):
Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

十、Redis持久化方式

10.1?什么是Redis持久化

由于 redis 的值放在內(nèi)存中,為防止突然斷電等特殊情況的發(fā)生,需要對數(shù)據(jù)進(jìn)行持久化備份。即將內(nèi)存數(shù)據(jù)保存
到硬盤。

10.2?Redis 持久化存儲方式

10.2.1?RDB持久化

RDB 是以二進(jìn)制文件,是在某個時間點將數(shù)據(jù)寫入一個臨時文件,持久化結(jié)束后,用這個臨時文件替換上次持久化 的文件,達(dá)到數(shù)據(jù)恢復(fù)。
優(yōu)點:使用單獨子進(jìn)程來進(jìn)行持久化,主進(jìn)程不會進(jìn)行任何 IO 操作,保證了 redis 的高性能
缺點: RDB 是間隔一段時間進(jìn)行持久化,如果持久化之間 redis 發(fā)生故障,會發(fā)生數(shù)據(jù)丟失。所以這種方式更適合 數(shù)據(jù)要求不嚴(yán)謹(jǐn)?shù)臅r候
這里說的這個執(zhí)行數(shù)據(jù)寫入到臨時文件的時間點是可以通過配置來自己確定的,通過配置 redis n 秒內(nèi)如果超過 m 個 key 被修改這執(zhí)行一次 RDB 操作。這個操作就類似于在這個時間點來保存一次 Redis 的所有數(shù)據(jù),一次快照 數(shù)據(jù)。所有這個持久化方法也通常叫做 snapshots
RDB 默認(rèn)開啟, redis.conf 中的具體配置參數(shù)如下;
#dbfilename:持久化數(shù)據(jù)存儲在本地的文件
dbfilename dump.rdb
#dir:持久化數(shù)據(jù)存儲在本地的路徑,如果是在/redis/redis-5.0.5/src下啟動的redis-cli,則數(shù)據(jù)會存儲在當(dāng)前src目錄下
dir ./
##snapshot觸發(fā)的時機,save
##如下為900秒后,至少有一個變更操作,才會snapshot
##對于此值的設(shè)置,需要謹(jǐn)慎,評估系統(tǒng)的變更操作密集程度
##可以通過“save”來關(guān)閉snapshot功能
#save時間,以下分別表示更改了1個key時間隔900s進(jìn)行持久化存儲;更改了10個key300s進(jìn)行存儲;更改10000個key60s進(jìn)行存儲。
save 900 1
save 300 10
save 60 10000
##當(dāng)snapshot時出現(xiàn)錯誤無法繼續(xù)時,是否阻塞客戶端“變更操作”,“錯誤”可能因為磁盤已滿/磁盤故障/OS級別異常等
stop-writes-on-bgsave-error yes
##是否啟用rdb文件壓縮,默認(rèn)為“yes”,壓縮往往意味著“額外的cpu消耗”,同時也意味這較小的文件尺寸以及較短的網(wǎng)絡(luò)傳輸時間
rdbcompression yes
注意 : 測試時使用 root 用戶操作

10.2.2?AOF持久化

Append-Only File ,將 操作 + 數(shù)據(jù) 以格式化指令的方式追加到操作日志文件的尾部,在 append 操作返回后 ( 已經(jīng) 寫入到文件或者將要寫入) ,才進(jìn)行實際的數(shù)據(jù)變更, 日志文件 保存了歷史所有的操作過程;當(dāng) server 需要數(shù)據(jù) 恢復(fù)時,可以直接 replay 此日志文件,即可還原所有的操作過程。 AOF 相對可靠, AOF 文件內(nèi)容是字符串,非常 容易閱讀和解析。
優(yōu)點:可以保持更高的數(shù)據(jù)完整性,如果設(shè)置追加 fifile 的時間是 1s ,如果 redis 發(fā)生故障,最多會丟失 1s 的數(shù) 據(jù);且如果日志寫入不完整支持 redis-check-aof 來進(jìn)行日志修復(fù); AOF 文件沒被 rewrite 之前(文件過大時會對 命令進(jìn)行合并重寫),可以刪除其中的某些命(比如誤操作的 flflushall )。
缺點: AOF 文件比 RDB 文件大,且恢復(fù)速度慢。
我們可以簡單的認(rèn)為 AOF 就是日志文件,此文件只會記錄 變更操作 ”( 例如: set/del ) ,如果 server 中持續(xù)的大 量變更操作,將會導(dǎo)致 AOF 文件非常的龐大,意味著 server 失效后,數(shù)據(jù)恢復(fù)的過程將會很長;事實上,一條數(shù) 據(jù)經(jīng)過多次變更,將會產(chǎn)生多條 AOF 記錄,其實只要保存當(dāng)前的狀態(tài),歷史的操作記錄是可以拋棄的;因為 AOF 持久化模式還伴生了“AOF rewrite” 。
AOF 的特性決定了它相對比較安全,如果你期望數(shù)據(jù)更少的丟失,那么可以采用 AOF 模式。如果 AOF 文件正在被 寫入時突然 server 失效,有可能導(dǎo)致文件的最后一次記錄是不完整,你可以通過手工或者程序的方式去檢測并修 正不完整的記錄,以便通過 aof 文件恢復(fù)能夠正常;同時需要提醒,如果你的 redis 持久化手段中有 aof ,那么在 server 故障失效后再次啟動前,需要檢測 aof 文件的完整性。
AOF 默認(rèn)關(guān)閉,開啟方法,修改配置文件 reds.confappendonly yes
##此選項為aof功能的開關(guān),默認(rèn)為“no”,可以通過“yes”來開啟aof功能
##只有在“yes”下,aof重寫/文件同步等特性才會生效
appendonly yes
##指定aof文件名稱
appendfilename appendonly.aof
##指定aof操作中文件同步策略,有三個合法值:always everysec no,默認(rèn)為everysec
appendfsync everysec
##在aof-rewrite期間,appendfsync是否暫緩文件同步,"no"表示“不暫緩”,“yes”表示“暫緩”,默認(rèn)為“no”
no-appendfsync-on-rewrite no
##aof文件rewrite觸發(fā)的最小文件尺寸(mb,gb),只有大于此aof文件大于此尺寸是才會觸發(fā)rewrite,默認(rèn)“64mb”,建議“512mb”
auto-aof-rewrite-min-size 64mb
##相對于“上一次”rewrite,本次rewrite觸發(fā)時aof文件應(yīng)該增長的百分比。
##每一次rewrite之后,redis都會記錄下此時“新aof”文件的大小(例如A),那么當(dāng)aof文件增長到A*(1 + p)之后
##觸發(fā)下一次rewrite,每一次aof記錄的添加,都會檢測當(dāng)前aof文件的尺寸。
auto-aof-rewrite-percentage 100
AOF 是文件操作,對于變更操作比較密集的 server ,那么必將造成磁盤 IO 的負(fù)荷加重;此外 linux 對文件操作采 取了“ 延遲寫入 手段,即并非每次 write 操作都會觸發(fā)實際磁盤操作,而是進(jìn)入了 buffffer 中,當(dāng) buffffer 數(shù)據(jù)達(dá)到 閥值時觸發(fā)實際寫入( 也有其他時機 ) ,這是 linux 對文件系統(tǒng)的優(yōu)化,但是這卻有可能帶來隱患,如果 buffffer 沒有 刷新到磁盤,此時物理機器失效( 比如斷電 ) ,那么有可能導(dǎo)致最后一條或者多條 aof 記錄的丟失。通過上述配置文 件,可以得知 redis 提供了 3 aof 記錄同步選項:
always :每一條 aof 記錄都立即同步到文件,這是最安全的方式,也以為更多的磁盤操作和阻塞延遲,是 IO 開支 較大。
everysec :每秒同步一次,性能和安全都比較中庸的方式,也是 redis 推薦的方式。如果遇到物理服務(wù)器故障,有 可能導(dǎo)致最近一秒內(nèi) aof 記錄丟失 ( 可能為部分丟失 )
no redis 并不直接調(diào)用文件同步,而是交給操作系統(tǒng)來處理,操作系統(tǒng)可以根據(jù) buffffer 填充情況 / 通道空閑時間 等擇機觸發(fā)同步;這是一種普通的文件操作方式。性能較好,在物理服務(wù)器故障時,數(shù)據(jù)丟失量會因 OS 配置有 關(guān)。
其實,我們可以選擇的太少, everysec 是最佳的選擇。如果你非常在意每個數(shù)據(jù)都極其可靠,建議你選擇一款 關(guān) 系性數(shù)據(jù)庫” 。

10.2.3?AOFRDB區(qū)別

(1)?RDB:
RDB 是在某個時間點將數(shù)據(jù)寫入一個臨時文件,持久化結(jié)束后,用這個臨時文件替換上次持久化的文件,達(dá)到數(shù)據(jù)恢復(fù)
優(yōu)點:使用單獨子進(jìn)程來進(jìn)行持久化,主進(jìn)程不會進(jìn)行任何 IO 操作,保證了 redis 的高性能
缺點: RDB 是間隔一段時間進(jìn)行持久化,如果持久化之間 redis 發(fā)生故障,會發(fā)生數(shù)據(jù)丟失。所以這種方式更適合 數(shù)據(jù)要求不嚴(yán)謹(jǐn)?shù)臅r候
(2)?AOF
Append-only fifile ,將 操作 + 數(shù)據(jù) 以格式化指令的方式追加到操作日志文件的尾部,在 append 操作返回后 ( 已經(jīng)寫 入到文件或者即將寫入) ,才進(jìn)行實際的數(shù)據(jù)變更, 日志文件 保存了歷史所有的操作過程;當(dāng) server 需要數(shù)據(jù)恢復(fù) 時,可以直接replay 此日志文件,即可還原所有的操作過程。 AOF 相對可靠,它和 mysql bin.log 、 apache.log 、 zookeeper中 txn-log 簡直異曲同工。 AOF 文件內(nèi)容是字符串,非常容易閱讀和解析。
優(yōu)點:可以保持更高的數(shù)據(jù)完整性,如果設(shè)置追加 fifile 的時間是 1s ,如果 redis 發(fā)生故障,最多會丟失 1s 的數(shù)據(jù);且 如果日志寫入不完整支持redis-check-aof 來進(jìn)行日志修復(fù); AOF 文件沒被 rewrite 之前(文件過大時會對命令進(jìn)行 合并重寫),可以刪除其中的某些命令(比如誤操作的flflushall )。
缺點: AOF 文件比 RDB 文件大,且恢復(fù)速度慢。

十一、Redis主從復(fù)制

持久化保證了即使 redis 服務(wù)重啟也不會丟失數(shù)據(jù),但是當(dāng) redis 服務(wù)器的硬盤損壞了可能會導(dǎo)致數(shù)據(jù)丟失,通過redis 的主從復(fù)制機制就可以避免這種單點故障(單臺服務(wù)器的故障)。
redis 中的數(shù)據(jù)和從上的數(shù)據(jù)保持實時同步 , 當(dāng)主 redis 寫入數(shù)據(jù)時通過主從復(fù)制機制復(fù)制到兩個從服務(wù)上。
主從復(fù)制不會阻塞 master ,在同步數(shù)據(jù)時, master 可以繼續(xù)處理 client 請求 .
主機 master 配置 : 無需配置
推薦主從模式同步數(shù)據(jù) :
? ? ? ? ? ? ? ? ? ? ? ? ? Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存
工作中一般選用:一主兩從或一主一從
數(shù)據(jù)會同步到從服務(wù)器。在這個集群中的幾臺服務(wù)器上都有同樣的數(shù)據(jù)。

11.1?主從搭建步驟:

主機:不用配置。僅僅只需要配置從機 , 從機 slave 配置 :( 這里是偽集群 )
(1)?第一步:復(fù)制出一個從機,注意使用root用戶
[root@localhost myapps]# cp redis/ redis1 -r
[root@localhost myapps]# ll
總用量 40
drwxr-xr-x. 3 root root 4096 2月 1 09:26 redis
drwxr-xr-x. 3 root root 4096 2月 1 09:27 redis1
(2) 第二步:修改從機的redis.conf
語法: replicaof // replicaof 主機 ip 主機端口號
提示 : 檢索文件 : 輸入 :/replicaof 當(dāng)前頁沒有時,輸入 n ,查找下一頁
? ? ? ? ? ?? Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

(3)?第三步:修改從機的port地址為6380 ?

在從機 redis.conf 中修改
Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

?(4)?第四步:清除從機中的持久化文件

[root@localhost bin]# rm -rf appendonly.aof dump.rdb
[root@localhost bin]# ll
總用量 15440
-rwxr-xr-x. 1 root root 4588902 7月 1 09:27 redis-benchmark
-rwxr-xr-x. 1 root root 22225 7月 1 09:27 redis-check-aof
-rwxr-xr-x. 1 root root 45443 7月 1 09:27 redis-check-dump
-rwxr-xr-x. 1 root root 4691809 7月 1 09:27 redis-cli
lrwxrwxrwx. 1 root root 12 7月 1 09:27 redis-sentinel -> redis-server
-rwxr-xr-x. 1 root root 6450337 7月 1 09:27 redis-server

(5)?第五步:啟動從機

[root@localhost redis1]# ./bin/redis-server ./redis.conf

(6)?第六步:啟動6380的客戶端

[root@localhost redis1]# ./bin/redis-cli -p 6380
127.0.0.1:6380> keys *
1) "mylist"
2) "num"
3) "bookCate1"
4) "newbook"
127.0.0.1:6380>
停止客戶端 : ./bin/redis-cli -p 6380 shutdown
注意:
1.主機一旦發(fā)生增刪改操作,那么從機會自動將數(shù)據(jù)同步到從機中
2. 從機不能執(zhí)行寫操作 , 只能讀
127.0.0.1:6380> get username
"hehe"
127.0.0.1:6380> set username haha
(error) READONLY You can't write against a read only slave.

(7)?復(fù)制的過程原理

1.當(dāng)從庫和主庫建立 MS(master slaver) 關(guān)系后,會向主數(shù)據(jù)庫發(fā)送 SYNC 命令;
2.主庫接收到 SYNC 命令后會開始在后臺保存快照( RDB 持久化過程),并將期間接收到的寫命令緩存起來;
3.快照完成后 , Redis 會將快照文件和所有緩存的寫命令發(fā)送給從 Redis ;
4.從 Redis 接收到后,會載入快照文件并且執(zhí)行收到的緩存命令;
5.主 Redis 每當(dāng)接收到寫命令時就會將命令發(fā)送從 Redis ,保證數(shù)據(jù)的一致;【內(nèi)部完成 , 所以 不支持客戶端在從 機人為寫數(shù)據(jù) ?!?
(8) 復(fù)制架構(gòu)中出現(xiàn)宕機情況?
Redis宕機 : 重啟就好
Redis 宕機 : 從數(shù)據(jù)庫 ( 從機 ) 中執(zhí)行 SLAVEOF NO ONE 命令,斷開主從關(guān)系并且提升為主庫繼續(xù)服務(wù) [ 把一個從做為 主機,這個時候新主機[ 之前的從機 ] 就具備寫入的能力 ] ;主服務(wù)器修好后,重新啟動后,執(zhí)行 SLAVEOF 命令,將其 設(shè)置為從庫[ 老主機設(shè)置為從機 ] [ 手動執(zhí)行,過程復(fù)雜,容易出錯。 ] 是否有更好的方案?

十二、Redis哨兵模式

哨兵模式 :給集群分配一個站崗的。
哨兵的作用就是對 Redis 系統(tǒng)的運行情況監(jiān)控,它是一個獨立進(jìn)程 , 它的功能:
1. 監(jiān)控主數(shù)據(jù)庫和從數(shù)據(jù)庫是否運行正常;
2. 主數(shù)據(jù)出現(xiàn)故障后自動將從數(shù)據(jù)庫轉(zhuǎn)化為主數(shù)據(jù)庫;
如果主機宕,開啟選舉工作,選擇一個從做主機。
環(huán)境準(zhǔn)備:一主兩從,啟動任一從機時,啟動哨兵模式
雖然哨兵 (sentinel) 釋出為一個單獨的可執(zhí)行文件 redis-sentinel , 但實際上它只是一個運行在特殊模式下的 Redis 服務(wù)器,你可以在啟動一個普通 Redis 服務(wù)器時通過給定 --sentinel 選項來啟動哨兵 (sentinel) 。
Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

12.1?第一步:配置哨兵:

哨兵主要是用來監(jiān)聽主服務(wù)器的,所以一般把哨兵部署在從服務(wù)器上監(jiān)聽。
配置哨兵:
1. 啟動哨兵進(jìn)程,首先需要創(chuàng)建哨兵配置文件 vi sentinel.conf, 可從源碼配置? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? redis5.0.5/sentinel.conf 中復(fù)制內(nèi)容,也可以直接自定義該文件到bin 目錄下
2. 在配置中輸入 :sentinel monitor mastername 內(nèi)網(wǎng) IP(127.0.0.1) 6379 1
3. 說明:
4. mastername 監(jiān)控主數(shù)據(jù)的名稱,自定義
5. 127.0.0.1 :監(jiān)控主數(shù)據(jù)庫的 IP;
6. 6379: 端口
7. 1 :最低通過票數(shù)

12.2?第二步:啟動哨兵:

哨兵是一個單獨的進(jìn)程,啟動之前確保主從服務(wù)是正常的。先啟動主服務(wù),后啟動從服務(wù)
? ? ? ? ? ? ? ? ? ? ? ? ? ?? Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

?把日志寫入指定的文件

[root@localhost bin]# ./redis-sentinel ./sentinel.conf >sent.log &
[1] 3373
啟動 redis 服務(wù)后,程序會自動配置文件 sentinel.conf ,并生成內(nèi)容,注意 : 若再起啟動需要刪除下生成的內(nèi)容。
哨兵啟動方式 :
[root@localhost bin]# ./redis-server sentinel.conf --sentinel
哨兵進(jìn)程控制臺:為 master 數(shù)據(jù)庫添加了一個監(jiān)控 .
Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

?同時多了哨兵進(jìn)程:

Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

查詢配置文件sentinel.conf中生成的內(nèi)容: ?

啟動哨兵的時候,修改了哨兵的配置文件。如果需要再次啟動哨兵,需要刪除 myid 唯一標(biāo)示。
(保險的做法就是啟動的一次,新配置一次)
Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

12.3?第三步:主機宕機

機房意外:斷電了。硬件故障:硬盤壞了。
列表:
Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

殺死主機:kill -9 pid?

[root@localhost redis6380]# kill -9 3342
哨兵控制臺:從庫自動提升為主庫。
哨兵工作 , 鏈接之前的從機確認(rèn):
127.0.0.1:6381> info replication
# Replication
role:slave
master_host:192.168.197.129
master_port:6379
哨兵替代運維。自動監(jiān)控完成。
同時也會自動修改 redis.conf 的主從配置文件。
replicaof 127.0.0.1 6380
指向了新主機。再次啟動原有的主機 , 原有的主機會變?yōu)閺臋C。
總結(jié) :
主從集群:主機有寫入權(quán)限。從機沒有,只有可讀。
意外宕機方案 :
手動恢復(fù):人為重啟服務(wù)器,主機宕,把從機設(shè)置為主機。
自動恢復(fù):使用哨兵監(jiān)控。自動切換主從。

十三、Redis集群方案

13.1?redis-cluster架構(gòu)圖

? ? ? ? ? ? ? ? ? ? ? ? ? ? Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

?架構(gòu)細(xì)節(jié):

(1) 所有的 redis 節(jié)點彼此互聯(lián) (PING-PONG 機制 ), 內(nèi)部使用二進(jìn)制協(xié)議優(yōu)化傳輸速度和帶寬 .
(2) 節(jié)點的 fail 是通過集群中超過半數(shù)的節(jié)點檢測有效時整個集群才生效 .
(3) 客戶端與 redis 節(jié)點直連 , 不需要中間 proxy . 客戶端不需要連接集群所有節(jié)點 , 連接集群中? ? ? ? 任何一個可用節(jié)點即可
(4)redis-cluster 把所有的物理節(jié)點映射到 [ 0-16383 ]slot ,cluster 負(fù)責(zé)維護(hù) node<->slot<-? ? ? ? ? ? >value
Redis 集群中內(nèi)置了 16384 個哈希槽,當(dāng)需要在 Redis 集群中放置一個 key-value 時, redis 先對 key 使用 crc16 算法算出一個結(jié)果,然后把結(jié)果對 16384 求余數(shù),這樣每個 key 都會對應(yīng)一個編號在 0-16383 之間的哈希槽, redis 會根據(jù)節(jié)點數(shù)量大致均等的將哈希槽映射到不同的節(jié)點
示例如下: Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

?

13.2?redis-cluster投票:容錯

? ? ? ? ? ? ?? Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

?心跳機制

(1) 集群中所有 master 參與投票 , 如果半數(shù)以上 master 節(jié)點與其中一個 master 節(jié)點通信超過? ? ? ? ? ?? (cluster-node-timeout), 認(rèn)為該master 節(jié)點掛掉 .
(2): 什么時候整個集群不可用 (cluster_state:fail)?
? ? ? a: 如果集群任意 master 掛掉 , 且當(dāng)前 master 沒有 slave ,則集群進(jìn)入 fail 狀態(tài)。也可以理解成集群的 [0-16383]slot 射不完全時進(jìn)入 fail 狀態(tài)。
? ? ? b:如果集群超過半數(shù)以上 master 掛掉,無論是否有 slave ,集群進(jìn)入 fail 狀態(tài)。

13.3?集群搭建步驟

(1)?第一步:安裝redis

(2)?第二步:創(chuàng)建集群目錄
[root@localhost redis]# mkdir redis-cluster

(3)?第三步:在集群目錄下創(chuàng)建節(jié)點目錄

? ? ? ? ? ? ? ? ? ? Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

?

搭建集群最少也得需要 3 臺主機,如果每臺主機再配置一臺從機的話,則最少需要 6 臺機器。 設(shè)計端口如下:創(chuàng)建 6 個redis 實例,需要端口號 7001~7006
[root@localhost myapps]# cp redis/ redis-cluster/7001 -r
[root@localhost myapps]# cd redis-cluster/7001
[root@localhost 7001]# ll
drwxr-xr-x. 2 root root 4096 7月 1 10:22 bin
-rw-r--r--. 1 root root 3446 7月 1 10:22 dump.rdb
-rw-r--r--. 1 root root 41404 7月 1 10:22 redis.conf

(4)?第四步:如果存在持久化文件,則刪除

[root@localhost 7001]# rm -rf appendonly.aof dump.rdb

(5)?第五步:修改redis.conf配置文件,打開Cluster-enable yes

說明: cluster-enable 是否支持集群
Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

(6)?第六步:修改端口

Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

(7)?第七步:復(fù)制出7002-7006機器

[root@localhost redis-cluster]# cp 7001/ 7002 -r
[root@localhost redis-cluster]# cp 7001/ 7003 -r
[root@localhost redis-cluster]# cp 7001/ 7004 -r
[root@localhost redis-cluster]# cp 7001/ 7005 -r
[root@localhost redis-cluster]# cp 7001/ 7006 -r
[root@localhost redis-cluster]# ll
total 28
drwxr-xr-x. 3 root root 4096 Jun 2 00:02 7001
drwxr-xr-x. 3 root root 4096 Jun 2 00:02 7002
drwxr-xr-x. 3 root root 4096 Jun 2 00:02 7003
drwxr-xr-x. 3 root root 4096 Jun 2 00:03 7004
drwxr-xr-x. 3 root root 4096 Jun 2 00:03 7005
drwxr-xr-x. 3 root root 4096 Jun 2 00:03 7006
-rwxr-xr-x. 1 root root 3600 Jun 1 23:52 redis-trib.rb

(8)?第八步:修改7002-7006機器的端口

(9)?第九步:啟動7001-7006這六臺機器,寫一個啟動腳本:自定義shel腳本
cd 7001
./bin/redis-server ./redis.conf
cd ..
cd 7002
./bin/redis-server ./redis.conf
cd ..
cd 7003
./bin/redis-server ./redis.conf
cd ..
cd 7004
./bin/redis-server ./redis.conf
cd ..
cd 7005
./bin/redis-server ./redis.conf
cd ..
cd 7006
./bin/redis-server ./redis.conf
cd ..

(10)?第十步:修改start-all.sh文件的權(quán)限

[root@localhost redis-cluster]# chmod u+x startall.sh

(11)?第十一步:啟動所有的實例

[root@localhost redis-cluster]# ./startall.sh

(12)?第十二步:創(chuàng)建集群(關(guān)閉防火墻)

注意:在任意一臺上運行 不要在每臺機器上都運行,一臺就夠了 redis 5.0.5 中使用 redis-cli --cluster 替代 redis-trib.rb,命令如下
redis-cli --cluster create ip:port ip:port --cluster-replicas 1
[root@localhost redis_cluster]# cd /home/admin/myapps/redis-cluster/7001/bin
[root@localhost bin]# ./redis-cli --cluster create 192.168.197.132:7001 192.168.197.132:7002
192.168.197.132:7003 192.168.197.132:7004 192.168.197.132:7005 192.168.197.132:7006 --clusterreplicas 1
\>>> Creating cluster
Connecting to node 127.0.0.1:7001: OK
Connecting to node 127.0.0.1:7002: OK
Connecting to node 127.0.0.1:7003: OK
Connecting to node 127.0.0.1:7004: OK
Connecting to node 127.0.0.1:7005: OK
Connecting to node 127.0.0.1:7006: OK
\>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
127.0.0.1:7001
127.0.0.1:7002
127.0.0.1:7003
Adding replica 127.0.0.1:7004 to 127.0.0.1:7001
Adding replica 127.0.0.1:7005 to 127.0.0.1:7002
Adding replica 127.0.0.1:7006 to 127.0.0.1:7003
[OK] All 16384 slots covered.

13.4?連接集群

命令 :
[root@localhost 7001]# ./bin/redis-cli -h 127.0.0.1 -p 7001 -c
-c :指定是集群連接
[root@localhost 7001]# ./bin/redis-cli -h 127.0.0.1 -p 7001 -c
127.0.0.1:7001> set username java123
-> Redirected to slot [14315] located at 127.0.0.1:7003
OK
關(guān)閉防火墻 :service iptables stop
查看防火墻狀態(tài) :service iptables status

13.5?查看集群信息

127.0.0.1:7003> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:3
cluster_stats_messages_sent:1186
cluster_stats_messages_received:1186

13.6?查看集群中節(jié)點信息

127.0.0.1:7003> cluster nodes
713218b88321e5067fd8ad25c3bf7db88c878ccf 127.0.0.1:7003 myself,master - 0 0 3 connected 10923-
16383
e7fb45e74f828b53ccd8b335f3ed587aa115b903 127.0.0.1:7001 master - 0 1498877677276 1 connected 0-
5460
b1183545245b3a710a95d669d7bbcbb5e09896a0 127.0.0.1:7006 slave
713218b88321e5067fd8ad25c3bf7db88c878ccf 0 1498877679294 3 connected
8879c2ed9c141de70cb7d5fcb7d690ed8a200792 127.0.0.1:7005 slave
4a312b6fc90bfee187d43588ead99d83b407c892 0 1498877678285 5 connected
4a312b6fc90bfee187d43588ead99d83b407c892 127.0.0.1:7002 master - 0 1498877674248 2 connected
5461-10922
4f8c7455574e2f0aab1e2bb341eae319ac065039 127.0.0.1:7004 slave
e7fb45e74f828b53ccd8b335f3ed587aa115b903 0 1498877680308 4 connected

13.7?Jedis連接集群

13.7.1?關(guān)閉防火墻

注意 : 如果 redis 重啟,需要將 redis 中生成的 dump.rdb nodes.conf 文件刪除,然后再重啟。

13.7.2?代碼實現(xiàn)

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>
注意 jedis 的版本,其他版本有可能報錯 :java.lang.NumberFormatException: For input string: "7002@17002"
public static void main(String[] args) throws IOException {
    // 創(chuàng)建一連接,JedisCluster對象,在系統(tǒng)中是單例存在
    Set<HostAndPort> nodes = new HashSet<HostAndPort>();
    nodes.add(new HostAndPort("192.168.197.132", 7001));
    nodes.add(new HostAndPort("192.168.197.132", 7002));
    nodes.add(new HostAndPort("192.168.197.132", 7003));
    nodes.add(new HostAndPort("192.168.197.132", 7004));
    nodes.add(new HostAndPort("192.168.197.132", 7005));
    nodes.add(new HostAndPort("192.168.197.132", 7006));
    JedisCluster cluster = new JedisCluster(nodes);
    // 執(zhí)行JedisCluster對象中的方法,方法和redis指令一一對應(yīng)。
    cluster.set("test1", "test111");
    String result = cluster.get("test1");
    System.out.println(result);
    //存儲List數(shù)據(jù)到列表中
    cluster.lpush("site-list", "java");
    cluster.lpush("site-list", "c");
    cluster.lpush("site-list", "mysql");
    // 獲取存儲的數(shù)據(jù)并輸出
    List<String> list = cluster.lrange("site-list", 0 ,2);
    for(int i=0; i<list.size(); i++) {
        System.out.println("列表項為: "+list.get(i));
    }
    // 程序結(jié)束時需要關(guān)閉JedisCluster對象
    cluster.close();
    System.out.println("集群測試成功!");
}

十四、Redis高端面試-緩存穿透,緩存擊穿,緩存雪崩問題

14.1?緩存的概念

什么是緩存 ?
廣義的緩存就是在第一次加載某些可能會復(fù)用數(shù)據(jù)的時候,在加載數(shù)據(jù)的同時,將數(shù)據(jù)放到一個指定的地點做保 存。再下次加載的時候,從這個指定地點去取數(shù)據(jù)。這里加緩存是有一個前提的,就是從這個地方取數(shù)據(jù),比從數(shù) 據(jù)源取數(shù)據(jù)要快的多。
java 狹義一些的緩存,主要是指三大類
1. 虛擬機緩存(ehcache, JBoss Cache
2. 分布式緩存(redis, memcache
3. 數(shù)據(jù)庫緩存
正常來說,速度由上到下依次減慢
緩存取值圖 :
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

14.2?緩存雪崩

緩存雪崩產(chǎn)生的原因
緩存雪崩通俗簡單的理解就是:由于原有緩存失效(或者數(shù)據(jù)未加載到緩存中),新緩存未到期間(緩存正常從 Redis中獲取,如下圖)所有原本應(yīng)該訪問緩存的請求都去查詢數(shù)據(jù)庫了,而對數(shù)據(jù)庫 CPU 和內(nèi)存造成巨大壓力, 嚴(yán)重的會造成數(shù)據(jù)庫宕機,造成系統(tǒng)的崩潰。
? ? ? ? ? ? ? ? ? ? ? ? ?? Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

?緩存失效的時候如下圖:

? ? ? ? ? ? ? ?? Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

解決方案:

1 在緩存失效后,通過加鎖或者隊列來控制讀數(shù)據(jù)庫寫緩存的線程數(shù)量。比如對某個 key 只允許一個線程查詢數(shù)據(jù) 和寫緩存,其他線程等待。雖然能夠在一定的程度上緩解了數(shù)據(jù)庫的壓力但是與此同時又降低了系統(tǒng)的吞吐量。
public Users getByUsers(Long id) {
    // 1.先查詢redis
    String key = this.getClass().getName() + "-" + 
    Thread.currentThread().getStackTrace([1].getMethodName()+ "-id:" + id;
    String userJson = redisService.getString(key);
    if (!StringUtils.isEmpty(userJson)) {
        Users users = JSONObject.parseObject(userJson, Users.class);
        return users;
    }
    Users user = null;
    try {
        lock.lock();
        // 查詢db
        user = userMapper.getUser(id);
        redisService.setSet(key, JSONObject.toJSONString(user));
    } catch (Exception e) {
    } finally {
        lock.unlock(); // 釋放鎖
    }
    return user;
}
注意 : 加鎖排隊只是為了減輕數(shù)據(jù)庫的壓力,并沒有提高系統(tǒng)吞吐量。假設(shè)在高并發(fā)下,緩存重建期間 key 是鎖著 的,這是過來1000 個請求 999 個都在阻塞的。同樣會導(dǎo)致用戶等待超時,這是個治標(biāo)不治本的方法。
2 分析用戶的行為,不同的 key ,設(shè)置不同的過期時間,讓緩存失效的時間點盡量均勻。

14.3?緩存穿透

????????緩存穿透是指用戶查詢數(shù)據(jù),在數(shù)據(jù)庫沒有,自然在緩存中也不會有。這樣就導(dǎo)致用戶查詢的時候,在緩存中找 不到,每次都要去數(shù)據(jù)庫再查詢一遍,然后返回空。這樣請求就繞過緩存直接查數(shù)據(jù)庫,這也是經(jīng)常提的緩存命中率問題。
解決方案 :
????????1.如果查詢數(shù)據(jù)庫也為空,直接設(shè)置一個默認(rèn)值存放到緩存,這樣第二次到緩沖中獲取就有值了,而不會繼續(xù)訪問 數(shù)據(jù)庫,這種辦法最簡單粗暴。
????????2.把空結(jié)果,也給緩存起來,這樣下次同樣的請求就可以直接返回空了,既可以避免當(dāng)查詢的值為空時引起的緩存穿透。同時也可以單獨設(shè)置個緩存區(qū)域存儲空值,對要查詢的key 進(jìn)行預(yù)先校驗,然后再放行給后面的正常緩存處理邏輯。
public String getByUsers2(Long id) {
    // 1.先查詢redis
    String key = this.getClass().getName() + "-" + Thread.currentThread().getStackTrace()
    [1].getMethodName()+ "-id:" + id;
    String userName = redisService.getString(key);
    if (!StringUtils.isEmpty(userName)) {
        return userName;
    }
    System.out.println("######開始發(fā)送數(shù)據(jù)庫DB請求########");
    Users user = userMapper.getUser(id);
    String value = null;
    if (user == null) {
        // 標(biāo)識為null
        value = "";
    } else {
        value = user.getName();
    }
    redisService.setString(key, value);
    return value;
}
注意:再給對應(yīng)的 ip 存放真值的時候,需要先清除對應(yīng)的之前的空緩存。

14.4?緩存擊穿

對于一些設(shè)置了過期時間的 key ,如果這些 key 可能會在某些時間點被超高并發(fā)地訪問,是一種非常 熱點 的數(shù)據(jù)。 這個時候,需要考慮一個問題:緩存被“ 擊穿 的問題,這個和緩存雪崩的區(qū)別在于這里針對某一 key 緩存,前者則是很多key 。
熱點 key:
某個 key 訪問非常頻繁,當(dāng) key 失效的時候有大量線程來構(gòu)建緩存,導(dǎo)致負(fù)載增加,系統(tǒng)崩潰。
解決辦法:
①使用鎖,單機用 synchronized,lock 等,分布式用分布式鎖。
②緩存過期時間不設(shè)置,而是設(shè)置在 key 對應(yīng)的 value 里。如果檢測到存的時間超過過期時間則異步更新緩存。

十五、Redis高端面試-分布式鎖

15.1?使用分布式鎖要滿足的幾個條件:

1. 系統(tǒng)是一個分布式系統(tǒng)(關(guān)鍵是分布式,單機的可以使用 ReentrantLock 或者 synchronized 代碼塊來實現(xiàn))
2. 共享資源(各個系統(tǒng)訪問同一個資源,資源的載體可能是傳統(tǒng)關(guān)系型數(shù)據(jù)庫或者 NoSQL
3. 同步訪問(即有很多個進(jìn)程同時訪問同一個共享資源。)

15.2?什么是分布式鎖?

線程鎖:主要用來給方法、代碼塊加鎖。當(dāng)某個方法或代碼使用鎖,在同一時刻僅有一個線程執(zhí)行該方法或該代碼 段。線程鎖只在同一JVM 中有效果,因為線程鎖的實現(xiàn)在根本上是依靠線程之間共享內(nèi)存實現(xiàn)的,比如 synchronized是共享對象頭,顯示鎖 Lock 是共享某個變量(state)。
進(jìn)程鎖:為了控制同一操作系統(tǒng)中多個進(jìn)程訪問某個共享資源,因為進(jìn)程具有獨立性,各個進(jìn)程無法訪問其他進(jìn)程 的資源,因此無法通過synchronized 等線程鎖實現(xiàn)進(jìn)程鎖。
分布式鎖:當(dāng)多個進(jìn)程不在同一個系統(tǒng)中,用分布式鎖控制多個進(jìn)程對資源的訪問。

15.3?應(yīng)用的場景

線程間并發(fā)問題和進(jìn)程間并發(fā)問題都是可以通過分布式鎖解決的,但是強烈不建議這樣做!因為采用分布式鎖解決 這些小問題是非常消耗資源的!分布式鎖應(yīng)該用來解決分布式情況下的多進(jìn)程并發(fā)問題才是最合適的。
有這樣一個情境,線程 A 和線程 B 都共享某個變量 X 。
如果是單機情況下(單 JVM ),線程之間共享內(nèi)存,只要使用線程鎖就可以解決并發(fā)問題
如果是分布式情況下(多 JVM ),線程 A 和線程 B 很可能不是在同一 JVM 中,這樣線程鎖就無法起到作用了,這時候 就要用到分布式鎖來解決。
分布式鎖可以基于很多種方式實現(xiàn),比如 zookeeper redis... 。不管哪種方式,他的基本原理是不變的:用一 個狀態(tài)值表示鎖,對鎖的占用和釋放通過狀態(tài)值來標(biāo)識。
這里主要講如何用 redis 實現(xiàn)分布式鎖。

15.4?使用redis的setNX命令實現(xiàn)分布式鎖

15.4.1 實現(xiàn)的原理:

Redis 為單進(jìn)程單線程模式,采用隊列模式將并發(fā)訪問變成串行訪問,且多客戶端對 Redis 的連接并不存在競爭 關(guān)系。redis SETNX 命令可以方便的實現(xiàn)分布式鎖。

15.4.2?基本命令解析

(1) setNX(SET if Not eXists)
語法:
SETNX key value
key 的值設(shè)為 value ,當(dāng)且僅當(dāng) key 不存在。
若給定的 key 已經(jīng)存在,則 SETNX 不做任何動作。
SETNX 是『SET if Not eXists』 ( 如果不存在,則 SET) 的簡寫
返回值:
設(shè)置成功,返回 1 。
設(shè)置失敗,返回 0 。
redis> EXISTS job # job 不存在
(integer) 0
redis> SETNX job "programmer" # job 設(shè)置成功
(integer) 1
redis> SETNX job "code-farmer" # 嘗試覆蓋 job ,失敗
(integer) 0
redis> GET job # 沒有被覆蓋
"programmer"
所以我們使用執(zhí)行下面的命令 SETNX 可以用作加鎖原語 (locking primitive) 。比如說,要對關(guān)鍵字 (key) foo 加鎖, 客戶端可以嘗試以下方式:
SETNX lock.foo <current Unix time + lock timeout + 1>
如果 SETNX 返回 1 ,說明客戶端已經(jīng)獲得了鎖, SETNX 將鍵 lock.foo 的值設(shè)置為鎖的超時時間(當(dāng)前時間 + 鎖 的有效時間)。 之后客戶端可以通過 DEL lock.foo 來釋放鎖。
如果 SETNX 返回 0 ,說明 key 已經(jīng)被其他客戶端上鎖了。如果鎖是非阻塞 (non blocking lock) 的,我們可以選 擇返回調(diào)用,或者進(jìn)入一個重試循環(huán),直到成功獲得鎖或重試超時(timeout) 。

(2)?getSET

先獲取 key 對應(yīng)的 value 值。若不存在則返回 nil ,然后將舊的 value 更新為新的 value 。
語法:
GETSET key value
將給定 key 的值設(shè)為 value ,并返回 key 的舊值 (old value)
當(dāng) key 存在但不是字符串類型時,返回一個錯誤。
返回值:
返回給定 key 的舊值 [ 之前的值 ] 。
當(dāng) key 沒有舊值時,也即是, key 不存在時,返回 nil 。
注意的關(guān)鍵點:(回答面試的核心點)
1 、同一時刻只能有一個進(jìn)程獲取到鎖。 setnx
2 、釋放鎖:鎖信息必須是會過期超時的,不能讓一個線程長期占有一個鎖而導(dǎo)致死鎖;
(最簡單的方式就是 del , 如果在刪除之前死鎖了。)
Redis從基礎(chǔ)到進(jìn)階篇(一),Redis,redis,數(shù)據(jù)庫,緩存

ex:

53秒設(shè)置--58秒到期

當(dāng)前時間為56秒,沒有過期文章來源地址http://www.zghlxwxcb.cn/news/detail-662621.html

當(dāng)前時間為 59 秒,過期 . (當(dāng)前時間大于設(shè)置的時間)
死鎖情況是在判斷超時后,直接操作業(yè)務(wù),設(shè)置過期時間,執(zhí)行業(yè)務(wù),然后刪除釋放鎖。其他進(jìn)程再次通過 setnx 來搶鎖。
解決死鎖:
上面的鎖定邏輯有一個問題: 如果一個持有鎖的客戶端失敗或崩潰了不能釋放鎖,該怎么解決
我們可以通過鎖的鍵對應(yīng)的時間戳來判斷這種情況是否發(fā)生了,如果當(dāng)前的時間已經(jīng)大于 lock.foo 的值,說明該鎖 已失效,可以被重新使用。
發(fā)生這種情況時,可不能簡單的通過 DEL 來刪除鎖,然后再 SETNX 一次(講道理, 刪除鎖的操作應(yīng)該是鎖擁有 者執(zhí)行的,這里只需要等它超時即可 ),當(dāng)多個客戶端檢測到鎖超時后都會嘗試去釋放它,這里就可能出現(xiàn)一個競 態(tài)條件, 讓我們模擬一下這個場景:
C0 操作超時了,但它還持有著鎖, C1 C2 讀取 lock.foo 檢查時間戳,先后發(fā)現(xiàn)超時了。 C1 發(fā)送 DEL lock.foo C1 發(fā)送SETNX lock.foo 并且成功了。 C2 發(fā)送 DEL lock.foo C2 發(fā)送 SETNX lock.foo 并且成功了。 這樣一來, C1 C2 都拿到了鎖!問題大了!
幸好這種問題是可以避免的,讓我們來看看C3這個客戶端是怎樣做的:
????????C3發(fā)送 SETNX lock.foo 想要獲得鎖,由于 C0 還持有鎖,所以 Redis 返回給 C3 一個 0 C3 發(fā)送 GET lock.foo 以檢查鎖 是否超時了,
????????如果沒超時,則等待或重試。 反之,如果已超時,C3 通過下面的操作來嘗試獲得鎖: GETSET lock.foo 通過 GETSET, C3 拿到的時間戳如果仍然是超時的,那就說明, C3 如愿以償拿到鎖了。 如果在 C3 之前,有個叫 C4 的客 戶端比C3 快一步執(zhí)行了上面的操作,那么 C3 拿到的時間戳是個未超時的值,這時, C3 沒有如期獲得鎖,需要再次 等待或重試。留意一下,盡管C3 沒拿到鎖,但它改寫了 C4 設(shè)置的鎖的超時值,不過這一點非常微小的誤差帶來的 影響可以忽略不計。
注意 :為了讓分布式鎖的算法更穩(wěn)鍵些,持有鎖的客戶端在解鎖之前應(yīng)該再檢查一次自己的鎖是否已經(jīng)超時, 再去做DEL 操作,因為可能客戶端因為某個耗時的操作而掛起,操作完的時候鎖因為超時已經(jīng)被別人獲得,這時就 不必解鎖了。
偽代碼 :
public static boolean lock(String lockName) {
    Jedis jedis = RedisPool.getJedis();
    //lockName可以為共享變量名,也可以為方法名,主要是用于模擬鎖信息
    System.out.println(Thread.currentThread() + "開始嘗試加鎖!");
    Long result = jedis.setnx(lockName, String.valueOf(System.currentTimeMillis() + 5000));
    if (result != null && result.intValue() == 1){
        System.out.println(Thread.currentThread() + "加鎖成功!");
        jedis.expire(lockName, 5);
        System.out.println(Thread.currentThread() + "執(zhí)行業(yè)務(wù)邏輯!");
        jedis.del(lockName);
        return true;
    } else {//判斷是否死鎖
        String lockValueA = jedis.get(lockName);
        //得到鎖的過期時間,判斷小于當(dāng)前時間,說明已超時但是沒釋放鎖,通過下面的操作來嘗試獲得鎖。下面邏輯防止死鎖[已經(jīng)過期但是沒有釋放鎖的情況]
        if (lockValueA != null && Long.parseLong(lockValueA) <                     
            System.currentTimeMillis()){
            String lockValueB = jedis.getSet(lockName,
            String.valueOf(System.currentTimeMillis() + 5000));
            //這里返回的值是舊值,如果有的話。之前沒有值就返回null,設(shè)置的是新超時。
            if (lockValueB == null || lockValueB.equals(lockValueA)){
                System.out.println(Thread.currentThread() + "加鎖成功!");
                jedis.expire(lockName, 5);
                System.out.println(Thread.currentThread() + "執(zhí)行業(yè)務(wù)邏輯!");
                jedis.del(lockName);
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }
}

到了這里,關(guān)于Redis從基礎(chǔ)到進(jìn)階篇(一)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • redis的緩存更新策略以及如何保證redis與數(shù)據(jù)庫的數(shù)據(jù)一致性

    redis的緩存更新策略有這么幾種: 1、由應(yīng)用直接和redis以及數(shù)據(jù)庫相連接: ?? ??? ?查詢數(shù)據(jù)時,應(yīng)用去redis中查詢,查不到的話再由應(yīng)用去數(shù)據(jù)庫中查詢,并將查詢結(jié)果放在redis; ?? ??? ?更新數(shù)據(jù)時,由應(yīng)用去觸發(fā)redis數(shù)據(jù)的刪除以及數(shù)據(jù)庫的update。 2、應(yīng)用只跟redi

    2024年02月13日
    瀏覽(25)
  • Redis如何保證緩存和數(shù)據(jù)庫一致性?

    現(xiàn)在我們在面向增刪改查開發(fā)時,數(shù)據(jù)庫數(shù)據(jù)量大時或者對響應(yīng)要求較快,我們就需要用到Redis來拿取數(shù)據(jù)。 Redis:是一種高性能的內(nèi)存數(shù)據(jù)庫,它將數(shù)據(jù)以鍵值對的形式存儲在內(nèi)存中,具有讀寫速度快、支持多種數(shù)據(jù)類型、原子性操作、豐富的特性等優(yōu)勢。 優(yōu)勢: 性能極高

    2024年01月16日
    瀏覽(41)
  • Redis---數(shù)據(jù)庫和緩存如何保證一致性?

    用「讀 + 寫」請求的并發(fā)的場景來分析: 假如某個用戶數(shù)據(jù)在緩存中不存在,請求 A 讀取數(shù)據(jù)時從數(shù)據(jù)庫中查詢到年齡為 20,在未寫入緩存中時另一個請求 B 更新數(shù)據(jù)。它更新數(shù)據(jù)庫中的年齡為 21,并且清空緩存。這時請求 A 把從數(shù)據(jù)庫中讀到的年齡為 20 的數(shù)據(jù)寫入到緩存

    2024年01月24日
    瀏覽(27)
  • Redis如何保障緩存與數(shù)據(jù)庫的數(shù)據(jù)一致性問題?

    Redis如何保障緩存與數(shù)據(jù)庫的數(shù)據(jù)一致性問題?

    目錄 一.最經(jīng)典的數(shù)據(jù)庫加緩存的雙寫雙刪模式 二. 高并發(fā)場景下的緩存+數(shù)據(jù)庫雙寫不一致問題分析與解決方案設(shè)計 三、上面高并發(fā)的場景下,該解決方案要注意的問題 1.1 Cache Aside Pattern概念以及讀寫邏輯 (1)讀的時候,先讀緩存,緩存沒有的話,那么就讀數(shù)據(jù)庫,然后取

    2023年04月21日
    瀏覽(29)
  • Springboot+Redis:實現(xiàn)緩存 減少對數(shù)據(jù)庫的壓力

    Springboot+Redis:實現(xiàn)緩存 減少對數(shù)據(jù)庫的壓力

    ????歡迎光臨,終于等到你啦???? ??我是蘇澤,一位對技術(shù)充滿熱情的探索者和分享者。???? ??持續(xù)更新的專欄 Redis實戰(zhàn)與進(jìn)階 本專欄講解Redis從原理到實踐 這是蘇澤的個人主頁可以看到我其他的內(nèi)容哦???? 努力的蘇澤 http://suzee.blog.csdn.net/ ? 目錄 緩存如何實現(xiàn)?

    2024年03月24日
    瀏覽(19)
  • 數(shù)據(jù)庫緩存服務(wù)——NoSQL之Redis配置與優(yōu)化

    數(shù)據(jù)庫緩存服務(wù)——NoSQL之Redis配置與優(yōu)化

    目錄 一、緩存概念 1.1 系統(tǒng)緩存 1.2 緩存保存位置及分層結(jié)構(gòu) 1.2.1 DNS緩存 1.2.2 應(yīng)用層緩存 1.2.3 數(shù)據(jù)層緩存 1.2.4 硬件緩存 二、關(guān)系型數(shù)據(jù)庫與非關(guān)系型數(shù)據(jù)庫 2.1 關(guān)系型數(shù)據(jù)庫 2.2 非關(guān)系型數(shù)據(jù)庫 2.3 關(guān)系型數(shù)據(jù)庫和非關(guān)系型數(shù)據(jù)庫區(qū)別: 2.4 非關(guān)系型數(shù)據(jù)庫產(chǎn)生背景 2.5 總結(jié)

    2024年02月15日
    瀏覽(24)
  • Redis數(shù)據(jù)庫 | 發(fā)布訂閱、主從復(fù)制、哨兵模式、緩存雪崩

    Redis數(shù)據(jù)庫 | 發(fā)布訂閱、主從復(fù)制、哨兵模式、緩存雪崩

    ??wei_shuo的個人主頁 ??wei_shuo的學(xué)習(xí)社區(qū) ??Hello World ! Redis 發(fā)布訂閱 (pub/sub) 是一種消息通信模式:發(fā)送者 (pub) 發(fā)送消息,訂閱者 (sub) 接收消息 Redis 客戶端可以訂閱任意數(shù)量的頻道 Redis主從復(fù)制是指在Redis中設(shè)置一個主節(jié)點(Master)和一個或多個從節(jié)點(Slave),

    2024年02月15日
    瀏覽(25)
  • Redis緩存MySQL數(shù)據(jù)庫存儲二者如何保證數(shù)據(jù)一致性

    在大型互聯(lián)網(wǎng)應(yīng)用中,由于數(shù)據(jù)庫讀寫頻繁、壓力大等原因,我們通常會使用緩存來減少數(shù)據(jù)庫的訪問次數(shù),提高系統(tǒng)的性能。而Redis作為一個高性能的內(nèi)存數(shù)據(jù)庫,成為了緩存的首選方案之一。但是,緩存和數(shù)據(jù)庫之間存在數(shù)據(jù)一致性的問題,如何解決這個問題呢?本文將

    2023年04月19日
    瀏覽(27)
  • redis面試題目-如何保證數(shù)據(jù)庫與緩存的數(shù)據(jù)一致性

    原視頻:https://www.bilibili.com/video/BV1Km4y1r75f?p=62vd_source=fa75329ae3880aa55609265a0e9f5d34 由于緩存和數(shù)據(jù)庫是分開的,無法做到原子性的同時進(jìn)行數(shù)據(jù)修改,可能出現(xiàn)緩存更新失敗,或者數(shù)據(jù)庫更新失敗的情況,這時候會出現(xiàn)數(shù)據(jù)不一致,影響前端業(yè)務(wù) 先更新數(shù)據(jù)庫,再更新緩存。緩

    2024年02月05日
    瀏覽(27)
  • Redis 緩存與數(shù)據(jù)庫雙寫不一致如何解決

    Redis緩存與數(shù)據(jù)庫雙寫不一致是一個常見的挑戰(zhàn),但可以通過一些方法來解決或減輕這種不一致性。以下是一些可能的解決方案: 事務(wù)處理: 在進(jìn)行緩存和數(shù)據(jù)庫雙寫時,確保它們被包含在同一事務(wù)中。這可以通過使用支持事務(wù)的數(shù)據(jù)庫和Redis事務(wù)來實現(xiàn)。這樣,要么兩者同

    2024年01月21日
    瀏覽(24)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包