说明:本文参考出处 https://www.cnblogs.com/Jason-Xiang/p/5364252.html。
redis事务
redis事务,一组命令的集合,同命令一样是最小执行单位(一个事务中的命令依次执行而不会被其他命令插入)。
如果客户端在发送EXEC命令之前断线了,则服务器会清空事务队列,事务中的所有命令都不会被执行。而一旦客户端发送了EXEC命令之后,事务中的所有命令都会被执行,即使此后客户端断线也没关系,因为服务器已经保存了事务中的所有命令。
相关命令
命令 | 说明 | 返回 |
---|---|---|
multi | 标记事务开始 | OK |
exec | 执行事务中的命令,然后恢复正常连接状态。当存在watch且其值被修改时,不执行 | 返回数组,依次时命令的返回值 当因为watch而不执行时,返回null |
discard | 取消先前事务中的命令病恢复正常连接状态。如果此时存在watch监控,则取消所有监控 | OK |
watch | 监控某一个或多个值,如果在exec之前这些值被修改,则事务不会执行 | OK |
unwatch | 清除监控,调用 exec、discard 时无需调用unwatch | OK |
上图展示了watch的使用情况
错误处理
1.语法错误:语法错误表示命令不存在或者参数错误,这种情况需要区分Redis的版本,Redis 2.6.5之前的版本会忽略错误的命令,执行其他正确的命令,2.6.5之后的版本会忽略这个事务中的所有命令,都不执行。
2.运行错误 运行错误表示命令在执行过程中出现错误,比如用GET命令获取一个散列表类型的键值。这种错误在命令执行之前Redis是无法发现的,所以在事务里这样的命令会被Redis接受并执行。如果食物里有一条命令执行错误,其他命令依旧会执行(包括出错之后的命令)。
Redis中的事务并没有关系型数据库中的事务回滚(rollback)功能,因此使用者必须自己收拾剩下的烂摊子。不过由于Redis不支持事务回滚功能,这也使得Redis的事务简洁快速。
redis实现分布式锁
分布式锁:是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。
Redis命令介绍:
Redis实现分布式锁主要用到命令是SETNX命令(SET if Not eXists)。
语法:SETNX key value
功能:当且仅当 key 不存在,将 key 的值设为 value ,并返回1;若给定的 key 已经存在,则 SETNX 不做任何动作,并返回0。
构建锁:将“lock:”+参数名设置为锁的键,使用SETNX命令尝试将一个随机的uuid设置为锁的值,并为锁设置过期时间,使用SETNX设置锁的值可以防止锁被其他进程获取。如果尝试获取锁的时候失败,那么程序将不断重试,直到成功获取锁或者超过给定是时限为止。
public String acquireLockWithTimeout(
Jedis conn, String lockName, long acquireTimeout, long lockTimeout)
{
String identifier = UUID.randomUUID().toString(); //锁的值
String lockKey = "lock:" + lockName; //锁的键
int lockExpire = (int)(lockTimeout / 1000); //锁的过期时间
long end = System.currentTimeMillis() + acquireTimeout; //尝试获取锁的时限
while (System.currentTimeMillis() < end) { //判断是否超过获取锁的时限
if (conn.setnx(lockKey, identifier) == 1){ //判断设置锁的值是否成功
conn.expire(lockKey, lockExpire); //设置锁的过期时间
return identifier; //返回锁的值
}
if (conn.ttl(lockKey) == -1) { //判断锁是否超时
conn.expire(lockKey, lockExpire);
}
try {
Thread.sleep(1000); //等待1秒后重新尝试设置锁的值
}catch(InterruptedException ie){
Thread.currentThread().interrupt();
}
}
// 获取锁失败时返回null
return null;
}
锁的释放:使用WATCH命令监视代表锁的键,然后检查键的值是否和加锁时设置的值相同,并在确认值没有变化后删除该键。
public boolean releaseLock(Jedis conn, String lockName, String identifier) {
String lockKey = "lock:" + lockName; //锁的键
while (true){
conn.watch(lockKey); //监视锁的键
if (identifier.equals(conn.get(lockKey))){ //判断锁的值是否和加锁时设置的一致,即检查进程是否仍然持有锁
Transaction trans = conn.multi();
trans.del(lockKey); //在Redis事务中释放锁
List<Object> results = trans.exec();
if (results == null){
continue; //事务执行失败后重试(监视的键被修改导致事务失败,重新监视并释放锁)
}
return true;
}
conn.unwatch(); //解除监视
break;
}
return false;
}