Home  >  Article  >  Database  >  Redis脚本实现分布式锁

Redis脚本实现分布式锁

WBOY
WBOYOriginal
2016-06-07 16:33:41918browse

redis被大量用在分布式的环境中,自然而然分布式环境下的锁如何解决,立马成为一个问题。例如我们当前的手游项目,服务器端是按业

redis被大量用在分布式的环境中,自然而然分布式环境下的锁如何解决,立马成为一个问题。例如我们当前的手游项目,服务器端是按业务模块划分服务器的,有应用服,战斗服等,但是这两个vm都有可能同时改变玩家的属性,这如果在同一个vm下面,就很容易加锁,但如果在分布式环境下就没那么容易了,当然利用redis现有的功能也有解决办法,比如redis的脚本。

redis在2.6以后的版本中增加了Lua脚本的功能,可以通过eval命令,,直接在RedisServer环境中执行Lua脚本,并且可以在Lua脚本中调用Redis命令。
使用脚本的好处:
1.减少网络开销:可以把一些要批量处理的功能,发在一个脚本里面执行,减少客户端和redis的交互次数
2.原子操作:这主要就是我们在这边主要利用的功能,在分布式环境下保证数据的原子性。
3.复用:客户端发送的脚本会永久的存储在redis中,这就意味着其他客户端可以复用这一脚本而不需要使用代码完成同样的逻辑。

 下面先看一段lua脚本:
local food=redis.call('hget',KEYS[1],'food');
food=food+ARGV[1];
redis.call('hset',KEYS[1],'food',food);
local diamond=redis.call('hget',KEYS[1],'diamond');
diamond=diamond+ARGV[2];
redis.call('hset',KEYS[1],'diamond',diamond);
注:redis.call是我们在脚本中调用redis命令,KEYS和ARGV2个数组,分别是键和参数,下标都是从1开始的,不是0。
 这段脚本的功能是取出 KEYS指定的玩家food(粮草)和diamond(玉石),然后就行修改,最后保存在redis中,脚本的执行,保证了整个操作的原子性。

下面我们用java代码来看看具体的实现过程

Jedis jedis = new Jedis("192.168.128.128", 6379);
// 1.初始玩家数据到redis中
GamePlayer player = new GamePlayer();
player.setId(1001);
player.setName("ksfzhaohui");
player.setFood(100);
player.setDiamond(100);
 
Map beanMap = BeanUtil.warp(player);// 将对象转换成map
String beanKey = getRedisBeanKey(player.getClass(), player.getId());
System.out.println("key:" + beanKey);
jedis.hmset(beanKey, beanMap);// 将玩家数据保存到redis中
 
首先模拟了一个玩家将玩家信息保存在redis中,这边的Id随便写了一个,正常的情况下都是通过redis的命令incr生成一个id
结果: 

Redis脚本实现分布式锁


 
String script = "local food=redis.call('hget',KEYS[1],'food');"
                + "food=food+ARGV[1];"
                + "redis.call('hset',KEYS[1],'food',food);"
                + "local diamond=redis.call('hget',KEYS[1],'diamond');"
                + "diamond=diamond+ARGV[2];"
                + "redis.call('hset',KEYS[1],'diamond',diamond);";
List keys = new ArrayList();
keys.add(beanKey);
List args = new ArrayList();
args.add("100");
args.add("100");
// 3.执行脚本
jedis.eval(script, keys, args);

指定键和参考,执行脚本,结果:

BeanUtil代码:

public class BeanUtil {
    private static Logger logger = Logger.getLogger(BeanUtil.class);
    private static final String CLASS = "class";
 
    /**
    * 将指定的对象数据封装成map
    *
    * @param bean
    *            对象数据
    * @return
    */
    @SuppressWarnings("all")
    public static Map warp(Object bean) {
        Map propertyMap = new HashMap();
        try {
            PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass())
                    .getPropertyDescriptors();
            for (PropertyDescriptor propertyDescriptor : ps) {
                String propertyName = propertyDescriptor.getName();
                if (propertyName != null && !propertyName.equals(CLASS)) {
                    Method getter = propertyDescriptor.getReadMethod();
                    if (getter != null) {
                        propertyMap.put(propertyName,
                                String.valueOf(getter.invoke(bean, null)));
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e);
        }
        return propertyMap;
    }
 
}
 
当然网上还有一些其他的方法,如: 用SETNX实现分布式锁
可以参考:

Ubuntu 14.04下Redis安装及简单测试

Redis集群明细文档

Ubuntu 12.10下安装Redis(图文详解)+ Jedis连接Redis

Redis系列-安装部署维护篇

CentOS 6.3安装Redis

Redis安装部署学习笔记

Redis配置文件redis.conf 详解

Redis 的详细介绍:请点这里
Redis 的下载地址:请点这里

本文永久更新链接地址:

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn