Redis功能細解

慢查詢緣由分析:

    與mysql同樣:當執行時間超過閥值,會將發生時間 耗時 命令記錄java

redis命令生命週期:發送 排隊 執行 返回mysql

    慢查詢只統計第3個執行步驟的時間redis

預設閥值:兩種方式,默認爲10毫秒sql

1.動態設置6379:> config set slowlog-log-slower-than 10000  //10毫秒10000微秒數組

      使用config set完後,若想將配置持久化保存到redis.conf,要執行config rewrite 服務器

2.redis.conf修改:找到slowlog-log-slower-than 10000 ,修改保存便可微信

注意:slowlog-log-slower-than =0記錄全部命令 -1命令都不記錄網絡

原理:慢查詢記錄也是存在隊列裏的,slow-max-len 存放的記錄最大條數,好比設置的slow-max-len=10,當有第11條慢查詢命令插入時,隊列的第一條命令就會出列,第11條入列到慢查詢隊列中, 能夠config set動態設置,也能夠修改redis.conf完成配置。數據結構

命令:併發

獲取隊列裏慢查詢的命令:slowlog get     查詢返回的結構以下

獲取慢查詢列表當前的長度:slowlog len  //以上只有1條慢查詢,返回1

對慢查詢列表清理(重置):slowlog reset //再查slowlog len 此時返回0 清空

對於線上slow-max-len配置的建議

    線上可加大slow-max-len的值,記錄慢查詢存長命令時redis會作截斷,不會佔用大量內存,線上可設置1000以上

對於線上slowlog-log-slower-than配置的建議

    默認爲10毫秒,根據redis併發量來調整,對於高併發比建議爲1毫秒

注意:

    1 慢查詢只記錄命令在redis的執行時間,不包括排隊、網絡傳輸時間

    2 慢查詢是先進先出的隊列,訪問日誌記錄出列丟失,需按期執行slow get,將結果存儲到其它設備中(如mysql)

redis-cli詳解

 ./redis-cli -r 3 -h 192.168.1.111 -a 12345678 ping //返回pong表示127.0.0.1:6379能通,r表明次數

./redis-cli -r 100 -i 1 info |grep used_memory_human //每秒輸出內存使用量,輸100次,i表明執行的時間間隔

./redis-cli -p 6379  -h 192.168.1.111 -a 12345678

對於咱們來講,這些經常使用指令以上可知足,但若是要了解更多執行redis-cli --help, 可百度

 

redis-server詳解

./redis-server ./redis.conf &  //指定配置文件啓動

./redis-server --test-memory 1024 //檢測操做系統可否提供1G內存給redis, 經常使用於測試,想快速佔滿機器內存作極端條件的測試,可以使用這個指令

 redis上線前,作一次測試

 

redis-benchmark:基準性測試,測試redis的性能

 redis-benchmark -h 192.168.1.111 -c 100 -n 10000  //100個客戶端同時請求redis,共執行10000次

會對各種數據結構的命令進行測試

測試命令事例:

一、redis-benchmark -h 192.168.1.111 -p 6379 -c 100 -n 100000

100個併發鏈接,100000個請求,檢測host爲localhost 端口爲6379的redis服務器性能

二、redis-benchmark -h 192.168.1.111 -p 6379 -q -d 100  

測試存取大小爲100字節的數據包的性能

三、redis-benchmark -h 192.168.1.111 -t set,lpush -n 100000 -q

只測試 set,lpush操做的性能,-q只顯示每秒鐘能處理多少請求數結果

四、redis-benchmark -h 192.168.1.111 -n 100000 -q script load "redis.call('set','foo','bar')"

只測試某些數值存取的性能, 好比說我在慢查詢中發現,大部分爲set語句比較慢,咱們本身能夠測一下Set是否是真的慢,

 

Pipeline:

pipeline 出現的背景:

    redis客戶端執行一條命令分4個過程:

    發送命令-〉命令排隊-〉命令執行-〉返回結果

    這個過程稱爲Round trip time(簡稱RTT, 往返時間),mget mset有效節約了RTT,但大部分命令(如hgetall,並無mhgetall)不支持批量操做,須要消耗N次RTT ,這個時候須要pipeline來解決這個問題

1,未使用pipeline執行N條命令

2,使用pipeline執行N條命令,後面會講到JAVA的jedis如何使用pipeline功能

3,使用pipeline和未使用pipeline的性能對比:

    小總結:使用pipeline執行速度比逐條執行要快,客戶端與服務端的網絡延遲越大,性能體現越明顯

4,原生的批命令(mset, mget等)與pipeline的對比:

    1.原生批命令是原子性,pipeline是非原子性,  (原子性概念:一個事務是一個不可分割的最小工做單位,要麼都成功要麼都失敗。原子操做是指你的一個業務邏輯必須是不可拆分的. 處理一件事情要麼都成功要麼都失敗,其實也引用了生物裏概念,分子-〉原子,原子不可拆分)

    2.原生批命令一命令多個key, 但pipeline支持多命令(存在事務),非原子性

    3.原生批命令是服務端實現,而pipeline須要服務端與客戶端共同完成

5,pipeline正確使用方式:

        使用pipeline組裝的命令個數不能太多,否則數據量過大,增長客戶端的等待時間,還可能形成網絡阻塞,能夠將大量命令的拆分多個小的pipeline命令完成

 

事務

        剛你們知道,pipeline是多條命令的組合,爲了保證它的原子性,redis提供了簡單的事務,什麼是事務?事務是指一組動做的執行,這一組動做要麼成功,要麼失敗。

1.redis的簡單事務,將一組須要一塊兒執行的命令放到multi和exec兩個命令之間,其中multi表明事務開始,exec表明事務結束

2.中止事務discard

3.命令錯誤,語法不正確,致使事務不能正常結束

4.運行錯誤,語法正確,但類型錯誤,事務能夠正常結束

能夠看到redis不支持回滾功能

watch命令

redis提供了簡單事務,之因此說簡單,不支持事務回滾

 

LUA語言與redis

LUA腳本語言是C開發的,相似存儲過程

使用腳本的好處以下:

•1.減小網絡開銷:原本5次網絡請求的操做,能夠用一個請求完成,原先5次請求的邏輯放在redis服務器上完成。使用腳本,減小了網絡往返時延。

•2.原子操做:Redis會將整個腳本做爲一個總體執行,中間不會被其餘命令插入。

•3.複用:客戶端發送的腳本會永久存儲在Redis中,意味着其餘客戶端能夠複用這一腳本而不須要使用代碼完成一樣的邏輯。

    6379>eval "return redis.call('get',KEYS[1])" 1 name //eval+腳本+KEYS[1]+鍵個數+鍵

                eval script numkeys key [key ...]

語法1: 

local int sum = 0
local int i =0
while i <= 100
do sum = sum+i
   i = i+1
end
print(sum)

語法2:

local tables myArray={「james」,」java」,false,34} //定義
local int sum = 0
print(myArray[3])  //返回false
for i = 1,100
do
   sum = sum+1
end
print(sum)
for j = 1,#myArray   //遍歷數組
do 
   print(myArray[j])
   if myArray[j] == 「james」 
then
  print(「true」)
  break
else
  print(「false」)
end
end

 

案例-實現訪問頻率限制: 實現訪問者 $ip 127.0.0.1在必定的時間 $time 20S內只能訪問 $limit 10次.使用JAVA語言實現:

private boolean accessLimit(String ip, int limit,
 int time, Jedis jedis) {
    boolean result = true;

    String key = "rate.limit:" + ip;
    if (jedis.exists(key)) {
        long afterValue = jedis.incr(key);
        if (afterValue > limit) {
            result = false;
        }
    } else {
        Transaction transaction = jedis.multi();
        transaction.incr(key);
        transaction.expire(key, time);
        transaction.exec();
    }
    return result;
}

以上代碼有兩點缺陷

可能會出現競態條件: 解決方法是用 WATCH 監控 rate.limit:$IP 的變更, 但較爲麻煩;

以上代碼在不使用 pipeline 的狀況下最多須要向Redis請求5條指令, 傳輸過多.

使用lua腳原本處理,包括了原子性:以下

    ../redis-cli -p 6379 -a 12345678 --eval ipCount.lua 192.168.1.111, 10 20

    ttl rate.limit:127.0.0.1

其中 keys[1] = rate.limit:127.0.0.1   argv[1]=10次,  argv[2]=20S失效

ipCount.lua內容:

local key =  KEYS[1]
local limit = tonumber(ARGV[1])
local expire_time = ARGV[2]

local is_exists = redis.call("EXISTS", key)
if is_exists == 1 then
    if redis.call("INCR", key) > limit then
        return 0
    else
        return 1
    end
else
    redis.call("SET", key, 1)
    redis.call("EXPIRE", key, expire_time)
    return 1
end

執行邏輯:使用redis-cli --eavl時,客戶端把lua腳本字符串發給redis服務端,將結果返回客戶端,以下圖

 

 

 

 

redis對lua腳本的管理:

1,redis-cli -h 192.168.1.111 -a 12345678 script load "$(cat random.lua)"   

將LUA腳本內容加載到redis, 獲得 返回的sha1值:afe90689cdeec602e374ebad421e3911022f47c0

redis-cli -h localhost -p 6379 EVALSHA afe90689cdeec602e374ebad421e3911022f47c0 0

再執行獲得的值調用

2, 6379〉script exists afe90689cdeec602e374ebad421e3911022f47c0  

檢查sha1值的LUA腳本是否加載到redis中, 返回1 已加載成功

3, 6379〉script flush

清空加載的lua腳本內容

4,6379〉script kill  

殺掉正在執行的LUA腳本,好比LUA比較耗時阻塞,殺掉

 

發佈與訂閱

       redis提供了「發佈、訂閱」模式的消息機制,其中消息訂閱者與發佈者不直接通訊,發佈者向指定的頻道(channel)發佈消息,訂閱該頻道的每一個客戶端均可以接收到消息

redis主要提供發佈消息、訂閱頻道、取消訂閱以及按照模式訂閱和取消訂閱

發佈消息   publish channel:test "hello world"

訂閱消息   subscrible channel:test

此時另外一個客戶端發佈一個消息:publish channel:test "james test"當前訂閱者客戶端會收到以下消息:

和不少專業的消息隊列(kafka rabbitmq),redis的發佈訂閱顯得很lower, 好比沒法實現消息規程和回溯, 但就是簡單,若是能知足應用場景,用這個也能夠

查看訂閱數   pubsub numsub channel:test    //頻道channel:test的訂閱數

取消訂閱    unsubscribe channel:test    //客戶端能夠經過unsubscribe命令取消對指定頻道的訂閱,取消後,不會再收到該頻道的消息

按模式訂閱和取消訂閱  psubscribe ch*    //訂閱以ch開頭的全部頻道

punsubscribe ch*  //取消以ch開頭的全部頻道

應用場景:

    一、今日頭條訂閱號、微信訂閱公衆號、新浪微博關注、郵件訂閱系統

    二、即便通訊系統

    三、羣聊部落系統(微信羣)

測試實踐:微信班級羣 class:20170101

一、學生C訂閱一個主題叫 :class:20170101

        >subscribe class:20170101

二、學生A針對class:20170101主體發送消息,那麼全部訂閱該主題的用戶都可以接收到該數據。

        >publish class:20170101 "hello world! I am A"

    三、學生B針對class:20170101主體發送消息,那麼全部訂閱該主題的用戶都可以接收到該數據。

        >publish class:20170101 "hello world! I am B"

    展現學生C接受到的A\B同窗發送過來的消息信息

        1) "subscribe"

        2) "class:20170101"

        3) (integer) 1

        1) "message"

        2) "class:20170101"

        3) "hello world! I am A"

        1) "message"

        2) "class:20170101"

        3) "hello word! I am B"

代碼例子見:redis-publish-subscribe工程

相關文章
相關標籤/搜索