跳转至

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()
            }
        }
    }
}

描述

该库为 OpenRestyngx_lua 模块实现了一个简单的 LRU 缓存。

该缓存还支持过期时间。

LRU 缓存完全驻留在 Lua VM 中,并受 Lua GC 的影响。因此,请不要期望它能跨操作系统进程边界共享。其优点是您可以缓存任意复杂的 Lua 值(例如深层嵌套的 Lua 表),而无需序列化的开销(如 ngx_luashared dictionary API)。缺点是您的缓存始终限制于当前的操作系统进程(即当前的 Nginx 工作进程)。在 init_by_lua 的上下文中使用此库并没有太大意义,因为缓存不会被任何工作进程共享(除非您只是想用预定义的项“预热”缓存,这些项将通过 fork() 被工作进程继承)。

该库提供了两种不同的实现形式,分别为两个类:resty.lrucacheresty.lrucache.pureffi。两者实现相同的 API。唯一的区别是后者是一个纯 FFI 实现,同时实现了一个基于 FFI 的哈希表用于缓存查找,而前者使用原生 Lua 表。

如果缓存命中率相对较高,您应该使用 resty.lrucache 类,因为它比 resty.lrucache.pureffi 更快。

然而,如果缓存命中率相对较低,并且可能会有大量的键被插入和移除,那么您应该使用 resty.lrucache.pureffi,因为 Lua 表在频繁移除键时表现不佳。如果您在这种用例中使用 resty.lrucache 类,您可能会在 LuaJIT 运行时看到 resizetab 函数调用在 on-CPU flame graphs 中非常活跃。

方法

要加载此库,

  1. 使用官方 OpenResty 版本 或按照 安装 指示进行操作。
  2. 使用 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)。nilttl 参数意味着该值永远不会过期(这是默认值)。

可选的 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:newsize 参数。

此方法在 v0.10 版本中添加。

capacity

语法: size = cache:capacity()

返回缓存可以容纳的最大项数。返回值与创建缓存时传递给 cache:newsize 参数相同。

此方法在 v0.10 版本中添加。

get_keys

语法: keys = cache:get_keys(max_count?, res?)

获取当前缓存中最多 max_count 的键列表。键将按 MRU 方式排序(最近使用的键优先)。

此函数返回一个包含键的 Lua(数组)表(带有整数键)。

max_countnil0 时,将返回所有键(如果有)。

当提供 res 表参数时,此函数将不会分配一个表,而是将键插入 res 中,并附加一个尾随的 nil 值。

此方法在 v0.10 版本中添加。

flush_all

语法: cache:flush_all()

刷新当前缓存实例中所有现有数据(如果有)。这是一个 O(1) 操作,应该比创建一个全新的缓存实例快得多。

但请注意,resty.lrucache.pureffiflush_all() 方法是一个 O(n) 操作。

先决条件

nginx.conf

http {
    ...
}

然后在 Lua 中加载库:lua local lrucache = require "resty.lrucache" ```

另见

GitHub

您可以在 nginx-module-lrucache 的 GitHub 仓库 中找到此模块的其他配置提示和文档。