事物
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)
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 0127.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需要註意以下幾點:
- 盡可能減少待排序鍵中元素的數量(n盡量小)
- 使用limit參數只獲取需要的數據(使m盡量小)
- 如果要排序的數據量較大,盡可能使用參數store參數將結果緩存
任務隊列
與任務隊列進行交互的實體有兩類,一類是“生產者”,一類是“消費者”。生產者會將需要執行的任務放入隊列中,而消費者不斷從任務隊列中讀入任務信息並執行
使用任務隊列有如下好處:
brpop命令接受兩個參數,第一個是鍵名,第二個是超時時間,單位是秒。當超過此時間還沒有獲得新元素則會返回nil,超時時間為“0”代表不限制等待時間,如果沒有新元素就永遠阻塞下去
- 松耦合。生產者和消費者無需知道彼此的實現細節,只需約定好任務的描述格式。這使得生產者和消費者可以由不同的團隊使用不同的編程語言編寫。
- 易於擴展消費者可以有多個,可以分布在不同的服務器中,借此可以輕易地降低單臺服務器的負載
客戶端A127.0.0.1:6379> brpop queue 0
客戶端B127.0.0.1:6379> lpush queue task (integer) 1
客戶端A127.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
客戶端A127.0.0.1:6379> blpop queue:1 queue:2 queue:3 0 1) "queue:3" 2) "mybatis" (11.10s)
客戶端B127.0.0.1:6379> lpush queue:1 hibernate (integer) 1 127.0.0.1:6379> lpush queue:2 java (integer) 1
客戶端A127.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個值,第一個值是消息類型,根據消息類型的不同,第二、第三個值的含義也不同。消息類型可能的取值有:
- subscribe。表示定有成功的反饋信息。 第二個值表示訂閱成功的頻道名稱,第三值是當前客戶端訂閱的頻道數量
- message。這個類型的回復是我們最關心的,它表示接收到的消息。第二個值表示產生消息的頻道名稱,第三個值是消息的內容
- 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
客戶端A127.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文章來源: