redis(二)高級用法
事務
redis的事務是一組命令的集合。事務同命令一樣都是redis的最小執行單元,一個事務中的命令要么執行要么都不執行。
首先需要multi命令來開始事務,用exec命令來執行事務。
127.0.0.1:6379> multi OK 127.0.0.1:6379> hset user:1 name xiaoming QUEUED 127.0.0.1:6379> hset user:1 name daxiong QUEUED 127.0.0.1:6379> exec 1) (integer) 0 2) (integer) 0 127.0.0.1:6379> hgetall user:1 1) "name" 2) "daxiong" 3) "score" 4) "61"
multi代表事務的開始,返回ok表示成功;
exec代表事務的執行,返回各個命令的執行結果;
在multi和exec中間添加需要執行的命令。
在multi開始后,所有命令都不會執行,而是全部暫時保存起來,在執行exec命令后會按照命令保存的順序依次執行各個命令。
如果事務執行過程中存在失敗的情況下(某一個命令執行失敗后其他命令會繼續執行),需要開發人員自行處理后果。
注意:redis不支持回滾操作,導致redis的錯誤異常需要開發人員處理。
watch
watch命令可以監控一個或多個鍵值的變化,一旦其中一個鍵被改變,之后的事務就不會執行,而且監控會一直持續到exec命令。
127.0.0.1:6379> set key 1 OK 127.0.0.1:6379> watch key OK 127.0.0.1:6379> set key 2 OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> set key 3 QUEUED 127.0.0.1:6379> exec (nil) 127.0.0.1:6379> get key "2"
生存時間
(1)設置key的超時時間,超時后redis會自動刪除給key值,類似於memcache中的超時時間。
expire key seconds
//設置成功返回1,失敗返回0
127.0.0.1:6379> set session:aabb uid1122 OK 127.0.0.1:6379> expire session:aabb 300 (integer) 1 127.0.0.1:6379> del session:aabb (integer) 1 127.0.0.1:6379> expire session:aabb 300 (integer) 0 127.0.0.1:6379> expire session:aabb 300 (integer) 1 127.0.0.1:6379> ttl session:aabb (integer) 290
(2)查詢剩余超時時間
ttl key
127.0.0.1:6379> expire session:aabb 300 (integer) 1 127.0.0.1:6379> ttl session:aabb (integer) 290
(3)取消超時時間
127.0.0.1:6379> get session:aabb "300" 127.0.0.1:6379> ttl session:aabb (integer) 280 127.0.0.1:6379> persist session:aabb (integer) 1 127.0.0.1:6379> ttl session:aabb (integer) -1
(4)如果使用設置相關的命令,會取消該鍵的超時間
緩存數據
在某些情況下,需要緩存一部分網站數據,而網站數據由需要持續的更新(假如需要兩個小時更新一次),那么可以采用redis進行緩存這部分數據,設置數據的超時時間為2小時,每當有請求訪問的時候首先到redis中查找該數據是否存在,如果存在直接讀取,如果不存在的話重新從數據庫中讀取該數據加載到redis中。
在緩存數據的時候需要考慮到被緩存數據的大小,如果緩存數據較大,會占用過多的內存資源,有必要在配置文件中限制內存的使用大小(maxmemory)。
當超過maxmemory的限制后,redis會根據maxmemory-policy參數指定的策略(包括LRU等算法)來刪除不需要的鍵。
排序
sort命令支持對集合類型、類表類型、有序集合類型進行排序。
127.0.0.1:6379> lpush list 1 2 6 3 4 9 8 (integer) 7 127.0.0.1:6379> sort list 1) "1" 2) "2" 3) "3" 4) "4" 5) "6" 6) "8" 7) "9"
可以對有序集合的值進行排序:
127.0.0.1:6379> zadd set 50 2 40 3 20 1 60 5 (integer) 4 127.0.0.1:6379> sort set 1) "1" 2) "2" 3) "3" 4) "5"
sort命令可以添加desc來實現倒序排序
127.0.0.1:6379> sort set desc 1) "5" 2) "3" 3) "2" 4) "1"
BY參數
很多時候我們需要根據ID對應的對象的某一個屬性進行排序,那么如何才能把多個不同的數據進行關聯查詢呢?
(1)首先,向userids中添加三個用戶id
127.0.0.1:6379> lpush userids 1 2 3 (integer) 3
(2)其次,分別對三個用戶添加分數
127.0.0.1:6379> set user_score_1 50 OK 127.0.0.1:6379> set user_score_2 30 OK 127.0.0.1:6379> set user_score_3 70 OK
(3)最后,使用sort、by命令來對對用戶按照默認情況以及分數的遞增和遞減進行排序。
127.0.0.1:6379> sort userids 1) "1" 2) "2" 3) "3" 127.0.0.1:6379> sort userids by user_score_* 1) "2" 2) "1" 3) "3" 127.0.0.1:6379> sort userids by user_score_* desc 1) "3" 2) "1" 3) "2"
GET參數
get參數並不影響排序,它的作用是使sort命令返回的結果不再是元素自身的值,而是get參數中指定的鍵值,同by參數一樣,支持字符串類型和散列類型的鍵。
127.0.0.1:6379> sort userids by user_score_* get user_name_* 1) "xiaoming" 2) "daxiong" 3) "xiaohong" 127.0.0.1:6379> sort userids by user_score_* desc get user_name_* 1) "xiaohong" 2) "daxiong" 3) "xiaoming"
STORE參數
store參數用於結果保存。
sort命令是redis的復雜命令之一,使用不好會造成性能的瓶頸。
sort命令的時間復雜度是O(n+mlog(m)),其中n是排序列表(集合和有序集合)中元素的個數,m是返回元素的個數。Redis在排序前會建立一個長度為n的的容器來存儲待排序元素,雖然是一個臨時的過程,但是多個較大數據的排序操作則會嚴重影響系統的性能。
因此,在開發中需要注意:
(1)盡可能減少排序鍵中的元素個數,降低n
(2)使用Limit參數只獲取需要的數據,降低n
(3)如果要排序的數據量較大,盡可能使用store名來緩存結果。
任務隊列
任務隊列一般適用於生產者和消費者之間通信的,那么在redis中很容易想到使用列表類型來實現任務隊列,具體方法是創建一個任務隊列,生產者主動lpush數據,而消費者去rpop數據,保持一個先進先出的循序。
但是這樣存在一個問題,消費者需要主動去請求數據,周期性的請求會造成資源的浪費,因此,redis提供了一個brpop的命令來解決這個問題。
BRPOP key timeout
brpop命令接收兩個參數,第一個參數key為鍵值,第二個參數timeout為超時時間。BRPOP命令取數據時候,如果暫時不存在數據,該命令會一直阻塞直到達到超時時間。如果timeout設置為0,那么就會無限等待下去。
優先級隊列
基於任務隊列,如何實現優先級隊列呢?
那么可以選擇多個任務隊列,而每個任務隊列的任務優先級是不同的。
redis提供了下面的命令,會從左邊第一個key開始讀下去知道返回一個數據。
brpop key [key...] timetout
發布/訂閱模式
redis提供了rabitmq類似的發布訂閱模式,通過生產者使用下面的命令來發布消息,
PUBLISH CHANNEL MESSAGE
消費者通過下面的消息來訂閱消息,
SUBSCRIBE CHANNEL MESSAGE
生產者:
#向channel.test發布消息 127.0.0.1:6379> publish channel.test hello (integer) 0 #返回0表明訂閱者為0,沒有發布消息 127.0.0.1:6379> publish channel.test hello (integer) 1 #返回n表明訂閱者為n,成功發布給1個消費者
消費者:
#訂閱channel.test消息 127.0.0.1:6379> subscribe channel.test Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "channel.test" 3) (integer) 1
#接收到來自channel.test的消息 1) "message" 2) "channel.test" 3) "hello"
管道
redis的底層通信協議對管道提供了支持。通過管道可以一次性發送多條命令並在執行完后一次性將結果返回,當一組命令中每條命令都不依賴之前命令的執行結果時就可以將這組命令一起通過管道發出。管道通過減少客戶端與redis的通信次數來實現降低往返實驗累計值的目的。
節省空間
(1)精簡鍵名和鍵值
(2)redis為每種數據類型提供了兩種內部編碼。例如散列類型的存儲是通過散列表來實現的,redis會根據數據的多少來選擇編碼類型,當數據較少的時候會采用緊湊但性能稍差的內部編碼方式,而數據變多時會把編碼方式改為散列表。