lrucache: 基于 LuaJIT FFI 的 Lua 领域 LRU 缓存
安装
如果您尚未设置 RPM 仓库订阅,请 注册。然后您可以继续以下步骤。
CentOS/RHEL 7 或 Amazon Linux 2
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-lrucache
CentOS/RHEL 8+、Fedora Linux、Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-lrucache
要在 NGINX 中使用此 Lua 库,请确保已安装 nginx-module-lua。
本文档描述了 lua-resty-lrucache v0.15,于 2024 年 10 月 10 日发布。
-- file myapp.lua: example "myapp" module
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, err = lrucache.new(200) -- allow up to 200 items in the cache
if not c then
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")
c:flush_all() -- flush all the cached data
end
return _M
## nginx.conf
http {
# only if not using an official OpenResty release
server {
listen 8080;
location = /t {
content_by_lua_block {
require("myapp").go()
}
}
}
}
描述
该库为 OpenResty 和 ngx_lua 模块实现了一个简单的 LRU 缓存。
该缓存还支持过期时间。
LRU 缓存完全驻留在 Lua VM 中,并受 Lua GC 的影响。因此,请不要期望它能跨操作系统进程边界共享。其优点是您可以缓存任意复杂的 Lua 值(例如深层嵌套的 Lua 表),而无需序列化的开销(如 ngx_lua 的 shared dictionary API)。缺点是您的缓存始终限制于当前的操作系统进程(即当前的 Nginx 工作进程)。在 init_by_lua 的上下文中使用此库并没有太大意义,因为缓存不会被任何工作进程共享(除非您只是想用预定义的项“预热”缓存,这些项将通过 fork() 被工作进程继承)。
该库提供了两种不同的实现形式,分别为两个类:resty.lrucache 和 resty.lrucache.pureffi。两者实现相同的 API。唯一的区别是后者是一个纯 FFI 实现,同时实现了一个基于 FFI 的哈希表用于缓存查找,而前者使用原生 Lua 表。
如果缓存命中率相对较高,您应该使用 resty.lrucache 类,因为它比 resty.lrucache.pureffi 更快。
然而,如果缓存命中率相对较低,并且可能会有大量的键被插入和移除,那么您应该使用 resty.lrucache.pureffi,因为 Lua 表在频繁移除键时表现不佳。如果您在这种用例中使用 resty.lrucache 类,您可能会在 LuaJIT 运行时看到 resizetab 函数调用在 on-CPU flame graphs 中非常活跃。
方法
要加载此库,
- 使用官方 OpenResty 版本 或按照 安装 指示进行操作。
- 使用
require将库加载到本地 Lua 变量中:
local lrucache = require "resty.lrucache"
或
local lrucache = require "resty.lrucache.pureffi"
new
语法: cache, err = lrucache.new(max_items [, load_factor])
创建一个新的缓存实例。失败时返回 nil 和描述错误的字符串。
max_items 参数指定此缓存可以容纳的最大项数。
load-factor 参数指定 resty.lrucache.pureffi 内部使用的 FFI 基于哈希表的“负载因子”;默认值为 0.5(即 50%);如果指定了负载因子,它将被限制在 [0.1, 1] 范围内(即如果负载因子大于 1,将饱和为 1;同样,如果负载因子小于 0.1,将限制为 0.1)。此参数仅对 resty.lrucache.pureffi 有意义。
set
语法: cache:set(key, value, ttl?, flags?)
设置一个带有值和过期时间的键。
当缓存满时,缓存将自动驱逐最近最少使用的项。
可选的 ttl 参数指定过期时间。时间值以秒为单位,但您也可以指定小数部分(例如 0.25)。nil 的 ttl 参数意味着该值永远不会过期(这是默认值)。
可选的 flags 参数指定与要存储的项相关联的用户标志值。它可以在以后与该项一起检索。用户标志在内部作为无符号 32 位整数存储,因此必须指定为 Lua 数字。如果未指定,标志将默认为 0。此参数在 v0.10 版本中添加。
get
语法: data, stale_data, flags = cache:get(key)
根据键获取值。如果键在缓存中不存在或已过期,将返回 nil。
从 v0.03 开始,如果可用,还将作为第二个返回值返回过期数据。
从 v0.10 开始,与存储项相关联的用户标志值也将作为第三个返回值返回。如果没有为项提供用户标志,则其默认标志将为 0。
delete
语法: cache:delete(key)
从缓存中移除指定键的项。
count
语法: count = cache:count()
返回当前存储在缓存中的项数 包括 过期项(如果有)。
返回的 count 值将始终大于或等于 0,并且小于或等于传递给 cache:new 的 size 参数。
此方法在 v0.10 版本中添加。
capacity
语法: size = cache:capacity()
返回缓存可以容纳的最大项数。返回值与创建缓存时传递给 cache:new 的 size 参数相同。
此方法在 v0.10 版本中添加。
get_keys
语法: keys = cache:get_keys(max_count?, res?)
获取当前缓存中最多 max_count 的键列表。键将按 MRU 方式排序(最近使用的键优先)。
此函数返回一个包含键的 Lua(数组)表(带有整数键)。
当 max_count 为 nil 或 0 时,将返回所有键(如果有)。
当提供 res 表参数时,此函数将不会分配一个表,而是将键插入 res 中,并附加一个尾随的 nil 值。
此方法在 v0.10 版本中添加。
flush_all
语法: cache:flush_all()
刷新当前缓存实例中所有现有数据(如果有)。这是一个 O(1) 操作,应该比创建一个全新的缓存实例快得多。
但请注意,resty.lrucache.pureffi 的 flush_all() 方法是一个 O(n) 操作。
先决条件
nginx.conf
http {
...
}
然后在 Lua 中加载库:lua
local lrucache = require "resty.lrucache"
```
另见
- OpenResty: https://openresty.org
- ngx_http_lua 模块: https://github.com/openresty/lua-nginx-module
- ngx_stream_lua 模块: https://github.com/openresty/stream-lua-nginx-module
- lua-resty-core 库: https://github.com/openresty/lua-resty-core
GitHub
您可以在 nginx-module-lrucache 的 GitHub 仓库 中找到此模块的其他配置提示和文档。