文章目錄
本篇是對"Redis深度歷險"的學習與思考
Remote dictionary service
install
書中提供了docker,源代碼,直接安裝三種方式,其他兩種我都玩過,現在說下源代碼安裝.
書中給出的貌似2.8版本,但是現在已經是6.0版本了,所以我執行的命令和 書中略有不同.
關於6.0的新特性還沒去仔細瞭解,貌似加入了多線程和ACL
# 這個指令和書中不一樣
git clone https://github.com/antirez/redis.git
cd redis
# 速度巨慢,我吃了飯回來纔好,看來源碼安裝被拋棄是有道理的
make
# 書中並沒有make test這一步,我是在執行make後按照提示執行了下一步,但我執行後報了兩次Error,心塞
make test
cd src
./redis-server --daemonize yes
基礎數據結構
書中講到有5種,這應該是老版本的了
string
Redis的字符串是動態字符串…內部結構的實現類似於Java的ArrayList,採用預分配冗餘空間的方式來減少內存的頻繁分配.
如果這樣的話不知道Redis能否預先制定value的capacity從而進一步減少重分配
字符串最大長度爲512MB
這些指令對於其他數據結構有些也是適用的
set
引號
[qbit@manjaro src]$ ./redis-cli
127.0.0.1:6379> set Qbit qqq
OK
127.0.0.1:6379> get Qbit
"qqq"
127.0.0.1:6379> set Qbit "qqq"
OK
127.0.0.1:6379> get Qbit
"qqq"
127.0.0.1:6379> set Qbit "qqq
Invalid argument(s)
127.0.0.1:6379> set Qbit "thiis"whatisaid"1"
Invalid argument(s)
127.0.0.1:6379> set "Qbit" m
OK
127.0.0.1:6379> get Qbit
"m"
set Qb"it m
Invalid argument(s)
通過上面例子可以看出,如果key和value被雙引號引起來,和沒有雙引號是一個效果.如果雙引號不匹配或者出現其他位置就是非法的了
exists
官網說明
判斷是否存在,後面可以接上n個key,然後返回存在的key的個數,對於最普通的情況(也就是3.0之前的版本),下面是官方例子幫助理解
redis> SET key1 "Hello"
"OK"
redis> EXISTS key1
(integer) 1
redis> EXISTS nosuchkey
(integer) 0
redis> SET key2 "World"
"OK"
redis> EXISTS key1 key2 nosuchkey
(integer) 2
list
Redis的列表相當於Java語言裏面的LinkedList
quicklist
redis內部做了一些優化,當存儲數據較少的時候傾向於使用ziplist,另外這兩個會結合時候,後面再具體說
rpush rpop lpop lpush
xpush時可以接多個參數,依次push,一般優先用r
lindex lrange ltrim llen
看名字大概知道什麼意思,具體適用到時候再參見官網
blpop brpop
書中也介紹了list也可以作爲一個簡單的MQ,
需要注意的一點是消費者在pop無法獲得詩句的時候需要sleep一下,這樣同時減輕自己和Redis的CPU消耗
更好的方案是使用blpop brpop,其中b是blocking的意思,但是這裏還有一個坑,即使Redis的服務端對於長時間空閒的鏈接可能斷開
hash
這裏面有很多x開頭的操作對應了redis本身的一些操作,例如hincrby對應incr
set
zset
這是一個帶排序的set,排序規則就是分數,對應的,會有各種z開頭的操作,這裏講下常用的.
zrange zrevrange
分別升序和降序輸出,一般後面接0 -1表示從第0個到倒數第一個都輸出
zcard
相當於count
限流
滑動窗口
zset的一個用處是用來限流.限流是針對特定操作的限流,比如下面代碼就是針對特定user_id的特定action
import time
import redis
client=redis.StrictRedis()
def is_allowed(user_id,action,period,max_count):
#這個key的意思是對這個user_id的這個action進行限流
key='hist:%s:%s'%(user_id,action)
now=int(time.time()*1000)
with client.pipeline() as pipe:
#記錄本次操作
pipe.zadd(key,now,now)
#移除窗口外的操作
pipe.zremrangebyscore(key,0,now-period*1000)
#獲取窗口內數量
pipe.zcard(key)
#設置過期時間用於剔除冷數據
pipe.expire(key,period+1)
#執行
_,_,count,_=pipe.execute()
return count<max_count
可以看出來,如果進行不停的操作,即使由於限流導致操作不成功,也會對後期操作有負面影響.
HyperLogLog
這種數據結構涉及到三個pf開頭的指令(pf表示其發明人Philippe Flajolet):pfadd,pfcount,pfmerge,其中pfcount可以跟隨多個key,相當於先把他們merge在得到數
Bloom Filter
當布隆過濾器所某個值存在時,這個值可能不存在;當它說某個值不存在時,那就肯定不存在.
讓我詫異的是這些bf開頭的指令居然不是官方原生支持的,我使用源碼安裝後不支持,以後再繼續研究吧.不過書中提到使用bf.reserve來控制error_rate,可以記下來.
簡單說下原理,附上書中的圖
就是會對每個key做hash(圖中使用了f,g,h三個hash函數),然後把對應位圖中位置設置爲1,查找的時候看看對應的位圖是否爲1就可以了.所以位圖夠大才能避免碰撞,另外hash函數越多則越精確.
這裏有個網站幫助計算
GeoHash
毫無疑問,這又涉及到一些geo開頭的數據,需要提到的是刪除使用了zset的zrem指令來刪除
另外一個問題是對於geo的key而言,value往往很大,在集羣模式下遷移會有卡頓,所以建議單獨非集羣部署
keys vs scan
scan提供了查找功能,但是不像keys那樣會阻塞,並且支持分頁.不過這裏有個坑爹的特性就是返回的條數並不精準,特別是返回集爲空是並不代表沒有後續數據.因爲其limit其實是指定的slot的數量而不是記錄條數,另外它採用的是高位加法(相應的,普通加法可以理解爲低位加法),如圖所示
rehash
由於Redis採用了漸進式rehash,所以scan時要去新舊兩個槽裏去找
大key的查找
redis-cli -h 127.0.0.1 -p 6379 --bigkeys -i 0.1
上面的i參數是指每scan100就休眠0.1s,避免避免ops飆升觸發運維監控的報警,當然這樣會慢一些.所以也可以去掉.