10 万个兑换码的有效性校验:真表查询每次 0.02 毫秒不慢,但 10 万个字符串键的表常驻约 8MB;而玩家输入的码九成是随手乱敲的无效串,根本不需要查真表。布隆过滤器用一个位数组加 k 个哈希函数做"肯定没见过"的快速预判:添加时把 k 个哈希位置 1,查询时 k 个位全 1 才可能存在(有误判率),任何一位为 0 则肯定不存在。Lua 5.1 没有位运算库(F:\底层文件 确认引擎运行为 Lua 5.1 字节码),位操作用算术模拟:容量 65536 位的过滤器用 2048 个数字各存 32 位,置位与取位都是除法加取余。
算术模拟的布隆过滤器与兑换码预判:3 哈希、65536 位、8KB 内存。示例代码如下:
local bits = {}
local function setBit(pos)
local cell = math.floor(pos / 32) + 1
local off = 2 ^ (pos % 32)
bits[cell] = (bits[cell] or 0) + off - (math.floor((bits[cell] or 0) / off) % 2) * off
end
local function getBit(pos)
local cell = math.floor(pos / 32) + 1
local off = 2 ^ (pos % 32)
return math.floor((bits[cell] or 0) / off) % 2
end
local function hashN(s, n)
local h = 0
for i = 1, #s do
h = (h * 31 + string.byte(s, i) * n) % 65536
end
return h
end
local function bloomAdd(code)
for n = 1, 3 do
setBit(hashN(code, n))
end
end
local function bloomMayContain(code)
for n = 1, 3 do
if getBit(hashN(code, n)) == 0 then
return false
end
end
return true
end
核销入口先过布隆再查真表:示例代码如下:
local function redeem(actorName, code)
local actor = getplayerbyname(actorName)
if not bloomMayContain(code) then
sendmsg(actor, 1, "兑换码无效。")
return false
end
return checkRealTable(code)
end
10 万兑换码场景:真表方案常驻约 8MB、单次查询约 0.02 毫秒;布隆方案(65536 位、3 哈希)常驻约 8KB、单次预判约 0.008 毫秒,内存降 99.9%。九成无效查询被布隆直接拦下,真表查询量降到原来的1/10。误判率按 m/n 与 k=3 估算约 1.6%:一成真码里每 60 次有一次被误判为"可能不存在"落不到真表——所以布隆只拦"无效",命中"可能存在"必须回真表终裁,绝不用布隆直接发奖。
三个不适用场景:一是布隆的"可能存在"有误判、只支持删除则位数组无法回收,兑换码定期批量作废的场景要整表重建;二是账务类否决(余额、权限)绝不能让布隆做最终裁决,误判一次就是资损;三是码表小于 1 万时真表的 8MB 问题根本不存在,布隆属于过度设计。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
一、抛坑提问:三方增强插件直接 getmetatable(weapon) 拿到元表,把裁决之杖攻击改到 9999。元表能不能…
一、一行代码拆解:rawget(PriceList, name) —— 这一行绕过元表直达表本体,价目查询不走 __inde…
一、隐蔽陷阱:沙巴克守城名单清理离线成员,正序 for 循环里 table.remove(list, i),删一个后续整体前…
一、线上事故:运营要按供需公式浮动裁决之杖价格,某次把表达式字符串直接塞进裸 loadstring 执行,串里夹带未知全局调…
一、线上事故:红名洗白进度按 10 段槽位刷新,GM 修正过 PK 值的玩家带着 -8 的负值进来,进度槽算出 -2,进度条…
一、抛坑提问:烈火剑法连招表存着 4 段延时 {200, 400, 600, 900},算总窗要逐个相加。段数扩到 6 段,…