Redis 嵌入式開發

Redis 嵌入式開發

目前

目前來講,咱們使用Redis的場景大可能是僅僅將Redis做爲緩存系統來使用。其實,Redis發展到如今,因爲Redis的基於內存速度快,同時支持數據持久化等優良特色,業界不少地方,都是直接將Redis作爲數據庫使用。甚至,對於一些簡單的應用,咱們能夠直接把邏輯寫進Redis裏面,從而達到更高的效率。php

爲何是lua

  1. lua自己很是小巧,他的解釋器體積也很是小nginx

  2. 語言運行的效率相對較高,速度也比較快redis

  3. 有一些比較好的語言特性數據庫

這裏不是重點,再也不贅述json

爲何要在Redis裏面進行嵌入式開發

Redis運行的原理是基於C/S模型的,也就是說每次拉取數據就是一次的網絡請求。若是在一個接口裏面,咱們有幾個數據須要從Redis裏面獲取,可能就須要發好幾回網絡請求,這樣的網絡開銷是相對比較大的。如最左邊的圖緩存

圖片描述

若是咱們把,全部取數據的邏輯,所有放在Redis裏面,在Redis裏面就打包好,一次性的返回回來,咱們就能夠只須要一次網絡請求,就能夠把咱們須要的數據都拿回來(如中間的這幅圖)。這樣,咱們的網絡消耗就能大幅度的減少。甚至,在一些場合,咱們直接就能夠用Nginx直接找Redis要數據,不須要通過php-fpm/Uwsgi這個環節,就能夠把須要的緩存數據所有返回(如右邊的圖)。這就是經過在Redis進行嵌入式開發來提升網絡效率的原理。服務器

內部優化

最主要的Redis提供了EVAL方法來進行,其用法是網絡

eval ‘要執行的lua腳本代碼’ 要調用的存在redis裏面的數據的個數 他們的key 而後是要穿入的參數app

例子:異步

eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second

傳入的key是保存在全局變量KEYS這個table裏面,傳入的參數是保存在全局變量ARGV這個table裏面。全局變量是不可更改的。(table是lua中的一種數據類型)

爲何要單獨設計要穿入key呢?他的用途有不少。最主要的,是由於Redis是爲分佈式設計的數據庫,提早傳入key,就能夠分析出存的數據是在哪一個server上。

每次都要經過網絡傳輸一次lua腳本,顯然這個網絡開銷是比較大的。那麼Redis是怎麼解決這個問題的呢?Redis提供了一個叫EVALSHA的方法。只要Redis啓動着,每執行一個lua腳本,不管如何,Redis都會將這個腳本緩存下來,經過將腳本進行sha操做來標記。因爲其實腳本的變更其實很是的少,並且保存腳本自己並不須要什麼空間,這部分的資源消耗基本上能夠忽略不計。之後只須要只用EVALSHA方法,帶上你的腳本的sha值,Redis就能找到要執行的腳本進行執行。因爲sha的值事實上是遠遠短於你的代碼的值,因此這樣也能大大提升網絡的利用效率。

事實上,EVAL方法中自己就實現了這個策略。他會事先將你的腳本的 sha值傳過去,若是server端有這個腳本,他就直接執行,若是發現沒有這個腳本,Redis就會從新發送一次這個腳本的完整版,傳到server端執行,而後這個腳本就會被緩存下來。

何時不該該把腳本嵌入到Redis裏面

目前Redis的實現,是堵塞的。也就是說,一個Redis server,在同一時候,只能執行一個腳本。試想,若是你寫了一個邏輯很是複雜的腳本,這個腳本的執行時間很是長。這樣,這個時候若是有別的請求進來,就只能排隊,等待這個腳本結束了才能處理下一個請求。在這個時候,就連僅僅是獲取數據的命令都會被堵住。這樣,這個服務器端的效率就會被大大的拖慢。所以,若是你的腳本執行很是耗時,那麼這個時候你是不該該把它放在Redis裏面執行的。目前,我想到的,只有數據打包的工做適合放在redis裏面執行。

接下來咱們來作兩個實驗

接下來的實驗環境是:OpenResty來作容器。Nginx配置文件以下

server {
    listen 63945;
    server_name lua.redis.local;
    set $root_folder /Users/Coordinate35/OI/program/lua/lua-redis;

    lua_code_cache on;

    location /luaredis {
        default_type application/json;
        content_by_lua_file $root_folder/redis_with_lua.lua;
    }

    location /redis {
        default_type application/json;
        content_by_lua_file $root_folder/redis_without_lua.lua;
    }

    location /luaredistime {
        default_type application/json;
        content_by_lua_file $root_folder/redis_with_lua_time.lua;
    }

    location /redistime {
        default_type application/json;
        content_by_lua_file $root_folder/redis_without_lua_time.lua;
    }

    error_log /var/log/nginx/redis-test/error.log debug;
}

僅僅是數據拼裝的狀況

我首先在Redis裏面存入了10個數據,如圖:

圖片描述

而後這個是把數據打包處理放在了Redis裏面的代碼(redis_with_lua.lua):

local json = require "cjson"
local redis = require "resty.redis"

local red = redis:new()
local ok, err = red:connect('127.0.0.1', 6379)

local lua_in_redis = [[
    local response = {}
    local i = 1
    while i <= 10 do
        response[i] = redis.pcall('get', KEYS[i])
        i = i + 1 
    end
    return response
]]

local response = red:eval(lua_in_redis, 10, 'foo1', 'foo2', 'foo3', 'foo4', 'foo5', 'foo6', 'foo7', 'foo8', 'foo9', 'foo10')

ngx.say(response)

這個是把數據打包處理放在Nginx裏面的代碼(redis_without_lua.lua):

local json = require "cjson"
local redis = require "resty.redis"

local red = redis:new()
local ok, err = red:connect('127.0.0.1', 6379)

local basic = 'foo'
local response = {}
local i = 1

while i <= 10 do 
    local key = basic .. i
    response[i] = red:get(key)
    i = i + 1
end

ngx.say(response)

接下來,咱們用ab對接口進行壓力測試。每次測試前都會重啓一次nginx,來避免一些影響。

左圖是命令:

ab -n 5000 -c 100 -k http://lua.redis.local:63945/... (邏輯在redis)

右圖是命令:

ab -n 5000 -c 100 -k http://lua.redis.local:63945/... (邏輯在nginx)

圖片描述

顯然,這個時候,把邏輯放在Redis裏面效率要遠遠的比放在Nginx裏面高

當邏輯很複雜的時候

咱們使用將i從1自加到10000000來模擬一個耗時的邏輯。

這個是把數據打包處理放在了Redis裏面的代碼(redis_with_lua_time.lua):

local json = require "cjson"
local redis = require "resty.redis"

local red = redis:new()
local ok, err = red:connect('127.0.0.1', 6379)

local lua_in_redis = [[
    local response = {}
    local i = 1
    while i <= 10000000 do
        i = i + 1 
    end
    return response
]]

local response = red:eval(lua_in_redis, 10, 'foo1', 'foo2', 'foo3', 'foo4', 'foo5', 'foo6', 'foo7', 'foo8', 'foo9', 'foo10')

ngx.say(response)

這個是把邏輯放在了Nginx裏面的代碼(redis_without_lua_time.lua):

local json = require "cjson"
local redis = require "resty.redis"

local red = redis:new()
local ok, err = red:connect('127.0.0.1', 6379)

local basic = 'foo'
local response = {}
local i = 1

while i <= 10000000 do 
    i = i + 1
end

ngx.say(response)

一樣用ab進行壓力測試:

每次測試前都會重啓一次Nginx,來避免一些影響。

左圖是命令:

ab -n 5000 -c 100 -k http://lua.redis.local:63945/... (邏輯在Redis)

右圖是命令:ab -n 5000 -c 100 -k http://lua.redis.local:63945/... (邏輯在Nginx)

結果:

圖片描述

顯然,這個時候,把邏輯放在了Nginx裏面,效率要遠遠高於放在Redis裏面

結論

合理的把邏輯放在Redis裏面,可以大大的提升咱們服務器的運行效率。咱們應該把數據打包的工做(減小網絡請求)放在Redis裏面,而把複雜耗時的操做放在外部,經過異步等別的方式,來提升服務器的處理能力。

相關文章
相關標籤/搜索