Redis学习
Redis简介
Redis是完全开源免费的,遵守BDS协议,是一个高性能的NOSQL的key-value数据库,Redis是使用ANSI C语言编写的,支持网格,可基于内存亦可以持久化的日志型、key-value数据库,并提供了多种语言的API支持
NOSQL:指的是非关系型数据库,NOSQL即Not-Only SQL,可作为传统关系型数据库的补充
Redis特点
Redis优点
- 高性能 – Redis能读写的速度是110000次/s,写的速度是81000次/s
- 丰富的数据类型 --Redis支持的数据类型有String、Hash、List、Set、Ordered Set
- 原子性 – Redis的所有操作都是具有原子性的,即要么执行成功,要么执行失败完全不执行
- 丰富的特性 – Redis还支持publish、subscribe、通知、key过期等特性
- 高速读写 – Redis使用自己实现的分离器,没有使用lock(MySQL),因此效率非常高
Redis缺点
- 持久化 – Redis直接将数据存储到内存中,如果要将数据持久化到硬盘中,Redis有两种方式实现持久化
- 定时快照:每隔一段时间将整个内存中Redis数据库的内容持久化到硬盘,每次操作均是全量数据,因此对磁盘读写量较大
- 语句追加:只追加变化的数据,但是追加的log可能过大,同时所有的操作均需要重新执行一遍,回复速度慢
- 耗内存 – 因为Redis所有的数据都是加载到内存中来实现高速读写,这样的话就会导致内存占用过高
Centos7安装Redis
Redis官网:http://redis.io
1、安装gcc
## 因为Redis是使用C语言编写的,我们安装需要进行编译,则需要安装gcc
yum install -y gcc automake autoconf libtool make
2、获取Redis安装包
wget http://download.redis.io/releases/redis-5.0.8.tar.gz
3、解压下载好的压缩包
tar -zxvf redis-5.0.8.tar.gz -C /opt
4、编译Redis
cd /opt/redis-5.0.8 && make
出现:
Hint:It's a good idea to run 'make test' :)
表示安装成功
5、进行安装Redis
## 安装到指定目录/usr/local/redis
make PREFIX=/usr/local/redis install
6、Redis启动、关闭
## 启动Redis服务端
cd /usr/local/redis/bin/ && ./redis-server
## 启动Redis客户端
./redis-cli -h ip地址 -p 端口号 ## 不设置参数默认是127.0.0.1 端口号是6379
cd /usr/local/redis/bin/ && ./redis-cli
## 有关参数的详细信息可以使用--help参数进行查看
./redis-cli --help
## 查看启动的Redis
ps -ef | grep -i redis
## 启动客户端执行PING可以测试与服务端的连接
[root@localhost bin]# ./redis-cli
127.0.0.1:6379> ping
PONG
## 关闭客户端,在服务端输入以下命令
exit
## 关闭服务端,在客户端输入以下命令
shutdown
./redis-cli shutdown
7、配置Redis
首先将Redis停止掉,因为是前台启动的可以直接ctrl+c停止掉
## 复制解压目录下的redis.conf文件到安装文件的目录下
cp /opt/redis-5.0.8/redis.conf /usr/local/redis/
## 启动Redis并加载自己的配置文件
./redis-server /path/redis.conf
Redis配置文件redis.conf
## 绑定的主机地址,如果需要设置任何IP都可以访问,则注释掉即可
bind 127.0.0.1
## 指定Redis的启动端口号,默认是6379
port 6379
## 设置客户端的连接超时时间,设置为0则表示关闭超时断开功能
timeout 300
## Redis默认是以前台形式启动的,只需要修改no为yes则可以以守护进程的方式启动Redis
daemonize no
## Redis以守护进程运行时会把pid写入到/var/run/redis_6379.pid文件,我们进行手动配置
pidfile /var/run/redis_6379.pid
## 设置日志的级别,可用的级别有:debug、verbose、notice、warning
loglevel verbose
## 指定日志输出的文件名称,默认为空
logfile ""
## 设置Redis的数据库数量,默认为16,每个数据库的下标从0开始,可以使select进行切换
databases 16
## 指定在多长的时间内有多少次更新操作,Redis默认提供了三种方式
## save <seconds> <changes>
## 需要注意的是如果在时间范围内修改了对应的数据,但是将Redis非正常停止(杀死进程、服务器关机、服务器断电)则会引起数据丢失
save 900 1 # 900秒(15分钟)后,如果至少更改了1个键
save 300 10 # 300秒(5分钟)后,如果至少更改了10个按键
save 60 10000 # 60秒后,如果至少更改了10000个键
## 指定了数据存储到本地是否进行压缩操作,默认为yes
rdbcompression yes
## 指定本地数据库文件名称
dbfilename dump.rdb
## 指定本地数据库存放目录,目录必须存在,否则启动报错
dir ./
## 设置连接Redis的密码,最好将密码设置为较为复杂的密码,以防止穷举暴力破解
requirepass admin@123
## 设置最大客户端连接数量,如果设置为0则表示不显示,如果超过最大连接数量则提示:max number of clients reached
maxclients 10000
## 设置Redis的最大内存限制
maxmemory <bytes>
## 指定在每次更新操作之后进行日志记录,默认no
appendonly no
## 指定更新日志文件名称,默认为ppendonly.aof
appendfilename "appendonly.aof"
## 指定更新日志的条件
## no 表示每次等待操作系统进行数据缓存同步到磁盘(快)
## always 表示每次更新操作后手动调用fsync()将数据写入到磁盘(慢、安全)
## everysec 表示每秒同步一次(默认值)
appendfsync everysec
Redis内存淘汰机制
1、为数据设置超时时间
-
设置超时时间
除了字符串有特殊的方式,别的数据类型都需要依赖expire
如果没有设置超时时间,那么数据将永久有效
设置超时时间之后,又不想让数据过期,则使用persist key
## 常用的方式 expire key time(秒) ## 字符串独有的方式 setex(String key,int seconds,String value)
2、采用LRU算法动态将不用的数据删除
内存管理的一种页面置换算法,对于在内存中但又不用的数据块(内存块)叫做LRU,操作系统会根据那些数据属于LRU而将其移出内存而腾出空间来加载别的数据
volatile-lru : 设定超时时间的数据中删除不常使用的数据(常用)
allkeys-lru : 查询所有的key中最近不常使用的数据进行删除,这是应用最广泛的策略(常用)
volatile-random : 在设定了超时的数据中随机删除
allkeys-random : 查询所有的key,然后随机删除
volatile-ttl : 查询全部设定超时时间的数据,之后排序,将马上要过期的数据进行删除
noeviction : 如果设置为该属性,则不会进行删除操作,如果内存溢出则报错返回
volatile-lfu : 从所有配置了过期时间的key中删除使用频率最少的key
allkeys-lfu : 从所有key中删除使用频率最少的key
远程连接Redis
远程连接Redis需要使用到远程连接工具RedisDesktopManager
如果要远程连接,则需要完成Redis以下配置:
开启Redis的远程连接
设置Redis的连接密码
RedisDesktopManager官方网站地址:https://redisdesktop.com/
远程连接工具安装
RedisDesktopManager从0.9.4版本开始不再提供免费的版本,需要安装的话有两种办法
1、下载0.9.4之前的版本进行安装
2、下载安装破解版本
3、使用一些开源的Redis管理工具项目提供的免费版的工具
- RedisDesktopManager-Windows:https://github.com/jfcherng/RedisDesktopManager-Windows/releases
- AnotherRedisDesktopManager:https://github.com/qishibo/AnotherRedisDesktopManager/releases
Docker安装Redis
在https://hub.docker.com/搜索Redis并选择适合的版本
下载镜像
docker pull redis
创建容器并运行
docker run -d --name redis_6379 -p 6379:6379 redis --requirepass '密码'
Redis常用命令
常用key命令
## 返回满足条件的所有key,可以使用*模糊匹配 keys a*
keys *
## 是否存在指定的key,存在返回1,不存在返回0
exists key
## 设置指定key的过期时间,单位:秒
expire key second
## 删除key
del key
## 查询key的剩余存活时间,返回剩余时间,单位:秒,不存在返回-2,存在但是没有剩余时间返回-1
ttl key
## 取消过期时间
persist key
## 修改key的过期时间,单位:毫秒
pexpire key millisecond
## 选择数据库,index为数据库标识,0开始
select index
## 将当前数据库中的数据转移到其他数据库
move key dbindex
## 返回一个随机的key
randomkey
## 重命名key
rename oldkeyname newkeyname
## 查询key的存储类型
type key
## 打印命令
echo
## 查询数据库的key数量
dbsize
## 查看数据库信息
info
## 实时传输收到的请求的配置key,返回Redis相关的配置,*代表返回所有配置
config get *
## 清空当前数据库
flushdb
## 清空所有数据库
flushall
key的命名规范
Redis单个key的最大允许存入512M
- key不要太长,尽量不要超过1024字节,key的长度会消耗内存且降低查询效率
- key不要太短,不然会降低可读性
- 命名之间的连接最好使用
:
这样就可以将相同的进行归类 - 同一项目中最好使用统一的命名规则
- key区分大小写
Redis数据类型
String类型
String 是Redis最基本的数据类型,一个key最大能存储512MB的数据
String类型是二进制的,也就是说String可以包含任何数据,比如序列化的对象,一张图片,简单的字符串,一个数字等
应用场景
- 用于保存单个字符串或者json、xml数据
- 保存图片文件
- 计数器(阅读数,粉丝数)
String命令
赋值语法
SET KEY_NAME 'VALUE' :多次赋值会将key对应的值覆盖,且无视类型
## (not exist)如果key不存在,则赋值并返回1,如果key存在,则不设置,并返回0(解决了分布式锁)
setnx key value
## (expired)设置key的值为value,过期时间为10秒,10秒后key清除
setex key 10 value
## 替换字符串
setrange string range value
## 批量写入
mset key1 value1 key2 value2...
## 自增,将key中存储的数字+1,如果key不存在,则初始化key的值为0,然后在执行自增操作
incr key
## 指定增量的值
incrby key 增值
## 自减,将存储的字符类型的数字-1,自增自减需要注意的是存储的值必须是字符类型的数字
decr key
## 指定减量的值
decrby key 减值
## 字符串拼接,用于给指定key的值拼接字符(末尾追加),如果key不存在,则直接赋值
append key value
取值语法
## Redis get命令用于获取指定key的值,如果key不存在,则返回nil,如果存储的值不是一个字符串,则返回一个错误
get key_name
## 用于获取存储在指定key中字符串的子字符串,字符串的截取范围有start和end两个偏移量决定(包前包后)
getrange key start end
## 对key所存储的字符串的值,获取该偏移量上的位(bit)
getbit key offset
## getset命令用于设置指定key的值,返回key的旧值,当key不存在时返回nil
getset key_name value
## 返回key所存储的字符串的长度
serlen key
## 批量取值
mget key1 key2...
删除语法
## 删除指定的key,如果存在,则返回数字类型
del key
Hash类型
Hash是String类型的field和value的映射表,hash可以看成一个key-value的map容器特别适合存储对象,相比较而言将一个对象存储在hash类型要比存储在String类型中占用更少的内存空间
Redis中每个hash可以存储2的32次方
应用场景
- 存储对象内容,比如用户基本信息对象等
Hash命令
赋值语法
## 为指定的key设定field和value,如果key已经存在则是在已经存在的key中追加对应的field和value,如果对应的field也存在了,则是给对应的field重新赋值
hset key field value
## 同时设定多个field和value
hmset key field1 value1 field2 value2....
## 类似于字符串的setnx,只判断field是否存在,如果不存在,才会新增,否则不进行增加
hsetnx key field value
## 为指定的key中的指定字段的整数增加增量
hincrby key field 增量
## 为指定的key中的指定字段的浮点增加增量
hincrbyfloat key field 增量
取值语法
## 获取指定key中field对应的值
hget key field
## 获取指定key中多个field对应的值
hmget key field1 field2...
## 返回指定key中存储的所有field和value
hgetall key
## 获取key对应的所有hash表中的字段
hkeys key
## 获取key对应的hash表中的字段数量
hlen key
## 查询hash表key中指定的field字段是否存在
hexists key field
删除语法
## 可以将key对于的hash类型中存储的field对应的值删除掉
hdel key field
## 删除整个key对应的hash数据
del key
Redis-Client
Redis-Client介绍
jedis api地址:https://tool.oschina.net/uploads/apidocs/redis/clients/jedis/Jedis.html
redisson 官网地址:https://redisson.org/
redisson git项目地址:https://github.com/redisson/redisson
lettuce 官网地址:https://lettuce.io/
lettuce git项目地址:https://github.com/lettuce-io/lettuce-core
需要注意的是在Springboot2之后,对Redis的支持默认使用了lettuce
概念
Jedis:是Redis的Java实现客户端,提供了比较全面的Redis命令的支持,
Redisson:实现了分布式和可扩展的Java数据结构。
Lettuce:高级Redis客户端,用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器。
优点
Jedis:比较全面的提供了Redis的操作特性
*Redisson:促使使用者对Redis的关注分离,提供很多分布式相关操作服务,例如,分布式锁,分布式集合,可通过Redis支持延迟队列
*Lettuce:基于Netty框架的时间驱动通讯层,其方法调用是异步的,Lettuce的API是线程安全的,可以操作单个Lettuce连接来完成各种操作
可伸缩性
Jedis:使用阻塞的I/O,且其方法调用都是同步的,程序流需要等到sockets处理完I/O才能执行,不支持异步。Jedis客户端实例不是线程安全的,所以需要通过连接池来使用Jedis。
Redisson:基于Netty框架的事件驱动的通信层,其方法调用是异步的。Redisson的API是线程安全的,所以可以操作单个Redisson连接来完成各种操作
Lettuce:可以支持Redis4,但是需要jdk8及以上
SpringBoot整合Jedis
注意:Jedis2.7以上版本才支持集群的操作
所有代码均已上传gitee:https://gitee.com/frostysun/Learn.git
引入依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
配置application
spring:
redis:
port: 6379 # Redis的端口号
password: admin@123 # Redis的密码
host: 192.168.3.20 # Redis的服务器地址
jedis:
pool:
max-active: 10 #最大连接数,默认为8
max-idle: 6 # 最大空闲数量,默认为8
min-idle: 2 # 最小空间数量,默认为0
timeout: 2000 # 连接超时
Config
创建类文件:com.example.jedis.JedisConfig
package com.example.jedis;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* @author : frosty0804@gmail.com
* @version : v1.0
* @projectName : Learn
* @className : JedisConfig
* @description : TODO(Jedis连接初始化配置类)
* @date : 2020/3/26 15:44
*/
@Slf4j
@Configuration
public class JedisConfig {
/**
* Redis的服务器地址
*/
@Value("${spring.redis.host}")
private String host;
/**
* Redis的端口号
*/
@Value("${spring.redis.port}")
private int port;
/**
* Redis的密码
*/
@Value("${spring.redis.password}")
private String password;
/**
* 连接超时时间
*/
@Value("${spring.redis.timeout}")
private int timout;
/**
* 最大连接数
*/
@Value("${spring.redis.jedis.pool.max-active}")
private int maxActive;
/**
* 最大空闲数量
*/
@Value("${spring.redis.jedis.pool.max-idle}")
private int maxIdle;
/**
* 最小空间数量
*/
@Value("${spring.redis.jedis.pool.min-idle}")
private int minIdle;
@Bean
public JedisPool jedisPool() {
// 实例化JedisPoolConfig对象并将配置参数重赋值到对象中
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMinIdle(minIdle);
jedisPoolConfig.setMaxTotal(maxActive);
// 实例化JedisPool连接对象
JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timout, password);
log.info("JedisPool连接成功:" + host + ":" + port);
return jedisPool;
}
}
Jedis操作String类型
1、工具类
package com.example.jedis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
/**
* @author : frosty0804@gmail.com
* @version : v1.0
* @projectName : Learn
* @className : JedisUtil
* @description : TODO(Jedis工具类)
* @date : 2020/3/26 16:53
*/
@Component
public class JedisUtil {
@Autowired
private JedisPool jedisPool;
/**
* 获得jedis对象
*/
public Jedis getJedis() {
return jedisPool.getResource();
}
/**
* 释放jedis资源
*/
public void close(Jedis jedis) {
if (jedis != null) {
jedis.close();
}
}
}
2、Service
package com.example.service.impl;
import com.example.jedis.JedisUtil;
import com.example.model.User;
import com.example.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Service("UserService")
public class UserServiceImpl implements UserService {
/**
* 注入连接池
*/
@Autowired
private JedisPool jedisPool;
@Autowired
private JedisUtil jedisUtil;
/**
* TODO 字符串类型的演示
* 需求:
* 用户输入一个key
* 先判断Redis中是否存在该key
* 如果存在,则在Redis中进行查询,并返回该值
* 如果不存在,则在MySQL数据库进行查询,并将查询到的值赋给Redis然后返回
*
* @param key 请求Redis的key
* @return Redis中存在的key对应值
*/
@Override
public String getString(String key) {
// 初始化jedis对象
Jedis jedis = jedisUtil.getJedis();
String result = null;
try {
// 对调用jedis进行key的判断是否存在于Redis
if (jedis.exists(key)) {
log.info("数据存在,查询Redis中的数据");
} else {
String value = "我是一个没的感情的测试数据";
log.info("数据不存在于Redis,查询MySQL数据库:" + value);
log.info("将MySQL查询得到的数据赋值给Redis");
jedis.set(key, value);
}
result = jedis.get(key);
} catch (Exception e) {
log.error("jedis查询异常", e);
} finally {
jedisUtil.close(jedis);
}
return result;
}
}
3、单元测试
package com.example;
import com.example.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import redis.clients.jedis.JedisPool;
@SpringBootTest
class SpringbootJedisApplicationTests {
@Autowired
private JedisPool jedisPool;
@Autowired
private UserService userService;
/**
* 模拟操作String类型的数据
*/
@Test
void str() {
System.out.println(userService.getString("name"));
}
}
Jedis操作Hash类型
1、Model
package com.example.model;
import lombok.Data;
/**
* @author : frosty0804@gmail.com
* @version : v1.0
* @projectName : Learn
* @className : User
* @description : TODO(使用一句话概括这个类)
* @date : 2020/3/26 15:32
*/
@Data
public class User {
String id;
String name;
String age;
}
2、Service
package com.example.service.impl;
import com.example.jedis.JedisUtil;
import com.example.model.User;
import com.example.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import java.util.HashMap;
import java.util.Map;
/**
* @author : frosty0804@gmail.com
* @version : v1.0
* @projectName : Learn
* @className : UserServiceImpl
* @description : TODO(使用一句话概括这个类)
* @date : 2020/3/26 16:13
*/
@Slf4j
@Service("UserService")
public class UserServiceImpl implements UserService {
/**
* 注入连接池
*/
@Autowired
private JedisPool jedisPool;
@Autowired
private JedisUtil jedisUtil;
/**
* TODO hash类型的操作
* 需求:
* 前端传入一个用户ID
* 根据用户传入的ID进行查询对象信息
* 首先到Redis中查询,如果存在,则返回给页面
* 如果不存在,则去数据库查询,将结果写入到Redis中,并返回给页面
*
* @param id 传入要查询的用户ID号码
* @return 用户信息
*/
@Override
public User queryId(String id) {
Jedis jedis = jedisUtil.getJedis();
User user = new User();
Map<String, String> map = new HashMap<>();
try {
String key = "user:" + id;
if (jedis.exists(key)) {
log.info("Redis存在数据,查询Redis");
map = jedis.hgetAll(key);
user.setId(map.get("id"));
user.setName(map.get("name"));
user.setAge(map.get("age"));
} else {
log.info("Redis不存在数据,查询MySQL数据库");
user.setId(id);
user.setName("李四");
user.setAge("18");
log.info("将MySQL中查询到的数据赋值到Redis数据库中");
map.put("id", user.getId());
map.put("name", user.getName());
map.put("age", user.getAge());
jedis.hmset(key, map);
}
} catch (Exception e) {
log.error("查询Redis异常", e);
} finally {
jedisUtil.close(jedis);
}
return user;
}
}
3、单元测试
package com.example;
import com.example.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import redis.clients.jedis.JedisPool;
@SpringBootTest
class SpringbootJedisApplicationTests {
@Autowired
private JedisPool jedisPool;
@Autowired
private UserService userService;
@Test
void hash() {
System.out.println(userService.queryId("2"));
}
}
SpringBoot2.x整合lettuce
lettuce需要Springboot2.x版本才可以支持
引入依赖
<!-- 默认的lettuce客户端 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- redis依赖commons-pool这个一定要添加 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
配置文件设置
spring:
redis:
port: 6379
password: admin@123
host: 192.168.3.20
lettuce:
pool:
max-active: 8 # 连接池最大连接数量,默认为8,负值表示没有限制
max-idle: 8 # 连接池中最大空闲连接,默认为8
min-idle: 0 # 连接池中最小空闲连接,默认为0
max-wait: 1000 # 连接池最大阻塞等待时间,负值表示没有限制
shutdown-timeout: 100 # 超时关闭时间
RedisConfig配置文件
package com.example.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.lang.reflect.Method;
/**
* @author : frosty0804@gmail.com
* @version : v1.0
* @projectName : Learn
* @className : RedisConfig
* @description : TODO(RedisConfig配置文件)
* @date : 2020/3/27 11:20
*/
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
/**
* 自定义缓存key的生成策略,
*/
@Bean
@Override
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object o, Method method, Object... objects) {
StringBuilder sb = new StringBuilder();
sb.append(o.getClass().getName());
sb.append(method.getName());
for (Object obj : objects) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
/**
* 缓存配置管理器
*
* @param factory
* @return
*/
@Bean
public CacheManager cacheManager(LettuceConnectionFactory factory) {
// 以锁的方式创建RedisCacheWriter对象
RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(factory);
// 创建默认缓存配置对象
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
RedisCacheManager cacheManager = new RedisCacheManager(writer, configuration);
return cacheManager;
}
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer jsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jsonRedisSerializer.setObjectMapper(objectMapper);
StringRedisSerializer serializer = new StringRedisSerializer();
// 在使用注解@Bean返回RedisTemplate的时候,同时配置hashKey于hashValue的序列化方式
// key采用String的序列化方式
template.setKeySerializer(serializer);
// value采用jackson的序列化方式
template.setValueSerializer(jsonRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(serializer);
// hash的value采用jackson的序列化方式
template.setHashValueSerializer(jsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
lettuce操作String
1、服务层
package com.example.service;
import com.example.model.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.jws.soap.SOAPBinding;
/**
* @author : frosty0804@gmail.com
* @version : v1.0
* @projectName : Learn
* @className : UserService
* @description : TODO(服务层)
* @date : 2020/3/27 13:07
*/
@Slf4j
@Service
public class UserService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 需求:
* 用户输入一个key
* 先判断Redis中是否存在该key
* 如果存在,则在Redis中进行查询,并返回该值
* 如果不存在,则在MySQL数据库进行查询,并将查询到的值赋给Redis然后返回
*
* @param key 请求Redis的key
* @return Redis中存在的key对应值
*/
public String getString(String key) {
String value = null;
try {
// hasKey和existe类似,都是用来判断key是否存在
if (redisTemplate.hasKey(key)) {
log.info("key存在于Redis数据库");
value = String.valueOf(redisTemplate.opsForValue().get(key));
} else {
log.info("key不存在于Redis数据库,向MySQL查询数据");
value = "我是一个没的感情的测试数据";
log.info("将数据添加到Redis数据库");
redisTemplate.opsForValue().set(key, value);
}
} catch (Exception e) {
log.error("Redis数据库访问异常", e);
}
return value;
}
}
2、单元测试
package com.example;
import com.example.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SpringbootLettuceApplicationTests {
@Autowired
private UserService userService;
@Test
void test1() {
System.out.println(userService.getString("test1"));
}
}
lettuce操作Hash类型
1、服务层
package com.example.service;
import com.example.model.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.jws.soap.SOAPBinding;
/**
* @author : frosty0804@gmail.com
* @version : v1.0
* @projectName : Learn
* @className : UserService
* @description : TODO(使用一句话概括这个类)
* @date : 2020/3/27 13:07
*/
@Slf4j
@Service
public class UserService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 测试hash类型
*
* @param id 传入要查询的用户ID号码
* @return 用户信息
*/
public User queryId(String id) {
User user = new User();
try {
// 进行判断,是否存在对应的对象以及对象中的字段
if (redisTemplate.opsForHash().hasKey("user", id)) {
log.info("Redis中存在对应的对象数据,查询Redis");
user = (User) redisTemplate.opsForHash().get("user", id);
} else {
log.info("Redis中不存在对应的数据,查询MySQL数据库");
user.setId(id);
user.setName("张三");
user.setAge("22");
/**
* @param h 存储的对象名称
* @param hk 用户的主键
* @param hv 要存储的对象
*/
redisTemplate.opsForHash().put("user", id, user);
}
} catch (Exception e) {
log.error("Redis数据库访问异常", e);
}
return user;
}
}
2、单元测试
package com.example;
import com.example.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SpringbootLettuceApplicationTests {
@Autowired
private UserService userService;
@Test
void test2() {
System.out.println(userService.queryId("1002"));
}
}