咱們知道redis中的過時時間只能做用於key上。對於string數據結構來講,由於它是key/value的形式,只有一個value與key對應,因此當過時時間到了,整個key/value被移除,符合心理預期,皆大歡喜。但好多時候咱們用到的是其餘數據結構,好比:一個擁有多個元素的集合。因爲過時時間只能做用於key(集合數據結構能夠理解爲集合ID)上,當過時時間到了,整個集合被移除。通常使用集合的場景都不但願各個元素在同一時間過時,有時也但願進行與時間相關的查詢,這該怎麼辦呢?redis
redis有一種數據結構是Sorted Set,有序集合,它的實現是hash table(element->score, 用於實現zscore及判斷element是否在集合內)和skiplist(score->element,按score排序)的混合體。 skiplist有點像平衡二叉樹那樣,不一樣範圍的score被分紅一層一層,每層是一個按score排序的鏈表。其中zadd/zrem是O(log(N)),zrangebyscore/zremrangebyscore是O(log(N)+M),N是Set大小,M是結果/操做元素的個數。可見,本來可能很大的N被很關鍵的Log了一下,1000萬大小的Set,複雜度也只是幾十不到。固然,若是一次命中不少元素M很大那誰也沒辦法了。數據結構
這裏咱們用到了它以下特性:運維
1. 元素惟一性能
2. 每一個元素擁有一個score優化
3. 全部元素依據score進行有序排列編碼
4. 可經過score來進行查詢unix
咱們能夠藉助這些特性來讓集合中的元素擁有時間維度。每當add一個元素時,把當前時間的unix timestamp做爲score設置到這個元素上,這樣sorted set會根據這個timestamp將元素排序存儲。排序
場景一:當咱們查詢最近1分鐘內有更新的元素時,能夠使用命令 zrangebyscore key min max來獲取。例如:three
zadd set1 1522598879 "one"ip
zadd set1 1522598969 "two"
zadd set1 1522598979 "three"
執行查詢:zrangebyscore set1 1522598920 1522598980
返回值:"two"和"three"
場景二:當咱們查詢最新更新的2個元素,能夠使用 zrevrange key start stop來獲取。例如:
zadd set1 1522598879 "one"
zadd set1 1522598969 "two"
zadd set1 1522598979 "three"
執行查詢:zrevrange set1 0 1
返回值:"three"和"two"
場景三:當咱們須要刪除最近1分鐘沒有過更新的元素,能夠使用 zremrangebyscore key min max 來刪除過時元素。例如:
zadd set1 1522598879 "one"
zadd set1 1522598969 "two"
zadd set1 1522598979 "three"
執行命令: zremrangebyscore set1 0 1522598920
執行結果:刪除了元素"one"
通常來說,咱們會啓動一個後臺任務來不斷進行過時元素的刪除操做,任務的重複執行間隔能夠視業務對過時數據的容忍度。若是容忍度較高,能夠設置時間久一點,相反能夠設置時間短一些。
當咱們應用到生產環境時,還應該考慮內存佔用和訪問效率。有序集合的長度較短或者體積較小的時候,Redis能夠選擇使用ziplist的緊湊存儲方式來存儲這些結構,從而達到優化存儲空間的目的。然而,ziplist會以序列化的方式存儲數據,這些序列化數據每次被讀取的時候都要就行解碼,每次被寫入的時候都要進行局部的從新編碼,而且可能須要對內存裏的數據進行移動。所以讀寫一個長度較大的ziplist可能會形成性能問題。從咱們的生產運維經驗上來說,如下參數設置能夠借鑑(根據業務和實際狀況進行不斷調整):
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
即:當有序集合的元素都小於64字節而且元素數量小於128個的時候,使用ziplist,反之使用skiplist。