127.0.0.1:6379> set seq_id 1 // 初始化自增ID为1
OK
127.0.0.1:6379> incr seq_id // 增加1,并返回递增后的数值
(integer) 2
------------------------------------------------------------------------------------------------------------
@Service
public class SequenceFactory {
@Autowired
RedisTemplate<String, String> redisTemplate;
/**
* @param key
* @param value
* @param expireTime
* @Title: set
* @Description: set cache.
*/
public void set(String key, int value, Date expireTime) {
RedisAtomicLong counter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
counter.set(value);
counter.expireAt(expireTime);
}
/**
* @param key
* @param value
* @param timeout
* @param unit
* @Title: set
* @Description: set cache.
*/
public void set(String key, int value, long timeout, TimeUnit unit) {
RedisAtomicLong counter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
counter.set(value);
counter.expire(timeout, unit);
}
------------------------------------------------------------------------------------------------------------
/**
* @param key
* @return
* @Title: generate
* @Description: Atomically increments by one the current value.
*/
public long generate(String key) {
RedisAtomicLong counter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
return counter.incrementAndGet();
}
/**
* @param key
* @return
* @Title: generate
* @Description: Atomically increments by one the current value.
*/
public long generate(String key, Date expireTime) {
RedisAtomicLong counter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
counter.expireAt(expireTime);
return counter.incrementAndGet();
}
/**
* @param key
* @param increment
* @return
* @Title: generate
* @Description: Atomically adds the given value to the current value.
*/
public long generate(String key, int increment) {
RedisAtomicLong counter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
return counter.addAndGet(increment);
}
/**
* @param key
* @param increment
* @param expireTime
* @return
* @Title: generate
* @Description: Atomically adds the given value to the current value.
*/
public long generate(String key, int increment, Date expireTime) {
RedisAtomicLong counter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
counter.expireAt(expireTime);
return counter.addAndGet(increment);
}
}
关于分布式锁,一般有三种选择:
另外几种模式都无法避免两个问题:
所以redis官方针对这种情况提出了红锁(Redlock)的概念:
分布式锁使用场景:多个服务间 + 保证同一时刻内 + 同一用户只能有一个请求(防止关键业务出现数据冲突和并发错误)。
Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(REDIS_LOCK, value, 10L, TimeUnit.SECONDS);

@Autowired
private Redisson redisson;
public static final String REDIS_LOCK = "REDIS_LOCK";
@GetMapping("/doSomething")
public String doSomething(){
RLock redissonLock = redisson.getLock(REDIS_LOCK);
redissonLock.lock();
try {
//doSomething
}finally {
//添加后,更保险
if(redissonLock.isLocked() && redissonLock.isHeldByCurrentThread()) {
redissonLock.unlock();
}
}
}
package com.yubin.case5;
import com.yubin.RedisLock;
import redis.clients.jedis.Jedis;
import java.time.LocalTime;
import static com.yubin.LockConstants.*;
public class LockCase5 extends RedisLock {
public LockCase5(Jedis jedis, String lockKey) {
super(jedis, lockKey);
}
@Override
public void lock() {
while (true) {
String result = jedis.set(lockKey, lockValue, NOT_EXIST, SECONDS, 30);
if (OK.equals(result)) {
System.out.println("线程id:"+Thread.currentThread().getId() + "加锁成功!时间:"+LocalTime.now());
//开启定时刷新过期时间
isOpenExpirationRenewal = true;
scheduleExpirationRenewal();//调用父类中的定时续期方法
break;
}
System.out.println("线程id:"+Thread.currentThread().getId() + "获取锁失败,休眠10秒!时间:"+LocalTime.now());
//休眠10秒
sleepBySencond(10);
}
}
@Override
public void unlock() {
System.out.println("线程id:"+Thread.currentThread().getId() + "解锁!时间:"+LocalTime.now());
String checkAndDelScript = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else " +
"return 0 " +
"end";
jedis.eval(checkAndDelScript, 1, lockKey, lockValue);//执行解锁的lua脚本
isOpenExpirationRenewal = false;//标识位置为false,锁续期的for循环推出;
}
}
package com.yubin;
import redis.clients.jedis.Jedis;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
public abstract class RedisLock implements Lock {
protected Jedis jedis;
protected String lockKey;
protected String lockValue;
protected volatile boolean isOpenExpirationRenewal = true;
public RedisLock(Jedis jedis,String lockKey) {
this(jedis, lockKey, UUID.randomUUID().toString()+Thread.currentThread().getId());
}
public RedisLock(Jedis jedis, String lockKey, String lockValue) {
this.jedis = jedis;
this.lockKey = lockKey;
this.lockValue = lockValue;
}
public void sleepBySencond(int sencond){
try {
Thread.sleep(sencond*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 开启定时刷新
*/
protected void scheduleExpirationRenewal(){
Thread renewalThread = new Thread(new ExpirationRenewal());
renewalThread.start();
}
@Override
public void lockInterruptibly(){}
@Override
public Condition newCondition() {
return null;
}
@Override
public boolean tryLock() {
return false;
}
@Override
public boolean tryLock(long time, TimeUnit unit){
return false;
}
/**
* 刷新key的过期时间,每隔10秒续期30秒
*/
private class ExpirationRenewal implements Runnable{
@Override
public void run() {
//isOpenExpirationRenewal续期标识位用volatile修饰,保证可见性
while (isOpenExpirationRenewal){
System.out.println("执行延迟失效时间中...");
String checkAndExpireScript = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('expire',KEYS[1],ARGV[2]) " +
"else " +
"return 0 end";
jedis.eval(checkAndExpireScript, 1, lockKey, lockValue, "30");//执行续期的lua脚本
//休眠10秒(保证每10秒钟续期30秒)
sleepBySencond(10);
}
}
}
}
synchronized单机版oK,上分布式
nginx分布式微服务单机锁不行
取消单机锁,上Redis分布式锁setnx
只加了锁,没有释放锁,出异常的话,可能无法释放锁,必须要在代码层面finally释放锁
宕机了,部署了微服务代码层面根本没有走到finally这块,没办法保证解锁,这个key没有被删除,需要有lockKey的过期时间设定
为redis的分布式锁key,增加过期时间,此外,还必须要setnx+过期时间必须同一行
必须规定只能自己删除自己的锁,你不能把别人的锁删除了,防止张冠李戴,1删2,2删3
Redis集群环境下,我们自己写的也不oK,因为不能保证锁续期问题。直接上RedLock之Redisson落地实现