Redis簡介
Redis是一個(gè)開源的,內(nèi)存中的數(shù)據(jù)結(jié)構(gòu)存儲系統(tǒng),它可以用作數(shù)據(jù)庫、緩存和消息中間件。JAVA客戶端是jedis。Redis6.0已經(jīng)支持多線程了(面試提問單線程問題的時(shí)候要注意了)。
Redis數(shù)據(jù)結(jié)構(gòu)(羅列)
- String:二進(jìn)制安全的字符串
- Lists:安插入順序排序的字符串元素集合?;臼擎湵?。
- Sets:無序不重復(fù)集合。
- Sorted sets(zset):里面的元素總是通過score進(jìn)行排序。有序集合。
- Hashes:鍵值都是字符串的哈希表。
- Bit arrays:位集合(可以實(shí)現(xiàn)類似布隆過濾器的功能結(jié)構(gòu))
- HyperLogLog:是用來做基數(shù)統(tǒng)計(jì)的算法。用于估計(jì)一個(gè)set中元素?cái)?shù)量的概率性的數(shù)據(jù)結(jié)構(gòu)。
- Geospatial Indexes:地理空間索引
- Streams:流信息
Redis keys
Redis key值是二進(jìn)制安全的。(二進(jìn)制安全 是指,在傳輸數(shù)據(jù)時(shí),保證二進(jìn)制數(shù)據(jù)的信息安全,也就是不被篡改、破譯等,如果被攻擊,能夠及時(shí)檢測出來。),可以是空值,也可以是任何二進(jìn)制序列。
key的自動(dòng)創(chuàng)建和刪除
在實(shí)踐中或例子里可以看到,我們沒有在推入元素之前創(chuàng)建空的list,或者在list沒有元素時(shí)刪除它。在list為null刪除key,并使用lpush時(shí)而創(chuàng)建空list. 這是Redis的職責(zé)。適用于list,zset,set,hash類型。
概括如下:
1當(dāng)我們向一個(gè)聚合數(shù)據(jù)類型中添加元素時(shí),如果目標(biāo)鍵不存在,就在添加元素前創(chuàng)建空的聚合數(shù)據(jù)類型。
2當(dāng)我們從聚合數(shù)據(jù)類型中移除元素時(shí),如果值仍然是空的,鍵自動(dòng)被銷毀。
3對一個(gè)空的 key 3調(diào)用一個(gè)只讀的命令,比如 LLEN (返回 list 的長度),或者一個(gè)刪除元素的命令,將總是產(chǎn)生同樣的結(jié)果。該結(jié)果和對一個(gè)空的聚合類型做同個(gè)操作的結(jié)果是一樣的。
一、string 字符串
可以在一個(gè)鍵下保存一個(gè)圖片,值的長度不能超過512MB.
//設(shè)置鍵值
> set mykey somevalue
OK
//獲取鍵值
> get mykey
"somevalue"
//也可以通過命令進(jìn)行遞增操作
> set counter 100
OK
> incr counter //+1
(integer) 101
> incr counter
(integer) 102
> incrby counter 50 //+50
(integer) 152
INCR 命令將字符串值解析成整型后加一,然后在保存為字符串。類似命令有INCRBY,DECR,DECRBY。都是原子性操作,不會導(dǎo)致競爭的情況。
redis也可以做批量的操作,一次性設(shè)置獲取多個(gè)值。
> mset a 10 b 20 c 30
OK
> mget a b c
"10"
"20"
"30"
其他命令
exists key:返回1或0標(biāo)識給定key的值是否存在。
del key:返回1或0標(biāo)識給定key的值是否存在
type key:返回key對應(yīng)值的存儲類型。
expire key time:給值設(shè)定存活時(shí)間。time可以是毫秒或者秒。(如果用rename命令修改key,相關(guān)時(shí)間也會轉(zhuǎn)移到新名稱上。針對已有過期時(shí)間的key進(jìn)行操作,會刷新過期時(shí)間)
ttl key:用來查看key所對應(yīng)值的剩余時(shí)間。
redis沒有設(shè)置過期時(shí)間,ttl返回-1。
redis已經(jīng)大于過期時(shí)間,ttl返回-2.
設(shè)置了剩余時(shí)間,且沒過期的。ttl返回剩余時(shí)間。
沒有設(shè)置過期時(shí)間的值可以通過del進(jìn)行刪除。
redis keys的過期有兩種方式
1. 主動(dòng)過期
當(dāng)一些客戶端嘗訪問它時(shí),key會被發(fā)現(xiàn)并主動(dòng)過期。
2. 被動(dòng)過期
如果有一些keys,可能永遠(yuǎn)也不會訪問。所以定時(shí)隨機(jī)測試設(shè)置keys的過期時(shí)間。所有過期的keys將會從密鑰空間刪除。具體就是Redis每秒10次的操作。
a. 測試隨機(jī)的20個(gè)keys進(jìn)行相關(guān)過期檢測。
b. 刪除所有已經(jīng)過期的keys
c. 如果有多于25%的keys過期,重復(fù)a
Redis String 使用場景: 普通key/value存儲,比如統(tǒng)計(jì)IP訪問次數(shù)(利用key名 + incr)
二、Redis Lists
Redis lists基于Linked List實(shí)現(xiàn)。在頭部或者尾部添加一個(gè)元素的操作的時(shí)間復(fù)雜度是常數(shù)級別的。用LPUSH命令在十個(gè)元素的list頭部添加元素和在千萬級list頭部添加元素的速度相同。
對于數(shù)據(jù)庫來說,一個(gè)重要特性是能非??斓脑诹斜砩咸砑釉亍6襯edis lists能在常數(shù)時(shí)間內(nèi)取得常數(shù)長度。所以用linked list實(shí)現(xiàn)。
rpush key value:向list的右邊/尾部添加一個(gè)新元素。
lpush key value:向list的左邊/頭部添加一個(gè)新元素。
lrange key index index:從list中取出范圍內(nèi)元素,index可以為負(fù)數(shù),表示從右邊/尾部計(jì)數(shù)。
當(dāng)然也可以批量處理數(shù)據(jù)
> rpush mylist 1 2 3 4 5 "foo bar"
(integer) 9
> lrange mylist 0 -1
1) "first"
2) "A"
3) "B"
4) "1"
5) "2"
6) "3"
7) "4"
8) "5"
9) "foo bar"
**rpop/lpop key **:從list中刪除元素并返回刪除元素的值??梢栽陬^部/尾部操作。
**ltrim key index index **:把list從左邊截取指定長度
> rpush mylist 1 2 3 4 5
(integer) 5
> ltrim mylist 0 2
OK
> lrange mylist 0 -1
1) "1"
2) "2"
3) "3"
list值類型,可以用來實(shí)現(xiàn)聊天系統(tǒng),可以用來作為消息隊(duì)列(按照順序訪問數(shù)據(jù))。也可以使用lrange對結(jié)果進(jìn)行分頁。
用作為消息隊(duì)列時(shí),如果用lpush和rpop實(shí)現(xiàn)功能會出現(xiàn)一種情景(list為空,但是消費(fèi)者一直輪詢獲取,會增加redis的訪問壓力、和cpu消耗),所以redis提供了阻塞式訪問的brpop和blpop命令。可見詳解:http://www.redis.cn/commands/blpop.html
三、Redis Hash
由鍵值對組成。適合存儲對象型數(shù)據(jù)。Redis中的每個(gè)hash可以存儲 2^32個(gè)鍵值對。
可以對散列存儲的數(shù)字進(jìn)行自增或自薦
> hmset user:1000 username antirez birthyear 1977 verified 1
OK
> hget user:1000 username
"antirez"
> hget user:1000 birthyear
"1977"
> hgetall user:1000
1) "username"
2) "antirez"
3) "birthyear"
4) "1977"
5) "verified"
6) "1"
hmset key value(k,v):用于設(shè)置hash的多個(gè)域。
hget key k:用于獲取單個(gè)k對應(yīng)的v.
hmget key k k k:用于獲取多個(gè)k對應(yīng)的v.
**hincrby key k 10 **:針對單獨(dú)的值域進(jìn)行操作。
更多指令請查看:http://redis.io/commands#hash
Redis hash應(yīng)用場景:
存儲對象型數(shù)據(jù),如 人(屬性,值,屬性,值)
四、Redis Set
是對String的無序排列。
> sadd myset 1 2 3
(integer) 3
> smembers myset
1. 3
2. 1
3. 2
> sismember myset 3
(integer) 1
**sadd key v v v **:將新的元素添加到key中。
**smembers key **:返回所有元素,元素順序不定。
smembers key v :檢測某個(gè)元素是否存在
Sets適合用于標(biāo)識對象間的關(guān)系,比如標(biāo)識標(biāo)記/打標(biāo)簽。
被打上相同標(biāo)簽的元素列表
> sadd tag:1:news 1000
(integer) 1
> sadd tag:2:news 1000
(integer) 1
> sadd tag:5:news 1000
(integer) 1
> sadd tag:77:news 1000
(integer) 1
## 獲取一個(gè)對象的所有tag
> smembers news:1000:tags
1. 5
2. 1
3. 77
4. 2
set數(shù)據(jù)類型還有一個(gè)功能是sdiff,取差集(myset中排除myset2的值)。也可以用來對比兩個(gè)集合的交集SINTER(屬于A,也屬于B)??梢岳眠@個(gè)功能處理對賬數(shù)據(jù)。
> SADD myset "hello"
(integer) 1
> SADD myset "foo"
(integer) 1
> SADD myset "bar"
(integer) 1
> SADD myset2 "hello"
(integer) 1
> SADD myset2 "world"
(integer) 1
> SDIFF myset myset2
1) "foo"
2) "bar"
我在實(shí)際工作中,tag標(biāo)記做法直接在數(shù)據(jù)庫中做,方便多個(gè)維度的數(shù)據(jù)查詢。
位運(yùn)算的使用
關(guān)于redis set操作的更多命令查看:https://www.redis.net.cn/tutorial/3511.html
Redis set使用場景:
利用唯一性統(tǒng)計(jì)獨(dú)立IP等。利用對交集、并集、差集的計(jì)算對數(shù)據(jù)進(jìn)行過濾處理,如共同好友、推薦信息的數(shù)據(jù)過濾等。
五、Redis zset
Redis 有序集合和set集合一樣也是string類型元素的集合,且不允許重復(fù)的成員。
不同的是每個(gè)元素都會關(guān)聯(lián)一個(gè)double類型的分?jǐn)?shù)。redis正是通過分?jǐn)?shù)來為集合中的成員進(jìn)行從小到大的排序。
有序集合的成員是唯一的,但分?jǐn)?shù)(score)卻可以重復(fù)。(如果設(shè)置所有的元素score為一致的,則默認(rèn)按照字典序排序。)
> zadd hackers 1940 "Alan Kay"
(integer) 1
> zadd hackers 1957 "Sophie Wilson"
(integer 1)
> zadd hackers 1953 "Richard Stallman"
(integer) 1
> zadd hackers 1949 "Anita Borg"
(integer) 1
> zadd hackers 1965 "Yukihiro Matsumoto"
(integer) 1
> zadd hackers 1914 "Hedy Lamarr"
(integer) 1
> zadd hackers 1916 "Claude Shannon"
(integer) 1
> zadd hackers 1969 "Linus Torvalds"
(integer) 1
> zadd hackers 1912 "Alan Turing"
(integer) 1
## 正序輸出
> zrange hackers 0 -1
1) "Alan Turing"
2) "Hedy Lamarr"
3) "Claude Shannon"
4) "Alan Kay"
5) "Anita Borg"
6) "Richard Stallman"
7) "Sophie Wilson"
8) "Yukihiro Matsumoto"
9) "Linus Torvalds"
## 反序輸出
> zrevrange hackers 0 -1
1) "Linus Torvalds"
2) "Yukihiro Matsumoto"
3) "Sophie Wilson"
4) "Richard Stallman"
5) "Anita Borg"
6) "Alan Kay"
7) "Claude Shannon"
8) "Hedy Lamarr"
9) "Alan Turing"
## 1950以下的數(shù)據(jù)
> zrangebyscore hackers -inf 1950
1) "Alan Turing"
2) "Hedy Lamarr"
3) "Claude Shannon"
4) "Alan Kay"
5) "Anita Borg"
## 刪除1940~1960區(qū)間內(nèi)的元素
> zremrangebyscore hackers 1940 1960
(integer) 4
## 查詢zset中的元素index
> zrank hackers "Anita Borg"
(integer) 4
Redis ZSet使用場景:
排行榜、統(tǒng)計(jì)類的數(shù)據(jù)需求。
六、Redis Bitmap
位圖不是實(shí)際的數(shù)據(jù)類型,而是在String類型上定義的一組面向位的操作。
由于字符串是二進(jìn)制安全的,最大長度是512MB,轉(zhuǎn)換成位可以設(shè)置 2^32不同的位。
512MB = 2^9 * 2^3(byte) * 2^10(kb) * 2^10(mb) = 2^32(bit)
位圖的最大優(yōu)點(diǎn)之一,存儲信息時(shí)可以節(jié)省大量空間。
> set key big
位操作分為兩類:
1.固定時(shí)間的單個(gè)位操作(設(shè)置位為1或0獲取當(dāng)前位的值)
## 設(shè)置位值為1
> setbit key 10 1
(integer) 1
## 獲取
> getbit key 10
(integer) 1
## 獲取沒有存過的值時(shí)為0
> getbit key 11
(integer) 0
2.對位組的操作(獲取范圍內(nèi)的位的數(shù)量、效率很高)
> setbit key 0 1
(integer) 0
> setbit key 100 1
(integer) 0
> bitcount key
(integer) 2
注: Bitmap 和 HyperLogLog是基于String類型,但是拓展了自己的語義
Redis Bitmap使用情景
- 各種實(shí)時(shí)分析。
例如,假設(shè)您想知道網(wǎng)站用戶每天訪問量最長的時(shí)間。您從零開始計(jì)算天數(shù),即從您公開網(wǎng)站的那一天開始,并在用戶每次訪問該網(wǎng)站時(shí)對SETBIT進(jìn)行設(shè)置。作為位索引,您只需花費(fèi)當(dāng)前的unix時(shí)間,減去初始偏移,然后除以3600 * 24。
這樣,對于每個(gè)用戶,您都有一個(gè)小的字符串,其中包含每天的訪問信息。
> setbit key 20200310-UID 1
> setbit key 20200311-UID 1
> setbit key 20200311-UID 0
類似bloomfilter的實(shí)現(xiàn),防止緩存穿透。(將存量 + 增量的標(biāo)識數(shù)據(jù)進(jìn)行存儲,去判斷)
可以通過多個(gè)bitmap的交集、并集、not(非)、xor(疑惑)操作處理一些位運(yùn)算的邏輯
bitmap 可以使用RLE編碼進(jìn)一步壓縮空間
七、Redis HyperLogLogs(HLL)
屬于一種概率算法,(LC,LLC,HLL)三種越來越節(jié)省內(nèi)存,降低誤差率。
HyperLogLog優(yōu)點(diǎn),在輸入元素的數(shù)量或者體積非常大時(shí)。計(jì)算基數(shù)所需的空間總是固定很小的。每個(gè)HyperLogLog的鍵只需要花費(fèi)12KB內(nèi)存,在標(biāo)準(zhǔn)誤差0.81%的前提下,就可以計(jì)算接近2^64個(gè)不同的基數(shù)。
用bitmap存儲1一億個(gè)統(tǒng)計(jì)數(shù)據(jù)大概需要12M內(nèi)存;而在HLL中,只需要不到1K內(nèi)存就能做到。
HyperLogLog只會根據(jù)輸入元素來計(jì)算基數(shù),而不會存儲元素本身,所以不能返回各個(gè)元素。
## 添加指定元素到HLL中
> pfadd hll a b c d
(integer) 1
## 返回給定的HLL的基數(shù)估算值
> pfcount hll
(integer) 4
## 合并HLL
> pfmerge key sourceA [sourceB]
HLL的使用場景:常用來統(tǒng)計(jì)一個(gè)集合中不重復(fù)的元素個(gè)數(shù),例如網(wǎng)站PV,搜索關(guān)鍵詞數(shù)量,數(shù)據(jù)分析、網(wǎng)絡(luò)監(jiān)控及數(shù)據(jù)庫優(yōu)化等領(lǐng)域。
HLL比 bitmap更節(jié)省內(nèi)存,但有一定誤差( 標(biāo)準(zhǔn)誤差 0.81%)
八、Redis Geospatial Indexes(地理空間索引)
將制定的地理空間位置(經(jīng)度、緯度、名稱)添加到指定的key中。這些數(shù)據(jù)將會存儲到Sorted set中。目的是為了方便GEORADIUS或者GEORADIUSBYMEMBER命令對數(shù)據(jù)進(jìn)行半徑查詢等操作。
sorted set使用一種稱為Geohash的技術(shù)進(jìn)行填充。經(jīng)度和緯度的位是交錯(cuò)的,以形成一個(gè)獨(dú)特的52位整數(shù). 我們知道,一個(gè)sorted set 的double score可以代表一個(gè)52位的整數(shù),而不會失去精度。
這種格式允許半徑查詢檢查的1 + 8個(gè)領(lǐng)域需要覆蓋整個(gè)半徑,并丟棄元素以外的半徑。通過計(jì)算該區(qū)域的范圍,通過計(jì)算所涵蓋的范圍,從不太重要的部分的排序集的得分,并計(jì)算得分范圍為每個(gè)區(qū)域的sorted set中的查詢。
官方示例
## 增加意大利西西里島的兩個(gè)城市坐標(biāo)(Palermo和 卡塔尼亞)
redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2
## 返回兩個(gè)給定位置之間的距離。(默認(rèn)單位米)
redis> GEODIST Sicily Palermo Catania
"166274.1516"
## 以給定的經(jīng)緯度為中心,返回鍵包含的位置元素當(dāng)中, 與中心的距離不超過給定最大距離的所有位置元素。
## 100m
redis> GEORADIUS Sicily 15 37 100 km
1) "Catania"
## 200m
redis> GEORADIUS Sicily 15 37 200 km
1) "Palermo"
2) "Catania"
redis>
可以通過查詢不同地方的坐標(biāo)信息進(jìn)行驗(yàn)證。查詢點(diǎn)這里
Geospatial indexes使用場景:這里假設(shè)地球是一個(gè)球體,因?yàn)槭褂玫木嚯x公式是Haversine公式。這個(gè)公式僅適用于地球,而不是一個(gè)完美的球體。當(dāng)在社交網(wǎng)站和其他大多數(shù)需要查詢半徑的應(yīng)用中使用時(shí),這些偏差都不算問題。但是,在最壞的情況下的偏差可能是0.5%,所以一些地理位置很關(guān)鍵的應(yīng)用還是需要謹(jǐn)慎考慮。
九、Redis Streams
Stream是Redis 5.0引入的一種新數(shù)據(jù)類型,是一個(gè)新的強(qiáng)大的支持多播的可持久化的消息隊(duì)列。
相比于現(xiàn)有的PUB/SUB、BLOCKED LIST,其雖然也可以在簡單的場景下作為消息隊(duì)列來使用,但是Redis Stream無疑要完善很多。Redis Stream提供了消息的持久化和主備復(fù)制功能、新的RadixTree數(shù)據(jù)結(jié)構(gòu)來支持更高效的內(nèi)存使用和消息讀取、甚至是類似于Kafka的Consumer Group功能。
它以更抽象的方式對日志數(shù)據(jù)結(jié)構(gòu)進(jìn)行建模,但是日志的本質(zhì)仍然完好無損:像日志文件一樣,通常實(shí)現(xiàn)為僅在追加模式下打開的文件, Redis流主要是僅追加數(shù)據(jù)結(jié)構(gòu)。至少從概念上講,由于Redis是流式傳輸在內(nèi)存中表示的抽象數(shù)據(jù)類型,因此它們實(shí)現(xiàn)了更強(qiáng)大的操作,以克服日志文件本身的限制。
盡管數(shù)據(jù)結(jié)構(gòu)本身非常簡單,但Redis流卻成為最復(fù)雜的Redis類型的原因在于它實(shí)現(xiàn)了其他非強(qiáng)制性功能:一組阻止操作,使消費(fèi)者可以等待生產(chǎn)者將新數(shù)據(jù)添加到流中,此外還有一個(gè)稱為“ 消費(fèi)群體”的概念。
消費(fèi)者群體最初是由流行的稱為Kafka(TM)的消息傳遞系統(tǒng)引入的。Redis用完全不同的術(shù)語重新實(shí)現(xiàn)了一個(gè)類似的想法,但是目標(biāo)是相同的:允許一組客戶合作使用同一消息流的不同部分。
> XADD mystream * sensor-id 1234 temperature 19.8
1518951480106-0
##上面對XADD命令的調(diào)用使用自動(dòng)生成的條目ID
##將一個(gè)條目添加sensor-id: 1234, temperature: 19.8到key流中mystream,該條目ID是該命令返回的,具體來說是1518951480106-0。
##它以鍵名作為第一個(gè)參數(shù)mystream,第二個(gè)參數(shù)是標(biāo)識流中每個(gè)條目的條目ID。
## 獲取Stream中的項(xiàng)目數(shù):
> XLEN mystream
(integer) 1
針對流的操作有很多,可以點(diǎn)擊這里查看官方文檔
Redis stream使用場景:消息隊(duì)列,和kafka, RocketMq ,RabbitMq等各種消息中間件要按照當(dāng)前環(huán)境的情況和要求合理使用。
整理自 deathearth.com