基于redis的分布式锁

分布式锁需要考虑的内容

1. 锁的获取与释放消耗低;

2. 锁的存在判断与获取操作保持原子性;

3. 程序崩溃时锁的释放;

redis相关特性

1. 关于消耗

下面是官方的bench-mark数据:

测试完成了50个并发执行100000个请求。

设置和获取的值是一个256字节字符串。

Linux box是运行Linux 2.6,这是X3320 Xeon 2.5 ghz。

文本执行使用loopback接口(127.0.0.1)。

结果:读的速度是110000次/s,写的速度是81000次/s 。

redis在单次读写的效率和系统消耗上面的数据值得肯定。


2. 原子性

redis单个操作都是原子的,但是通过exists和get两种中操作就不一定能保证了,目前有两种方式:

  • watch

WATCH key [key ...]

监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。

  • 可用版本:

  • >= 2.2.0

  • 时间复杂度:

  • O(1)。

  • 返回值:

  • 总是返回 OK 。

  • setnx

SETNX key value

将 key 的值设为 value ,当且仅当 key 不存在。

若给定的 key 已经存在,则 SETNX 不做任何动作。

SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。

  • 可用版本:

  • >= 1.0.0

  • 时间复杂度:

  • O(1)

  • 返回值:

  • 设置成功,返回 1 。

    设置失败,返回 0 。

从代码编写成本上看,setnx更为方便,watch需要更多的代码完成原子操作。


3. 锁的释放

通过redis的expire设置key的时间,从而避免因为程序崩溃导致的无法获取锁的问题。

EXPIRE key seconds

为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。


4. 具体的代码实现

锁的接口声明及redis实现

package com.stg.lock;

/**
 * @author stg
 * 分布式锁接口
 */
public interface DistributeLock {

	public boolean lock();
	
	public boolean unlock();
}


package com.stg.lock.redis;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import redis.clients.jedis.Jedis;

import com.stg.lock.DistributeLock;
import com.stg.utils.RedisUtils;

public class RedisDistributeLock implements DistributeLock{

	private static final Logger logger = LoggerFactory.getLogger(RedisDistributeLock.class);
	
	/**
	 * 锁的key标识,根据业务逻辑调整
	 */
	private final String key;
	
	/**
	 * DistributeLock非单例,Jedis切勿声明为static
	 */
	private Jedis jedis;
	
	/**
	 * 超时时间,无限等待锁会造成系统崩溃
	 */
	private long timeout = 2 * 1000L;
	
	public RedisDistributeLock(String key){
		this.key = key;
		jedis = RedisUtils.getJedis();
	}
	
	@Override
	public boolean lock() {
		
		long tryLocktime = System.currentTimeMillis();
		while(System.currentTimeMillis() - tryLocktime < timeout) {
			
			// value可随意设置,存储时间类信息便于问题发生后的时间定位
			String value = String.valueOf(System.currentTimeMillis());
			
			// 尝试写入key(上锁)
			long result = jedis.setnx(key, value);
			if(result == 1) {
				// 获取成功,设置有效时间,单是秒
				jedis.expire(key, 30);
				logger.info("{} begin", Thread.currentThread().getName());
				return true;
			}
			
			// 等待一段时间后尝试重获取key
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				logger.error(e.getMessage(), e);
			}
			
		}
		return false;
	}

	@Override
	public boolean unlock() {
		long result = jedis.del(key);
		if(result != 1){
			logger.warn("unlock fail ,key -> " + key);
		}
		logger.info("{} end", Thread.currentThread().getName());
		return result == 1;
	}

}


工厂类

package com.stg.lock;

/**
 * @author stg
 * @param <T> 标识锁的key
 * 
 * 工厂类,用于创建锁对象
 */
public interface DistributeLockFactory<T> {

	public DistributeLock getLockBean(T key);
}
package com.stg.lock.redis;

import com.stg.lock.DistributeLock;
import com.stg.lock.DistributeLockFactory;

public class RedisDistributeLockFactory implements DistributeLockFactory<String> {

	@Override
	public synchronized DistributeLock getLockBean(String key) {
		DistributeLock lockBean = new RedisDistributeLock(key);
		return lockBean;
	}

}


其他工具类

package com.stg.utils;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class RedisUtils {
	
	private static JedisPool pool;
	
	static {
		JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxActive(1000);  
        config.setMaxIdle(100);  
        config.setMaxWait(100L);  
        config.setTestOnBorrow(true);  
        try{    
            pool = new JedisPool(config, "127.0.0.1", 6379, 3000);  
        } catch(Exception e) {  
            e.printStackTrace();  
        }  
	}

	public static Jedis getJedis(){
		Jedis jedis = pool.getResource();
		return jedis;
	}
	
}


测试类

package com.stg;

import java.io.Serializable;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.stg.lock.DistributeLock;
import com.stg.lock.DistributeLockFactory;
import com.stg.lock.redis.RedisDistributeLockFactory;

public class Main {

	private static final Logger logger = LoggerFactory.getLogger(Main.class);
	
	private static DistributeLockFactory<String> factory;

	public static void main(String[] args) {
		lockOperation();
		while(true) {
			try {
				Thread.sleep(30*1000L);
			} catch (InterruptedException e) {
				logger.error(e.getMessage(), e);
			}
		}
	}
	
	public static void lockOperation() {
		factory = new RedisDistributeLockFactory();
		int threadNum = 10;
		Executor executor = Executors.newFixedThreadPool(threadNum);
		for(int i = 0; i< threadNum; i++) {
			executor.execute(new Runnable(){

				@Override
				public void run() {
					User user = new User(10001, "zhangsan", 25);

					DistributeLock lockBean = factory.getLockBean(String.valueOf(user.getId()));

					try {
						if (lockBean.lock()) {
							// 业务逻辑处理
							try {
								/*
								 * ***注意***
								 * thread * sleeptime < 获取锁的超市时间
								 * 也就是说业务逻辑决定了锁超时时间的设定
								 */
								Thread.sleep(100L);
							} catch (InterruptedException e) {
								logger.error(e.getMessage(), e);
							}

						} else {

						}
					} finally {
						lockBean.unlock();
					}
				}
				
			});
		}
	}
	
}


class User implements Serializable {

	private static final long serialVersionUID = -5937959261213855736L;

	public User() {
	}

	public User(long id, String name, int age) {
		this.id = id;
		this.name = name;
		this.age = age;
	}

	private long id;

	private String name;

	private int age;

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
}


输出结果:

pool-1-thread-4 begin

pool-1-thread-4 end

pool-1-thread-5 begin

pool-1-thread-5 end

pool-1-thread-1 begin

pool-1-thread-1 end

pool-1-thread-6 begin

pool-1-thread-6 end

pool-1-thread-7 begin

pool-1-thread-7 end

pool-1-thread-2 begin

pool-1-thread-2 end

pool-1-thread-3 begin

pool-1-thread-3 end

pool-1-thread-10 begin

pool-1-thread-10 end

pool-1-thread-9 begin

pool-1-thread-9 end

pool-1-thread-8 begin

pool-1-thread-8 end

你可能感兴趣的:(基于redis的分布式锁)