Redis初識

背景

最近遇到好多redis的面試題,今天就來好好總結一下redis的使用。關於redis API的使用這裏就多說了,有興趣的能夠看下官網:Redis官網html

鍵的過時策略

在redis中當鍵到了過時的時間,就會立馬被刪除掉嗎?面試

咱們瞭解一下刪除策略的知識,刪除策略可分爲三種redis

  • 定時刪除(對內存友好,對CPU不友好)
  • 惰性刪除(對CPU極度友好,對內存極度不友好)
  • 按期刪除(折中)

惰性刪除是指每次從鍵空間取鍵的時候,判斷一下該鍵是否過時了,若是過時了就刪除。數據庫

Redis採用的是惰性刪除+按期刪除兩種策略,因此說,在Redis裏邊若是鍵到了過時的時間了,未必被立馬刪除的!segmentfault

內存淘汰機制

若是按期刪除漏掉了不少過時key,也沒及時去查(沒走惰性刪除),大量過時key堆積在內存裏,致使redis內存塊耗盡了,咋整?數組

咱們能夠設置內存最大使用量,當內存使用量超出時,會施行數據淘汰策略緩存

Redis的內存淘汰機制有如下幾種:網絡

volatile-lru:從已設置過時時間的數據集中挑選最近最少使用的數據淘汰
volatile-ttl:從已設置過時時間的數據集中挑選將要過時的數據淘汰。
volatile-random:從已設置過時時間的數據集中任意選擇數據淘汰
allkeys-lru:從數據集中挑選最近最少使用的數據淘汰
allkeys-random:從數據集中任意選擇數據淘汰
no-enviction:當內存達到限制的時候,不淘汰任何數據

慢查詢

簡介

和不少關係型數據庫同樣, Redis 也提供了慢查詢日誌記錄,Redis會把執行比較慢的命令放到內部的一個list列表中。數據結構

注意:慢查詢記錄的只是命令的執行時間,不包括網絡傳輸和排隊時間。

慢查詢配置

關於 Redis 慢查詢的配置有兩個,分別是 slowlog-log-slower-thanslowlog-max-len
slowlog-log-slower-than 用來控制慢查詢的閾值,全部執行時間超過該值的命令都會被記錄下來。該值的單位爲微秒,默認值爲 10000,若是設置爲 0,那麼全部的記錄都會被記錄下來,若是設置爲小於 0 的值,那麼對於任何命令都不會記錄,即關閉了慢查詢。能夠經過在配置文件中設置,或者用 config set 命令來設置:dom

config set slowlog-log-slower-than 10000

slowlog-max-len 用來設置存儲慢查詢記錄列表的大小,默認值爲 128,當該列表滿了時,若是有新的記錄進來,那麼 Redis 會把隊最舊的記錄清理掉,而後存儲新的記錄。在生產環境咱們能夠適當調大,好比調成 1000,這樣就能夠緩衝更多的記錄,方便故障的排查。配置方法和 slowlog-log-slower-than 相似,能夠在配置文件中指定,也能夠在命令行執行 config set 來設置:

config set slowlog-max-len 1000

查看慢查詢日誌

查詢日誌

Redis 專門提供了一組命令來查詢慢查詢日誌:

SLOWLOG GET

慢查詢redis.PNG

能夠看到這裏查到了兩條慢查詢記錄,分別是ZINCRRBY和ZREVRANGE命令
那麼記錄的中的1)2)3)4)分別表示什麼呢?
1)表示日誌惟一標識符uid
2)命令執行時系統的時間戳
3)命令執行的時長,以微妙來計算
4)命令和命令的參數

獲取當前慢查詢日誌記錄數

SLOWLOG LEN

慢查詢日誌重置

SLOWLOG RESET

Pipeline

pipeline命令用來批量操做redis命令

因爲redis是單線程的,下一次請求必須等待上一次請求執行完成後才能繼續執行。執行N次命令須要N次網絡時間+執行時間,然而使用Pipeline模式,客戶端能夠一次性的發送多個命令,也就是1次網絡時間+N次執行時間,這樣就大大的減小了網絡往返時間,提升了系統性能。

下面看下僞代碼:

Jedis redis = new Jedis("127.0.0.1", 6379);
Pipeline pipe = redis.pipelined();
       for (int i = 0; i < 10000; i++) {
            pipe.hmset("key_" + i, data); //將值封裝到PIPE對象,此時並未執行,還停留在客戶端
        }
        pipe.sync(); //將封裝後的PIPE一次性發給redis
        jedis.close;

 那麼問題來了,在什麼樣的情景下適合使用pipeline呢?

有些系統可能對可靠性要求很高,每次操做都須要立馬知道此次操做是否成功,是否數據已經寫進redis了,那這種場景就不適合。
還有的系統,多是批量的將數據寫入redis,容許必定比例的寫入失敗,那麼這種場景就可使用了,好比10000條一下進入redis,可能失敗了2條無所謂,後期有補償機制就好了,好比短信羣發這種場景,若是一下羣發10000條,按照第一種模式去實現,那這個請求過來,要好久才能給客戶端響應,這個延遲就太長了,若是客戶端請求設置了超時時間5秒,那確定就拋出異常了,並且自己羣發短信要求實時性也沒那麼高,這時候用pipeline最好了。

BitMap

Bitmap是一串連續的2進制數字(0或1),每一位所在的位置爲偏移(offset),在bitmap上可執行AND,OR,XOR以及其它位操做。

API

SETBIT

SETBIT KEY OFFSET VALUE

該命令用於對 key 所儲存的字符串值,設置或清除指定偏移量上的位(bit)。時間複雜度O(1)

在redis中,存儲的字符串都是以二進制的形式存在的。
如何經過SETBIT命令將'a'變成'b'呢?即將 01100001 變成 01100010(b的ASCII碼是98),其實就是將'a'中的offset 6從0變成1,將offset 7從1變成0。
每次SETBIT完畢以後,會返回該位置原先的bit值。

BITCOUNT

BITCOUNT KEY [START END]

該命令統計字符串中指定範圍裏bit被設置爲1的數量

GETBIT

GETBIT KEY OFFSET

返回key對應的string在offset處的bit值

BITOP

BITOP operation destkey key [key...]

能夠對多個二進制進行交集、並集等操做,並將結果保存到 destkey 上

實戰

一個簡單的例子:日活躍用戶

爲了統計今日登陸的用戶數,咱們創建了一個bitmap,每一位標識一個用戶ID。當某個用戶訪問咱們的網頁或執行了某個操做,就在bitmap中把標識此用戶的位置爲1。這樣的話咱們就能夠經過BITCOUNT命令獲得當日活躍用戶數。

注意:使用BitMap統計流量適用於億級流量系統,若是系統用戶只有10w的話使用set數據結構會更好。

GEO

GEO功能在Redis3.2版本提供,支持存儲地理位置信息用來實現諸如附近位置、搖一搖這類依賴於地理位置信息的功能。GEO的數據類型爲zset

geoadd

  • 添加經緯度信息
geoadd cityGeo 116.405285 39.904989 "北京"
geoadd cityGeo 121.472644 31.231706 "上海"

geopos

  • 查找指定key的經緯度信息,能夠指定多個key,批量返回
127.0.0.1:6379> geopos cityGeo 北京
1) "116.40528291463851929"
2) "39.9049884229125027"

geodist

  • 返回兩個地方的距離,能夠指定單位,好比米m,公里km,英里mi,英尺ft
127.0.0.1:6379> geodist cityGeo 北京 上海
"1067597.9668"
127.0.0.1:6379> geodist cityGeo 北京 上海 km
"1067.5980"

georadius

  • 以給定的經緯度爲中心,返回鍵包含的位置元素當中,與中心的距離不超過給定最大距離的全部位置元素
georadius cityGeo 116.405285 39.904989 100 km WITHDIST WITHCOORD ASC COUNT 5

能夠指定WITHDIST返回距離,WITHCOORD返回經緯度,WITHHASH返回geohash值
能夠指定ASC或DESC,根據距離來排序
能夠指定COUNT限定返回的記錄數

georadiusbymember

  • 和georadius同樣,不過中心點是由給定的位置元素決定的,而不是像georadius那樣,使用輸入的經度和緯度來決定中心點。
georadiusbymember cityGeo 北京 100 km WITHDIST WITHCOORD ASC COUNT 5

zrem

  • GEO沒有提供刪除成員的命令,可是由於GEO的底層實現是zset,因此能夠借用zrem命令實現對地理位置信息的刪除.
zrem cityGeo 北京

布隆過濾器

本質上布隆過濾器是一種數據結構,比較巧妙的機率型數據結構,特色是高效地插入和查詢,能夠用來告訴你 「某樣東西必定不存在或者可能存在」。

相比於傳統的 List、Set、Map 等數據結構,它更高效、佔用空間更少,可是缺點是其返回的結果是機率性的,而不是確切的。

原理

其本質就是一個只包含0和1的bit數組。具體操做當一個元素被加入到集合裏面後,該元素經過K個Hash函數運算獲得K個hash後的值,而後將K個值映射到這個位數組對應的位置,把對應位置的值設置爲1。查詢是否存在時,咱們就看對應的映射點位置若是全是1,他就極可能存在(跟hash函數的個數和hash函數的設計有關),若是有一個位置是0,那這個元素就必定不存在。

若是咱們要映射一個值到布隆過濾器中,咱們須要使用多個不一樣的哈希函數生成多個哈希值,並對每一個生成的哈希值指向的 bit 位置 1,例如針對值 「baidu」 和三個不一樣的哈希函數分別生成了哈希值 一、四、7,則上圖轉變爲
image

Ok,咱們如今再存一個值 「tencent」,若是哈希函數返回 三、四、8 的話

image

值得注意的是,4 這個 bit 位因爲兩個值的哈希函數都返回了這個 bit 位,所以它被覆蓋了。如今咱們若是想查詢 「dianping」 這個值是否存在,哈希函數返回了 一、五、8三個值,結果咱們發現 5 這個 bit 位上的值爲 0,說明沒有任何一個值映射到這個 bit 位上,所以咱們能夠很肯定地說 「dianping」 這個值不存在。而當咱們須要查詢 「baidu」 這個值是否存在的話,那麼哈希函數必然會返回 一、四、7,而後咱們檢查發現這三個 bit 位上的值均爲 1,那麼咱們能夠說 「baidu」 存在了麼?答案是不能夠,只能是 「baidu」 這個值可能存在。

這是爲何呢?答案跟簡單,由於隨着增長的值愈來愈多,被置爲 1 的 bit 位也會愈來愈多,這樣某個值 「taobao」 即便沒有被存儲過,可是萬一哈希函數返回的三個 bit 位都被其餘值置位了 1 ,那麼程序仍是會判斷 「taobao」 這個值存在。

應用場景

常見的適用應用場景有,利用布隆過濾器減小磁盤 IO 或者網絡請求,由於一旦一個值一定不存在的話,咱們能夠不用進行後續昂貴的查詢請求,好比能夠用來解決緩存穿透的問題。

結束語

今天先對redis有個初步的瞭解,下一篇咱們來深刻探討Redis:Redis進階

參考

Redis單排
詳解布隆過濾器

相關文章
相關標籤/搜索