分享

缓存对事务的支持

 xujin3 2018-06-17



本文节选自即将出版的《可伸缩服务架构:框架与中间件》一书,作者:李艳鹏、杨彪、李海亮、贾博岩、刘淏。


- 点击文末原文链接可以直达《可伸缩服务架构:框架与中间件》书籍主页。


在使用Redis缓存的业务场景时经常会有这样的需求:要求递减一个变量,如果递减后变量小于等于0,则返回一个标志;如果成功,则返回剩余的值,类似于数据库事务的实现。


在实现中需要注意服务器端的多线程问题及客户端的多线程问题。在服务器端可以利用服务器单线程执行LUA脚本来保证,或者通过WATCHEXECDISCARDEXEC来保证。


Redis中支持LUA脚本,由于Redis使用单线程实现,因此我们首先给出LUA脚本的实现方案。在如下代码中,我们看到变量被递减,并判断是否将小于0的操作放到LUA脚本里,利用Redis的单线程执行的特性完成这个原子递减的操作:

/**
* 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;
}

还可以通过Redis对事务的支持方法watchmulti来实现,类似于一个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 transaction 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;
}

END


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多