Redis入门
Redis是一个基于内存的key-value结构数据库
- 基于内存存储, 读写性能高
- 适合存储热点数据(热点商品,咨询,新闻)
- 企业应用广泛
- 中文官网: Redis中文网
- 英文网: https://rsdis.io
下载安装: Redis安装包分为Windows版本和Linux版本, Redis的windows版属于绿色软件, 解压后就能用
- 资料中已经提供了的安装包
- 解压后的核心文件
启动Redis服务: 进入到安装目录, 执行命令 redis-server.exe redis.windows.conf
- 命令格式: 启动命令 空格 配置文件
- 停止服务: ctrl + c
- redis默认执行在6379端口
- 双击 redis-server.exe 也可以快速启动redis服务
连接本地或者远程数据库
- 连接本地数据库, 执行 redis-cli.exe 命令,
- 验证连接执行 keys * 命令
- 退出连接执行 exit 命令
- 连接远程数据库, 执行 redis-cli.exe -h localhost -p 379 -a 12345 命令
- -h后边跟地址 -p后边跟端口 -a后边跟密码(默认无密码)
配置数据库的连接密码
- 关闭服务和连接
- 打开配置文件 redis.windows.conf
- 查找配置项
- 配置密码
Redis图形化操作工具
- 资料中有提供, 傻瓜式安装即可
- 使用图形化工具连接数据库
Redis数据类型
Redis存储的是key-value结构的数据, 其中key都是字符串类型, value有5种常用的数据类型
- 字符串string: 普通字符串, Redis中最简单的数据类型
- 哈希hash: 也叫散列, 类似于java中的HashMap结构, 类似于对象
- 列表list: 按照插入顺序排序, 可以有重复元素, 类似与java中的LinkedList, 类似于数组
- 集合set: 无序集合, 没有重复的元素, 类似于java中的HashSet, 类似于无需不重复的数组
- 有序集合sorted set / zset: 集合中每个元素关联一个分数(score), 根据分数升序排序, 没有重复元素
常用命令
在Redis中, 不同的数据类型, 操作数据的命令是不同的, 并且操作命令不区分大小写
字符串操作命令
set name jack
ok // 创建一个key, 名称是name, 值是jack
get name
jack // 获取name这个key的值
get aaa
null // 获取不存在的key的值, 返回null
setnx code 30 1234
ok //创建一个key,名称是code,值是1234, 有效期是30s, 到期自动删除
setnx age 18
1 // 创建一个key, 名称是age, 值是18
setnx age 20
0 // 创建失败, 因为age已经存在
哈希操作命令: Redis hash 是一个string类型的 field 和 value的映射表, hash特别适合用于储存对象
HSET 100 name xiaoming
1 // 创建一个key,名称是100, 属性名是name, 属性值是xiaoming
HSET 100 age 22
1 // 往100这个key添加属性, 属性名是age, 属性值是11
HGET 100 name
xiaoming //获取100这个key的name属性的值
HDEL 100 name
1 // 删除100这个key的name属性
HKEYS 100
age name // 获取100这个key中的所有属性名
HVALS 100
22 xiaoming // 获取100这个key中的所有属性值
列表操作命令: Redis列表是简单的字符串列表, 按照插入顺序排序, 可以理解为数组
LPUSH mylist a b c
3 //创建mylist列表,将abc 3个值插入到列表头部
LPUSH mylist a b c
4 //将d插入到列表头部
LRANGE mylist 0 -1
d c b a // 获取mylist列表指定范围内的元素
RPOP mylist
a //移除并返回列表最后一个元素
LLEN mulist
3 //获取列表长度
集合操作命令: Redis set 是string类型的无序集合, 集合成员是唯一的, 集合中不能出现重复数据
SADD set1 a b c d
4 // 新建set1集合,添加abcd 4个成员
SADD set1 a
0 // 向set1集合中添加a成员,失败,因为已存在
SMEMBERS set1
c d b a //返回set1集合中所有的成员
SCARD set1
4 // 获取set1集合的成员数
SADD set2 a b x y
4 // 新建set2集合,添加abxy 4个成员
SINTER set1 set2
b a //返回两个集合的交集
SUNION set1 set2
c y d a x b // 返回两个集合的并集
SREM set1 a
1 // 删除set1集合中的a成员
有序集合操作命令: Redis有序集合是string类型元素的集合, 且不允许有重复成员, 每个元素都会关联一个double类型的分数
ZADD zset1 10.0 a 10.5 b
2 //新建zset1集合,并添加ab两个成员,并指定成员分数
ZADD zset1 10.2 c
1 //向zset1集合添加c成员,并指定成员分数
ZRANGE zset1 0 -1
a c b //通过索引区间查询zset1集合中的成员
ZRANGE zset1 0 -1 withscore
a 10 c 10.2 b 10.5 //查询zset1集合中的成员和分数
ZINCRBY zset1 5.0 a
15 //对zset1集合中的a成员,加上5.0分
ZREM zset1 b
1 //移除zset1集合中的b成员
通用命令: 不分数据类型都可以使用的命令
KEYS *
mylist key1 name set1 set2 zset1 //查询所有key
KEYS set*
set1 set2 //查询所有以set开头的key
EXISTS name
1 //检查给定Key是否存在
TYPE name
string //返回key所储存的值的类型
DEL name set1 zset1
3 //在key存在时删除key
java操作Redis
Redis的java客户端很多, 根据项目情况按需选择
- Jedis: 对Redis命令进行封装, 语法与Redis命令保持一致
- Lettuce: 基于多线程的Redis封装,特点是性能好
- Spring Date Redis: 是Spring的一部分, 对Redis底层开发包进行了高度封装, 简化Redis操作
Spring Data Redis入门
1.导入mavn坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.配置Redis数据源
spring:
redis:
host: ${sky.redis.host}
port: ${sky.redis.port}
database: ${sky.redis.database}
sky:
redis:
host: localhost
port: 6379
password: 123456
database: 0
- 如果没有配置密码, 密码项可以省略
- redis会默认创建16个独立的数据库, 默认使用0号数据库
- 可以修改 datebase 属性切换数据库
- 每个数据库的数据是隔离的
3.编写配置类,创建RedisTemplate对象
@Configuration //标明配置类
@Slf4j
public class RedisConfiguration {
@Bean //项目启动时完成自动配置(按照类型自动注入对象)
public RedisTemplate redisTemplate (RedisConnectionFactory redisConnectionFactory) {
// 1.redisConnectionFactory是redis连接工厂对象
// 2.这个对象不用我们创建, redis起步依赖中已经创建好了
// 3.我们只需要通过@Bean注解, 让spring把这个对象注入进来就行
log.info("开始创建redis模版对象...");
RedisTemplate redisTemplate = new RedisTemplate<>();
// 设置redis的连接工厂对象
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 设置redis key的序列化器
// StringRedisSerializer() -> 创建字符串类型的redis序列化器
redisTemplate.setKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
}
4.通过RedisTemplate对象操作Redis
新建单元测试类: test/java/右键/new/java class/com.sky.test.SpringDataRedisTest
@SpringBootTest // 声明单元测试类
public class SpringDataRedisTest {
@Autowired
private RedisTemplate redisTemplate;
@Test // 声明单元测试方法
public void testRedisTemplate() {
// 检查redis模版对象是否成功创建
System.out.println(redisTemplate);
// 1.redis中不同的数据类型有不同的操作命令
// 2.要通过redis模版对象,拿到数据操作对象
// 3.通过不同的数据操作对象操作数据
ValueOperations valueOperations = redisTemplate.opsForValue();
HashOperations hashOperations = redisTemplate.opsForHash();
ListOperations listOperations = redisTemplate.opsForList();
SetOperations setOperations = redisTemplate.opsForSet();
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
}
}
使用字符串操作对象操作字符串类型的数据
@SpringBootTest // 声明单元测试类
public class SpringDataRedisTest {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void testString(){
//原始命令 set get setex setnx
redisTemplate.opsForValue().set("city","北京"); // 添加数据
String city = (String) redisTemplate.opsForValue().get("city"); //获取数据
redisTemplate.opsForValue().set("code","1234",3, TimeUnit.MINUTES); //添加数据(设置超时时间)
redisTemplate.opsForValue().setIfAbsent("lock","1"); //只有在key不存在时添加数据
}
}
- 设置redis的key的序列化器的作用
使用哈希操作对象操作哈希类型的数据
@SpringBootTest // 声明单元测试类
public class SpringDataRedisTest {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void textHash() {
//原始命令 hset hget hdel hkeys hvals
HashOperations hashOperations = redisTemplate.opsForHash();
hashOperations.put("100", "name", "tom"); // 设置数据
hashOperations.put("100", "age", "18"); // 设置数据
String name = (String) hashOperations.get("100", "name"); // 获取指定数据
Set keys = hashOperations.keys("100"); // 获取所有key
List values = hashOperations.values("100"); // 获取所有value
hashOperations.delete("100", "age"); // 删除指定数据
}
}
使用列表操作对象操作列表类型的数据
@SpringBootTest // 声明单元测试类
public class SpringDataRedisTest {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void testList() {
// 原始命令 lpush lrange rpop llen
ListOperations listOperations = redisTemplate.opsForList();
listOperations.leftPushAll("mylist", "a", "b", "c"); // 插入多条数据
listOperations.leftPush("mylist", "d"); // 插入单条数据
List mylist = listOperations.range("mylist", 0, -1); // 查询全部数据
listOperations.rightPop("mylist"); // 移除一条数据
Long size = listOperations.size("mylist");//查看列表长度
}
}
使用集合操作对象操作集合类型的数据
@SpringBootTest // 声明单元测试类
public class SpringDataRedisTest {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void testSet() {
// 原始命令 sadd smembers scard sinter sunion srem
SetOperations setOperations = redisTemplate.opsForSet();
setOperations.add("set1", "a", "b", "c", "d"); // 创建集合1
setOperations.add("set2", "a", "b", "x", "y"); // 创建集合2
Set members = setOperations.members("set1"); // 获取集合成员
Long size = setOperations.size("set1");// 获取集合长度
Set intersect = setOperations.intersect("set1", "set2");// 获取两个集合的交集
setOperations.union("set1", "set2"); // 获取两个集合的并集
setOperations.remove("set1", "a", "b"); // 删除集合成员
}
}
使用有序集合操作对象操作有序集合类型的数据
@SpringBootTest // 声明单元测试类
public class SpringDataRedisTest {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void testZset() {
// 原始命令 zadd zrange zincrby zrem
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
zSetOperations.add("zset1", "a", 10); // 添加成员
zSetOperations.add("zset1", "b", 12); // 添加成员
zSetOperations.add("zset1", "c", 9); // 添加成员
Set zset1 = zSetOperations.range("zset1", 0, -1); // 获取zset所有成员
zSetOperations.incrementScore("zset1", "c", 10); // 修改成员的排序值
zSetOperations.remove("zset1", "a", "b"); // 移除成员
}
}
使用redis模版对象执行通用操作命令
@SpringBootTest // 声明单元测试类
public class SpringDataRedisTest {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void testCommon() {
// 原始命令 keys exists type del
Set keys = redisTemplate.keys("*"); //获取所有的key
System.out.println(keys);
Boolean name = redisTemplate.hasKey("name"); //判断是否存在这个key
Boolean set1 = redisTemplate.hasKey("set1"); //判断是否存在这个key
// 遍历所有key
for (Object key: keys) {
DataType type = redisTemplate.type(key); //获取key的类型
System.out.println(type.name()); //输出类型的名称
}
redisTemplate.delete("mylist"); //删除key
}
}
店铺状态设置
查看原型, 分析需求, 在管理端可以设置店铺状态
接口设计: 要设计三个接口, 设计营业状态接口, 客户端查询店铺状态接口, 管理端查询店铺状态接口
- 管理端发出的请求, 统一使用/admin作为前缀
- 用户端发出的请求 ,统一使用/user作为前缀
数据库设计: 如果针对一个字段创建一张表其实并不合适, 所以使用redis数据库, 基于Redis的字符串进行储存
管理端接口
@RestController("adminShopController") // 指定bean的名字
@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);
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);
return Result.success(status);
}
}
客户端接口
@RestController("userShopController") // 指定bean的名字
@RequestMapping("/user/shop")
@Api(tags = "店铺相关接口")
@Slf4j
public class ShopController {
public static final String KEY = "SHOP_STATUS";
@Autowired
private RedisTemplate redisTemplate;
/**
* 获取店铺的营业状态
*
* @return
*/
@GetMapping("/status")
@ApiOperation("获取店铺的营业状态")
public Result<Integer> getStatus() {
Integer status = (Integer) redisTemplate.opsForValue().get(KEY);
log.info("获取到店铺营业状态:{}", status);
return Result.success(status);
}
}
前后端联调测试
分组扫描接口文档: 通过groupName("描述")方法设置接口文档分组扫描
/**
* 配置类,注册web层相关组件
*/
@Configuration
@Slf4j
public class WebMvcConfiguration extends WebMvcConfigurationSupport {
/**
* 通过knife4j生成接口文档
*
* @return
*/
@Bean
public Docket docket1() {
ApiInfo apiInfo = new ApiInfoBuilder()
.title("苍穹外卖项目接口文档")
.version("2.0")
.description("苍穹外卖项目接口文档")
.build();
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.groupName("管理端接口")
.apiInfo(apiInfo)
.select()
.apis(RequestHandlerSelectors.basePackage("com.sky.controller.admin"))
.paths(PathSelectors.any())
.build();
return docket;
}
/**
* 通过knife4j生成接口文档
*
* @return
*/
@Bean
public Docket docket2() {
ApiInfo apiInfo = new ApiInfoBuilder()
.title("苍穹外卖项目接口文档")
.version("2.0")
.description("苍穹外卖项目接口文档")
.build();
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.groupName("客户端接口")
.apiInfo(apiInfo)
.select()
.apis(RequestHandlerSelectors.basePackage("com.sky.controller.user"))
.paths(PathSelectors.any())
.build();
return docket;
}
}