Redis 简单使用

前言

之前在 秒杀系统简单实现 有提到 spring boot 整合 redis,有必要单独提出来讲一下.

说明

redis 一般在学习并发优化,分布式之类的东西时接触到,不严谨地讲,redis 在这些场景被用于提高服务端响应速度.而 redis 到底是啥,结合它是如何来解决前面这个问题的,能更好地理解.

redis 的官方定义是”一个把结构化的数据放在内存中的一个存储系统”.这个定义很开放,你可以用 redis 做数据库,也可以用来缓存或作消息中间件.

redis 的数据放在内存中,这就是它优化服务端响应速度的一个原因.像一般的数据库,如 MySQL,MySQL 的数据存放在磁盘,在不考虑其它原因的情况下,可以直观地认为内存中的数据当然比磁盘中的数据读取地快,所以 redis 起到了优化的作用.

在具体的优化中,redis 一般起缓存作用.在冷启动时,数据仍然通过原有方式获取,然后存放在 redis 中,而之后需要读取数据,就可以直接从 redis 获得.

安装

redis 版本 : 5.0.3

linux 版本 : centos 7

解压版安装,稍微详细点.

下载地址 : https://redis.io/download

解压,打开,执行 make.可以用 j 参数,执行 make j8,

有可能缺少依赖(在我的环境下,只遇到过缺少以来的报错),按提示安装缺少的依赖即可.

PS : 使用默认值执行了 make install,会在 /usr/local/bin 生成安装目录,可全局执行 redis 相关命令…

使用

运行 redis : redis-server

PS : 执行 redis-server 如果遇到某些错误信息,其实当前窗口已经给出了解决办法.看不明白可以搜索引擎查看.(我现在写这篇博文已经不晓得之前遇到过啥问题了…)

打开执行 redis 命令的窗口 : redis-cli

关于启动,主要就是这俩命令.我们需要启动 redis-server,然后在 redis-cli 中对 redis 进行操作.(如增删改数据…)

这里就会遇到问题.

在不进行任何配置的情况下,使用的是默认配置.执行 redis-server 后,该程序会在前台执行.而我们实际对 redis 进行操作(增删改数据之类的)需要打开 redis-cli.

但是执行 redis-server 是在前台运行 redis,如果在想在当前窗口运行 redis-cli 就只有杀掉这个前台进程,然后执行 redis-cli.但是此时,redis 已经被关闭了,所以在 redis-cli 执行的操作都不会成功.

PS : 如果已经整合到 spring,进行数据操作时会报错NOAUTH Authentication required.

解决方案一 : 在一个终端执行 redis-server ,重开一个窗口执行 redis-cli ,然后执行相应操作.

解决方案二 : 修改配置文件,从而达到后台执行 redis-server 的效果,然后就能在当前窗口执行 redis-cli 了.

修改 /redis/redis.conf 中的 daemonize nodaemonize yes.

进入 /redis/src

执行 ./redis-server ../redis.conf 意思就是以自己的配置(后台执行)来运行 redis-server.

ok.

然后使用命令 ps -ef|grep redis 检查是否后台启动成功.

spring boot 2.x 整合 redis

我采用的是 gradle + yml.

配置需要在三个地方动手…

  1. gradle 中引入必要依赖.
  2. yml 中完善配置
  3. 添加一个 config 配置类

build.gradle

1
2
3
4
5
6
7
8
9
10
11
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-redis', version: '2.1.1.RELEASE'

// https://mvnrepository.com/artifact/com.alibaba/fastjson
compile group: 'com.alibaba', name: 'fastjson', version: '1.2.47'

//必要的
compile('org.apache.commons:commons-pool2:2.6.0')

//spring cache 效果:redis 里的数据删了,spring 仍能查到数据,就是通过它拿到的
compile('org.springframework.boot:spring-boot-starter-cache')

第一个 spring-boot-starer-data-redis 是主要依赖,用于 redis 与 spring boot 交互.

第二个 fastjson 用于 java 对象和 json 对象之间序列化的转换.

第三个 commons-pool ,具体不知道是干嘛的…

搜索得到 : Commons Pool组件提供了一整套用于实现对象池化的框架

大概是维护对象池,提高数据读取速度…

第四个 spring-boot-starter-cache 不要也可以,猜测是 spring 框架的缓存.因为我进行排除对比,有这个依赖时,redis 的数据删除了,spring 仍能不通过数据库拿到数据.删除就不起作用了.

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
server:
port: 8082

spring:
# 属性值可以为空,但是不能没有以下字段,否则启动报错
datasource:
url: jdbc:mysql://118.126.test.120:3306/test?serverTimezone=UTC
username:
password:
driver-class-name: com.mysql.jdbc.Driver
# spring boot 2.x 配置方式
redis:
# Redis数据库索引(默认为0)
database: 0
# Redis服务器地址
host: localhost
# Redis服务器连接端口(默认 6379)
port: 6379
# Redis服务器连接密码(默认为空)
password:
# pringboot2.x 需要这样配置
lettuce:
pool:
max-active: 8
max-wait: -1ms
min-idle: 0

主要在后面的 redis 配置…

上面是 spring boot 2.x 的配置方式,下面是旧的配置方式.

1
2
3
4
5
6
7
8
9
10
11
12
13
# spring boot 旧配置方式
spring:
redis:
hostName:
port:
password:
database:
timeout: 0
pool:
max-active: 8
max-wait: -1
max-idle: 8
min-idle: 0

不同点在新的配置方式里,需要手动指定 redis 连接池.

比如还可以:

1
2
3
4
5
6
7
8
9
10
11
12
redis:
port:
host:
password:
database:
timeout:
jedis:
pool:
max-active:
max-idle:
min-idle:
max-wait:

指定 redis 连接池为 jedis…

spring config

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
* @description: TODO
* Created by hqweay on 1/1/19 2:07 PM
* 这个配置类仍然是必须的,不然向 redis 插入数据会失败
*/
@Configuration
@EnableCaching
public class RedisConfiguration {

@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory connectionFactory) {
RedisTemplate template = new RedisTemplate();
template.setConnectionFactory(connectionFactory);

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

template.setValueSerializer(jackson2JsonRedisSerializer);
template.setKeySerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;
}


@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
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);

RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);

RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
cacheConfiguration = cacheConfiguration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));
cacheConfiguration.entryTtl(Duration.ofMinutes(10));
CacheManager cacheManager = new RedisCacheManager(redisCacheWriter, cacheConfiguration);
return cacheManager;

}
}

这一块的使用我还没弄清楚,代码来自网络,整理不当,忘记从哪 copy 的了…

@EnableCaching 注解似乎是与前文在 gradle 引入的 spring-boot-starter-cache 相联系,开启 spring-cache,cacheManager 是为此实现的一个方法.

使用

直接看封装的这个操作类…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Component
public class RedisDao {

@Autowired
private RedisTemplate redisTemplate;

public boolean putProductList(ArrayList<Product> products) {
if (redisTemplate.hasKey("productList")) {
return false;
} else {
redisTemplate.opsForValue().set("productList", products);
}
return true;
}

public ArrayList<Product> getProductList() throws SkillException {
try {
return (ArrayList<Product>) redisTemplate.opsForValue().get("productList");
} catch (SkillException e) {
e.setStatusEnum(REDIS_GET_ERROR);
throw e;
}
}


public void test(){
OrderKey orderKey = new OrderKey();
orderKey.setPhone("1155555");
redisTemplate.opsForValue().set("11",orderKey);
redisTemplate.opsForValue().set("myse","mys");
System.out.println(redisTemplate.opsForValue().get("33"));
}
}

序列化

spring boot 与 redis 交互有两种方式,一个是直接序列化,一个是转化为 json,以字符串格式存储.

这里使用的是 json 存储对象,前面有提到,用 fastjson 把 对象转换为 json 对象(这也是序列化嘛),然后存入 redis.读取的时候,会自动把 json 转换为 Object 对象,自己再转型一下就行了.

比如上面代码中的:

1
(ArrayList<Product>) redisTemplate.opsForValue().get("productList");

redis 的连接池

在上面的配置中有提到,spring boot 使用 redis 的连接池有 Jedis 和 Lettuce.

这两个有啥区别,怎么选型呢?

我在练习秒杀系统时,视频教程用的是 Jedis,但是 spring boot 2.0 以后已经把之前的 Jedis 改为 Lettuce 了.(也是配置改动的原因)

具体有啥区别,搜索有答案,自己也没深入了解.

就用 Lettuce 吧…

其它

学习 redis,各种入门教程会有各种 set,get 等基本命令,但是只是简单使用的话,暂时没必要了解,因为已经被封装了一层,可以直接用 java 操作…