Linux(redis服务2(事务、Jedis、整合SpringBoot、对象的序列化))

Linux操作系统

Redis服务

(一)事务操作
1. 概述

redis单条命令是原子性操作,但事务不保证原子性。
redis事务的本质是:一组命令的集合!

  • 特点:
    事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
    事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
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> get k2
QUEUED
# 执行事务
127.0.0.1:6379> exec
1) OK
2) OK
3) "v2"
# 执行完一个事务后,这个事务就停止了,再次开启才可以继续下一个事务的操作!

放弃事务:
刷新一个事务中所有在排队等待的指令,并且将连接状态恢复到正常。

# 开启事务
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> DISCARD
OK
127.0.0.1:6379> get k2
(nil)

注意:

  • 开启事务后,出现错误的入队命令,事务执行会报错!所有命令都会执行失败!
  • 当开启事务后出现如:string不能执行 incr 加一的操作,执行事务后,不会影响事务的执行,这条命令会返回ERROR,但其他命令会执行成功,不影响操作!
3. 监控操作

WATCH:标记所有指定的key 被监视起来,在事务中有条件的执行(乐观锁)。
正常执行:

# 创建一个存有100元的key
127.0.0.1:6379> set money 100
OK
# 创建一个花费统计key
127.0.0.1:6379> set spend 0
OK
# 监控money
127.0.0.1:6379> watch money
OK
# 开启事务
127.0.0.1:6379> multi
OK
# 花费50元
127.0.0.1:6379> decrby money 50
QUEUED
# 花费统计加50元
127.0.0.1:6379> incrby spend 50
QUEUED
# 执行事务
127.0.0.1:6379> exec
1) (integer) 50
2) (integer) 50
# 当事务执行完后,watch就会失效

多线程插队,事务执行失败,此时体现了watch当作乐观锁:
开启两个服务操作上面的事务,在第一个服务没有执行事务之前,让第二个服务插队修改money的值,由于watch存在,所以再次提交事务就会失败:

# 服务一
127.0.0.1:6379> watch money         # 再次监控
OK
127.0.0.1:6379> multi               # 开启事务
OK
127.0.0.1:6379> decrby money 20     # 再次花费20元
QUEUED
127.0.0.1:6379> incrby spend 20     # 花费统计加20元
QUEUED

# 服务二
127.0.0.1:6379> get money           # 此时money还是50
"50"
127.0.0.1:6379> set money 200       # 在服务一执行事务之前将money设置为200
OK

# 服务一执行事务:结果为nil
127.0.0.1:6379> exec
(nil)

解决方法:unwatch

127.0.0.1:6379> unwatch           # 先unwatch:刷新一个事务中已被监视的所有key
OK
127.0.0.1:6379> multi             # 重新开启事务
OK
...
(二)Jedis
1. 概述

Jedis是Redis官方推荐的Java连接开发工具。
这里我们在windows环境下测试:首先保证win环境存在redis服务。
redis的zip包下载地址:https://github.com/MicrosoftArchive/redis/releases,下载解压即可用!
在这里插入图片描述

2. Jedis的测试
  1. 创建项目,导入相关依赖:
<!-- 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>
  1. 测试连接redis
import redis.clients.jedis.Jedis;

public class Test{
	public static void main(String[] args){
	    // 创建一个Jedis对象
		Jedis jedis = new Jedis("127.0.0.1",6379);
		// ping命令测试
		system.out.println(jedis.ping());
	} 
}

结果:
在这里插入图片描述
可以看到Jedis的方法都是redis的命令:
在这里插入图片描述

3. Jedis事务操作测试
  1. 正常事务的执行
import com.alibaba.fastjson.JSONObject;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

public class Test2 {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1",6379);
        JSONObject json = new JSONObject();
        json.put("name", "zhangsan");
        json.put("age", "20");
        String string = json.toJSONString();
        // 开启事务
        Transaction multi = jedis.multi();
        try {
            multi.set("user1",string);
            multi.set("user2",string);
            // 执行事务
            multi.exec();
        } catch (Exception e) {
            // 出现异常则放弃事务
            multi.discard();
            e.printStackTrace();
        } finally {
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user2"));
            // 关闭连接
            jedis.close();
        }
    }
}
/*
结果:
{"name":"zhangsan","age":"20"}
{"name":"zhangsan","age":"20"}
*/
  1. 异常事务
import com.alibaba.fastjson.JSONObject;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

public class Test2 {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1",6379);
        jedis.flushDB();        // 刷新数据库
        JSONObject json = new JSONObject();
        json.put("name", "zhangsan");
        json.put("age", "20");
        String string = json.toJSONString();
        // 开启事务
        Transaction multi = jedis.multi();
        try {
            multi.set("user1",string);
            multi.set("user2",string);
            // 模拟error
            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();
        }
    }
}
/*
结果:
java.lang.ArithmeticException: / by zero
	at com.chen.Test2.main(Test2.java:23)
null
null
*/
(三) SpringBoot整合redis
1. 创建SpringBoot项目

在这里插入图片描述
引入redis后的pom.xml文件依赖:

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

点进spring-boot-starter-data-redis发现:lettuce依赖。
在SpringBoot 2.x 版本后,jedis被替换为lettuce
jedis:采用直连,多线程操作时,存在线性不安全,需使用jedis pool;
lettuce:采用netty,在多线程下不存在不安全。

2. 编写application.properties文件
# 配置类
spring.redis.host=127.0.0.1
spring.redis.port=6379

SpringBoot的自动配置类:RedisAutoConfiguration
在这里插入图片描述
自动配置类绑定的properties类:RedisProperties

@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
	/**
	 * Database index used by the connection factory.
	 */
	private int database = 0;    // 默认使用0号数据库
	/**
	 * Connection URL. Overrides host, port, and password. User is ignored. Example:
	 * redis://user:password@example.com:6379
	 */
	private String url;
	/**
	 * Redis server host.
	 */
	private String host = "localhost";
	/**
	 * Login password of the redis server.
	 */
	private String password;
	/**
	 * Redis server port.
	 */
	private int port = 6379;    // 默认端口6379
	...
3. 测试

在test中注入RedisTemplate类,先来测试一下:

@SpringBootTest
class RedisSpringbootApplicationTests {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void contextLoads() {
        redisTemplate.opsForValue().set("k1","v1");
        redisTemplate.opsForValue().set("k2","哈哈哈 ");
        System.out.println(redisTemplate.opsForValue().get("k1"));
        System.out.println(redisTemplate.opsForValue().get("k2"));
    }
}

运行结果:
在这里插入图片描述
此类中其他操作:(事务、数据库…)

@SpringBootTest
class RedisSpringbootApplicationTests {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void contextLoads() {
		// 事务操作
		redisTemplate.multi();
		redisTemplate.exec();
		// 数据库的操作
        RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
        connection.flushDb();
        connection.flushAll();
    }
}

RedisTemplate 类的 opsForValue类似于string,还有其他的操作(hash、set、list…)
在这里插入图片描述

4. 对象序列化

编写自己的序列化配置类:

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        // 为了方便开发使用<String,Object>
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);
        // json序列化:用json解析任意的对象
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        // string序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用string序列化
        template.setKeySerializer(stringRedisSerializer);
        // hash的key采用string序列化
        template.setHashKeySerializer(stringRedisSerializer);
        // value采用json序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value采用json序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

测试类:

@SpringBootTest
class RedisSpringbootApplicationTests {

    @Autowired
    @Qualifier("redisTemplate")
    private RedisTemplate redisTemplate;

    @Test
    void contextLoads() {
        redisTemplate.opsForValue().set("k1","v1");
        redisTemplate.opsForValue().set("k2","哈哈哈 ");
        System.out.println(redisTemplate.opsForValue().get("k1"));
        System.out.println(redisTemplate.opsForValue().get("k2"));
    }

    @Test
    public void test(){
        User user = new User("zhangsan", 20);
        redisTemplate.opsForValue().set("user",user);
        System.out.println(redisTemplate.opsForValue().get("user"));
    }
}

结果:
在这里插入图片描述

//下篇再见…谢谢
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值