使用場景
常規key-value緩存應用。
常規計數: 微博數, 粉絲數
Hashs
在Memcached中,我們經常將一些結構化的信息打包成hashmap,在客戶端序列化后存儲為一個字符串的值,比如用戶的昵稱、年齡、性別、積分等,這時候在需要修改其中某一項時,通常需要將所有值取出反序列化后,修改某一項的值,再序列化存儲回去。這樣不僅增大了開銷,也不適用於一些可能並發操作的場合(比如兩個並發的操作都需要修改積分)。而Redis的Hash結構可以使你像在數據庫中Update一個屬性一樣只修改某一項屬性值。
它是一個String類型的field和value的映射表,它的添加和刪除都是平均的,hash特別適合用於存儲對象,對於將對象存儲成字符串而言,hash會占用更少的內存,並且可以更方便的存取整個對象. 它和java的HashMap完全類似
常用方法
方法 |
說明 |
特性 |
hset |
設置一個hash 的field為指定值,如果key不存在則先創建 |
hset tab ke1 val1 |
hget |
獲取某個hash的某個field值 |
hget tab ke1 |
hsetnx |
類似string只是操作的是hash |
|
hmset |
批量設置hash的內容 |
|
hmget |
獲取hash表的全部key值 |
Hmget key field1 field2 |
hincrby |
給hash表的某個字段增加值 |
|
hexists |
判斷hash表中某個key是否存在 |
|
hlen |
返回hash表中的key數量 |
|
hdel |
刪除指定hash表的某個鍵值對 |
|
hkeys |
返回hash表中所有的key |
|
hvals |
返回hash表中所有的value |
|
hgetall |
獲取hash表中所有key和value |
|
使用場景
存儲部分變更數據
如用戶信息等。
Lists
Lists 就是鏈表,略有數據結構知識的人都應該能理解其結構。使用Lists結構,我們可以輕松地實現最新消息排行等功能。Lists的另一個應用就是消息隊列,可以利用Lists的PUSH操作,將任務存在Lists中,然后工作線程再用POP操作將任務取出進行執行。Redis還提供了操作Lists中某一段的api,你可以直接查詢,刪除Lists中某一段的元素。
Redis的list是每個子元素都是String類型的雙向鏈表,可以通過push和pop操作從列表的頭部或者尾部添加或者刪除元素,這樣List即可以作為棧,也可以作為隊列。
常用方法
方法 |
說明 |
特性 |
lpush |
在key所對應的list頭部添加一個元素 |
l的意思是left |
rpush |
在key說對應的list尾部添加一個元素 |
r的意思是right |
lrange |
顯示list里面的內容 |
lrange 0 -1 //全部顯示 |
linsert |
在key對應的list |
linsert mylist before one myvalue |
lset |
設置list中指定下標元素的值 |
lset mylist index myvalue |
lrem |
從key對應的list中刪除n個和value相同的元素,結果返回影響元素的個數,n<0從尾部開始刪除,n=0全刪除 |
lrem mylist count "value" |
ltrim |
保留指定key范圍內的數據,返回ok成功 |
ltrim mylist 0 3 //0-3是保留的范圍 |
lpop |
從list的頭部刪除一個元素,並返回該刪除的元素 |
|
rpop |
從list的尾部彈出一個元素,並返回該刪除的元素 |
|
rpoplpush |
從第一個list的尾部元素異常元素並添加到第二個list的頭部 |
rpoplpush mylistA mylistB |
lindex |
返回list位置的元素 |
lindex mylist 3 |
llen |
返回list中元素的個數 |
llen mylist |
使用場景
消息隊列系統
使用list可以構建隊列系統,使用sorted set甚至可以構建有優先級的隊列系統。
比如:將Redis用作日志收集器
實際上還是一個隊列,多個端點將日志信息寫入Redis,然后一個worker統一將所有日志寫到磁盤。
取最新N個數據的操作
記錄前N個最新登陸的用戶Id列表,超出的范圍可以從數據庫中獲得。
//把當前登錄人添加到鏈表里
ret = r.lpush("login:last_login_times", uid)
//保持鏈表只有N位
ret = redis.ltrim("login:last_login_times", 0, N-1)
//獲得前N個最新登陸的用戶Id列表
last_login_list = r.lrange("login:last_login_times", 0, N-1)
比如sina微博:
在Redis中我們的最新微博ID使用了常駐緩存,這是一直更新的。但是我們做了限制不能超過5000個ID,因此我們的獲取ID函數會一直詢問Redis。只有在start/count參數超出了這個范圍的時候,才需要去訪問數據庫。
我們的系統不會像傳統方式那樣“刷新”緩存,Redis實例中的信息永遠是一致的。SQL數據庫(或是硬盤上的其他類型數據庫)只是在用戶需要獲取“很遠”的數據時才會被觸發,而主頁或第一個評論頁是不會麻煩到硬盤上的數據庫了。
Sets
Sets 就是一個集合,集合的概念就是一堆不重復值的組合。利用Redis提供的Sets數據結構,可以存儲一些集合性的數據。
案例:
在微博應用中,可以將一個用戶所有的關注人存在一個集合中,將其所有粉絲存在一個集合。Redis還為集合提供了求交集、並集、差集等操作,可以非常方便的實現如共同關注、共同喜好、二度好友等功能,對上面的所有集合操作,你還可以使用不同的命令選擇將結果返回給客戶端還是存集到一個新的集合中。
Set是集合,是String類型的無序集合,set是通過hashtable實現的,概念和數學中個的集合基本類似,可以交集,並集,差集等等,set中的元素是沒有順序的。
常用方法
方法 |
說明 |
特性 |
sadd |
向名稱為key的set中添加元素,返回影響元素的個數,0為失敗,1為成功 |
sadd myset value |
smembers |
查看集合中所有的成員 |
smebers myset |
srem |
刪除集合的一個元素 |
srem myset two |
spop |
隨機返回並刪除set中一個元素 |
spop myset |
sdiff |
返回所有set與第一個set的差集 |
sdiff myset1 myset2 |
sdiffstore |
比較差集並且存儲到另一個set中,返回1代表成功 |
sdiffstore setstoreSet mySet1 myset2 |
sinter |
返回所有給定集合的交集 |
sinter myset1 mysert2 //1集合和2集合的交集 |
sinterstore |
返回給定集合的交集並存儲到另一個集合 |
sinterstore desset myset1 myset2 //存到desset集合中 |
sunion |
返回所有給定集合的並集 |
sunion set1 set2 |
sunionstore |
返回所有的並集並且存儲到另一個集合中,返回影響的元素個數 |
sunionstore destSet myset1 myset2 |
smove |
把第一個集合的元素移動到第二個集合中 |
smove myset myset 你好 |
scard |
返回集合中元素的個數 |
scard myset1 |
sismember |
測試某個元素是否在集合中,返回0是不是,大於0是存在 |
sismember mykey1 你好 |
srandmember |
隨機返回個集合中的元素 |
srandmemeber myset1 |
使用場景
交集,並集,差集:(Set)
//book表存儲book名稱
set book:1:name ”The Ruby Programming Language”
set book:2:name ”Ruby on rail”
set book:3:name ”Programming Erlang”
//tag表使用集合來存儲數據,因為集合擅長求交集、並集
sadd tag:ruby 1
sadd tag:ruby 2
sadd tag:web 2
sadd tag:erlang 3
//即屬於ruby又屬於web的書?
inter_list = redis.sinter("tag.web", "tag:ruby")
//即屬於ruby,但不屬於web的書?
inter_list = redis.sdiff("tag.ruby", "tag:web")
//屬於ruby和屬於web的書的合集?
inter_list = redis.sunion("tag.ruby", "tag:web")
獲取某段時間所有數據去重值
這個使用Redis的set數據結構最合適了,只需要不斷地將數據往set中扔就行了,set意為集合,所以會自動排重。
Sorted Sets
和Sets相比,Sorted Sets增加了一個權重參數score,使得集合中的元素能夠按score進行有序排列,比如一個存儲全班同學成績的Sorted Sets,其集合value可以是同學的學號,而score就可以是其考試得分,這樣在數據插入集合的時候,就已經進行了天然的排序。可以用Sorted Sets來做帶權重的隊列,比如普通消息的score為1,重要消息的score為2,然后工作線程可以選擇按score的倒序來獲取工作任務。讓重要的任務優先執行。
常用方法
方法 |
說明 |
特性 |
zadd |
向zset中添加元素member,score 用於排序,如果元素存在,則更新其順序,返回0代表沒添加成功 |
ZADD key score member zadd myset 3 itim |
zrange |
取出集合中的元素 |
zrange myset 0 -1 withscores//顯示序號 by index |
zrem |
刪除名稱為key的zset中的元素member |
zrem myset itim |
zincrby |
修改元素的排序,如果元素不存在則添加該元素,且排序的score值為增加值 |
zincrby myzset score itim |
zrank |
返回元素在集合中的排序位置,就是索引值 |
zrank myzset itim //itim在集合中的位置 |
zrevrank |
返回從大到小的排序索引值,就是逆序位置 |
zrevrangk myzset itim//逆序的位置 |
zrevrange |
返回集合中從大到小排序(降序)的,索引start到end的所有元素 |
zrevrange myzset 0 -1 //逆序后的元素 |
zrangebyscore |
根據排序索引的scores來返回元素 |
zrangebyscore myzset 1 3 withscores// |
zcount |
返回集合中給定區間的數量 |
zcount myzset 2 4 //集合中2-4索引元素的個數 |
zcard |
返回集合中所有元素的個數 |
zcard myzset //返回所有元素的個數 |
zremrangebyrank |
刪除集合中排序在給定區間的所有元素(按索引刪除) |
zremrangebyrank myzset 2 3 // |
zremrangebyscore |
刪除集合中在給定排序區間的元素 (按順序刪除) |
zremrangebyscore myzset 2 5 // |
使用場景
排行榜應用,取TOP N操作
這個需求與上面需求的不同之處在於,前面操作以時間為權重,這個是以某個條件為權重,比如按頂的次數排序,這時候就需要我們的sorted set出馬了,將你要排序的值設置成sorted set的score,將具體的數據設置成相應的value,每次只需要執行一條ZADD命令即可。
//將登錄次數和用戶統一存儲在一個sorted set里
zadd login:login_times 5 1
zadd login:login_times 1 2
zadd login:login_times 2 3
ZADD key score member
//當用戶登錄時,對該用戶的登錄次數自增1
ret = r.zincrby("login:login_times", 1, uid)
//那么如何獲得登錄次數最多的用戶呢,逆序排列取得排名前N的用戶
ret = r.zrevrange("login:login_times", 0, N-1)
ZREVRANGE key start stop [WITHSCORES]
比如在線游戲的排行榜,根據得分你通常想要:
- 列出前100名高分選手
- 列出某用戶當前的全球排名
這些操作對於Redis來說小菜一碟,即使你有幾百萬個用戶,每分鍾都會有幾百萬個新的得分。
模式是這樣的,每次獲得新得分時,我們用這樣的代碼:
ZADD leaderboard <score> <username>
你可能用userID來取代username,這取決於你是怎么設計的。
得到前100名高分用戶很簡單:ZREVRANGE leaderboard 0 99。
用戶的全球排名也相似,只需要:ZRANK leaderboard <username>。
ZRANK key member
Determine the index of a member in a sorted set
需要精准設定過期時間的應用
比如你可以把上面說到的sorted set的score值設置成過期時間的時間戳,那么就可以簡單地通過過期時間排序,定時清除過期數據了,不僅是清除Redis中的過期數據,你完全可以把Redis里這個過期時間當成是對數據庫中數據的索引,用Redis來找出哪些數據需要過期刪除,然后再精准地從數據庫中刪除相應的記錄。
范圍查找
比如:有一個IP范圍對應地址的列表,現在需要給出一個IP的情況下,迅速的查找到這個IP在哪個范圍,也就是要判斷此IP的所有地。
例如:查詢IP是否存在的問題;
ADSM,查詢IP是否在其他分組中存在。寫json文件
sadd向名稱為key的set中添加元素,返回影響元素的個數,0為失敗,1為成功
例如:有下面兩個范圍,10-20和30-40
A_start 10, A_end 20
B_start 30, B_end 40
我們將這兩個范圍的起始位置存在Redis的Sorted Sets數據結構中,基本范圍起始值作為score,范圍名加start和end為其value值:
redis 127.0.0.1:6379> zadd ranges 10 A_start
(integer) 1
redis 127.0.0.1:6379> zadd ranges 20 A_end
(integer) 1
redis 127.0.0.1:6379> zadd ranges 30 B_start
(integer) 1
redis 127.0.0.1:6379> zadd ranges 40 B_end
(integer) 1
這樣數據在插入Sorted Sets后,相當於是將這些起始位置按順序排列好了。
現在我需要查找15這個值在哪一個范圍中,只需要進行如下的zrangbyscore查找:
redis 127.0.0.1:6379> zrangebyscore ranges (15 +inf LIMIT 0 1
1) "A_end"
這個命令的意思是在Sorted Sets中查找大於15的第一個值。(+inf在Redis中表示正無窮大,15前面的括號表示>15而非>=15)
查找的結果是A_end,由於所有值是按順序排列的,所以可以判定15是在A_start到A_end區間上,也就是說15是在A這個范圍里。至此大功告成。
當然,如果你查找到的是一個start,比如咱們用25,執行下面的命令
redis 127.0.0.1:6379> zrangebyscore ranges (25 +inf LIMIT 0 1
1) "B_start"
返回結果表明其下一個節點是一個start節點,也就是說25這個值不處在任何start和end之間,不屬於任何范圍。
Pub/Sub
Pub/Sub 從字面上理解就是發布(Publish)與訂閱(Subscribe),在Redis中,你可以設定對某一個key值進行消息發布及消息訂閱,當一個key值上進行了消息發布后,所有訂閱它的客戶端都會收到相應的消息。這一功能最明顯的用法就是用作實時消息系統,比如普通的即時聊天,群聊等功能。
案例:
Qlocenter 下發策略
使用場景
Pub/Sub構建實時消息系統
Redis的Pub/Sub系統可以構建實時的消息系統