文章目录
Redis入门第三天:主要介绍了如何用idea连接redis、Redis事务以及Redis分布式锁的相关内容。
Redis
系列文章
IDEA连接Redis
安装
- 首先先要下载驱动包,确保下载合适的驱动包下载jar包
- 在classpath中包含此驱动包
测试连接
import redis.clients.jedis.Jedis;
public class jedisUtils {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost");
System.out.println("连接成功");
System.out.println("服务正在运行"+jedis.ping());
}
}
结果:
Redis 事务
事务介绍
- Redis事务是通过MULTI,EXEC,DISCARD和WATCH这四个命令来完成的
- Redis的单个命令都是原子性的,所以这里确保事务性的对象是命令集合
- Redis将命令集合序列化并确保处于 事务的命令集合连续且不被打断的执行
- Redis不支持回滚的操作
相关命令
MULTI
- 用于标记事务块的开始
- Redis会将后续的命令逐个放入队列中,然后使用EXEC命令原子化的执行这个命令序列
EXEC
- 在一个事务中执行所有先前放入队列的命令,然后恢复正常的连接状态
DISCARD
- 清除所有先前在一个事务中放入队列的命令,然后恢复正常连接状态
WATCH
- 当某个事务需要按条件执行时,就需要这个命令将给定的键设置为受监控的状态
- 语法:
- WATCH key [key …]
- 可以实现Redis的乐观锁
UNWARCH
- 清楚所有先前为一个事务监控的锁
Demo
- 标记开始与执行
-
监控
在Redis中使用watch可以决定事务时执行还是回滚,
在MULTI 命令前使用watch监控某些键值对,然后使用multi命令开启事务,执行各类对数据结构进行操作的命令,这些命令就会进入队列
当Redis使用EXEC命令执行事务的时候,首先会去对比被watch命令所监控的键值对
发生变化就不会执行任何事务中的命令,而是去回滚事务
不发生变化就执行事务队列中的命令、提交事务
无论事务是否回滚,Redis都会去取消执行事务前的watch命令
Redis为什么不支持回滚
- 大多数事务失败都是语法错误或者类型错误,都是可以在开发阶段进行避免的
- Redis为了性能方面,忽略了事务回滚
Redis实现分布式锁
锁的处理
- 单应用中使用锁:单线程多线程
- synchronize lock
- 分布式应用中使用锁:多线程
分布式锁的实现方式
- 数据库的乐观锁
- 给予zookeeper的分布式锁
- 给予redis的分布式锁
分布式锁的注意事项
- 互斥性:在任意时刻,只有一个客户端能持有锁
- 同一性:加锁和解锁必须是同一个客户端,客户端子集不能把别人加的锁给解了
- 避免死锁:即使只有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁
实现分布式锁
获取锁
-
方式一:使用set命令
- SET KEY VALUE [EX seconds] [PX milliseconds] [NX|XX]
- EX seconds:设置指定的到期时间 秒
- PX milliseconds:设置指定的到期时间 毫秒
- NX:仅在键不存在时设置键
- XX:只有在键已存在时才设置
- SET KEY VALUE [EX seconds] [PX milliseconds] [NX|XX]
-
方式二:使用setnx命令:先拿setnx来抢锁,抢到后再用expire给锁加一个过期时间防止锁忘记释放
package com.cyb.redis.utils; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; public class jedisUtils { private static String ip = "192.168.31.200"; private static int port = 6379; private static JedisPool pool; static { pool = new JedisPool(ip, port); } public static Jedis getJedis() { return pool.getResource(); } public static boolean getLock(String lockKey, String requestId, int timeout) { //获取jedis对象,负责和远程redis服务器进行连接 Jedis je=getJedis(); //参数3:NX和XX //参数4:EX和PX String result = je.set(lockKey, requestId, "NX", "EX", timeout); if (result=="ok") { return true; } return false; } public static synchronized boolean getLock2(String lockKey, String requestId, int timeout) { //获取jedis对象,负责和远程redis服务器进行连接 Jedis je=getJedis(); //参数3:NX和XX //参数4:EX和PX Long result = je.setnx(lockKey, requestId); if (result==1) { je.expire(lockKey, timeout); //设置有效期 return true; } return false; } }
释放锁
package com.cyb.redis.utils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class jedisUtils {
private static String ip = "192.168.31.200";
private static int port = 6379;
private static JedisPool pool;
static {
pool = new JedisPool(ip, port);
}
public static Jedis getJedis() {
return pool.getResource();
}
/**
* 释放分布式锁
* @param lockKey
* @param requestId
*/
public static void releaseLock(String lockKey, String requestId) {
Jedis je=getJedis();
if (requestId.equals(je.get(lockKey))) {
je.del(lockKey);
}
}
}