对Redis变量原子递减到0的操作

为什么80%的码农都做不了架构师?>>>   hot3.png

在使用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 result = tx.exec();

    // If error, return -1;
    if (result == null || result.isEmpty()) {
        return -1;
    }

    // Extract the first result
    for (Object rt : result) {
        return Long.valueOf(rt.toString());
    }

    // The program never comes here.
    return -1;
} 
   

此功能点将会封装在我的Redic框架中。

参考:

原始实现:http://cloudate.net/wp-content/uploads/2015/08/redis-atomic.jpg
Redis CAS: http://my.oschina.net/OutOfMemory/blog/300173

转载于:https://my.oschina.net/chaun/blog/780644

你可能感兴趣的:(数据库,php,java)