redis 基礎(chǔ)數(shù)據(jù)類(lèi)型
Sting類(lèi)型 :
Redis 的字符串是動(dòng)態(tài)字符串,是可以修改的字符串芍耘,內(nèi)部結(jié)構(gòu)實(shí)現(xiàn)上類(lèi)似于 Java 的 ArrayList,采用預(yù)分配冗余空間的方式來(lái)減少內(nèi)存的頻繁分配斋竞,如圖中所示,內(nèi)部為當(dāng)前字符串實(shí)際分配的空間 capacity 一般要高于實(shí)際字符串長(zhǎng)度 len秃殉。當(dāng)字符串長(zhǎng)度小于 1M 時(shí),擴(kuò)容都是加倍現(xiàn)有的空間复濒,如果超過(guò) 1M脖卖,擴(kuò)容時(shí)一次只會(huì)多擴(kuò) 1M 的空間。需要注意的是字符串最大長(zhǎng)度為 512M畦木。
操作命令:
set key value
get key
exists key
del key
批量設(shè)置
mset key1 value1 key2 value2 key3 value3
mget key1 key2 key3
過(guò)期設(shè)置
expire key1 5 5秒后過(guò)期
setex key 5 value? #5s 后過(guò)期砸泛,等價(jià)于 set+expire
setnx key value 沒(méi)有key 返回1設(shè)置成功,key已存在 返回0
計(jì)數(shù)
incr key? # 將key對(duì)應(yīng)的value值 遞增加1 ,遞增若超過(guò)最大數(shù)值則報(bào)錯(cuò)? (incrby age 5 按5遞增)
list類(lèi)型 :
Redis 的列表相當(dāng)于 Java 語(yǔ)言里面的 LinkedList,注意它是鏈表而不是數(shù)組琢融。這意味著 list 的插入和刪除操作非常快,時(shí)間復(fù)雜度為 O(1)纳令,但是索引定位很慢挽荠,時(shí)間復(fù)雜度為 O(n)克胳,這點(diǎn)讓人非常意外。
當(dāng)列表彈出了最后一個(gè)元素之后圈匆,該數(shù)據(jù)結(jié)構(gòu)自動(dòng)被刪除毯欣,內(nèi)存被回收。
操作命令:
rpush key? value1 value2 value3 存放元素 value1在最左邊
llen key? 查詢(xún)list長(zhǎng)度
lpop key? 從左邊取出對(duì)應(yīng)的key的值臭脓,取出就沒(méi)有了? (右邊進(jìn)左邊出:隊(duì)列 )
rpop key? 從右邊取出對(duì)應(yīng)的key的值,取出就沒(méi)有了? (右邊進(jìn)右邊出:棧 )
慢操作
lindex 相當(dāng)于 Java 鏈表的get(int index)方法腹忽,它需要對(duì)鏈表進(jìn)行遍歷来累,性能隨著參數(shù)index增大而變差,
index可以為負(fù)數(shù),index=-1表示倒數(shù)第一個(gè)元素窘奏,同樣index=-2表示倒數(shù)第二個(gè)元素嘹锁。
lindex key 1 取出下標(biāo)為1,也就是放進(jìn)去的第二個(gè)元素? O(n) 慎用
lrange key 0 -1 # 獲取所有元素着裹,O(n) 慎用
ltrim key 1 -1 為了截取下標(biāo)為1 一直到最后? 若區(qū)間為負(fù)數(shù)則清空列表 ltrim key 1 0
快速列表
首先在列表元素較少的情況下會(huì)使用一塊連續(xù)的內(nèi)存存儲(chǔ)领猾,這個(gè)結(jié)構(gòu)是ziplist,也即是壓縮列表骇扇。它將所有的元素緊挨著一起存儲(chǔ)摔竿,分配的是一塊連續(xù)的內(nèi)存。當(dāng)數(shù)據(jù)量比較多的時(shí)候才會(huì)改成quicklist少孝。因?yàn)槠胀ǖ逆湵硇枰母郊又羔樋臻g太大继低,會(huì)比較浪費(fèi)空間,而且會(huì)加重內(nèi)存的碎片化稍走。比如這個(gè)列表里存的只是 int 類(lèi)型的數(shù)據(jù)袁翁,結(jié)構(gòu)上還需要兩個(gè)額外的指針 prev 和 next 。所以 Redis 將鏈表和 ziplist 結(jié)合起來(lái)組成了 quicklist婿脸。也就是將多個(gè) ziplist 使用雙向指針串起來(lái)使用粱胜。
hash (字典):數(shù)組 + 鏈表二維結(jié)構(gòu) 值只能是字符串
Java 的 HashMap 在字典很大時(shí),rehash 是個(gè)耗時(shí)的操作狐树,需要一次性全部 rehash焙压。Redis 為了高性能,不能堵塞服務(wù)抑钟,所以采用了漸進(jìn)式 rehash 策略冗恨。漸進(jìn)式 rehash 會(huì)在 rehash 的同時(shí),保留新舊兩個(gè)hash結(jié)構(gòu)味赃,查詢(xún)時(shí)會(huì)同時(shí)查詢(xún)兩個(gè)hash結(jié)構(gòu)掀抹,然后在后續(xù)的定時(shí)任務(wù)中以及 hash 操作指令中,循序漸進(jìn)地將舊 hash 的內(nèi)容一點(diǎn)點(diǎn)遷移到新的 hash 結(jié)構(gòu)中心俗。當(dāng)搬遷完成了傲武,就會(huì)使用新的hash結(jié)構(gòu)取而代之蓉驹。
hash可以存儲(chǔ)一個(gè)結(jié)構(gòu)體部分字段數(shù)據(jù),比如String類(lèi)型數(shù)據(jù)若一次序列化整個(gè)結(jié)構(gòu)存儲(chǔ)導(dǎo)致網(wǎng)絡(luò)流量浪費(fèi)揪利。hash也有缺點(diǎn)存儲(chǔ)消耗大于單個(gè)字符串态兴。根據(jù)實(shí)際情況來(lái)選擇 hash還是string來(lái)存儲(chǔ)
操作命令:
hset books java "think in java" # 命令行的字符串如果包含空格,要用引號(hào)括起來(lái)? 若是更新操作直接覆蓋
hget books java
hgetall books
批量 set
hmset books java "effective java" python "learning python" golang "modern golang programming"
計(jì)數(shù)
hincrby user-laoqian age 1
應(yīng)用:存放數(shù)據(jù)對(duì)象
set類(lèi)型:(集合)
Redis 的集合相當(dāng)于 Java 語(yǔ)言里面的 HashSet疟位,它內(nèi)部的鍵值對(duì)是無(wú)序的唯一的瞻润。它的內(nèi)部實(shí)現(xiàn)相當(dāng)于一個(gè)特殊的字典,字典中所有的 value 都是一個(gè)值NULL甜刻。當(dāng)集合中最后一個(gè)元素移除之后绍撞,數(shù)據(jù)結(jié)構(gòu)自動(dòng)刪除,內(nèi)存被回收得院。
操作命令:
sadd key value? 成功返回1 失敗返回0
sadd key value1 value2? 成功返回2 失敗返回0
smembers key? # 注意順序傻铣,和插入的并不一致,因?yàn)?set 是無(wú)序的 查出所有
sismember key value? ? # 查詢(xún)某個(gè) value 是否存在祥绞,相當(dāng)于 contains(o) 存在返回1 不存在返回0
scard books? # 獲取長(zhǎng)度相當(dāng)于 count()
spop books? # 彈出一個(gè) 隨機(jī)
應(yīng)用 set 結(jié)構(gòu)可以用來(lái)存儲(chǔ)活動(dòng)中獎(jiǎng)的用戶(hù) ID非洲,因?yàn)橛腥ブ毓δ埽梢员WC同一個(gè)用戶(hù)不會(huì)中獎(jiǎng)兩次蜕径。
zset類(lèi)型: (有序列表)
zset 內(nèi)部的排序功能是通過(guò)「跳躍列表」數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn)的两踏,它的結(jié)構(gòu)非常特殊,也比較復(fù)雜兜喻。
操作命令:
zadd key? score? value 按score來(lái)排序
zrange key 0 -1 # 按 score 排序列出缆瓣,參數(shù)區(qū)間為排名范圍 全部取出
zrevrange key 0 -1 # 按 score 逆序列出,參數(shù)區(qū)間為排名范圍
zcard key # 獲取長(zhǎng)度相當(dāng)于 count()
zscore key value? # 獲取指定 value 的 score? # 內(nèi)部 score 使用 double 類(lèi)型進(jìn)行存儲(chǔ)虹统,所以存在小數(shù)點(diǎn)精度問(wèn)題
zrank key value? # 排名
zrangebyscore key 0 8.91? # 根據(jù)分值區(qū)間遍歷 zset
zrangebyscore books -inf 8.91 withscores # 根據(jù)分值區(qū)間 (-∞, 8.91] 遍歷 zset弓坞,同時(shí)返回分值。inf 代表 infinite,無(wú)窮大的意思。
zrem key value # 刪除 value
應(yīng)用:zset 可以用來(lái)存粉絲列表蔫骂,value 值是粉絲的用戶(hù) ID,score 是關(guān)注時(shí)間族吻。我們可以對(duì)粉絲列表按關(guān)注時(shí)間進(jìn)行排序。
zset 還可以用來(lái)存儲(chǔ)學(xué)生的成績(jī)珠增,value 值是學(xué)生的 ID超歌,score 是他的考試成績(jī)。我們可以對(duì)成績(jī)按分?jǐn)?shù)進(jìn)行排序就可以得到他的名次蒂教。
容器型數(shù)據(jù)結(jié)構(gòu)的通用規(guī)則:
list/set/hash/zset 這四種數(shù)據(jù)結(jié)構(gòu)是容器型數(shù)據(jù)結(jié)構(gòu)巍举,它們共享下面兩條通用規(guī)則:
create if not exists
如果容器不存在,那就創(chuàng)建一個(gè)凝垛,再進(jìn)行操作懊悯。比如 rpush 操作剛開(kāi)始是沒(méi)有列表的蜓谋,Redis 就會(huì)自動(dòng)創(chuàng)建一個(gè),然后再 rpush 進(jìn)去新元素炭分。
drop if no elements
如果容器里元素沒(méi)有了桃焕,那么立即刪除元素,釋放內(nèi)存捧毛。這意味著 lpop 操作到最后一個(gè)元素观堂,列表就消失了。
過(guò)期時(shí)間:
Redis 所有的數(shù)據(jù)結(jié)構(gòu)都可以設(shè)置過(guò)期時(shí)間呀忧,時(shí)間到了师痕,Redis 會(huì)自動(dòng)刪除相應(yīng)的對(duì)象。需要注意的是過(guò)期是以對(duì)象為單位荐虐,比如一個(gè) hash 結(jié)構(gòu)的過(guò)期是整個(gè) hash 對(duì)象的過(guò)期,而不是其中的某個(gè)子 key丸凭。
還有一個(gè)需要特別注意的地方是如果一個(gè)字符串已經(jīng)設(shè)置了過(guò)期時(shí)間福扬,然后你調(diào)用了 set 方法修改了它,它的過(guò)期時(shí)間會(huì)消失惜犀。