Redis分布式锁 实现秒杀系统 SET命令实现

本文深入解析Redis实现分布式锁的方法,包括锁的获取与释放过程,使用Lua脚本保证原子性,以及通过设置过期时间防止死锁。适用于高并发场景下的资源控制。

基于Redis命令:SET key valueNX EX max-lock-time  

命令教程链接:http://www.redis.cn/commands/set.html

可适用于redis单机和redis集群模式

1.SET命令是原子性操作,NX指令保证只要当key不存在时才会设置value

2.设置的value要有唯一性,来确保锁不会被误删(value=系统时间戳+UUID)

3.当上述命令执行返回OK时,客户端获取锁成功,否则失败

3.客户端可以通过redis释放脚本来释放锁

4.如果锁到达了最大生存时间将会自动释放

 

只有当前key的value和传入的value相同才会执行DEL命令

import java.util.Collections;
import java.util.UUID;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.exceptions.JedisException;
/**
 * 
 * @author rencai.tang
 *
 */
public class DistributedLock {
	private static final String LOCK_SUCCESS = "OK";
    private static final Long RELEASE_SUCCESS = 1L;
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";
    
    private final JedisPool jedisPool;

    public DistributedLock(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    /**
     * 加锁
     * @param locaName  锁的key
     * @param acquireTimeout  获取超时时间
     * @param timeout   锁的超时时间
     * @return 锁标识
     */
    public String lockWithTimeout(String locaName,
                                  long acquireTimeout, long timeout) {
        Jedis jedis = null;
        String retIdentifier = null;
        try {
            // 获取连接
        	jedis = jedisPool.getResource();
            // 随机生成一个value
            String identifier = UUID.randomUUID().toString();
            // 锁名,即key值
            String lockKey = "lock:" + locaName;
            // 超时时间,上锁后超过此时间则自动释放锁
            int lockExpire = (int)(timeout / 1000);

            // 获取锁的超时时间,超过这个时间则放弃获取锁
            long end = System.currentTimeMillis() + acquireTimeout;
            while (System.currentTimeMillis() < end) {//重试机制
            	/*第一个为key,我们使用key来当锁,因为key是唯一的。
            	第二个为value,我们传的是requestId,很多童鞋可能不明白,有key作为锁不就够了吗,为什么还要用到value?原因就是我们在上面讲到可靠性时,分布式锁要满足加锁和解锁必须是同一个客户端,通过给value赋值为requestId,我们就知道这把锁是哪个请求加的了,在解锁的时候就可以有依据。requestId可以使用UUID.randomUUID().toString()方法生成。
            	第三个为nxxx,这个参数我们填的是NX,意思是SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作;
            	第四个为expx,这个参数我们传的是PX,意思是我们要给这个key加一个过期的设置,具体时间由第五个参数决定。
            	第五个为time,与第四个参数相呼应,代表key的过期时间。*/
            	String result = jedis.set(lockKey, identifier, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, lockExpire);
            	if (LOCK_SUCCESS.equals(result)) {
            		retIdentifier = identifier;
                    return retIdentifier;
                }
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        } catch (JedisException e) {
            e.printStackTrace();
        } finally {
            if (jedis != null) {
            	jedis.close();
            }
        }
        return retIdentifier;
    }

    /**
     * 释放锁
     * @param lockName 锁的key
     * @param identifier    释放锁的标识
     * @return
     */
    public boolean releaseLock(String lockName, String identifier) {
        Jedis jedis = null;
        String lockKey = "lock:" + lockName;
        try {
        	jedis = jedisPool.getResource();
        	/*
        	 * 确保释放锁的过程是原子性的,使用lua代码
        	 */
        	 String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
             Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(identifier));
             if (RELEASE_SUCCESS.equals(result)) {
                 return true;
             }
             return false;
        } catch (JedisException e) {
            e.printStackTrace();
        } finally {
            if (jedis != null) {
            	jedis.close();
            }
        }
        return false;
    }
}

在分布式环境中,对资源进行上锁有时候是很重要的,比如抢购某一资源,订单防重,这时候使用分布式锁就可以很好地控制资源。
当然,在具体使用中,还需要考虑很多因素,比如超时时间的选取,获取锁时间的选取对并发量都有很大的影响,上述实现的分布式锁也只是一种简单的实现,主要是一种思想。

下一次我会使用zookeeper实现分布式锁,使用zookeeper的可靠性是要大于使用redis实现的分布式锁的,但是相比而言,redis的性能更好。

上面的代码可以在我的GitHub中进行查看,地址如下:https://github.com/trcflyer/distributedlock

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值