最近在寫一個腳手架,發現其中 redis
的使用場景還挺多,因而總結下它的常見使用場景javascript
<!--more-->java
> set User:1:name shanyue EX 100 NX OK > get User:1:name "shanyue"
緩存是 redis
出鏡率最高的一種使用場景,僅僅使用 set/get
就能夠實現,不過也有一些須要考慮的點node
> set 5d27e60e6fb9a07f03576687 '{"id": 10086, role: "ADMIN"}' EX 7200 OK > get 5d27e60e6fb9a07f03576687 "{\"id\": 10086, role: \"ADMIN\"}"
這也是很經常使用的一種場景,不過相對於有狀態的 session,也能夠考慮使用 JWT,各有利弊python
> lpush UserEmailQueue 1 2 3 4 lpop UserEmailQueue > rpop UserEmailQueue 1 > rpop UserEmailQueue 2
能夠把 redis
的隊列視爲分佈式隊列,做爲消息隊列時,生產者在一頭塞數據,消費者在另外一頭出數據: (lpush/rpop, rpush/lpop)。不過也有一些不足,而這些不足有多是致命的,不過對於一些丟幾條消息也不要緊的場景仍是能夠考慮的git
redis
的持久化配置> sadd UrlSet http://1 (integer) 1 > sadd UrlSet http://2 (integer) 1 > sadd UrlSet http://2 (integer) 0 > smembers UrlSet 1) "http://1" 2) "http://2"
scrapy-redis 做爲分佈式的爬蟲框架,即是使用了 redis
的 Set
這個數據結構來對將要爬取的 url 進行去重處理。github
# https://github.com/rmax/scrapy-redis/blob/master/src/scrapy_redis/dupefilter.py def request_seen(self, request): """Returns True if request was already seen. Parameters ---------- request : scrapy.http.Request Returns ------- bool """ fp = self.request_fingerprint(request) added = self.server.sadd(self.key, fp) return added == 0
不過當 url
過多時,會有內存佔用過大的問題web
set Lock:User:10086 06be97fc-f258-4202-b60b-8d5412dd5605 EX 60 NX # 釋放鎖,一段 LUA 腳本 if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end
這是一個最簡單的單機版的分佈式鎖,有如下要點redis
EX
表示鎖會過時釋放NX
保證原子性當你使用分佈式鎖是爲了解決一些性能問題,如分佈式定時任務防止執行屢次 (作好冪等性),並且鑑於單點 redis
掛掉的可能性很小,可使用這種單機版的分佈式鎖。shell
限流即在單位時間內只容許經過特定數量的請求,有兩個關鍵參數編程
最多見的場景: 短信驗證碼一分鐘只能發送兩次
FUNCTION LIMIT_API_CALL(ip): current = GET(ip) IF current != NULL AND current > 10 THEN ERROR "too many requests per second" ELSE value = INCR(ip) IF value == 1 THEN EXPIRE(ip,1) END PERFORM_API_CALL() END
可使用計數器對 API 的請求進行限流處理,可是要注意幾個問題
這時候能夠經過編程,根據 TTL key
進行進一步限制,或者使用一個 LIST
來維護每次請求打來的時間戳進行實時過濾。如下是 node
實現的一個 Rate Limter
。參考源碼 node-rate-limiter-flexible
this.client .multi() .set(rlKey, 0, 'EX', secDuration, 'NX') .incrby(rlKey, points) .pttl(rlKey) .exec((err, res) => { if (err) { return reject(err); } return resolve(res); }) if (res.consumedPoints > this.points) { // ... } else if (this.execEvenly && res.msBeforeNext > 0 && !res.isFirstInDuration) { // ... setTimeout(resolve, delay, res); } else { resolve(res); }
能夠經過 redis 的 PUB/SUB
來在 websocket server 間進行交流。能夠參考如下項目