緩存利器、Lua模塊下的共享內存


上一節講到了worker進程的共享內存,它利用豐富的指令使數據的緩存操作變得非常簡單,但它也存在一些缺點。

1.worker進程之間會有鎖競爭,在高並發的情況下會增加性能開銷。
2.只支持Lua布爾值、數字、字符串和nil類型的數據,無法支持table類型的數據。
3.在讀取數據時有反序列化操作,會增加CPU開銷。

共享內存在Ngx_Lua中作為緩存工具還是非常出色的。筆者在生產環境中,曾多次使用lua_shared_dict的各種特性,並未感受到存在明顯的性能問題。但如果讀者還是介意這些缺點或需要緩存更復雜的數據的話,可以使用lua-resty-lrucache。

10.2.1 安裝lua-resty-lrucache

lua-resty-lrucache是基於Ngx_Lua的緩存利器,它擁有如下優點。

1.支持更豐富的數據類型,可以把table存放在value中,這對數據結構復雜的業務非常有用。

2.可以預先分配key的數量,不用設置固定的內存空間,在內存的使用上更為靈活。

3.每個worker進程獨立緩存,所以當worker進程同時讀取同一個key 時不存在鎖競爭。

但它與lua_shared_dict相比也有一些缺點。

1.因為數據不在worker之間共享,所以無法保證在更新數據時,數據在同一時間的不同worker進程上完全一致。

2.雖然可以支持復雜的數據結構,但可使用的指令卻很少,如不支持消息隊列功能。

3.重載Nginx配置時,緩存數據會丟失。如果使用lua_shared_dict,則不會如此。

有利就有弊,讀者在使用時可以根據自身需求進行選擇。lua-resty-lrucache的安裝方式和其他的lua-resty模塊一樣,如下所示:

# git clone https://github.com/openresty/lua-resty-lrucache.git # cp -r lua-resty-lrucache/lib/resty/lrucache* \ /usr/local/nginx_1.12.2/conf/lua_modules/resty/

10.2.2 使用lua-resty-lrucache進行緩存的方法

通過下面的例子來了解一下lua-resty-lrucache的使用方式,首先需要對模塊進行加載,方法如下:
local lrucache = require "resty.lrucache"
local lrucache = require "resty.lrucache.pureffi"
讀者在加載lua-resty-lrucache時,需要把上面的2個文件復制到lua_package_path所設置的路徑上。它們的作用是一樣的,但性能有所區別:resty.lrucache適合用來緩存命中率高或讀操作遠遠大於寫操作的緩存業務;resty.lrucache.pureffi適合用來緩存命中率低或需要對key進行頻繁增、刪操作的緩存業務。請根據業務需求進行選擇。
然后,將下面的代碼寫入test_m.lua中,並將此文件放到lua_package_path的路徑下,代碼如下:

local _M = {}
local lrucache = require "resty.lrucache" --在緩存上聲明1個1000個key的列表 local cache, err = lrucache.new(1000) if not cache then return error("failed to create the cache: " .. (err or "unknown")) end --此函數用來往緩存中存儲key/value的值 local function mem_set() --set()中的內容從左到右順序依次是key、value、有效期(2s) cache:set("a", 19, 2) cache:set("b", {"1","2","3"},0.001) --支持插入table類型的數據 return end --此函數用來獲取緩存里的value。 a即value的值,如果a為nil,則表示value不存在或已過期;如果stale_data有值,也說明value已過期 local function mem_get(key) local a,stale_data = cache:get(key) return a,stale_data end function _M. fromcache () --獲取a的值 local a,stale_data = mem_get("a") --如果a存在,就輸出a的值 if a then ngx.say("a: ", a) --如果a不存在且stale_data有值,就輸出過期的value,並重新執行存儲操作,然后再次輸出value elseif stale_data then ngx.say("a 已經過期: " , stale_data) mem_set() local a_again = mem_get("a") ngx.say("a: ", a_again ) --如果a 和 stale_data都不存在,則執行存儲操作后再輸出value else ngx.say("no found a") mem_set() local a_again = mem_get("a") ngx.say("a: ", a_again ) end end return _M 修改nginx.conf文件,代碼如下: location / { content_by_lua_block { --加載模塊,執行數據的讀取操作 require("test_m").fromcache() } }

重載Nginx配置,執行結果如下:

# curl 'http://testnginx.com/' no found a a: 19 [root@testnginx ~]# curl 'http://testnginx.com/' a: 19 [root@testnginx ~]# curl 'http://testnginx.com/' a: 19 [root@testnginx ~]# curl 'http://testnginx.com/' a 已經過期: 19 a: 19

從執行結果可以看出:
1.第1次請求,因為a沒有值,所以先輸出“no found a”,然后又執行了存儲操作。

2.第2次請求,因為有緩存值,直接輸出value。

3.第3次請求,仍然有緩存值,直接輸出value。

4.第4次請求,因為為緩存數據設置的有效期很短,此時已經過期,所以輸出了過期的value,並再次執行存儲操作,又輸出了value。

如果嘗試重載Nginx配置,會發現每次重啟(restart)后a都沒有值,因為在重載配置的過程中,緩存數據會丟失。
下面將對lua-resty-lrucache的常見指令進行說明。


new
語法:cache, err = lrucache.new(max_items [, load_factor])
含義:創建1個緩存實例。如果創建失敗會返回nil,並將錯誤信息返回給err。
max_items用來聲明緩存key的數量,從這個設置可以看出它雖然沒有規定內存的使用大小,但規定了key的數量。
load_factor參數是加載resty.lrucache.pureffi模塊時才會用到的,它基於FFI(Foreign Function Interface,外部功能接口)的hash表的負載因子,值的區間在0.1~1之間,默認值是0.5。負載因子與hash數據的讀取時間和對內存空間大小的權衡有關,有興趣的讀者可以自行查詢相關信息。


set
語法:cache:set(key, value, ttl)
含義:把key/value存儲到緩存中。ttl是緩存的有效期,以秒為單位,默認值是0,表示不會過期;支持設置為0.001s。


get
語法:data, stale_data = cache:get(key)
含義:獲取指定key的值,如果key不存在或已過期,就返回nil;如果存在過期數據,過期的值會賦值給stale_data。


delete
語法:cache:delete(key)
含義:從緩存中移除指定的key。


flush_all
語法:cache:flush_all(key)
含義:刷新整個緩存區域的數據,等於清空內存中的數據。這種方式比創建新的緩存實例要快得多。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM