为什么80%的码农都做不了架构师?>>>
在使用Redis缓存的业务场景的时候, 经常会有这样的需求, 需求要求递减一个变量, 如果递减后变量小于等于0, 然后返回一个标志, 如果成功, 则返回剩余值。
实现中需要注意服务器端的多线程问题以及客户端的多线程问题。服务器端可以利用服务器单线程执行LUA脚本来保证,或者通过WATCH, EXEC, DISCARD, EXEC来保证。客户端我们我们保证一个jedis客户端同时之分配给一个线程,可以利用对象池来保证。
下面直接上代码:
LUA实现
/**
* Implemented by LUA. Minus a key by a value, then return the left value.
* If the left value is less than 0, return -1; if error, return -1.
*
* @param key
* the key of the redis variable.
* @param value
* the value to minus off.
* @return the value left after minus. If it is less than 0, return -1; if
* error, return -1.
*/
public long decrByUntil0Lua(String key, long value) {
// If any error, return -1.
if (value <= 0)
return -1;
// The logic is implemented in LUA script which is run in server thread,
// which is single thread in one server.
String script = " local leftvalue = redis.call('get', KEYS[1]); "
+ " if ARGV[1] - leftvalue > 0 then return nil; else "
+ " return redis.call('decrby', KEYS[1], ARGV[1]); end; ";
Long leftValue = (Long) jedis.eval(script, 1, key, "" + value);
// If the left value is less than 0, return -1.
if (leftValue == null)
return -1;
return leftValue;
}
CAS实现
/**
* Implemented by CAS. Minus a key by a value, then return the left value.
* If the left value is less than 0, return -1; if error, return -1.
*
* No synchronization, because redis client is not shared among multiple
* threads.
*
* @param key
* the key of the redis variable.
* @param value
* the value to minus off.
* @return the value left after minus. If it is less than 0, return -1; if
* error, return -1.
*/
public long decrByUntil0Cas(String key, long value) {
// If any error, return -1.
if (value <= 0)
return -1;
// Start the CAS operations.
jedis.watch(key);
// Start the transation.
Transaction tx = jedis.multi();
// Decide if the left value is less than 0, if no, terminate the
// transation, return -1;
String curr = tx.get(key).get();
if (Long.valueOf(curr) - value < 0) {
tx.discard();
return -1;
}
// Minus the key by the value
tx.decrBy(key, value);
// Execute the transation and then handle the result
List
此功能点将会封装在我的Redic框架中。
参考:
原始实现:http://cloudate.net/wp-content/uploads/2015/08/redis-atomic.jpg
Redis CAS: http://my.oschina.net/OutOfMemory/blog/300173