Redis

Redis

redis是一个基于内存的key-value结构的NoSQL数据库
访问数据量高的话可以用Redis
默认端口号6379

NoSQL

关系型数据库是数据库和数据库之间存在关系,但是存在磁盘上,读写数据比较差。
而NoSQL是非关系型数据库,格式比较灵活

Redis数据类型

注:redis里面的null是nil

字符串String
1
2
3
4
5
6
7
8
新增:set key value   如果value有空格,要用引号引起来
查询:get key
删除:del key
新增的时候设置过期时间: setex key 过期时间 value
查看剩余时间:ttl key
根据键判断记录是否存在:exists key
新增的时候判断是否存在:setnx key value
自增/减操作:incre/decr key
哈希(大的map套一个小map)
1
2
3
4
5
6
7
8
新增:hset key hkey hvalue     hset 大键 小键   可以理解为把键值对存到一个大键中
查询:所有 hgetall key
单个 hget key hkey
获取所有hkey(小键): hkeys key
获取所有hvalue: hvals key
删除: 单个 hdel key hkey
所有 del key
修改就是覆盖,直接新增原来的名字就可以
列表list(有序且重复)是一个双向链表,添加的时候是可以从头添加,也可以从尾添加,但查询的时候只能从左开始查询

1
2
3
4
5
6
7
新增:左压 lpush key value
右压 rpush key value
查询元素:
lrange key [开始索引 结束索引] 最后一个索引是-1
查询列表长度: llen key
删除元素; 左弹 lpop key
右弹 rpop key
集合set(hash表,无序,不可重复)
1
2
3
4
5
6
7
8
9
10
新增: sadd key value
查询元素: smembers key
查询集合数量:scard key
删除元素:srem key value
比如添加小明的爱好: asdd ming it music sport
添加小红的爱好: asdd hong it music tiaowu
求集合的交集: sinter key1 key2
比如查小明和小红共同的爱好(交集): sinter ming hong 结果就是 it music
求集合的并集: sunion key1 key2
同上查询全部的爱好(并集):sunion ming hong 结果 it music sport tiaowu
有序集合zset(有序,不可重复)
1
2
3
4
5
6
7
8
9
10
新增: zadd key score value    score是分数,指定分数会按照分数排序
查询:
升序 zrange key start end [withscores]
降序 zrevrange key start end [withscores]
比如新增语文成绩排名:
zadd yuwen 81 ming
zadd yuwen 86 hong
加分: zincrby key increment member
比如小明语文加10分: zincrby yuwen 10 ming
删除: zrem key value

通用命令

模糊查询键

1
keys *

删除多个键

1
del key key ...

根据键判断值类型

1
type key

选择数据库: 默认有16个数据库

1
select 0~15

用java操作Redis

Spring对Redis进行了整合,提供了Spring Data Redis,它将所有的类型命令封装到了一个类中,名字叫: RedisTemplate
导入坐标:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

添加redis配置:

1
2
3
4
5
6
7
8
9
10
11
sky:
redis:
host: localhost
port: 6379
password: itheima

spring:
redis:
host: ${sky.redis.host}
port: ${sky.redis.port}
password: ${sky.redis.password}

如果要让一个java对象存到Redis内存中,必须要实现序列化接口!

1
实体类 implements Serializable

字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 //字符串类型
ValueOperations valueOperations = redisTemplate.opsForValue();
//存
valueOperations.set("小明", 18);
//取
valueOperations.get("小明");
//存短信验证码,5分钟有效时间
valueOperations.set("手机号:111","1024", Duration.ofSeconds(300));
//存java对象
Category category = new Category();
category.setName("羽毛球");
valueOperations.set("对象",category);
//取java对象
valueOperations.get("对象");

列表

1
2
3
4
5
6
7
8
9
10
11
12
//列表操作对象
ListOperations listOperations = redisTemplate.opsForList();
// 左压入
listOperations.leftPushAll("泡泡","a","b","c");
// 查询 c b a
List mylist = listOperations.range("泡泡", 0, -1);
System.out.println(mylist);
// 右弹出
System.out.println(listOperations.rightPop("泡泡"));
// 查询 c b
List mylist1 = listOperations.range("泡泡", 0, -1);
System.out.println(mylist1);

哈希

1
2
3
4
5
6
7
8
9
10
11
12
13
 //哈希类型
HashOperations hashOperations = redisTemplate.opsForHash();
//存
hashOperations.put("大key","小key","值");
hashOperations.put("语文","小明",89);
hashOperations.put("语文","小红",99);
//取一个
hashOperations.get("语文", "小明");
//取所有
Set hkeys = hashOperations.keys("语文");
for (Object key : hkeys) {
hashOperations.get("语文", key);
}

集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//集合操作对象
SetOperations setOperations = redisTemplate.opsForSet();
// 存 蔡徐坤
// setOperations.add("caixukun","唱","跳","rap","打篮球");
// 存
// setOperations.add("ming","爱坤","唱","rap");
// 查询
Set set = setOperations.members("ming");
System.out.println(set);
// 交集
System.out.println(setOperations.intersect("caixukun", "ming"));
// 并集
System.out.println(setOperations.union("caixukun", "ming"));
// 删除
setOperations.remove("caixukun","rap");

有序集合

1
2
3
4
5
6
7
8
9
10
// 有序集合操作对象
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
// 存
// zSetOperations.add("语文","后羿",66.4);
// zSetOperations.add("语文","安其拉",45.2);
// zSetOperations.add("语文","妲己",23.2);
// 安其拉买了皮肤,+ 30攻击力
zSetOperations.incrementScore("语文","安其拉",30);
// 查询 降序
System.out.println(zSetOperations.reverseRange("语文", 0, -1));

公共代码

1
2
3
4
5
6
7
// 查询c开头的  keys c*
Set keys = redisTemplate.keys("c*");
System.out.println(keys);
// 批量删除
redisTemplate.delete(keys);
// 判断key是否存在
System.out.println(redisTemplate.hasKey("caixukun"));
案例:后台管理端有个营业中和打烊中,只要一个1和0的值,存sql数据库比较多余,所以这里可以存储到Redis

前端需要一个1或者0,我们通过键存储值,前端设置店铺状态为打烊就会传过来一个1,然后我们通过Redis存储到Redis数据库中。然后查询店铺状态的时候,从Redis中获取到值返回即可
小程序端要同步的话,同样从Redis中获取值返回即可

在高并发的情况下,频繁查询会使数据库性能下降,我们可以对其进行优化,添加缓存功能:

适合经常访问但不经常修改的数据放在Redis中比较优~
把即将查询到的数据保存到Redis中,在数据库不变化的情况下,后面的查询从Redis中读取数据,就能提高查询性能

用户访问过一次后,会去sql数据库查询数据,之后把查询到的数据保存到到Redis缓存,下次用户再查询的时候,会从Redis缓存中获取数据,提高性能

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
@Autowired //调用service查询数据库
private DishService dishService;

@Autowired 调用Redis
private RedisTemplate redisTemplate;

// 根据分类id查询菜品列表(小程序段)
@GetMapping("/list")
public Result getList(@RequestParam Long categoryId) {
//代码从上往下执行
List<DishVO> voList = null;
// ---------------------start
// 先查询redis中是否有该分类的菜品缓存
String dishKey = "DISH:" + categoryId;
if (redisTemplate.hasKey(dishKey)) { //如果有值,则代表有数据
log.info("查询缓存..."); //然后查询Redis中的缓存数据
voList = (List<DishVO>) redisTemplate.opsForValue().get(dishKey);
return Result.success(voList); //返回Redis中的缓存数据
}
// ---------------------end
// 封装dto条件
DishPageDTO dishPageDTO = DishPageDTO.builder()
.categoryId(categoryId)
.status(1)
.build();
// 调用service查询,返回结果
log.info("查询数据库...");
voList = dishService.getParamList(dishPageDTO);
// -------------------------- start
// 将数据库数据同步到缓存,从数据库查询到的数据存到缓存
redisTemplate.opsForValue().set(dishKey, voList);
// -------------------------- end
return Result.success(voList); //返回从数据库查询到的数据
}
如果修改了数据库的数据,但用户访问的时候还是读取的缓存,会出现脏读,我们必须保持数据一致性

我们需要在新增,修改,删除的时候清理缓存,在执行完操作后清理缓存,我们id是从菜品分类id中获取的,所以通过菜品分类id清理就可以了

1
redisTemplate.delete("DISH:"+dishDTO.getCategoryId());

修改菜品的时候,有可能会改,有可能不会改,所以为了保险起见,我们直接把修改的所有缓存都清理

1
2
Set dishKey = redisTemplate.keys("DISH*"); //只要是这个开头的,都清理,而不是根据id清理了
redisTemplate.delete(dishKey);

同时批量删除的时候前端只传了菜品的id,没有传分类id,所以这里我们也需要全部清理缓存