(大圖地址[https://user-gold-cdn.xitu.io/2019/11/8/16e49e23acdfe101])
一.redis的5種數據類型:
1.基礎理解:
string 字符串(可以為整形、浮點型和字符串,統稱為元素)
list 列表(實現隊列,元素不唯一,先入先出原則)
set 集合(各不相同的元素)
hash hash散列值(hash的key必須是唯一的)
sort set 有序集合
2.所有命令:http://redisdoc.com/
——————————————————————————————————————————————————————————————————————————————————————————————————
二.命令
(1).string類型的常用命令:它能夠存儲任何類型的字符串,包含二進制數據。可以用於存儲郵箱,JSON化的對象,甚至是一張圖片,一個字符串允許存儲的最大容量為512MB。
SET key value SET 賦值;
SETNX key value 如果key不存在則賦值,如果key存在不賦值;
SETEX key timeout value 設置KEY的過期時間
GET key GET 取值;
INCR key INCR 遞增數字,僅僅對數字類型的鍵有用,相當於Java的i++運算;
INCRBY key increment INCRBY 增加指定的數字,僅僅對數字類型的鍵有用,相當於Java的i+=3,increment可以為負數,表示減少;
DECR key DECR 遞減數字,僅僅對數字類型的鍵有用,相當於Java的i––;
DECRBY key decrement DECRBY 減少指定的數字,僅僅對數字類型的鍵有用,相當於Java的i-=3,decrement可以為正數,表示增加;
INCRBYFLOAT key increment INCRBYFLOAT 增加指定浮點數,僅僅對數字類型的鍵有用;
APPEND key value APPEND 向尾部追加值,相當於Java中的”hello”.append(“ world”);
STRLEN key STRLEN 獲取字符串長度;
MSET key1 value1 [key2 value2 ...] MSET 同時設置多個key的值;
MGET key1 [key2 ...] MGET 同時獲取多個key的值;
位操作
GETBIT key offset GETBIT獲取一個鍵值的二進制位的指定位置的值(0/1);
SETBIT key offset value SETBIT設置一個鍵值的二進制位的指定位置的值(0/1);
BITCOUNT key [start end] BITCOUNT 獲取一個鍵值的一個范圍內的二進制表示的1的個數;
BITOP OP desKey key1 key2 BITOP 該命令可以對多個字符串類型鍵進行位運算,並將結果存儲到指定的鍵中,BITOP支持的運算包含:OR,AND,XOR,NOT;
BITPOS key 0/1 [start, end] BITPOS 獲取指定鍵的第一個位值為0或者1的位置;
(2).list:
列表類型(list)用於存儲一個有序的字符串列表,常用的操作是向隊列兩端添加元素或者獲得列表的某一片段。列表內部使用的是雙向鏈表(double linked list)實現的,所以向列表兩端添加元素的時間復雜度是O(1),獲取越接近列表兩端的元素的速度越快。但是缺點是使用列表通過索引訪問元素的效率太低(需要從端點開始遍歷元素)。所以列表的使用場景一般如:朋友圈新鮮事,只關心最新的一些內容。借助列表類型,Redis還可以作為消息隊列使用。
查詢:
1.lindex [lindex key index]:通過索引index獲取列表的元素、key>=0從頭到尾,key<0從尾到頭
2.lrange [lrange key range_l range_r]:0 表頭、-1表尾
增加:
1.lpush [lpush key valus...] 類似於壓棧操作,將元素放入頭部
2.lpushx [lpushx key valus]:只能插入已經存在的key,且一次只能插入一次
3.rpush [rpush key valus...] :將元素push在list的尾部
4.rpushx [rpushx key valus...] :相對於lpushx
5.linsert [linsert key before/after pivot value]:將值插入到pivot的前面或后面。返回列表元素個數。如果參照點pivot不存在不插入。如果有多個pivot,以離表頭最近的為准
刪除:
1.lpop 、rpop:分別為刪除頭部和尾部,返回被刪除的元素
2.ltrim [ltrim key range_l range_r]:保留區域類的元素,其他的刪除
3.lrem [lrem key count value] :移除等於value的元素,當count>0時,從表頭開始查找,移除count個;當count=0時,從表頭開始查找,移除所有等於value的;當count<0時,從表尾開始查找,移除|count| 個
修改:
1.lset [lset key index value] : 設置列表指定索引的值,如果指定索引不存在則報錯
RPOPLPUSH source destination 將元素從一個列表轉義到另一個列表;
(3).set類型支持的常用命令:集合在概念在高中課本就學過,集合中每個元素都是不同的,集合中的元素個數最多為2的32次方-1個,集合中的元素是沒有順序的。
SADD key value1 [value2 value3 ...] 添加元素;
SREM key value2 [value2 value3 ...] 刪除元素;
SMEMBERS key 獲得集合中所有元素;
SISMEMBER key value 判斷元素是否在集合中;
SDIFF key1 key2 [key3 ...] 對集合做差集運算,先計算key1和key2的差集,然后再用結果與key3做差集;
SINTER key1 key2 [key3 ...] 對集合做交集運算;
SUNION key1 key2 [key3 ...] 對集合做並集運算;
SCARD key 獲得集合中元素的個數;
SPOP key 從集合中隨機彈出一個元素;
SDIFFSTORE destination key1 key2 [key3 ...] 對集合做差集並將結果存儲在destination;
SINTERSTORE destination key1 key2 [key3 ...] 對集合做交集運算並將結果存儲在destination;
SUNIONSTORE destination key1 key2 [key3 ...] 對集合做並集運算並將結果存儲在destination;
SRANDMEMBER key [count] 隨機獲取集合中的元素,當count>0時,會隨機中集合中獲取count個不重復的元素,當count<0時,隨機中集合中獲取|count|和可能重復的元素。
SMOVE source destination member 將 member 元素從 source 集合移動到 destination 集合
(4).hash數據類型支持的常用命令:
散列類型相當於Java中的HashMap,他的值是一個字典,保存很多key,value對,每對key,value的值個鍵都是字符串類型,換句話說,散列類型不能嵌套其他數據類型。一個散列類型鍵最多可以包含2的32次方-1個字段。
-
HDEL key field [field2] 刪除一個或多個哈希表字段
-
HEXISTS key field 查看哈希表 key 中,指定的字段是否存在, 返回0 || 1
-
HGET key field 獲取存儲在哈希表中指定字段的值
-
HGETALL key 獲取在哈希表中指定 key 的所有字段和值
-
HINCRBY key field increment 為哈希表 key 中的指定字段的整數值加上增量 increment
-
HINCRBYFLOAT key field increment 為哈希表 key 中的指定字段的浮點數值加上增量 increment
-
HKEYS key 獲取所有哈希表中的字段
-
HLEN key 獲取哈希表中字段的數量
-
HMGET key field1 [field2] 獲取所有給定字段的值
-
HMSET key field1 value1 [ field2 value2 ]同時將多個 field-value (域-值)對設置到哈希表 key 中
-
HSET key field value將哈希表 key 中的字段 field 的值設為 value
-
HSETNX key field value只有在字段 field 不存在時,設置哈希表字段的值
-
HVALS key 獲取哈希表中所有值
-
HSCAN key cursor [MATCH pattern] [COUNT count] 迭代哈希表中的鍵值對
(5).sort set:有序集合類型與集合類型的區別就是他是有序的。有序集合是在集合的基礎上為每一個元素關聯一個分數,這就讓有序集合不僅支持插入,刪除,判斷元素是否存在等操作外,還支持獲取分數最高/最低的前N個元素。有序集合中的每個元素是不同的,但是分數卻可以相同。有序集合使用散列表和跳躍表實現,即使讀取位於中間部分的數據也很快,時間復雜度為O(log(N)),有序集合比列表更費內存。
ZADD key score1 value1 [score2 value2 score3 value3 ...] 添加元素;
ZSCORE key value 獲取元素的分數;
ZRANGE key start stop [WITHSCORE] 獲取排名在某個范圍的元素,按照元素從小到大的順序排序,從0開始編號,包含start和stop對應的元素,WITHSCORE選項表示是否返回元素分數;
ZREVRANGE key start stop [WITHSCORE] 獲取排名在某個范圍的元素,和上一個命令用法一樣,只是這個倒序排序的;
ZRANGEBYSCORE key min max 獲取指定分數范圍內的元素,包含min和max,(min表示不包含min,(max表示不包含max,+inf表示無窮大;
ZINCRBY key increment value 增加某個元素的分數;
ZCARD key 獲取集合中元素的個數;
ZCOUNT key min max 返回有序集 key 中, score 值在 min 和 max 之間(默認包括 score 值等於 min 或 max )的成員的數量
ZREM key value1 [value2 ...] 刪除一個或多個元素;
ZREMRANGEBYRANK key start stop 按照排名范圍刪除元素;
ZREMRANGEBYSCORE key min max 按照分數范圍刪除元素;
ZRANK key value 獲取正序排序的元素的排名;
ZREVRANK key value 獲取逆序排序的元素的排名;
ZINTERSTORE destination numbers key1 key2 [key3 key4 ...] WEIGHTS weight1 weight2 [weight3 weight4 ...] AGGREGATE SUM | MIN | MAX 計算有序集合的交集並存儲結果,numbers表示參加運算的集合個數,weight表示權重,aggregate表示結果取值;
ZUNIONSTORE 計算有序幾個的並集並存儲結果,用法和上面一樣;
zadd的理解: 將一個或多個 member 元素及其 score 值加入到有序集 key 當中。如果某個 member 已經是有序集的成員,那么更新這個 member 的 score 值,並通過重新插入這個 member 元素,來保證該 member 在正確的位置上。score 值可以是整數值或雙精度浮點數。如果 key 不存在,則創建一個空的有序集並執行 ZADD 操作。當 key 存在但不是有序集類型時,返回一個錯誤。
(6).其他常用命令:
1.Redis刪除某個數據庫中以某個字符開頭的key
redis-cli -n 9 keys "c_recom" | xargs redis-cli -n 9 del
redis-cli -h 10.132.3.213 -p 6379 -a 888k6 -n 4 keys “ecard” | xargs redis-cli -h 10.132.3.213 -p 6379 -a 888k6 -n 4 del
2.鍵值相關命令
type key 查看key類型
keys * 查看所有key
exists key 查看是否有這個key
del key 刪除key
expire key 100 設置key100秒生存期
ttl key 獲取key的有效時長 [-1存在未設置 -2不存在] ,返回 key 的剩余生存時間
select 0 選擇到0數據庫 [ redis默認的數據庫是0~15一共16個數據庫 ]
move key 1 將當前數據庫中的key移動到其他的數據庫[ 數據庫1 ]
persist key 移除key的過期時間
randomkey 隨機返回數據庫里面的一個key
rename key2 key3 重命名key2 為key3
3.服務器相關命令
ping PONG 返回響應是否連接成功
echo 在命令行打印一些內容
select 0~15 編號的數據庫
quit || exit 退出客戶端
dbsize 返回當前數據庫中所有key的數量
info 返回redis的相關信息
config get dir/* 實時傳儲收到的請求
flushdb 刪除當前選擇數據庫中的所有key
flushall 刪除所有數據庫中的數據庫
4.info http://redisdoc.com/server/info.html
memory相關:
used_memory : 由 Redis 分配器分配的內存總量,以字節(byte)為單位
used_memory_human : 以人類可讀的格式返回 Redis 分配的內存總量
used_memory_rss : 從操作系統的角度,返回 Redis 已分配的內存總量(俗稱常駐集大小)。這個值和 top 、 ps 等命令的輸出一致。
used_memory_peak : Redis 的內存消耗峰值(以字節為單位)
used_memory_peak_human : 以人類可讀的格式返回 Redis 的內存消耗峰值
used_memory_lua : Lua 引擎所使用的內存大小(以字節為單位)
mem_fragmentation_ratio : used_memory_rss 和 used_memory 之間的比率
mem_allocator : 在編譯時指定的, Redis 所使用的內存分配器。可以是 libc 、 jemalloc 或者 tcmalloc 。
在理想情況下, used_memory_rss 的值應該只比 used_memory 稍微高一點兒。
當 rss > used ,且兩者的值相差較大時,表示存在(內部或外部的)內存碎片。
內存碎片的比率可以通過 mem_fragmentation_ratio 的值看出。
當 used > rss 時,表示 Redis 的部分內存被操作系統換出到交換空間了,在這種情況下,操作可能會產生明顯的延遲。
Because Redis does not have control over how its allocations are mapped to memory pages, high used_memory_rss is often the result of a spike in memory usage.
當 Redis 釋放內存時,分配器可能會,也可能不會,將內存返還給操作系統。
如果 Redis 釋放了內存,卻沒有將內存返還給操作系統,那么 used_memory 的值可能和操作系統顯示的 Redis 內存占用並不一致。
查看 used_memory_peak 的值可以驗證這種情況是否發生。
5.啟動&關閉
啟動
/usr/local/Cellar/redis/3.2.5/bin/redis-server /usr/local/etc/redis.conf
[ redis-server /usr/local/etc/redis.conf ]
redis指定配置文件啟動
vim /usr/local/etc/redis.conf
daemonize no(默認),改成 yes,意思是是否要后台啟動。
關閉
不能用 kill 暴力關閉,因為可能會丟失數據,
使用./redis-cli shutdown,redis 會先保存好數據,再關閉
——————————————————————————————————————————————————————————————————————————————————————————————
三.使用場景
String
String數據結構是簡單的key-value類型,value其實不僅可以是String,也可以是數字。
常規key-value緩存應用;
常規計數:微博數,粉絲數等。
hash
Redis hash是一個string類型的field和value的映射表,hash特別適合用於存儲對象。
存儲部分變更的數據,如用戶信息等。
list
list就是鏈表,略有數據結構知識的人都應該能理解其結構。使用Lists結構,我們可以輕松地實現最新消息排行等功能。List的另一個應用就是消息隊列,可以利用List的PUSH操作,將任務存在List中,然后工作線程再用POP操作將任務取出進行執行。Redis還提供了操作List中某一段的api,你可以直接查詢,刪除List中某一段的元素。
Redis的list是每個子元素都是String類型的雙向鏈表,可以通過push和pop操作從列表的頭部或者尾部添加或者刪除元素,這樣List即可以作為棧,也可以作為隊列。
消息隊列系統
使用list可以構建隊列系統,使用sorted set甚至可以構建有優先級的隊列系統。
比如:將Redis用作日志收集器
實際上還是一個隊列,多個端點將日志信息寫入Redis,然后一個worker統一將所有日志寫到磁盤。
取最新N個數據的操作
//把當前登錄人添加到鏈表里
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數據庫(或是硬盤上的其他類型數據庫)只是在用戶需要獲取“很遠”的數據時才會被觸發,而主頁或第一個評論頁是不會麻煩到硬盤上的數據庫了。
set
set就是一個集合,集合的概念就是一堆不重復值的組合。利用Redis提供的set數據結構,可以存儲一些集合性的數據。set中的元素是沒有順序的。
案例:
在微博應用中,可以將一個用戶所有的關注人存在一個集合中,將其所有粉絲存在一個集合。Redis還為集合提供了求交集、並集、差集等操作,可以非常方便的實現如共同關注、共同喜好、二度好友等功能,對上面的所有集合操作,你還可以使用不同的命令選擇將結果返回給客戶端還是存集到一個新的集合中。
交集,並集,差集
//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的書?
diff_list = redis.sdiff("tag:ruby", "tag:web")
//屬於ruby和屬於web的書的合集?
union_list = redis.sunion("tag:ruby", "tag:web")
獲取某段時間所有數據去重值
這個使用Redis的set數據結構最合適了,只需要不斷地將數據往set中扔就行了,set意為集合,所以會自動排重。
sorted set
和set相比,sorted set增加了一個權重參數score,使得集合中的元素能夠按score進行有序排列,比如一個存儲全班同學成績的sorted set,其集合value可以是同學的學號,而score就可以是其考試得分,這樣在數據插入集合的時候,就已經進行了天然的排序。可以用sorted set來做帶權重的隊列,比如普通消息的score為1,重要消息的score為2,然后工作線程可以選擇按score的倒序來獲取工作任務。讓重要的任務優先執行。
排行榜應用,取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
//當用戶登錄時,對該用戶的登錄次數自增1
ret = r.zincrby("login:login_times", 1, uid)
//那么如何獲得登錄次數最多的用戶呢,逆序排列取得排名前N的用戶
ret = r.zrevrange("login:login_times", 0, N-1)
比如在線游戲的排行榜,根據得分你通常想要:
- 列出前100名高分選手
- 列出某用戶當前的全球排名
這些操作對於Redis來說小菜一碟,即使你有幾百萬個用戶,每分鍾都會有幾百萬個新的得分。
模式是這樣的,每次獲得新得分時,我們用這樣的代碼:
ZADD leaderboard
你可能用userID來取代username,這取決於你是怎么設計的。
得到前100名高分用戶很簡單:
ZREVRANGE leaderboard 0 99
用戶的全球排名也相似,只需要:
ZRANK leaderboard
需要精准設定過期時間的應用
比如你可以把上面說到的sorted set的score值設置成過期時間的時間戳,那么就可以簡單地通過過期時間排序,定時清除過期數據了,不僅是清除Redis中的過期數據,你完全可以把Redis里這個過期時間當成是對數據庫中數據的索引,用Redis來找出哪些數據需要過期刪除,然后再精准地從數據庫中刪除相應的記錄。
范圍查找
來自Redis在Google Group上的一個問題,有一位同學發貼求助,說要解決如下的一個問題:他有一個IP范圍對應地址的列表,現在需要給出一個IP的情況下,迅速的查找到這個IP在哪個范圍,也就是要判斷此IP的所有地。這個問題引來了Redis作者Salvatore Sanfilippo(@antirez)的回答。解答如下:
例如有下面兩個范圍,10-20和30-40
- A_start 10, A_end 20
- B_start 30, B_end 40
我們將這兩個范圍的起始位置存在Redis的sorted set數據結構中,基本范圍起始值作為score,范圍名加start和end為其value值:
redis 127.0.0.1:6379> zadd ranges 10 A_start
redis 127.0.0.1:6379> zadd ranges 20 A_end
redis 127.0.0.1:6379> zadd ranges 30 B_start
redis 127.0.0.1:6379> zadd ranges 40 B_end
這樣數據在插入sorted set后,相當於是將這些起始位置按順序排列好了。
現在我需要查找15這個值在哪一個范圍中,只需要進行如下的zrangbyscore查找:
redis 127.0.0.1:6379> zrangebyscore ranges (15 +inf LIMIT 0 1
這個命令的意思是在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
返回結果表明其下一個節點是一個start節點,也就是說25這個值不處在任何start和end之間,不屬於任何范圍。
當然,這個例子僅適用於類似上面的IP范圍查找的案例,因為這些值范圍之間沒有重合。如果是有重合的情況,這個問題本身也就變成了一個一對多的問題。
Pub/Sub
Pub/Sub 從字面上理解就是發布(Publish)與訂閱(Subscribe),在Redis中,你可以設定對某一個key值進行消息發布及消息訂閱,當一個key值上進行了消息發布后,所有訂閱它的客戶端都會收到相應的消息。這一功能最明顯的用法就是用作實時消息系統,比如普通的即時聊天,群聊等功能。
使用場景
Pub/Sub構建實時消息系統
Redis的Pub/Sub系統可以構建實時的消息系統
比如很多用Pub/Sub構建的實時聊天系統的例子。
參考:
深入淺出redis底層數據結構系列
https://www.cnblogs.com/jaycekon/p/6227442.html(上)
https://www.cnblogs.com/jaycekon/p/6277653.html(下)
http://www.redis.net.cn/tutorial/3501.html
http://www.cnblogs.com/markhe/p/5689356.html
http://www.cnblogs.com/ggjucheng/p/3349102.html
https://blog.csdn.net/Richard_Jason/article/details/53130369