一、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排队。
四、综合优化策略与实战建议
监控与调优结合
使用ngx.log记录 GC 耗时和内存变化,动态调整参数:local start = ngx.now() collectgarbage("collect") ngx.log(ngx.INFO, "GC 耗时:", ngx.now() - start)避免共享内存滥用
大对象存储可改用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)连接池预热
在init_worker_by_lua阶段预先建立连接:init_worker_by_lua_block { local mysql = require "resty.mysql" local db = mysql:new() db:connect{...} db:set_keepalive() }
总结:
- GC 优化平衡了内存和延迟,需根据业务特点调整。
- 共享内存适合高频访问的小数据,连接池则对数据库性能至关重要。
- 始终通过压测验证配置效果,避免理论最优但实际不适用的情况。
评论