文章目录
- Linux操作系统
- Redis服务
- (一)事务操作
- 1. 概述
- 2. 事务的执行
- 3. 监控操作
- (二)Jedis
- 1. 概述
- 2. Jedis的测试
- 3. Jedis事务操作测试
- (三) SpringBoot整合redis
- 1. 创建SpringBoot项目
- 2. 编写application.properties文件
- 3. 测试
- 4. 对象序列化
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的测试
- 创建项目,导入相关依赖:
<!-- 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>
- 测试连接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事务操作测试
- 正常事务的执行
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"}
*/
- 异常事务
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"));
}
}
结果:
//下篇再见…谢谢