51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Lua script attempted to access a non local key in a cluster node

你好,我是猿java。

使用 {#使用}

业务场景是电商库存计算,公司使用的是redis cluster集群,因为涉及到一些简单的库存计算,为了保证原子性,特使用了lua脚本,lua的函数脚本如下:

|---------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | -- type都是java代码中传入的String值,sku为Long型 local function availableRealSaleCal(type,sku) local key = formatKey(type, sku) -- 销售库存 = (if 可售卖量 then 销售库存 = min(可售库存,可售卖量)else 销售库存 = 可售库存 end) local availableRealSale = 0; local availableSale = redis.call('INCRBY', key..":AVAILABLE_SALE", 0); local saleLimit = redis.call('HGET', key, 'sale_limit'); redis.call('SET', stocksKey .. ":AVAILABLE_REAL_SALE", availableRealSale); return availableRealSale end --库存key local function formatKey(type, sku) return "stock:"..type..":"..":{"..sku.."}" end; |

说明 {#说明}

在上面的lua脚本中,有{sku}这样的用法,{}是在redis cluster里面的特有的hash tags,具体使用可以参考redis的官方文档:Redis hash tags文档,
Redis hash slot计算

疑问 {#疑问}

代码上线半年没有出现过问题,怎么突然会出现运营无法增加库存,特查看了一下服务器的错误日志,日志详情如下:

|-----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 | Caused by: redis.clients.jedis.exceptions.JedisDataException: ERR Error running script (call to f_1fbde7f097d74a7d77c854c93b308d36d164dbf9): @user_script:371: @user_script: 371: Lua script attempted to access a non local key in a cluster node at redis.clients.jedis.Protocol.processError(Protocol.java:115) |

看到这个错误,一脸懵,第一次遇到,于是google了一下,找到几个类似的问题,大致意思也差不多,下面给出一个stackover上面的例子,链接如下:stackoverflow相同的错误
lua代码摘取如下:

|---------------|--------------------------------------------------------------------------------------------------| | 1 2 3 | local f3=redis.call('HGET',KEYS[1],'1'); local f4=redis.call('HGET',f3,'1') ; return f4; |

错误解释是在lua中,执行多条语句,要保证key hash的slot是同一个,否则就会出现上面的错误,比如:KEYS[1]和 f3 hash后不在同一个slot就会出现上述错误。

解决思路 {#解决思路}

顺着 google的那个例子,反观我们的lua脚本,整个lua都是根据{sku}来做hash,然后{sku}的结果不稳定,导致几条语句执行的时候hash到不同的slot中? 顺着这个思路,最后发现:线上跑的代码,sku都是传的14位的Long,没有出现问题,现在传入的sku是15位的Long出现问题,是sku的长度导致{sku}结果值不稳定,最后在中间部门同时的配合下,找到了中间件的执行log:stockskey:stock:40-248-000008:{1.112422310001e+14},太奇怪了,sku传入的明明是一个Long的类型,怎么变成{1.112422310001e+14}这个奇怪的内容,
原来在中间件有个cjson的操作,当传入的Long类型位数大于14时,会把Long会转成科学计数法,真实要hash的key变了,导致{sku}计算不稳定, 最后把Long转String问题解决

学习交流 {#学习交流}

赞(7)
未经允许不得转载:工具盒子 » Lua script attempted to access a non local key in a cluster node