Redis的事务以及Jedis操作Redis
一、事务
Redis事务本质:一组命令一块执行。一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行!
一次性、顺序性、排他性执行一些列的命令。
Redis事务没有隔离级别的概念!
所有的命令在事务中,并没有直接被执行,只有发起执行命令的时候才会执行。Exce
Redis单条命令是保证原子性的,但是事务是不保证原子性的!
Redis的事务:
- 开启事务(multi)
- 命令入队(……)
- 执行事务(exec)
1、正常执行事务!
127.0.0.1:6379> multi # 开启事务
OK
# 命令入队
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> EXEC # 执行事务
1) OK
2) OK
3) "v2"
4) OK
2、放弃事务
127.0.0.1:6379> MULTI # 开启事务
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> DISCARD # 取消事务
OK
127.0.0.1:6379> get k4 # 事务队列中的命令都不会被执行
(nil)
3、编译型异常(代码有问题,命令有错!),事务中所有的命令都不会被执行!
127.0.0.1:6379> MULTI # 开启事务
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> getset k3 # 错误的命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> set k5 v5
QUEUED
127.0.0.1:6379> EXEC # 执行事务报错
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k5 # 所有的命令都不会被执行
(nil)
4、运行时异常(1/0),如果队列中存在语法性错误,那么执行命令的时候,其他命令是可以正常执行的!错误命令抛出异常
127.0.0.1:6379> set k1 "v1"
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> INCR k1 # 存在语法性错误
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> EXEC
1) (error) ERR value is not an integer or out of range
2) OK
3) OK
4) "v3"
127.0.0.1:6379> get k2
"v2"
5、监控 Watch,面试常问
悲观锁:
- 很悲观,什么时候都会出问题,无论做什么都会加锁!
乐观锁:
- 很乐观,认为什么时候都不会出现问题,所以不会上锁!更新数据的时候判断一下在此期间是否有人修改过这个数据。
- 获取version
- 更新的时候比较version
测试:
正常执行完毕
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> WATCH money # 监视 Money 对象
OK
127.0.0.1:6379> MULTI # 事务正常结束,数据期间没有发生变动,这个时候就正常执行成功!
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY out 20
QUEUED
127.0.0.1:6379> EXEC
1) (integer) 80
2) (integer) 20
测试多线程修改值,使用watch充当乐观锁操作
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECRBY money 10
QUEUED
127.0.0.1:6379> INCRBY out 10
QUEUED
127.0.0.1:6379> EXEC # 执行之前,另外一个线程修改了我们的值,就会导致执行失败,如果没有变化则执行成功
(nil)
127.0.0.1:6379> unwatch # 如果发现事务执行失败,就先解锁
OK
127.0.0.1:6379> watch money # 然后重新加锁,再次监视
OK
二、使用Jedis来操作Redis
什么是Jedis?是Redis官方推荐的java连接开发工具!使用java操作redis的中间件!
1、导入对应的依赖
<dependencies>
<!--导入jedis的包-->
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
</dependencies>
2、编码测试
-
连接数据库
-
操作命令
-
断开连接
先要打开本地的Redis服务,并保持开启。
package cn.nongyanxia;
import redis.clients.jedis.Jedis;
public class TestPing {
public static void main(String[] args) {
// 1、new一个jedis对象
Jedis jedis = new Jedis("127.0.0.1" ,6379);
// jedis所有的命令就是我们之前学习的所有指令!
System.out.println(jedis.ping());
}
}
连接成功,输出:
3、常用的API
String
List
Set
Hash
Zset
package cn.nongyanxia;
import redis.clients.jedis.Jedis;
import java.util.Set;
public class TestKey {
public static void main(String[] args) {
Jedis jedis = new Jedis("47.112.230.16",6379);
System.out.println("清空数据:"+jedis.flushDB());
System.out.println("判断某个键是否存在:"+jedis.exists("username"));
System.out.println("新增键值对:"+jedis.set("username", "nongyanxia"));
System.out.println("新增键值对:"+jedis.set("password", "123456"));
System.out.println("系统中所有的键如下:");
Set<String> keys = jedis.keys("*");
System.out.println(keys);
System.out.println("删除键password:"+jedis.del("password"));
System.out.println("判断键password是否存在:"+jedis.exists("password"));
System.out.println("查看键username存储值的类型:"+jedis.type("username"));
System.out.println("随机返回key空间的一个:"+jedis.randomKey());
System.out.println("重命名key:"+jedis.rename("username", "name"));
System.out.println("取出改后的name:"+jedis.get("name"));
System.out.println("切换数据库:"+jedis.select(0));
System.out.println("删除当前选择数据库中的所有key:"+jedis.flushDB());
System.out.println("返回当前数据库中key的数目:"+jedis.dbSize());
System.out.println("删除所有数据库中的所有key:"+jedis.flushAll());
}
}
其余类型的代码就不一一写出了,和linux中的命令没有任何区别。
三、远程连接Redis
package cn.nongyanxia;
import redis.clients.jedis.Jedis;
public class TestPing {
public static void main(String[] args) {
// 1、new一个jedis对象
Jedis jedis = new Jedis("47.112.230.16" ,6379);
// jedis所有的命令就是我们之前学习的所有指令!
System.out.println(jedis.ping());
}
}
1、配置阿里云安全组出入配置。
2、配置开放防火墙6379端口
3、修改Redis的配置文件:
注释掉文件中 bind 127.0.0.1
设置protect-mode yes 为 no
4、关闭redis服务并重启即可成功。
Q.E.D.