鍍金池/ 教程/ Java/ 緩存
定時(shí)任務(wù)
函數(shù)的參數(shù)
超時(shí)
一個(gè) openresty 內(nèi)存“泄漏”問題
獲取 uri 參數(shù)
局部變量
sleep
灰度發(fā)布
TIME_WAIT
代碼覆蓋率
連接池
CentOS 平臺(tái)安裝
稀疏數(shù)組
如何只啟動(dòng)一個(gè) timer 工作?
變量的共享范圍
break,return 關(guān)鍵字
Nginx
SQL 注入
如何引用第三方 resty 庫
不同階段共享變量
獲取請(qǐng)求 body
動(dòng)態(tài)生成的 lua-resty-redis 模塊方法
動(dòng)態(tài)加載證書和 OCSP stapling
repeat 控制結(jié)構(gòu)
編碼為 array 還是 object
Nginx 靜態(tài)文件服務(wù)
執(zhí)行階段概念
Lua 函數(shù)
日期時(shí)間函數(shù)
健康監(jiān)測(cè)
與其他 location 配合
for 控制結(jié)構(gòu)
函數(shù)定義
HTTPS 時(shí)代
點(diǎn)號(hào)與冒號(hào)操作符的區(qū)別
String 庫
文件操作
OpenResty 最佳實(shí)踐
<code>ngx.shared.DICT</code> 非隊(duì)列性質(zhì)
使用動(dòng)態(tài) DNS 來完成 HTTP 請(qǐng)求
代碼規(guī)范
什么是 JIT?
Windows 平臺(tái)安裝
正確的記錄日志
LuaNginxModule
不用標(biāo)準(zhǔn)庫
C10K 編程
控制結(jié)構(gòu)
請(qǐng)求中斷后的處理
Lua 環(huán)境搭建
Test::Nginx 能指定現(xiàn)成的 nginx.conf,而不是自動(dòng)生成一個(gè)嗎
Lua 基礎(chǔ)數(shù)據(jù)類型
動(dòng)態(tài)限速
PostgresNginxModule
簡(jiǎn)單API Server框架
API 測(cè)試
location 匹配規(guī)則
虛變量
單元測(cè)試
防止 SQL 注入
select + set_keepalive 組合操作引起的數(shù)據(jù)讀寫錯(cuò)誤
阻塞操作
全動(dòng)態(tài)函數(shù)調(diào)用
Web 服務(wù)
典型應(yīng)用場(chǎng)景
Nginx 新手起步
TLS session resumption
輸出響應(yīng)體
調(diào)用代碼前先定義函數(shù)
module 是邪惡的
怎樣理解 cosocket
模塊
Socket 編程發(fā)展
如何對(duì) Nginx Lua module 添加新 api
如何在后臺(tái)開啟輕量級(jí)線程完成定時(shí)任務(wù)?
如何定位問題
table 庫
json 解析的異常捕獲
如何安裝火焰圖生成工具
lua 中如何 continue
if 是邪惡的
為什么我們的域名不能被解析
抵制使用 module() 定義模塊
測(cè)試
body 在 location 中的傳遞
Lua 入門
子查詢
pipeline 壓縮請(qǐng)求數(shù)量
如何發(fā)起新 HTTP 請(qǐng)求
Lua 簡(jiǎn)介
緩存失效風(fēng)暴
Ubuntu 平臺(tái)安裝
日志輸出
緩存
Lua 面向?qū)ο缶幊?/span>
Nginx 陷阱和常見錯(cuò)誤
Redis 接口的二次封裝(發(fā)布訂閱)
日志
訪問有授權(quán)驗(yàn)證的 Redis
正則表達(dá)式
lock
熱裝載代碼
調(diào)用 FFI 出現(xiàn) &quot;table overflow&quot;
數(shù)據(jù)合法性檢測(cè)
禁止某些終端訪問
控制結(jié)構(gòu) if-else
調(diào)試
與 Docker 使用的網(wǎng)絡(luò)瓶頸
PostgresNginxModule 模塊的調(diào)用方式
用 do-end 整理你的代碼
FFI
什么時(shí)候使用
簡(jiǎn)介
環(huán)境搭建
Mac OS X 平臺(tái)安裝
火焰圖
負(fù)載均衡
while 型控制結(jié)構(gòu)
如何定位 openresty 崩潰 bug
使用 Nginx 內(nèi)置綁定變量
判斷數(shù)組大小
請(qǐng)求返回后繼續(xù)執(zhí)行
Redis 接口的二次封裝
KeepAlive
反向代理
協(xié)議無痛升級(jí)
數(shù)學(xué)庫
元表
Vanilla 介紹
HelloWorld
LuaCjsonLibrary
持續(xù)集成
代碼靜態(tài)分析
網(wǎng)上有大量對(duì) Lua 調(diào)優(yōu)的推薦,我們應(yīng)該如何看待?
script 壓縮復(fù)雜請(qǐng)求
非空判斷
性能測(cè)試
函數(shù)返回值
API 的設(shè)計(jì)
kong 介紹
表達(dá)式
不支持事務(wù)
LuaRestyDNSLibrary 簡(jiǎn)介

緩存

緩存的原則

緩存是一個(gè)大型系統(tǒng)中非常重要的一個(gè)組成部分。在硬件層面,大部分的計(jì)算機(jī)硬件都會(huì)用緩存來提高速度,比如 CPU 會(huì)有多級(jí)緩存、RAID 卡也有讀寫緩存。在軟件層面,我們用的數(shù)據(jù)庫就是一個(gè)緩存設(shè)計(jì)非常好的例子,在 SQL 語句的優(yōu)化、索引設(shè)計(jì)、磁盤讀寫的各個(gè)地方,都有緩存,建議大家在設(shè)計(jì)自己的緩存之前,先去了解下 MySQL 里面的各種緩存機(jī)制,感興趣的可以去看下High Performance MySQL這本非常有價(jià)值的書。

一個(gè)生產(chǎn)環(huán)境的緩存系統(tǒng),需要根據(jù)自己的業(yè)務(wù)場(chǎng)景和系統(tǒng)瓶頸,來找出最好的方案,這是一門平衡的藝術(shù)。

一般來說,緩存有兩個(gè)原則。一是越靠近用戶的請(qǐng)求越好,比如能用本地緩存的就不要發(fā)送 HTTP 請(qǐng)求,能用 CDN 緩存的就不要打到 Web 服務(wù)器,能用 Nginx 緩存的就不要用數(shù)據(jù)庫的緩存;二是盡量使用本進(jìn)程和本機(jī)的緩存解決,因?yàn)榭缌诉M(jìn)程和機(jī)器甚至機(jī)房,緩存的網(wǎng)絡(luò)開銷就會(huì)非常大,在高并發(fā)的時(shí)候會(huì)非常明顯。

OpenResty 的緩存

我們介紹下在 OpenResty 里面,有哪些緩存的方法。

使用 Lua shared dict

我們看下面這段代碼:

function get_from_cache(key)
    local cache_ngx = ngx.shared.my_cache
    local value = cache_ngx:get(key)
    return value
end

function set_to_cache(key, value, exptime)
    if not exptime then
        exptime = 0
    end

    local cache_ngx = ngx.shared.my_cache
    local succ, err, forcible = cache_ngx:set(key, value, exptime)
    return succ
end

這里面用的就是 ngx shared dict cache。你可能會(huì)奇怪,ngx.shared.my_cache 是從哪里冒出來的?沒錯(cuò),少貼了 nginx.conf 里面的修改:

lua_shared_dict my_cache 128m;

如同它的名字一樣,這個(gè) cache 是 Nginx 所有 worker 之間共享的,內(nèi)部使用的 LRU 算法(最近最少使用)來判斷緩存是否在內(nèi)存占滿時(shí)被清除。

使用Lua LRU cache

直接復(fù)制下春哥的示例代碼:

local _M = {}

-- alternatively: local lrucache = require "resty.lrucache.pureffi"
local lrucache = require "resty.lrucache"

-- we need to initialize the cache on the Lua module level so that
-- it can be shared by all the requests served by each nginx worker process:
local c = lrucache.new(200)  -- allow up to 200 items in the cache
if not c then
    return error("failed to create the cache: " .. (err or "unknown"))
end

function _M.go()
    c:set("dog", 32)
    c:set("cat", 56)
    ngx.say("dog: ", c:get("dog"))
    ngx.say("cat: ", c:get("cat"))

    c:set("dog", { age = 10 }, 0.1)  -- expire in 0.1 sec
    c:delete("dog")
end

return _M

可以看出來,這個(gè) cache 是 worker 級(jí)別的,不會(huì)在 Nginx wokers 之間共享。并且,它是預(yù)先分配好 key 的數(shù)量,而 shared dict 需要自己用 key 和 value 的大小和數(shù)量,來估算需要把內(nèi)存設(shè)置為多少。

如何選擇?

shared.dict 使用的是共享內(nèi)存,每次操作都是全局鎖,如果高并發(fā)環(huán)境,不同 worker 之間容易引起競(jìng)爭(zhēng)。所以單個(gè) shared.dict 的體積不能過大。lrucache 是 worker 內(nèi)使用的,由于 Nginx 是單進(jìn)程方式存在,所以永遠(yuǎn)不會(huì)觸發(fā)鎖,效率上有優(yōu)勢(shì),并且沒有 shared.dict 的體積限制,內(nèi)存上也更彈性,但不同 worker 之間數(shù)據(jù)不同享,同一緩存數(shù)據(jù)可能被冗余存儲(chǔ)。

你需要考慮的,一個(gè)是 Lua lru cache 提供的 API 比較少,現(xiàn)在只有 get、set 和 delete,而 ngx shared dict 還可以 add、replace、incr、get_stale(在 key 過期時(shí)也可以返回之前的值)、get_keys(獲取所有 key,雖然不推薦,但說不定你的業(yè)務(wù)需要呢);第二個(gè)是內(nèi)存的占用,由于 ngx shared dict 是 workers 之間共享的,所以在多 worker 的情況下,內(nèi)存占用比較少。

上一篇:防止 SQL 注入下一篇:LuaNginxModule