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

在使用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<Object> 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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值