利用redis实现分布式锁

分布式锁通常有三种实现方式:

1. 数据库乐观锁;redis

2. 基于ZooKeeper的分布式锁;数据库

3. 基于Redis的分布式锁;缓存

这里大概说一下三种方式的优缺点,数据库乐观锁优势是实现简单,只须要for update关键词就能够实现,缺点是没法知足高并发量以及数据库读写频繁的系统tomcat

ZooKeeper分布式锁不管是从性能以及实现的功能来讲都是很是优秀,只是在开发起来须要必定的基础,对新手可能不是很友好并发

而本文主要讲第三种利用redis实现分布式锁,优势是开发相对简单,能知足必定并发量的系统,缺点是存在线程争抢锁的问题,当并发量到达必定级别,多个线程去争抢同一个锁,对性能的影响较大分布式


事务以及原子性

虽然Redis是单线程运行,可是在分布式的状况下对同一资源进行操做仍是会出现问题,下图是一个简单的例子高并发

因此必定要保证tomcat1以及tomcat2读写的原子性,既读与写要么都执行,要么都不执行。关于事务的原子性能够查询这里性能

那么如何保证呢,redis在2.6中加入了lua脚本功能能够轻松的解决这个问题,下面是一个简单的例子实现了上述的加100操做ui

Jedis jedis = jedisPool.getResource();
String script = "local a = redis.call('get', KEYS[0])   a = a + 100   redis.call('set', a)";

jedis.eval(script, 1, rname+"Lock",RedisCacheFactory.FactoryUUID,"1000");

分布式锁的具体实现

大概讲一下思路:首先加锁的方式是向redis里存入一个KEY-VALUE,KEY存入的加锁对象能够是方法、类、数据等等,VALUE存入持有锁的节点(例如tomcat1)lua

大概整理了一下几个问题:

Q:为何VALUE存入持有锁的节点

A:为的是防止A加的锁被B给解除,保证只有持有锁的节点才能解锁

Q:怎么存入持有锁的节点

A:这里只是个人思路是在tomcat启的时候生成一个uuid做为该tomcat的token存入到VALUE中

 

Q:怎么防止死锁

A:利用Redis设置键的过时时间

下面贴出部分代码,仅供参考

加锁

JedisPool jedisPool = new JedisPool(new JedisPoolConfig(),RedisInstance.hostName,Integer.parseInt(RedisInstance.port),5000,password);
Jedis jedis = jedisPool.getResource();
// key1 : key值  argv1 :value值     argv2  :过时时间 
String script = "if redis.call('EXISTS',KEYS[1]) ==0 then redis.call('set',KEYS[1],ARGV[1]) redis.call('EXPIRE',KEYS[1],ARGV[2]) return 1  else return 0 end";
long result =  (long) jedis.eval(script, 1, rname+"Lock",RedisCacheFactory.FactoryUUID,"1000");
jedis.close();
jedisPool.close();

解锁

JedisPool jedisPool = new JedisPool(new JedisPoolConfig(),RedisInstance.hostName,Integer.parseInt(RedisInstance.port),5000,password);
Jedis jedis = jedisPool.getResource();
String script = "if redis.call('EXISTS',KEYS[1]) ==1 and redis.call('GET',KEYS[1])==ARGV[1]  then return redis.call('del',KEYS[1]) else return 0 end";
long result =  (long) jedis.eval(script, 1, rname+"Lock",RedisCacheFactory.FactoryUUID);
jedis.close();
jedisPool.close();

 以上仅我的意见,若有错误的地方,还请各位海涵。

后面可能会整合此次缓存改造的全部环节发出来给你们参考 一下

补充说明一下:lua操做redis时若是操做多个key不在同一节点下会出错,缘由是由于Cluster会将数据自动分布到不一样的节点(虚拟的16384个slot,具体看这里)。

解决办法 后面会贴出详细教程

相关文章
相关标签/搜索