为了缓解数据库压力,数据会缓存在lua table,满足频繁读写需求,再定时检查,发现数据变了则落地。
问题来了,lua table怎么检查数据变了?
table检查数据变了
答案很简单,使用 metatable,这里贴一个简单的例子。
-- db数据
db = {openid="openid_123", token="token_123"}
-- 缓存
cache = {}
setmetatable(cache, {
	__index = function(t, k)
		return db[k]
	end,
	__newindex = function(t, k, v)
		if k ~= "__dirty" then
			db.__dirty = true
		end
		db[k] = v
	end,
	__pairs = function(t, k)
		return next, db, k
	end,
})
for k, v in pairs(cache) do
	print(k, v)
end
cache.__dirty = nil
print("cache.__dirty", cache.__dirty)
cache.token = "token_456"
print("cache.__dirty", cache.__dirty)
执行结果如下:
> dofile("test.lua")
token   token_123
openid  openid_123
cache.__dirty      nil
cache.__dirty      true
问题到这里就结束了。
接下来,我们再提出一个问题,lua table如何检查子项为table的数据变了?
table检查子项为table的数据变了
先说思路,要遍历 table,找到所有子项的table,都设置 metatable
注意处理好两个问题:
1、值为table,table之间可能相互引用,就可能存在m对n的引用关系
2、子table下可能又有table,所有子table修改都要更新根table状态
为此,实现如下(非完整版本):
local function dirty_check(root)
	local dummy = {}
	local g_set = {}
	local function check_table(t)
		if type(t) ~= "table" then
			return
		end
		local mt = getmetatable(t)
		local tv = g_set[t]
		if not tv then
			local tv = {}
			for k, v in pairs(t) do
				check_table(v)
				tv[k] = v
				rawset(t, k, nil) -- 强制使 table 触发 newindex
			end
			g_set[t] = tv
		end
		if not mt then
			setmetatable(t, dummy)
		end
	end
	dummy.__newindex = function(t, k, v)
		if k ~= "__dirty" then
			g_set[root]["__dirty"] = true
			check_table(v)
		end
		g_set[t][k] = v -- 存在内存泄漏,v 为 table 需要清理 g_set
		
	end
	dummy.__index = function(t, k)
		return g_set[t][k]
	end
	dummy.__pairs = function(t, k)
		return next, g_set[t], k
	end
	dummy.__len = function(t)
		return #g_set[t]
	end
	check_table(root)
end
-- 例子
local tb = {1,2,3, {}}
dirty_check(tb)
for k,v in pairs(tb) do
	print(k,v)
end
print("table dirty 1", tb.__dirty)
tb[3]=1
print("table dirty 2", tb.__dirty)
tb.__dirty = nil
tb[3]=2
print("table dirty 3", tb.__dirty)
tb.__dirty = nil
local t={{},{},{},{},{}}
tb[4] = t
print("table dirty 4", tb.__dirty)
tb.__dirty = nil
t[3][1] = 1
print("table dirty 5", tb.__dirty)
tb.__dirty = nil
t[3][1] = 2
print("table dirty 6", tb.__dirty)
tb.__dirty = nil
t[3][2] = {}
print("table dirty 7", tb.__dirty)
执行结果如下:
> dofile("test.lua")
1       1
2       2
3       3
4       table: 0x7c6a20
table dirty 1   nil
table dirty 2   true
table dirty 3   true
table dirty 4   true
table dirty 5   true
table dirty 6   true
table dirty 7   true
文章最后,贴下完整版本,包含了内存泄漏的处理,有兴趣看看,有问题撩我 https://github.com/chenweiqi/lua_util

大佬,现在用的是skynet还是erlang框架
现在使用skynet了
大佬有空方便技术交流下嘛(联系方式,取取经),看了大佬大部分csdn的博客,感觉大佬的理解对于新人很有启发
已加