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());
    }
}

连接成功,输出:

image-20201017155837080

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、配置阿里云安全组出入配置。

image-20201017171427739

2、配置开放防火墙6379端口

3、修改Redis的配置文件:

​ 注释掉文件中 bind 127.0.0.1

​ 设置protect-mode yes 为 no

4、关闭redis服务并重启即可成功。

Q.E.D.