基于lua-redis实现榜单类服务的数据一致性

最近遇到一个bug,在涨幅榜(涨幅大于0)中出现了涨幅为负数的情况。通过解决这个bug学习到了如何在redis中使用lua来保持数据的一致性。
有兴趣的可以下载app(币世界),其中有很多榜单,这里简化处理流程,以便突出重点。

需求描述(简化)

实现币种的涨跌幅榜,并且涨幅榜中涨幅全是正的,跌幅榜中涨幅全是负数。

榜单存储设计
  • 把币种的涨幅(从+inf到-inf)和币种id放到zset中,并把币种的涨幅作为score。
  • 把币种的基本信息放到redis的hash中。
榜单流程设计
  • 从zset中读取币种id
    • 读取涨幅榜数据 ZREVRANGEBYSCORE top +inf (0 LIMIT 0 10
    • 读取跌幅榜数据 ZRANGEBYSCORE top -inf (0 LIMIT 0 10
  • 从hash中读取所有的币种详情

问题所在

  • 问题发生在当读取zset的时候,如果hash中币种的信息(涨幅)发生改变,那么极有可能出现榜单和涨幅不对应的情况,进一步抽象是需要在多次读取中保持事务。
  • 有兴趣同学可以思考下,为什么没把币种信息直接放到到zset中?

如何解决?

  • 可以使用 Lua 脚本实现原子性操作,因此读取zset和读取hash放到同一个lua中这个问题就解决了。
  • 简化代码
	local res = {}
	local data = nil
	if ARGV[6] == "2"
	then
	    data = redis.call("ZREVRANGEBYSCORE", KEYS[1], ARGV[3], ARGV[2], "LIMIT", ARGV[4], ARGV[1])
	else
	    data = redis.call("ZRANGEBYSCORE", KEYS[1], ARGV[2], ARGV[3], "LIMIT", ARGV[4], ARGV[1])
	end
	if table.getn(data) > 0
	then
	    res[2] = redis.call("HMGET", "coin_top_coin", unpack(data))
	end
	return res

最终部分代码

d := redisFetcher.ExecuteScript(`
	local res = {}
	if ARGV[5] == "0"
	then
    	res[1] = redis.call("ZCARD", KEYS[1])
	end
	local data=nil
	if ARGV[6] == "2"
	then
    	 data = redis.call("ZREVRANGEBYSCORE", KEYS[1], ARGV[3], ARGV[2],"LIMIT", ARGV[4], ARGV[1])
	else
    	 data = redis.call("ZRANGEBYSCORE", KEYS[1], ARGV[2], ARGV[3],"LIMIT",ARGV[4], ARGV[1])
	end
	if (ARGV[5] > "0")
	then
		local temp={}
		local rankDatas = redis.call("ZRANGEBYSCORE", "top_rank",  "-inf", "+inf","LIMIT",0,ARGV[5])
		for k,v in pairs(data) do
			for k1,v1 in pairs(rankDatas) do
				if v == v1 then
					table.insert(temp,v)
				end
			end
		end
		data = temp
		res[1] = table.getn(data)
	end
	if table.getn(data)>0
	then
		res[2] = redis.call("HMGET", "coin_top_coin", unpack(data))
	end
	return res
	`,
[]string{topForm.TopName}, int64(size), topForm.Min, topForm.Max, int(offset), topForm.Scope, topForm.SortType).([]interface{})

总结

  • 可以使用 Lua 脚本实现原子性操作
  • 如果多次请求有结果依赖的时候,可以把请求操作改成lua,放到redis端执行。
发布了33 篇原创文章 · 获赞 21 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/u013356254/article/details/94847697