redis 详解-第三课

redis事务

redis事务本质:一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺执行
一次性、顺序性、排他性!执行一系列的命令!

Redis 单条命令保证原子性,但是单条事务不保证原子性。没有隔离级别的概念,所有的命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行!exec
redis的事务

  • 开启事务(multi)
  • 命令入队(…)
  • 执行事务(exec)

说明:每一个事务执行完就结束了,如果要执行其他命令要重写开启事务,并执行事务。

正常的事务执行

127.0.0.1:6379> multi		# 开启事务
OK
127.0.0.1:6379(TX)> set key1 value1
QUEUED
127.0.0.1:6379(TX)> set key2 value2
QUEUED
127.0.0.1:6379(TX)> get key1
QUEUED
127.0.0.1:6379(TX)> get key2
QUEUED
127.0.0.1:6379(TX)> exec # 执行事务
1) OK
2) OK
3) "value1"
4) "value2"

放弃事务

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set key1 value1
QUEUED
127.0.0.1:6379(TX)> set key2 value2
QUEUED
127.0.0.1:6379(TX)> set key3 value3
QUEUED
127.0.0.1:6379(TX)> discard			# 取消事务
OK
127.0.0.1:6379> get key3			# 事务队列中命令都不会执行
(nil)

编译性异常(代码有问题!命令有错!),事务中所有的命令都不会执行

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> getset k3
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> exec  # 执行事务报错,多有命令都报错
(error) EXECABORT Transaction discarded because of previous errors

运行时异常(0/1),如果事务队列中存在语法性,那么执行命令的时候,其他命令可以正常执行,错误时抛出异常

127.0.0.1:6379> set k1 "v1"
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> incr k1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> get k3
QUEUED
127.0.0.1:6379(TX)> 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"
127.0.0.1:6379> get k3
"v3"

监控!watch

悲观锁:

  • 认为什么时候都会出问题,无论什么时候都加锁
    乐观锁:
  • 认为什么时候都不会出问题,所以不会上锁,更新数据的时候去判断一下,在此期间,是否有人修改过这个数据
  • 获取version
  • 更新的时候比较version

redis监视测试

# 正常执行成功
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(TX)> decrby money 20
QUEUED
127.0.0.1:6379(TX)> incrby out 20
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 80
2) (integer) 20
# 测试多线程修改值,使用watch可以当做redis的乐观锁操作

#线程一执行
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby money 10
QUEUED
127.0.0.1:6379(TX)> incrby out 10
QUEUED
# 线程二执行
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> set money 1000
OK
# 线程一执行
127.0.0.1:6379(TX)> exec
(nil)

127.0.0.1:6379> unwatch 		# 如果事务执行失败,线取消监视
OK
127.0.0.1:6379> watch money		# 重新监视
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby money 10
QUEUED
127.0.0.1:6379(TX)> incrby out 10
QUEUED
127.0.0.1:6379(TX)> exec	# 比对监视的值是否发生了变化,如果有变化,那么可以执行成功,如果变了就执行失败
1) (integer) 990
2) (integer) 30

Jedis

使用java来操作redis
什么是jedis 是redis官方推荐java连接开发工具!使用java操作redis中间件!如果你要使用java操作redis,那么对jedis要十分的熟悉。

1、导入对应的依赖

<!-- 导入jedis的包 -->
    <dependencies>
        <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>4.0.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.78</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.25</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

2、编码测试:

如果连接报错:请参考:https://blog.csdn.net/weixin_43452467/article/details/108621355
1 切换到root用户,并输入命令:firewall-cmd --query-port=6379/tcp 确认端口是否开放
2 输入命令: firewall-cmd --get-active-zones 拿到zone名称
3 输入命令:firewall-cmd --zone=public --add-port=6379/tcp --permanent,永久开放6379端口
4 输入命令:firewall-cmd --reload ,重启防火墙
5 再次查看端口是否开放了:firewall-cmd --query-port=6379/tcp

  • 连接数据库
  • 操作命令
  • 断开连接
public class TestPing {
    public static void main(String[] args) {
        // 1、new Jedis 对象即可
        Jedis jedis = new Jedis("xxx.xxx.xxx.xxx",6379);
        System.out.println(jedis.ping());
    }
}

输出:
在这里插入图片描述
常用的API:
String
List
Set
Hash
Zset
命令操作如下:

在这里插入代码片

事务

public static void main(String[] args) {
        // 1、new Jedis 对象即可
        Jedis jedis = new Jedis("xxx.xxx.xxx.xxx",6379);
        System.out.println(jedis.ping());
        jedis.flushDB();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("name","zhang");
        jsonObject.put("age",30);
        // 开启事务
        Transaction multi = jedis.multi();
        String result = jsonObject.toJSONString();
		// jedis.watch(result); // 监控,可加入乐观锁
        try {
            multi.set("user1",result);
            multi.set("user2",result);
            int i = 1/0;    // 代码抛出异常执行失败
            multi.exec();   // 执行事务
        } catch (Exception e) {
            multi.discard();    // 放弃事务
            e.printStackTrace();
        } finally {
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user2"));
            jedis.close();  // 关闭连接
        }

SpringBoot整合

SpringBoot操作数据:spring-data jpa jdbc mongodb redis
说明:在springBoot2.x之后,原来使用的jedis被替换为lettuce
jedis:采用的是直连,多个线程操作的话,是不安全的,如果想要避免不安全的,使用jedis pool连接池!类似于BIO,有线程阻塞
lettuce:采用netty,实例可以在多个线程中进行共享,不存在线程不安全的情况!可以减少线程数据了,更像是NIO模式。
源码分析:

@Bean
@ConditionalOnMissingBean(name = {"redisTemplate"}) // 我们可以自己定义一个RedisTemplate来替换这个默认的
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        // 默认的redisTemplate没有过多的设置,redis对象都是需要序列化
        // 两个泛型都是object,object的类型,我们后续使用需要强制转换<String,object>
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean // 由于 string 是redis中最常用的类型,所以说单独提出来一个bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

整合测试
1、导入依赖

	<dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-data-redis</artifactId>
      </dependency>

2、配置连接

# redis 配置
spring.redis.host=8.140.152.241
spring.redis.port=6379

3、测试

package com.zhang;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisTemplate;

@SpringBootTest
class Redis02SpringbootApplicationTests {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void contextLoads() {

        // redisTemplate 操作不同的数据类型,api和我们的指令是一样的
        // opsForValue 操作字符串类似于string
        // opsForSet
        // opsForGeo
        //  opsForHash
        //  opsForList
        // opsForZSet

        // 除了基本的操作,我们常用的方法都可以用redisTemplate操作,比如事务,CRUD
        // 获取redis的连接对象
        // RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
        // connection.flushAll();
        // connection.flushDb();
        redisTemplate.opsForValue().set("name","zhang");
        redisTemplate.opsForValue().set("age",30);
        System.out.println(redisTemplate.opsForValue().get("name"));
        System.out.println(redisTemplate.opsForValue().get("age"));
    }
}

序列化之后就可以传输;
在这里插入图片描述

在这里插入图片描述
设置自己的RedisTemplate

@Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) throws UnknownHostException {
        RedisTemplate<String, Object> template = new RedisTemplate();
        template.setConnectionFactory(factory);
        // json序列化配置
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也同样采用string的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化采用Jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash 的value序列化方式采用Jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值