一、LuaJIT 垃圾回收优化:让内存管理更高效

在 OpenResty 中,LuaJIT 的垃圾回收(GC)机制直接影响性能。默认情况下,GC 是自动运行的,但在高并发场景下,频繁的 GC 会导致请求延迟增加。我们可以通过调整 GC 参数来优化性能。

示例:调整 GC 步进和阈值

-- 技术栈:OpenResty + LuaJIT  
-- 设置 GC 步进(每次回收的内存块大小)  
collectgarbage("setstepmul", 200)  -- 默认是 200,增大可减少 GC 频率  
-- 设置 GC 暂停阈值(内存占用达到多少时触发 GC)  
collectgarbage("setpause", 110)    -- 默认是 200,降低可让 GC 更早触发  

注释说明

  • setstepmul 值越大,单次 GC 回收的内存越多,但可能造成短时卡顿。
  • setpause 值越小,GC 触发越早,适合内存敏感型应用。

适用场景

  • 高并发 API 服务,需要减少 GC 导致的延迟波动。
  • 长时间运行的定时任务,避免内存泄漏。

注意事项

  • 过度调大 setstepmul 可能导致单次 GC 耗时过长。
  • 在内存受限的环境中,setpause 不宜设置过低。

二、共享内存分配:多 Worker 间的数据共享

OpenResty 的多个 Worker 进程间默认不共享 Lua 变量。通过共享内存字典(lua_shared_dict),我们可以实现高效的数据共享,避免重复计算。

示例:使用共享字典缓存数据

-- 技术栈:OpenResty  
-- 在 nginx.conf 中定义共享内存区  
-- http { lua_shared_dict my_cache 10m; }  

local shared_cache = ngx.shared.my_cache  

-- 写入数据(带过期时间)  
shared_cache:set("user_123", "{'name':'张三'}", 60)  -- 60 秒后过期  

-- 读取数据  
local user_data = shared_cache:get("user_123")  
if not user_data then  
    -- 缓存未命中时从数据库加载  
    user_data = fetch_from_db()  
end  

注释说明

  • set 方法的第三个参数是过期时间(秒)。
  • 共享内存区大小需在 Nginx 配置中预先定义。

优缺点

  • 优点:减少数据库查询,提升响应速度。
  • 缺点:内存占用需谨慎规划,避免 OOM。

三、连接池参数配置:复用数据库连接

频繁创建和销毁数据库连接会消耗大量资源。OpenResty 的 resty.mysql 等库支持连接池,通过合理配置可大幅提升性能。

示例:MySQL 连接池配置

-- 技术栈:OpenResty + resty.mysql  
local mysql = require "resty.mysql"  

-- 初始化连接池  
local function get_mysql_conn()  
    local db, err = mysql:new()  
    if not db then  
        ngx.log(ngx.ERR, "创建连接失败: ", err)  
        return nil  
    end  

    -- 设置超时时间(毫秒)  
    db:set_timeout(1000)  

    -- 从连接池获取连接  
    local ok, err = db:connect{  
        host = "127.0.0.1",  
        port = 3306,  
        database = "test_db",  
        pool = "my_pool",          -- 连接池名称  
        pool_size = 100,           -- 连接池大小  
        backlog = 50               -- 等待队列长度  
    }  
    if not ok then  
        ngx.log(ngx.ERR, "连接失败: ", err)  
        return nil  
    end  
    return db  
end  

-- 使用后归还连接  
local db = get_mysql_conn()  
if db then  
    -- 执行查询...  
    db:set_keepalive(60000, 100)  -- 闲置60秒,最多保留100个连接  
end  

注释说明

  • pool_size 需根据服务器资源和并发量调整。
  • set_keepalive 将连接放回池中,避免重复创建。

常见问题

  • 连接泄漏:忘记归还连接会导致池耗尽。
  • 池大小不足:高并发时可能触发 backlog 排队。

四、综合优化策略与实战建议

  1. 监控与调优结合
    使用 ngx.log 记录 GC 耗时和内存变化,动态调整参数:

    local start = ngx.now()  
    collectgarbage("collect")  
    ngx.log(ngx.INFO, "GC 耗时:", ngx.now() - start)  
    
  2. 避免共享内存滥用
    大对象存储可改用 ffi 直接操作内存:

    local ffi = require "ffi"  
    ffi.cdef[[  
        void* malloc(size_t size);  
        void free(void* ptr);  
    ]]  
    local buf = ffi.C.malloc(1024)  -- 分配1KB内存  
    -- ...使用后手动释放  
    ffi.C.free(buf)  
    
  3. 连接池预热
    init_worker_by_lua 阶段预先建立连接:

    init_worker_by_lua_block {  
        local mysql = require "resty.mysql"  
        local db = mysql:new()  
        db:connect{...}  
        db:set_keepalive()  
    }  
    

总结

  • GC 优化平衡了内存和延迟,需根据业务特点调整。
  • 共享内存适合高频访问的小数据,连接池则对数据库性能至关重要。
  • 始终通过压测验证配置效果,避免理论最优但实际不适用的情况。