跳到主要内容

基础

入门

简介

Redis 是用 C 语言开发的一个开源的高性能键值对(key-value)数据库,官方提供的数据是可以达到 100000+ 的 QPS(每秒内查询次数)。它存储的 value 类型比较丰富,也被称为结构化的 NoSQL 数据库

NoSql(Not Only SQL),不仅仅是 SQL,泛指非关系型数据库。NoSQL 数据库并不是要取代关系型数据库,而是关系型数据库的补充 关系型数据库:

  • Mysql
  • Oracle
  • DB2
  • SQLServer

非关系型数据库:

  • Redis
  • Mongo DB
  • MenCached

Redis应用场景:

  • 缓存
  • 任务队列
  • 消息队列
  • 分布式锁

下载和安装

下载地址:https://github.com/MicrosoftArchive/redis/releases

Linux 安装:

  1. 将 Redis 安装包上传到 Linux
  2. 解压安装包,命令:tar -zxvf redis-4.0.0.tar.gc -C /usr/local
  3. 安装 Redis 的依赖环境 gcc,命令:apt-get install gcc-c++
  4. 进入/usr/local/redis-4.0.0,进行编译,命令:make
  5. 进入 Redis 的 src 目录,进行安装,命令:make install

Windows:

Redis 的 Windows 是绿色软件,直接解压使用即可,解压后目录结构如下:

20240516215257

服务启动和停止

Linux:

进入 src 目录,使用 redis-server,默认端口号为 6379

Ctrl+C 停止 Redis 服务

修改 redis.conf,将daemonize no修改为daemonize yes这样 Redis 就可以后台运行了

Windows:

双击 redis-server.exe 启动服务

Ctrl+C 停止服务

设置密码

打开redis.windows-service.conf配置文件,找到requirepass foobared字样,在其后面追加一行,输入requirepass 123456。这是访问 Redis 时所需的密码,一般测试情况下可以不用设定密码。不过,即使是作为本地访问,也建议设定一个密码

20240516215400

Linux同理,Linux中配置文件为 redis.conf Linux登录:src/redis-cli -h localhost -a 123456

远程连接

将配置文件中的 bind 注释掉

数据类型

介绍

Redis 存储的是 key-value 结构的数据,其中 key 是字符串类型,value 有五种常用的数据类型

  • 字符串 string
  • 哈希 hash
  • 列表 list
  • 集合 set
  • 有序集合 sorted set

常用数据类型

20240516215436

常用命令

字符串常用命令

常用命令:

  • SET key value设置指定 key 的值
  • GET key获取指定 key 的值
  • SETEX key seconds value设置指定 key 的值,并将 key 的过期时间设置为 seconds 秒
  • SETNX key value只有在 key 不存在时设置 key 的值

Key的结构

Redis 的 Key 允许有多个单词组成层级结构,多个单词之间用 ':' 隔开,格式如下:

20240516215521

这个格式并非固定,也可以根据自己的需求来删除或添加词条

例如项目名叫 heima,有 user 和 product 两种不同类型的数据,我们可以这样定义 Key:

  • user 相关的 Key:heima:user:1
  • product 相关的 Key:heima:product:1

如果 Value 是一个 Java 对象,例如一个 User 对象,则可以将对象序列化为 JSON 字符串后存储:

20240516215533

哈希操作命令

Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象,常用命令:

  • HSET key field value:将哈希表key中的字段 field 的值设置为 value
  • HGET key field:获取存储在哈希表中指定字段的值
  • HDEL key field:删除存储在哈希表中的指定字段值
  • HKEYS key:获取哈希表中所有的字段
  • HVALS key:获取哈希表中所有值
  • HGETALL key:获取在哈希表中指定 key 的所有字段和值

20240516215634

列表操作命令

Redis 列表是简单的字符串列表,按照插入顺序排序,常用命令:

  • LPUSH key value1 value2:将一个或多个值插入到列表头部
  • LRANGE key start stop:获取列表指定范围内的元素
  • RPOP key:移除并获取列表最后一个元素
  • LLEN key:获取列表长度
  • BRPOP key1 key2 timeout:移除并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止

20240516215723

20240516215729

集合操作命令

Redis set 是 string 类型的无序集合,集合成员是唯一的,这就意味着集合中不能出现重复的数据,常用命令:

  • SADD key member1 member2:向集合中添加一个或多个成员
  • SMEMBERS key:返回集合中的所有成员
  • SCARD key:获取集合中的成员数
  • SINTER key1 key2:返回给定所有集合的交集
  • SUNION key1 key2:返回所有给定集合的并集
  • SDIFF key1 key2:返回给定所有集合的差集
  • SREM key member1 member2:移除集合中一个或多个成员

20240516215820

有序集合操作命令

Redis sorted set 有序集合是 string 类型元素的集合,且不允许重复的成员。每个元素都会关联一个 double 类型的分数(score)。Redis 正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但是分数可以重复

常用命令:

  • ZADD key score1 member1 score2 member2:向有序集合中添加一个或多个成员,或者更新已存在成员的分数
  • ZRANGE key start stop WITHSCORE:通过索引区间返回有序集合中指定区间内的成员
  • ZINCRBY key increment member:有序集合中对指定成员的分数加上增量increment 20240516215935
  • ZREM key member member...:移除有序集合中的一个或多个成员

20240516215943

通用命令

  • KEYS pattern:查找所有符合给定模式(pattern)的 key
  • EXISTS key:检查给定 key 是否存在
  • TYPE key:返回 key 所存储的值的类型
  • TTL key:返回给定 key 的剩余生存时间(TTL,time to live),以秒为单位
  • DEL key:用于在 key 存在时删除 key

注意

Redis 默认操控的是 0 号数据库,可以通过select index来进行数据库切换,Redis 默认提供了 16 个数据库,可以在配置文件中修改数量

在Java中操作Redis

介绍

Redis 的 Java 客户端很多,官方推荐三种:

  • Jedis
  • Lettuce
  • Redission

Spring 对 Redis 客户端进行了整合,提供了 Spring Data Redis,在 Spring Boot 项目中还提供了对应的 Starter,即spring-boot-starter-data-redis

Jedis

Jedis 的 Maven 坐标

<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.0</version>
</dependency>

使用 Jedis 操作 Redis 的步骤是:

//1. 获取连接
Jedis jedis = new Jedis("localhost",6379);
//2. 执行操作
jedis.set("username","hsy");
//3. 关闭连接
jedis.close();

Spring Data Redis

在 Spring Boot 项目中,可以使用 Spring Data Redis 来简化 Redis 操作,Maven 坐标:

<!--Redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--连接池依赖-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>

Spring Data Redis 中提供了一个高度封装的类:RedisTemplate,针对 jedis 客户端中大量 api 进行了归类封装,将同一类型操作封装为 operation 接口,具体分类如下:

  • ValueOperations 简单K-V操作
  • SetOperationsset 类型数据操作
  • ZSetOperatonszset 类型数据操作
  • HashOperationsmap 类型数据操作
  • ListOperationslist 类型数据操作

20240516220201

配置文件

spring:
application:
name: springdataredis
#Redis相关配置
redis:
host: localhost
port: 6379
#password: 123456
database: 0 #默认操控的是零号数据库
jedis:
#Redis连接池设置
pool:
max-active: 8 #最大连接数
max-wait: 1ms #连接池阻塞最大等待时间
max-idle: 4 #连接池中的最大空闲连接
min-idle: 0 #连接池中的最小空闲连接
@Autowired
private RedisTemplate redisTemplate;

@Test
public void testString(){
redisTemplate.opsForValue().set("city","beijing");
}

运行上述代码,会发现 key 有问题,是框架序列化导致的

20240516220219

解决方法:

创建一个配置类 RedisConfig

@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
// 创建Template
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
// 设置连接工厂
redisTemplate.setConnectionFactory(connectionFactory);
// 设置序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// key和hashKey采用String序列化
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setHashKeySerializer(RedisSerializer.string());
// value和hashValue采用JSON序列化
redisTemplate.setValueSerializer(jsonRedisSerializer);
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
return redisTemplate;
}
}

注意:

如果运行后报错,则表示需要引入依赖:

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>

在日常开发不需要引入这个依赖,因为 Spring MVC 中自带这个依赖

20240516220240

value 的值不用管,在代码中获取之后会进行反序列化,会还原成原来的样子

例子:

package xyz.kbws;

import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.*;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;
import java.util.Set;

@SpringBootTest
@RunWith(SpringRunner.class)
class SpringdataredisApplicationTests {

@Autowired
private RedisTemplate redisTemplate;

/**
* 操作string类型数据
*/
@Test
public void testString(){
redisTemplate.opsForValue().set("city","beijing");
String value = (String) redisTemplate.opsForValue().get("city");
System.out.println(value);

//如果key已经存在则返回false,表示没有执行操作
Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent("city", "nanjing");
}
/**
* 操作Hash类型数据
*/
@Test
public void testHash(){
HashOperations hashOperations = redisTemplate.opsForHash();

//存值
hashOperations.put("002","name","hsy");
hashOperations.put("002","age","20");
hashOperations.put("002","address","bj");

//取值
String name = (String) hashOperations.get("002", "name");

//获取Hash结构中的所有字段
Set keys = hashOperations.keys("002");
for (Object key:keys){
System.out.println(key);
}
//获取Hash结构中所有的值
List values = hashOperations.values("002");
for (Object value : values) {
System.out.println(value);
}
}
/**
* 操作List类型数据
*/
@Test
public void testList(){
ListOperations listOperations = redisTemplate.opsForList();

//存值
listOperations.leftPush("mylist","a");
listOperations.leftPushAll("mylist","b","c","d");
//取值
List<String> mylist = listOperations.range("mylist", 0, -1);
for (String o : mylist) {
System.out.println(o);
}
//获得列表长度
Long size = listOperations.size("mylist");
int lSize = size.intValue();
for (int i = 0; i < lSize; i++) {
//出队列
String element = (String) listOperations.rightPop("mylist");
System.out.println(element);
}
}
/**
* 操作Set类型的数据
*/
@Test
public void testSet(){
SetOperations setOperations = redisTemplate.opsForSet();
//存值
setOperations.add("myset","a","b","c","a");
//取值
Set<String> myset = setOperations.members("myset");
for (String s : myset) {
System.out.println(s);
}
//删除
setOperations.remove("myset","a","b");
}
/**
* 操作ZSet
*/
@Test
public void testZSet(){
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
//存值
zSetOperations.add("myZSet","a",10.0);
zSetOperations.add("myZSet","b",11.0);
zSetOperations.add("myZSet","c",12.0);
zSetOperations.add("myZSet","a",13.0);
//取值
Set<String> myZSet = zSetOperations.range("myZSet", 0, -1);
for (String s : myZSet) {
System.out.println(s);
}
//修改分数
zSetOperations.incrementScore("myZSet","b",20.0);
//删除成员
zSetOperations.remove("myZSet","a","b");
}
/**
* 通用操作
*/
@Test
public void testCommon(){
//获取Redis中所有的key
Set<String> keys = redisTemplate.keys("*");
for (String key : keys) {
System.out.println(key);
}
//判断某个key是否存在
Boolean itcast = redisTemplate.hasKey("itcast");
//删除指定key
redisTemplate.delete("myZSet");
//获取指定key对应的value的数据类型
DataType myset = redisTemplate.type("myset");
System.out.println(myset.name());
}
}

尽管 JSON 的序列化方式可以满足我们的需求,但依然存在一些问题,如图:

20240516220259

为了在反序列化时知道对象的类型,JSON 序列化器会将类的 Class 类型写入 JSON 结果中,存入 Redis,会带来额外的内存开销

为了节省内存空间,我们并不会使用 JSON 序列化器来处理 Value,而是统一使用 String 序列化器,要求只能存储 String 类型的 Key 和 Value。当需要存储 Java 对象时,手动完成对象的序列化和反序列化

20240516220318

Spring 默认提供了一个 StringRedisTemplate 类,他的 Key 和 Value 的序列化方式默认就是 String 类型,省去了我们自定义 RedisTemplate 的过程

20240516220328