我有一个可以将用户余额更新一定量的功能:
export function incrementUsersOpenOrderBalance(order, amount, multi) {
multi.hincrby('order_sums', `${order.GetUserId()}-${order.coin}`, amount)
}
在99%的时间内都能正常运行。它在一个redlock中:
try {
lock = await rateLock.lock(`locks:balance:${userId}`, 60000)
let userBalance = await balancestore.getBalanceForUserId(userId)
do {
let multi = redis.multi();
executedTrades = await placeOrder(orderProcessing, userBalance, multi)
await completeTradesInTransaction(executedTrades, execMulti)
} while (1);
} catch(e) {
...
} finally {
if (lock) {
lock.unlock()
lock = null
}
}
(execMulti
)正在呼叫await multi.execAsync()
内部有很多逻辑,但是每个呼叫仅更新一次用户平衡。由于某些原因,它有时不会HINCRBY -XXXXXXX
并减少正确的数量。我注意到趋势每发生一次,它实际上就会运行此HINCRBY -XXXXXXX
命令,然后进行下一个订单处理,然后实际上会将用户余额设置为-XXXXXXX
。我猜想它发生在HINCRBY -XXXXXXX
调用期间,实际上它覆盖了值而不是更新它。
我已经跑过redis-cli MONITOR
了几次,奇怪的是我觉得一切都很好。
1543351721.451263 [0 lua] "set" "locks:balance:userid" "random_value" "NX" "PX" "60000"
1543351721.473371 [0 10.56.5.109:47958] "multi"
1543351721.473533 [0 10.56.5.109:47958] "hincrby" "order_sums" "userid" "-2483395200000"
1543351721.473598 [0 10.56.5.109:47958] "hincrby" "sums" "order-sum" "-3520000"
1543351721.473635 [0 10.56.5.109:47958] "exec"
1543351721.483502 [0 lua] "get" "locks:balance:userid"
1543351721.483543 [0 lua] "del" "locks:balance:userid"
1543351721.485265 [0 lua] "set" "locks:balance:userid" "new_random_value" "NX" "PX" "60000"
1543351721.501101 [0 10.56.5.109:47958] "multi"
1543351721.501331 [0 10.56.5.109:47958] "hincrby" "sums" "order_sum" "-3910000"
1543351721.501376 [0 10.56.5.109:47958] "hincrby" "order_sums" "userid" "-2758544100000"
1543351721.501507 [0 10.56.5.109:47958] "exec"
1543351721.503079 [0 lua] "get" "locks:balance:userid"
1543351721.503104 [0 lua] "del" "locks:balance:userid"
如您所见,它每次都会锁定,然后以MULTI
开始事务,执行hincrby,然后是EXEC
,然后释放锁定。
此后,我将其发布余额,并将userid
余额设置为-2483395200000
。我已经多次注意到这种行为。
基本上HINCRBY
很少覆盖数据而不是更新数据。