Redis安装
首先是Redis的下载地址,事实上,Redis已经出到7的版本了,我们这里使用的是5的版本。(3也能用)
Redis下载地址
我们将Redis
下载下来并解压:
我们如何启动呢?
redis-server.exe redis.windows.conf
使用客户端连接redis:
redis-cli.exe
当然,我们还可以连接其他主机上的redis数据库
redis-cli.exe -h localhost -p 6379
随后,我们想到,Redis作为一种数据库,为何没有像MySQL一样指定用户名与密码呢,事实上,Redis是没有用户的,只需要指定密码即可,而默认的redis是不指定密码的,因此我们可以自己设置一下,打开redis.windows.conf
文件,搜索pass
,大约在502行,有段代码:#requirepass foobared
,将前面的#号取消,后面将foobared
改为我们设置的密码即可,如下:
随后我们将Redis的服务关闭,随后再次输入先前的连接命令,发现需要认证。
随后在先前的命令后加上认证用户的密码即可。
redis-cli.exe -h localhost -p 6379 -a 123456
此时我们发现,我们每次使用这个命令行来操作redis很不方便,因此我们最终选择使用一个图形化界面Another Redis Desktop Manager
来操作redis。
安装成功后点击新建链接,我们输入下面的内容:
随后我们就可以看到redis目前的使用情况了:
Redis中的数据类型
Redis
是一种基于内存的Key-Value
结构型数据库,其特点与应用场景如下:
- 基于内存存储,读写性能高
- 适合存储热点数据(热点商品、资讯、新闻)
Redis
的key
是一种字符串类型,而value
的基本数据类型则为以下几种:
- 字符串 string
- 哈希 hash
- 列表 list
- 集合 set
- 有序集合 sorted set/zset
下面对这五种数据类型的特点以及所适合的应用场景进行介绍:
字符串(string
):普通字符串,Redis
中最简单的数据类型
哈希(hash
):也叫散列,类似于Java中的HashMap
结构,它包含一个域与一个值,用于存储对象比较合适
列表(list
):按照插入顺序排序,可以有重复元素,类似于Java中的LinkedList
,如朋友圈点赞顺序
集合(set
):无序集合,没有重复元素,类似于Java
中的Hashset
,朋友圈交叉,求交集
有序集合(sorted set/zset
):集合中每个元素关联一个分数(score
),根据分数升序排序,没有重复元素,,排行榜
常用命令
Redis 字符串类型常用命令:
命令格式 | 作用 |
---|---|
SET key value | 设置指定key的傎 |
GET key | 获取指定key的值 |
SETEX key seconds value | 设置指定key的值,并将 key 的过期时间设为 seconds 秒(生成验证码) |
SETNX key value | 只有在 key 不存在时设置 key 的值(分布式锁) |
直接在Redis的图形界面中操作即可。
字符串常用命令
哈希操作常用命令
Redis hash 是一个string类型的 field 和 value 的映射表,hash特别适合用于存储对象,常用命令:
命令 | 含义 |
---|---|
HSET key field value | 将哈希表 key 中的字段 field 的值设为 value |
HGET key field | 获取存储在哈希表中指定字段的值 |
HDEL key field | 删除存储在哈希表中的指定字段 |
HKEYS key | 获取哈希表中所有字段 |
HVALS key (VALS代表Values) | 获取哈希表中所有值,通过key获取value,这个value是第二级value值 |
列表操作命令
Redis 列表是简单的字符串列表,按照插入顺序排序,常用命令:
命令 | 含义 |
---|---|
LPUSH key value1 [value2] | 将一个或多个值插入到列表头部,这里的L不是List而是Left |
LRANGE key start stop | 获取列表指定范围内的元素 |
RPOP key | 移除并获取列表最后一个元素 |
LLEN key | 获取列表长度 |
集合操作命令
Redis set 是string类型的无序集合。集合成员是唯一的,集合中不能出现重复的数据,常用命令:
命令 | 含义 |
---|---|
SADD key member1 [member2] | 向集合添加一个或多个成员 |
SMEMBERS key | 返回集合中的所有成员(members) |
SCARD key | 获取集合的成员数 |
SINTER key1 [key2] | 返回给定所有集合的交集 |
SUNION key1 [key2] | 返回所有给定集合的并集 |
SREM key member1 [member2] | 删除集合中一个或多个成员 |
有序集合操作命令
Redis有序集合是string类型元素的集合,且不允许有重复成员。每个元素都会关联一个double类型的分数。常用命令:
命令 | 含义 |
---|---|
ZADD key score1 member1 [score2 member2] | 向有序集合添加一个或多个成员 |
ZRANGE key start stop [WITHSCORES] | 通过索引区间返回有序集合中指定区间内的成员 |
ZINCRBY key increment member | 有序集合中对指定成员的分数加上增量increment (增加increase) |
ZREM key member [member …] | 移除有序集合中的一个或多个成员 |
通用命令操作
Redis的通用命令是不分数据类型的,都可以使用的命令
命令 | 含义 |
---|---|
KEYS pattern | 查找所有符合给定模式( pattern)的 key |
EXISTS key | 检查给定 key 是否存在 |
TYPE key | 返回 key 所储存的值的类型 |
DEL key | 该命令用于在 key 存在是删除 key |
Redis在Java中的使用
Redis
的 Java
客户端很多,常用的几种:
- eJedis:官方指定,命令和Redis的命令相同
- Lettuce:性能好
- Spring Data Redis:Spring Data Redis 是Spring的一部分,对 Redis 底层开发包进行了高度封装。
在 Spring 项目中,可以使用Spring Data pedis来简化操作
由于我们使用的是Spring框架,因此我们选用Spring Data Redis来进行操作,其操作遵循下面几个步骤:
- 导入
Spring Data Redis
的maven
坐标 - 配置
Redis
数据源 - 编写配置类,创建
RedisTemplate
对象 - 通过
RedisTemplate
对象操作Redis
导入Spring Data Redis
的maven
坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置Redis
数据源,其与配置mysql的datasource是平级的
sky:
redis:
host: 127.0.0.1
port: 6379
password: 123456
database: 10
这里需要注意database: 10的含义,代表使用第11个数据库,当然这个可以不配置,默认为0
编写配置类,创建RedisTemplate
对象,加入@Configuration注解,会在服务启动时加载
package com.sky.config;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
@Slf4j
public class RedisConfiguration {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
log.info("开始创建redis模板对象...");
RedisTemplate redisTemplate = new RedisTemplate();
//设置redis的连接工厂对象
redisTemplate.setConnectionFactory(redisConnectionFactory);
//设置redis key的序列化器
redisTemplate.setKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
}
那么如何使用呢?针对Redis的五种数据类型,Spring封装了不同的接口:
package com.sky.test;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.*;
import java.util.concurrent.TimeUnit;
@SpringBootTest
public class SpingBootRedisTest {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void test(){
System.out.println(redisTemplate);
//针对Redis常用的五种数据类型,Spring分别封装了对应的接口用于操作各个数据类型
//操作String类型
ValueOperations valueOperations = redisTemplate.opsForValue();
//操作hash类型
HashOperations hashOperations = redisTemplate.opsForHash();
//操作List列表类型
ListOperations listOperations = redisTemplate.opsForList();
//操作集合类型
SetOperations setOperations = redisTemplate.opsForSet();
//操作有序集合类型
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
}
}
测试String类型
@Test
public void testString(){
//set get setex setnx
redisTemplate.opsForValue().set("name","鹏翔");
//获取set
String name= (String) redisTemplate.opsForValue().get("name");
//setex 设置时间
redisTemplate.opsForValue().set("code","1234",10, TimeUnit.MINUTES);
//setnx 不存在则创建
redisTemplate.opsForValue().setIfAbsent("lock","1");
redisTemplate.opsForValue().setIfAbsent("lock","2");//
}
我们选用的数据库是10号,查看一下,刚刚的数据已经插入成功了,但我们发现key没有问题,但value无论是英文还是中文都貌似出现了乱码,事实上其并不是乱码,而是由于我们在使用Java对Redis进行操作时对Value进行了序列化,事实上我们的key原本也是要进行序列化的,但我们在配置时给设置了String类型的序列化,因此就没有问题了,而Value的类型是不确定的,因此不能指定。
//设置redis key的序列化器
redisTemplate.setKeySerializer(new StringRedisSerializer());
如果不对Key进行String序列化,则会调用默认的序列化器,结果便是下面这样:
测试Hash类型
@Test
public void testhash(){
//hset hget hdel hkeys hvals
HashOperations hashOperations = redisTemplate.opsForHash();
hashOperations.put("person1","name","鹏翔");
hashOperations.put("person1","age","18");
String o = (String) hashOperations.get("person1", "name");
System.out.println(o);
Set keys = hashOperations.keys("person1");
System.out.println(keys);
List values = hashOperations.values("person1");
System.out.println(values);
hashOperations.delete("person1","age");
}
结果如下:
测试List类型
@Test
public void testList(){
ListOperations listOperations = redisTemplate.opsForList();
//lpush lrange rpop llen
//pushAll可以插入一个或多个
listOperations.leftPushAll("mylist","a","b","c","d");
//push只能插入一个
listOperations.leftPushAll("mylist","e");
//读取List
List mylist = listOperations.range("mylist", 0, -1);
System.out.println(mylist);
//获取List的大小
Long size = listOperations.size("mylist");
System.out.println(size);
listOperations.rightPop("mylist");//从右端删除元素
Long size1 = listOperations.size("mylist");
System.out.println(size1);
}
测试Set类型
@Test
public void testSet(){
//sadd smembers scard sinter sunion srem
SetOperations setOperations = redisTemplate.opsForSet();
//向集合内插入成员
setOperations.add("set1","王维","李白","白居易");
setOperations.add("set2","王昌龄","李白","杜甫");
//查看集合内的成员
Set set1 = setOperations.members("set1");
System.out.println(set1);
//查看集合的大小
Long size = setOperations.size("set1");
System.out.println(size);
//求两个集合的并集
Set union = setOperations.union("set1", "set2");
System.out.println(union);
//求两个集合的交集
Set intersect = setOperations.intersect("set1", "set2");
System.out.println(intersect);
//删除集合内的指定值
Long remove = setOperations.remove("set1", "李白");
System.out.println(remove);
}
测试ZSet类型
@Test
public void testZSet(){
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
//zadd zrange zincrby zrem
//添加成员
zSetOperations.add("scores","三国演义",10);
zSetOperations.add("scores","水浒传",9);
zSetOperations.add("scores","红楼梦",10);
zSetOperations.add("scores","西游记",8);
//遍历成员
Set score1 = zSetOperations.range("scores", 0, -1);
System.out.println(score1);
//加分
zSetOperations.incrementScore("socres","三国演义",1);
Set score2 = zSetOperations.range("scores", 0, -1);
System.out.println(score2);
//删除成员
zSetOperations.remove("scores","三国演义","水浒传");
Set score3 = zSetOperations.range("scores", 0, -1);
System.out.println(score3);
}
测试通用类型
@Test
public void testcommon(){
//keys exists type del
Set keys = redisTemplate.keys("*");
System.out.println(keys);
Boolean name = redisTemplate.hasKey("name");
System.out.println(name);
for(Object key:keys){
DataType type = redisTemplate.type(key);
System.out.println(type);
}
redisTemplate.delete("name");
}
至此,完成了Redis各种数据类型的测试,注意,测试完成后,我们应该将SpringBootTest的注解注释掉,否则每次启动服务都会进行测试,影响速度。
应用场景
营业状态设置
营业状态只有两个值,没必要再设计一个数据表,因此可以使用Redis来存储。这个接口可以设置两个,分别是用户端与管理端
package com.sky.controller.admin;
import com.sky.result.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
@RestController("adminShopController")//注意这里要起个别名,因为User端有个名字一样的
@RequestMapping("/admin/shop")
@Api(tags = "店铺相关接口")
@Slf4j
public class ShopController {
public static final String KEY = "SHOP_STATUS";
@Autowired
private RedisTemplate redisTemplate;
/**
* 设置店铺的营业状态
* @param status
* @return
*/
@PutMapping("/{status}")
@ApiOperation("设置店铺的营业状态")
public Result setStatus(@PathVariable Integer status) {
log.info("设置店铺的营业状态为:{}",status == 1 ? "营业中" : "打烊中");
redisTemplate.opsForValue().set(KEY, status);
return Result.success();
}
/**
* 获取店铺的营业状态
* @return
*/
@GetMapping("/status")
@ApiOperation("获取店铺的营业状态")
public Result<Integer> getStatus(){
Integer status = (Integer) redisTemplate.opsForValue().get(KEY);
log.info("获取到店铺的营业状态为:{}",status == 1 ? "营业中" : "打烊中");
return Result.success(status);
}
}
清除缓存
通过RedisTemplate
对象操作Redis
,这个功能多用在用户端。
其主要有清理缓存操作,由于我们的数据被修改了,那么原本的就要被清理掉
private void clearCache(String pattern) {
Set keys = redisTemplate.keys(pattern);
redisTemplate.delete(keys);
}
查询信息
查询菜品前,先查Redis
,看是否有,没有则再查MySQL
,并将查到的结果放入到Redis
中
@GetMapping("/list")
@ApiOperation("根据分类id查询菜品")
public Result<List<DishVO>> list(Long categoryId) {
// 构造redis中的key,规则:dish_分类Id
String key = "dish_" + categoryId;
// 查询redis中是否存在菜品数据
List<DishVO> list = (List<DishVO>) redisTemplate.opsForValue().get(key);
if (list != null && list.size() > 0) {
// 如果存在,直接返回,无需查询数据库
return Result.success(list);
}
// 如果不存在,查询数据库,将查询到的数据放入redis中
Dish dish = new Dish();
dish.setCategoryId(categoryId);
dish.setStatus(StatusConstant.ENABLE);//查询起售中的菜品
list = dishService.listWithFlavor(dish);
// 放入redis
redisTemplate.opsForValue().set(key, list);
return Result.success(list);
}