nginx 共享内存(1)

本文详细分析了在Nginx中使用ngx.shared存储大内存变量时遇到的内存问题,着重探讨了set操作中的内存分配过程,包括shdict_store函数和ngx_slab_alloc_locked的实现,以及红黑树在查找和内存管理中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

因为业务需要使用ngx.shared 共享内存,但是存储大内存变量时报no memory,实际显示还有内存,怀疑是小内存碎片,分配不出大内存,通过走读源码发现,nginx 是在一块连续的地址上进行内存分配,并将内存按照page大小进行操作。

ngx.shared.dict set实现

下面的代码基于openresty1.21.4.3 版本

走读ngx.shared源码,分析set操作时,如何从内存池中获取内存

ngx.shared.dict 实现逻辑位于文件 shdict.lua中

local _, dict = next(ngx_shared, nil)
if dict then
    local mt = getmetatable(dict)
    if mt then
        mt = mt.__index
        if mt then
            mt.get = shdict_get
            mt.get_stale = shdict_get_stale
            mt.incr = shdict_incr
            mt.set = shdict_set
            mt.safe_set = shdict_safe_set
            mt.add = shdict_add
            mt.safe_add = shdict_safe_add
            mt.replace = shdict_replace
            mt.delete = shdict_delete
            mt.flush_all = shdict_flush_all
            mt.ttl = shdict_ttl
            mt.expire = shdict_expire
            mt.capacity = shdict_capacity
            mt.free_space = shdict_free_space
        end
    end
end

set调用share_set函数,实现为shdict_store, op为0

local function shdict_set(zone, key, value, exptime, flags)
    return shdict_store(zone, 0, key, value, exptime, flags)
end
local function shdict_store(zone, op, key, value, exptime, flags)
    zone = check_zone(zone)

    if not exptime then
        exptime = 0
    elseif exptime < 0 then
        error('bad "exptime" argument', 2)
    end

    if not flags then
        flags = 0
    end

    if key == nil then
        return nil, "nil key"
    end
	-- 如果key不为字符串,转换成字符串
    if type(key) ~= "string" then
        key = tostring(key)
    end

    local key_len = #key
    if key_len == 0 then
        return nil, "empty key"
    end
    -- 不能存储长度大于65535的key
    if key_len > 65535 then
        return nil, "key too long"
    end

    local str_val_buf
    local str_val_len = 0
    local num_val = 0
    local valtyp = type(value)

    -- print("value type: ", valtyp)
    -- print("exptime: ", exptime)
	-- 定义了value可以存储的类型:string / number /nil /boolean, nil 用于删除key时使用
    if valtyp == "string" then
        valtyp = 4  -- LUA_TSTRING
        str_val_buf = value
        str_val_len = #value

    elseif valtyp == "number" then
        valtyp = 3  -- LUA_TNUMBER
        num_val = value

    elseif value == nil then
        valtyp = 0  -- LUA_TNIL

    elseif valtyp == "boolean" then
        valtyp = 1  -- LUA_TBOOLEAN
        num_val = value and 1 or 0

    else
        return nil, "bad value type"
    end

    local rc = ngx_lua_ffi_shdict_store(zone, op, key, key_len,
                                        valtyp, str_val_buf,
                                        str_val_len, num_val,
                                        exptime * 1000, flags, errmsg,
                                        forcible)

    -- print("rc == ", rc)

    if rc == 0 then  -- NGX_OK
        return true, nil, forcible[0] == 1
    end

    -- NGX_DECLINED or NGX_ERROR
    return false, ffi_str(errmsg[0]), forcible[0] == 1
end

核心函数

int ngx_http_lua_ffi_shdict_store(void *zone, int op,
    const unsigned char *key, size_t key_len, int value_type,
    const unsigned char *str_value_buf, size_t str_value_len,
    double num_value, long exptime, int user_flags, char **errmsg,
    int *forcible);

set时op的值为0,为了方便阅读,删除op非0是的逻辑

int
ngx_http_lua_ffi_shdict_store(ngx_shm_zone_t *zone, int op, u_char *key,
    size_t key_len, int value_type, u_char *str_value_buf,
    size_t str_value_len, 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值