Redis
介绍
Redis(Remote Dictionary Server)是用C语言开发的一个基于内存的键值对数据库
- 所有数据都在内存中,访问速度非常快:读的速度是110000次/s,写的速度是81000次/s
- 适合存储热点数据(商品、新闻资讯)
- 存储的value类型比较丰富,也称为结构化NoSQL数据库(非关系型数据库)
安装
下载
中文网地址:https://www.redis.net.cn/
解压
目录或文件 | 作用 |
---|---|
redis-benchmark | 性能测试工具 |
redis-check-aof | AOF文件修复工具 |
redis-check-dump | RDB文件检查工具(快照持久化文件) |
redis-cli | 命令行客户端 |
redis-server | 启动redis服务器 |
redis.windows.conf | redis核心配置文件 |
修改配置
启动服务
使用
管理员身份
运行,注意:必须指定配置文件,窗口启动之后,不能关闭
测试联通性
图形客户端
数据结构
键值 <String,5种数据类型>
Redis采用的是键值对存储,键的类型只能为字符串,值支持五种数据类型
- 字符串(string):普通字符串,Redis中最简单的数据类型
- 哈希(hash):也叫散列,类似于Java中的HashMap结构
- 列表(list):按照插入顺序排序,可以有重复元素,类似于Java中的LinkedList
- 集合(set):无序集合,没有重复元素,类似于Java中的HashSet
- 有序集合(sorted set/zset):集合中每个元素关联一个分数(score),根据分数升序排序,没有重复元素
命令
String 字符串
字符串类型是Redis中最为基础的数据存储类型。
* 增加数据:set key value * 获取数据:get key * 删除数据:del key * 增加数据的时候设置过期时间:setex key 存活时间(单位是s) value * 增加的时候判断可以是否存在:setnx key value * 自增(减)操作:incr/decr key incrby/decrby key step
Hash 哈希
Hash类型极其类似于java中的Map,值里面可以存放一组组的键值对,该类型非常适合于存储对象类型的信息。
* 增加数据:hset key hkey hvalue * 获取数据(单个):hget key hkey * 获取数据(所有):hgetall key * 获取所有hkey: hkeys key * 获取所有hvalue: hvals key * 删除数据(单个): hdel key hkey * 删除数据(所有): del key
List 列表
List类型底层是一个双向字符串链表,里面的元素是
有序的,可重复的
,我们可以从链表的任何一端进行元素的增删。* 添加数据:lpush(rpush) key value * 查询数据:lrange key [开始索引 结束索引] * 列表长度:llen key * 删除数据:lpop(rpop) key * 移出并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止:BRPOP key timeout
Set 集合
Set类型底层是一张hash表,里面的元素是无序的,不可重复的
* 添加数据:sadd key value * 查看数据:smembers key * 获取元素数量:scard key * 移除集合元素:srem key value
ZSet 有序集合
ZSet,也称sortedSet,在Set的基础上,加入了有序功能,在添加元素的时候,允许指定一个分数,它会按照这个分数排序。
* 添加数据:zadd key score value * 查询数据:zrange key [开始索引 结束索引] * 删除数据:zrem key value
通用命令
通用命令指的是不受数据类型限制的一批命令
1. 模糊查询键:keys 模糊匹配规则
2. 根据键判断记录是否存在:exists key
3. 根据键判断值类型:type key
4. 返回key的剩余生存时间:TTL key
5. 选择数据库: select 库索引[从0开始]
6. 清空当前数据库: flushdb
7. 清空所有数据库: flushall
Java操作
Spring对Redis客户端进行了整合,提供了 SpringDataRedis,在SpringBoot项目中还提供了对应的starter,即spring-boot-starter-data-redis
SpringDataRedis是Spring的一部分,对Redis底层开发包进行了高度封装,提供了一个高度封装的类:RedisTemplate,用于操作各种数据类型
- ValueOperations:简单K-V操作
- SetOperations: set类型数据操作
- ZSetOperations: zset类型数据操作
- HashOperations: hash类型的数据操作
- ListOperations: list类型的数据操作
环境准备
① 在项目中添加依赖(已完成)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
② application.yaml中添加配置
spring:
redis:
host: localhost
port: 6379
password: itheima
database: 0 # 操作的是0号数据库
③ 提供配置类
package com.sky.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.cache.annotation.CachingConfigurerSupport;
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.RedisSerializer;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
// Redis配置类
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
//原生RedisTemplate
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
//json序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = getJackson2JsonRedisSerializer();
// -------- 设置key value 序列化方式 --------
// key采用String的序列化方式
template.setKeySerializer(RedisSerializer.string());
// hash的key也采用String的序列化方式
template.setHashKeySerializer(RedisSerializer.string());
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
private Jackson2JsonRedisSerializer getJackson2JsonRedisSerializer() {
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
//设置ObjectMapper访问权限
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//记录序列化之后的数据类型,方便反序列化
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL);
//LocalDatetime序列化,默认不兼容jdk8日期序列化
JavaTimeModule timeModule = new JavaTimeModule();
timeModule.addDeserializer(LocalDate.class,
new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
timeModule.addDeserializer(LocalDateTime.class,
new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
timeModule.addSerializer(LocalDate.class,
new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
timeModule.addSerializer(LocalDateTime.class,
new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
//关闭默认的日期格式化方式,默认UTC日期格式 yyyy-MM-dd’T’HH:mm:ss.SSS
om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
om.registerModule(timeModule);
jackson2JsonRedisSerializer.setObjectMapper(om);
return jackson2JsonRedisSerializer;
}
}
④ 提供测试类
package com.sky.test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
@SpringBootTest
public class SpringDataRedisTest {
@Autowired
private RedisTemplate redisTemplate;
}
String
@Test
public void testString() {
//0 获取操作简单kv子对象
ValueOperations ops = redisTemplate.opsForValue();
//1. 增加
ops.set("1001", "zhangsan");
//2. 获取
System.out.println(ops.get("1001"));
//3. 创建的时候设置有效时间 setex
ops.set("1002", "李四", 300, TimeUnit.SECONDS);
//4. 添加的时候判断是否有值(没有值的时候再添加) setnx
System.out.println(ops.setIfAbsent("1003", "王五"));//true
System.out.println(ops.setIfAbsent("1003", "王五"));//false
//5. 删除 del
redisTemplate.delete("1003");
//6. 保存对象
Dept dept = new Dept(1,"开发部", LocalDateTime.now(),LocalDateTime.now());
ops.set("dept:1", dept);
System.out.println(ops.get("dept:1"));
}
Hash
//哈希
@Test
public void testHash(){
//获取当前数据类型对应的操作对象
HashOperations ops = redisTemplate.opsForHash();
//1. 新增 hset 2001 name zhangsan
ops.put("2001","name","zhangsan");
HashMap<Object, Object> map = new HashMap<>();
map.put("name","李四");
map.put("age",18);
map.put("password",123456);
ops.putAll("2002",map);
Dept dept = new Dept(1, "开发部", LocalDateTime.now(), LocalDateTime.now());
ops.putAll("2003", BeanUtil.beanToMap(dept));
//2. 获取
//hget 2002 name
System.out.println(ops.get("2002", "name"));
//hgetall 2002
System.out.println(ops.entries("2002"));
Map m = ops.entries("2003");
System.out.println(BeanUtil.mapToBean(m, Dept.class, true));
//hkeys 2002
System.out.println(ops.keys("2002"));
//hvals 2002
System.out.println(ops.values("2002"));
//3. 删除
//hdel 2002 name
ops.delete("2002","name","password");
}
List
//操作列表类型数据
@Test
public void testList() {
//0. 获取操作具体类型对象
ListOperations ops = redisTemplate.opsForList();
//1. 添加数据
ops.leftPush("3001", "张三"); //lpush 3001 张三
ops.leftPushAll("3001", "李四", "王五"); //lpush 3001 李四 王五
//2. 查询长度
System.out.println(ops.size("3001"));//llen 3001
//3. 查询数据
List list = ops.range("3001", 0, -1); //lrange 3001 0 -1
for (Object o : list) {
System.out.println(o);
}
//4. 删除
ops.rightPop("3001"); //rpop 2001
redisTemplate.delete("3001");//del 3001
}
Set
//操作集合类型数据
@Test
public void testSet() {
//0. 获取操作具体类型对象
SetOperations ops = redisTemplate.opsForSet();
//1. 添加数据
ops.add("4001", "张三", "李四", "王五"); //sadd 4001 张三 李四 王五
//2. 查询
Set members = ops.members("4001"); //smembers 4001
for (Object member : members) {
System.out.println(member);
}
//3. 长度
System.out.println(ops.size("4001")); //scard 4001
//4. 移除
ops.remove("4001", "张三", "李四"); //srem 4001 张三 李四
}
ZSet
//操作有效集合类型数据
@Test
public void testZSet() {
//0. 获取操作具体类型对象
ZSetOperations ops = redisTemplate.opsForZSet();
//1. 添加数据 zadd 5001 10 张三
ops.add("5001", "张三", 10);
ops.add("5001", "李四", 30);
ops.add("5001", "王五", 20);
//2. 查询 zrange 5001 0 -1
Set set = ops.range("5001", 0, -1);
for (Object o : set) {
System.out.println(o);
}
//3. 长度 zcard 5001
System.out.println(ops.size("5001"));
//4. 移除 zrem 5001 张三
ops.remove("5001", "张三", "李四");
}
通用
//测试通用
@Test
public void testCommon() {
//键模糊查询 keys *
Set keys = redisTemplate.keys("*");
for (Object key : keys) {
System.out.println(key);
}
//判断键是否存在 exists 5001
System.out.println(redisTemplate.hasKey("5001"));//true
System.out.println(redisTemplate.hasKey("6001"));//false
//判断键类型 type 5001
System.out.println(redisTemplate.type("5001"));//zset
//存活时间 ttl 5001
System.out.println(redisTemplate.getExpire("5001"));// -1
}
案例
将前面做的项目中的部门信息缓存到redis中
package com.itheima.controller;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.itheima.anno.LogAnno;
import com.itheima.pojo.Dept;
import com.itheima.service.DeptService;
import com.itheima.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
@Autowired
private RedisTemplate<String,String> redisTemplate;
//查询列表
@GetMapping("/depts")
public Result findAll() {
//线程Redis中查询
String listJsonFromRedis = redisTemplate.opsForValue().get("DEPT_LIST");
List<Dept> deptList = null;
if (StrUtil.isNotEmpty(listJsonFromRedis)){
deptList = JSONUtil.toList(listJsonFromRedis, D ept.class);
}else{
deptList = deptService.findAll();
//保存到redis
redisTemplate.opsForValue().set("DEPT_LIST",JSONUtil.toJsonStr(deptList));
}
return Result.success(deptList);
}
//新增
@LogAnno(methodDesc = "新增部门")
@PostMapping("/depts")
public Result save(@RequestBody Dept dept) {
deptService.save(dept);
//清空缓存中的数据列表
redisTemplate.delete("DEPT_LIST");
return Result.success();
}
//删除
@LogAnno(methodDesc = "删除部门")
@DeleteMapping("/depts/{id}")
public Result deleteById(@PathVariable("id") Integer id) {
//清空缓存中的数据列表
redisTemplate.delete("DEPT_LIST");
deptService.deleteById(id);
return Result.success();
}
}
- redis命令 Java操作 工具提示完成操作
新增部门")
@PostMapping(“/depts”)
public Result save(@RequestBody Dept dept) {
deptService.save(dept);
//清空缓存中的数据列表
redisTemplate.delete("DEPT_LIST");
return Result.success();
}
//删除
@LogAnno(methodDesc = "删除部门")
@DeleteMapping("/depts/{id}")
public Result deleteById(@PathVariable("id") Integer id) {
//清空缓存中的数据列表
redisTemplate.delete("DEPT_LIST");
deptService.deleteById(id);
return Result.success();
}
}
>1. redis命令 Java操作 工具提示完成操作
>2. 部门数据的缓存