Redis進階

分類:存儲 時間:2017-02-01

事物

Redis中的事物是一組命令的集合。事物同命令一樣都是Redis的執行最小單元,一個事物的命令要麽都執行,要麽都不執行。

事物的原理是先將屬於一個事物的命令發送給Redis,然後Redis依次執行這些命令

127.0.0.1:6379> multi
OK
127.0.0.1:6379> sadd Java "spring" "hibernate" "mybatis"
QUEUED
127.0.0.1:6379> sadd php "ThinkPHP" "Yii"
QUEUED
127.0.0.1:6379> exec
1) (integer) 3
2) (integer) 2
127.0.0.1:6379> smembers java
1) "mybatis"
2) "spring"
3) "hibernate"
127.0.0.1:6379> smembers php
1) "Yii"
2) "ThinkPHP"

Redis保證一個事物中的所有命令要麽都執行,要麽都不執行。如果發送exec命令前客戶線程斷了,則Redis會清空事物隊列


開啟事物後,如果發生語法錯誤,提交exec後,Redis就會直接返回錯誤,連正確的命令也不會執行

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set key 1
QUEUED
127.0.0.1:6379> set key
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379> sadd key 2
QUEUED
127.0.0.1:6379> set key 3
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get key
(nil)


如果錯誤在開啟事物時並沒有發現,這樣的命令是會被Redis執行的

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set key 1
QUEUED
127.0.0.1:6379> sadd key 2
QUEUED
127.0.0.1:6379> set key 3
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
3) OK
127.0.0.1:6379> get key
"3"

Redis不支持關系數據庫的回滾(rollback)功能,因此在事物上保持簡潔和快速


watch命令在事務開始之前監視任意數量的鍵, 當執行exec提交事物時, 如果任意一個被監視的鍵已經被其他客戶端修改了, 那麽整個事務不再執行, 直接返回失敗

客戶端執行A

127.0.0.1:6379> get name
"Tom"
127.0.0.1:6379> watch name
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name Sam
QUEUED
127.0.0.1:6379> exec
(nil)


客戶端B執行

127.0.0.1:6379> set name Jerry
OK
127.0.0.1:6379> get name
"Jerry"

時間 客戶端A 客戶端B t1 watch name   t2 multi   t3   set name Jerry t4 set name Sam   t5 exec  
在客戶端A調用exec提交事物之前,客戶端已經對name的值做了修改,所以客戶端A最後執行事物失敗


生存時間

expire key seconds可以設置一個鍵的生命周期,單位是秒,使用ttl可以返回鍵的剩余生存時間,當返回-2代表鍵的生存時間已過或鍵從未存在

127.0.0.1:6379> set foo bar
OK
127.0.0.1:6379> expire foo 100
(integer) 1
127.0.0.1:6379> ttl foo
(integer) 95
127.0.0.1:6379> ttl foo
(integer) 62
127.0.0.1:6379> ttl foo
(integer) -2
127.0.0.1:6379> get foo
(nil)


persist可以使有生命周期的鍵變為永久,除了persist,set和getset同樣可以清除鍵的生命周期

127.0.0.1:6379> set foo bar
OK
127.0.0.1:6379> expire foo 300
(integer) 1
127.0.0.1:6379> ttl foo
(integer) 288
127.0.0.1:6379> ttl foo
(integer) 265
127.0.0.1:6379> persist foo
(integer) 1
127.0.0.1:6379> ttl foo
(integer) -1
127.0.0.1:6379> get foo
"bar"



expireat命令使用Unix時間作為第二個參數表示鍵的生存時間(秒)

127.0.0.1:6379> set foo bar
OK
127.0.0.1:6379> expireat foo 1478989320
(integer) 1
127.0.0.1:6379> ttl foo
(integer) 35


pexpireat與expireat類似,但第二個參數表示鍵的生存時間(毫秒)

127.0.0.1:6379> set foo bar
OK
127.0.0.1:6379> pexpireat foo 1478989620000
(integer) 1
127.0.0.1:6379> ttl foo
(integer) 36


排序

sort命令可以對列表類型、集合類型和有序集合類型進行排序

列表排序

127.0.0.1:6379> lpush mylist 5 8 1 2 9 4 3 6 7
(integer) 9
127.0.0.1:6379> sort mylist
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "7"
8) "8"
9) "9"


集合排序,然而使用sort對有序集合進行排序會忽略元素的分數,只對元素自身的值進行排序

127.0.0.1:6379> zadd myset 12 11 10 13 15 14 16 19
(integer) 4
127.0.0.1:6379> zrange myset 0 -1
1) "13"
2) "11"
3) "14"
4) "19"
127.0.0.1:6379> sort myset
1) "11"
2) "13"
3) "14"
4) "19"



sort命令加上alpha還可以對字母進行排序,但僅限對字母,列表不能有數字

127.0.0.1:6379> lpush mylistalpha a c e d B C A
(integer) 7
127.0.0.1:6379> sort mylistalpha alpha
1) "a"
2) "A"
3) "B"
4) "c"
5) "C"
6) "d"
7) "e"



sort命令加上desc可以降序排列,limit可以截取第一個和第二個

127.0.0.1:6379> sort mylist desc
1) "9"
2) "8"
3) "7"
4) "6"
5) "5"
6) "4"
7) "3"
8) "2"
9) "1"
127.0.0.1:6379> sort mylist desc limit 1 2
1) "8"
2) "7"



by參數

很多情況下列表、集合或有序集合存儲的元素代表的是對象的id,單純對這些id做排序有時意義並不大。更多時候是希望根據id對應的對象的某個屬性進行排序。為了滿足這一要求,可以在sort命令使用by參數

by參數的語法“by參考鍵”。其中參考鍵可以是字符串類型鍵或者是散列類型鍵的某個字段(鍵名->字段名)。如果提供了by參數,sort命令不再根據自身的值進行排序,而是對每個元素會用元素的值替換參考鍵中的第一個“*”,並獲取其值,然後依據該值對元素進行排序

127.0.0.1:6379> lpush sortbylist 1 2 3
(integer) 3
127.0.0.1:6379> set itemscore:1 50
OK
127.0.0.1:6379> set itemscore:2 100
OK
127.0.0.1:6379> set itemscore:3 -10
OK
127.0.0.1:6379> sort sortbylist by itemscore:* desc
1) "2"
2) "1"
3) "3"
127.0.0.1:6379> lpush sortbylist 4 5
(integer) 5
127.0.0.1:6379> set itemscore:4 50
OK
127.0.0.1:6379> set itemscore:5 20
OK
127.0.0.1:6379> sort sortbylist by itemscore:* desc
1) "2"
2) "4"
3) "1"
4) "5"
5) "3"
127.0.0.1:6379> hset itemhash:1 num 20
(integer) 1
127.0.0.1:6379> hset itemhash:2 num -10
(integer) 1
127.0.0.1:6379> hset itemhash:3 num 9
(integer) 1
127.0.0.1:6379> hset itemhash:4 num 15
(integer) 1
127.0.0.1:6379> hset itemhash:5 num 0
127.0.0.1:6379> sort sortbylist by itemhash:*->num desc
1) "4"
2) "1"
3) "3"
4) "5"
5) "2"


get參數

get參數不影響排序,它的作用是使得sort命令返回的不再是元素自身的值,而是get參數中指定的鍵值

127.0.0.1:6379> hset itemhash:1 name John
(integer) 1
127.0.0.1:6379> hset itemhash:2 name Tom
(integer) 1
127.0.0.1:6379> hset itemhash:3 name Amy
(integer) 1
127.0.0.1:6379> hset itemhash:4 name Sam
(integer) 1
127.0.0.1:6379> hset itemhash:5 name Rose
(integer) 1
127.0.0.1:6379> sort sortbylist by itemhash:*->num desc get itemhash:*->name
1) "John"
2) "Sam"
3) "Amy"
4) "Rose"
5) "Tom"


可以使用多個get獲取不同的字段,如果要返回本身的id,可以使用get #

127.0.0.1:6379> lpush sortbylist 6
(integer) 6
127.0.0.1:6379> sort sortbylist by itemhash:*->num desc get itemhash:*->name get itemhash:*->age get #
 1) "John"
 2) "15"
 3) "1"
 4) "Sam"
 5) "16"
 6) "4"
 7) "Amy"
 8) "19"
 9) "3"
10) (nil)
11) (nil)
12) "6"
13) "Rose"
14) "20"
15) "5"
16) "Tom"
17) "25"
18) "2"


sort會直接返回結果,可以使用store存儲結果,其結果會存儲在列表中

127.0.0.1:6379> sort sortbylist by itemhash:*->num desc get itemhash:*->name get itemhash:*->age get # store sort.result
(integer) 18
127.0.0.1:6379> type sort.result
list
127.0.0.1:6379> lrange sort.result 0 -1
 1) "John"
 2) "15"
 3) "1"
 4) "Sam"
 5) "16"
 6) "4"
 7) "Amy"
 8) "19"
 9) "3"
10) ""
11) ""
12) "6"
13) "Rose"
14) "20"
15) "5"
16) "Tom"
17) "25"
18) "2"

sort命令是Redis最強大最復雜的命令之一,如果使用不好很容易稱為性能瓶頸。sort命令的時間復雜度為O(n+mlogm),其中n表示要排序的列表、集合或者有序集合中的元素個數,m表示要返回的元素個數。n較大的時候sort命令的性能較低,並且sort命令會建立一個長度為n的容器來存儲待排序的元素,使用sort需要註意以下幾點:

  1. 盡可能減少待排序鍵中元素的數量(n盡量小)
  2. 使用limit參數只獲取需要的數據(使m盡量小)
  3. 如果要排序的數據量較大,盡可能使用參數store參數將結果緩存

任務隊列

與任務隊列進行交互的實體有兩類,一類是“生產者”,一類是“消費者”。生產者會將需要執行的任務放入隊列中,而消費者不斷從任務隊列中讀入任務信息並執行

使用任務隊列有如下好處:

  1. 松耦合。生產者和消費者無需知道彼此的實現細節,只需約定好任務的描述格式。這使得生產者和消費者可以由不同的團隊使用不同的編程語言編寫。
  2. 易於擴展消費者可以有多個,可以分布在不同的服務器中,借此可以輕易地降低單臺服務器的負載
brpop命令接受兩個參數,第一個是鍵名,第二個是超時時間,單位是秒。當超過此時間還沒有獲得新元素則會返回nil,超時時間為“0”代表不限制等待時間,如果沒有新元素就永遠阻塞下去


客戶端A
127.0.0.1:6379> brpop queue 0

客戶端B
127.0.0.1:6379> lpush queue task
(integer) 1

客戶端A

127.0.0.1:6379> brpop queue 0
1) "queue"
2) "task"
(11.85s)
127.0.0.1:6379> lrange queue 0 -1
(empty list or set)



除了brpop之外,Redis還提供了blpop,兩者的區別是:brpop是從隊列的右邊取元素,而blpop與之相反。


客戶端A

127.0.0.1:6379> blpop queue:1 queue:2 queue:3 0


客戶端B
127.0.0.1:6379> lpush queue:3 mybatis
(integer) 1

客戶端A

127.0.0.1:6379> blpop queue:1 queue:2 queue:3 0
1) "queue:3"
2) "mybatis"
(11.10s)

客戶端B

127.0.0.1:6379> lpush queue:1 hibernate
(integer) 1
127.0.0.1:6379> lpush queue:2 java
(integer) 1

客戶端A

127.0.0.1:6379> blpop queue:1 queue:2 queue:3 0
1) "queue:1"
2) "hibernate"
127.0.0.1:6379> blpop queue:1 queue:2 queue:3 0
1) "queue:2"
2) "java"



發布/訂閱模式

除了實現任務隊列外,Redis還提供了一組命令讓開發者實現“發布/訂閱”模式。“發布/訂閱”模式同樣可以實現進程間的消息傳遞,其原理是這樣的:

“發布/訂閱”模式包含兩種角色,分別是發布者和訂閱者。訂閱者可以訂閱一個或若幹個頻道,發布者可以向指定的頻道發送消息,所有訂閱此頻道的訂閱者都會收到此消息


客戶端A訂閱channe1.1頻道

127.0.0.1:6379> subscribe channe1.1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channe1.1"
3) (integer) 1

發布者發布消息的命令是publish,用法是publish channel message

客戶端B往頻道發送hi,因為有一個客戶端訂閱了channe1.1,所以返回值為1

127.0.0.1:6379> publish channe1.1 hi
(integer) 1


客戶端A查看到客戶端B發送的消息

127.0.0.1:6379> subscribe channe1.1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channe1.1"
3) (integer) 1
1) "message"
2) "channe1.1"
3) "hi"

執行subscribe命令後客戶端會進入訂閱狀態,處於此狀態下的客戶端不能使用除subscribe/unsubscribe/psubscribe/punsubscribe 這4個屬於“發布/訂閱”模式的命令之外的命令,否則會報錯

進入訂閱狀態後客戶端可能收到三種類型的回復。每種類型的回復都包含3個值,第一個值是消息類型,根據消息類型的不同,第二、第三個值的含義也不同。消息類型可能的取值有:

  1. subscribe。表示定有成功的反饋信息。 第二個值表示訂閱成功的頻道名稱,第三值是當前客戶端訂閱的頻道數量
  2. message。這個類型的回復是我們最關心的,它表示接收到的消息。第二個值表示產生消息的頻道名稱,第三個值是消息的內容
  3. unsubscribe。表示成功取消訂閱某個頻道。第二個值是對應的頻道名稱,第三個值是當前客戶端訂閱的頻道數量,當此值為0時客戶端會退出訂閱狀態


使用psubscribe命令訂閱指定的規則

客戶端A

127.0.0.1:6379> psubscribe channel.?
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "channel.?"
3) (integer) 1


客戶端B
127.0.0.1:6379> publish channel.1 java
(integer) 1
127.0.0.1:6379> publish channel.2 python
(integer) 1

客戶端A

127.0.0.1:6379> psubscribe channel.?
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "channel.?"
3) (integer) 1
1) "pmessage"
2) "channel.?"
3) "channel.1"
4) "java"
1) "pmessage"
2) "channel.?"
3) "channel.2"
4) "python"

punsubscribe與psubscribe類似,可以匹配多個頻道,但是將取消訂閱所匹配的頻道









Tags: hibernate previous because spring number

文章來源:


ads
ads

相關文章
ads

相關文章

ad