Redis和Java在多线程应用程序中的帮助!
我们有一个应用程序,当前有线程(约50个线程)来处理事务。Redis和Java在多线程应用程序中的帮助!
我们建立了一个redis数据库,并使用DECRBY
从用户帐户中扣除信用额度。
这里是这个过程的一个例子:
1. Get amount of credits for this transaction
2. Get current credit amount from from Redis: GET <key>
3. If amount of credits exceeds amount cost of transaction continue
4. DECRBY the transaction amount from Redis.
我这里的问题是显而易见的,当用户积分达到0时,它失败的交易(好),但是它允许约10-20通过线程进行交易。
我曾想过用Redis设置WATCH, MULTI, EXEC
,然后重试,但这不会造成瓶颈(我认为它叫做竞争条件),因为线程会不断争取完成交易。
有什么建议吗?
这不是最常规的做法IMO(最常见的方式可能是在RDBMS中使用锁),但使用WATCH, MULTI, EXEC
看起来类似于CAS,它对我来说似乎不太奇怪。
我假设Redis的作者打算用WATCH
这样使用。表现的含义显然取决于如何实现这个东西(我不知道),但我敢打赌,它会表现的很好。
这是因为在您的情况下,对于相同密钥的几乎没有争用的可能性会很小(用户为他/她自己发出交易的机会有多大?),成功率第一次交换操作将会非常好。所以重试只会发生在非常罕见的情况下。由于Redis似乎是一个可信的框架,他们也可能知道他们在做什么(即较少的争用=对Redis来说很容易,因此它可以处理它!)。
锁定是你需要的。由于数据库锁定开销很大,因此可以使用SETNX在Redis中实现简单的锁定方案,并避免竞争条件。这里很好解释 - http://redis.io/commands/setnx。但是您仍然需要在应用程序级别执行重试。
您可以尝试使用由Redisson框架提供的基于Redis的Lock对象实现,而不是使用WATCH-MULTI命令重试。使用WATCH-MULTI会在每次尝试时向Redis提出不必要的请求,这些请求比已获取的锁更慢。
下面是代码示例:
Lock lock = redisson.getLock("transationLock");
lock.lock();
try {
... // instructions
} finally {
lock.unlock();
}
你希望我们确认添加事务/同步到您的代码(这是绝对需要的正确性,你似乎就知道了),很可能会引进竞争和因此可能会损害性能?是的,它会。问题的关键是什么? – chetan 2011-02-28 05:26:22
我想知道的是,如果我使用WATCH,MULTI,EXEC和retries,这是做事的最佳方式?此外,由于使用这种方法,确认性能受到伤害。 – James 2011-02-28 05:39:34