简单来说,Redis是基于内存的 key - value 存储系统。官方文档:https://redis.io/docs/
Redis 是 NoSQL 的一种。
关于SQL 与 NoSQL
SQL 是关系型数据库,数据是以表格形式存储的,是结构化的(有约束,有字段长度、类型等),比如 MySQL,Oracle等,它们使用SQL作为查询语言。强调数据的一致性和完整性,提供ACID(原子性、一致性、隔离性、持久性)事务保证
NoSQL 是非关系型数据库,通常不遵循关系数据库的结构规范,可以是 Redis 这种键值型存储的,还有 MongoDB 这种文档型存储的等等。每个NoSQL数据库都有自己独特的查询语言。通常提供最终一致性,可能牺牲一些数据一致性以换取更高的性能和可扩展性。
应用场景上:
- SQL 数据库:数据结构固定,相关业务对数据安全性,一致性要求高的场景
- NoSQL 数据库:数据结构不固定,对安全性,一致性要求不高,对性能要求高的场景
为什么需要 Redis
随着应用系统访问量越来越大,数据库性能会达到瓶颈。由于内存的读写速度远高于磁盘,所以如果把数据存到内存中,就可以大大提高性能,于是就有了Redis。
Redis 最核心的特性就是性能高,因为它基于内存,能够提供快速的数据存取能力。其他特性,如支持数据持久化(内存–>磁盘)、主从复制、哨兵模式和高可用性等,都是围绕着提高数据存取效率这一核心目的来构建的。
Redis 安装、配置和启动
参考B站 up 的视频
【Windows和Linux安装Redis】 https://www.bilibili.com/video/BV1ZR4y1c7PH/?share_source=copy_web&vd_source=f2d7b32c487a37a3e88341b014bc8114
Redis 的三种使用方式
CLI 命令行
API 写代码的方式操作Redis
GUI 图形用户界面
- Another Redis Desktop Manager,安装:https://github.com/qishibo/AnotherRedisDesktopManager/releases
Redis 通用命令
可以查官方文档:https://redis.io/commands/,通过按组筛选找到需要的命令 Filter by group…
或者可以命令行输入 help @ 查看命令,比如 help @Generic,但不如官方文档详细
Generic,通用命令
-
KEYS:查看符合模板的所有key。效率低不建议在生产环境上使用
-
DEL:删除指定的一个或多个key,如果某个key不存在,则忽略该key。
-
EXISTS:如果key存在,则返回
-
EXPIRE:给一个key设置有效期,到期自动删除,最好每个key都设置有效期
-
TTL:查看一个key的剩余有效期,-1永久有效,-2失效
Redis 数据结构
key 是字符串,value 支持不同数据类型
具体命令,查官方文档:https://redis.io/commands/,通过按组筛选找到需要的命令 Filter by group…
五种基本数据类型
String 字符串(最常用)
List 列表
Set 集合
SortedSet 有序集合
Hash 哈希
五种高级数据类型
Stream 消息队列
Geospatial 地理空间
HyperLogLog
Bitmap 位图
Bitfield 位域
Redis 的 Java 客户端
Jedis
独立于 Spring 操作 Redis 的 客户端,提供了接近 Redis 原生命令的 API,使得开发者能直接、方便地执行 Redis 命令。
通过 JedisPool
类实现连接池功能,以复用连接,减少创建和销毁连接的开销,提高应用程序性能。
虽然单个 Jedis 实例不是线程安全的,但当配合连接池使用时,每个线程从池中获取自己的 Jedis 实例,从而保证了线程安全。
Lettuce
高阶 的操作 Redis 的 Java 客户端
支持同步、异步和响应式编程,并且是线程安全的
Spring Data Redis
关于 Spring Data,它是 Spring 的一部分,简单来说是一个帮助程序员更容易与各种数据库交互的工具。
Spring Data Redis 整合了 Lettuce 和 Jedis,提供了 RedisTemplate 工具类统一API来操作 Redis。
-
引入依赖
spring-boot-starter-data-redis
-
在 application.yml 中配置Redis信息。注意:
Spring Boot 2.x,配置的命名空间是 spring.redis
springboot 3.x ,配置的命名空间是 spring.data.redis
spring: application: name: springboot3-redis data: redis: host: 192.168.200.130 port: 6379 database: 1 password: 123456 timeout: 3000ms lettuce: # springboot2.x后默认使用lettuce,如果选择Jedis,需要额外引入它的依赖 pool: max-active: 20 # 最大连接数,负值表示没有限制,默认8 max-wait: -1 # 最大阻塞等待时间,负值表示没限制,默认-1 max-idle: 8 # 最大空闲连接,默认8 min-idle: 0 # 最小空闲连接,默认0 # Logger Config logging: level: com.hexadecimal: debug
-
注入 RedisTemplate。或者也可以用 StringRedisTemplate,这取决于你的具体需求。
StringRedisTemplate
默认使用StringRedisSerializer
对键和值进行序列化,内部使用getBytes(),使字符串对象变字节,适合存储简单的字符串数据;RedisTemplate
默认使用JdkSerializationRedisSerializer
,内部使用 ObjectOutPutStream,适用于任何Java对象的序列化,但这种方式可读性差,并且会增加存储开销,因为它会产生较大的序列化后的字节流(通常需要自定义序列化器)
-
可以根据需要自定义 RedisTemplate 的序列化器,例如使用更高效的序列化库如
Jackson2JsonRedisSerializer
、GenericJackson2JsonRedisSerializer
或OxmSerializer
,这些都可以帮助减小序列化后数据的大小,并提高性能。@Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory){ // 创建 RedisTemplate 对象 RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); // 设置连接工厂 redisTemplate.setConnectionFactory(factory); //创建 Json 序列化工具 GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer(); // 设置key的序列化策略 redisTemplate.setKeySerializer(RedisSerializer.string()); redisTemplate.setHashKeySerializer(jsonRedisSerializer); // 设置value的序列化策略 redisTemplate.setValueSerializer(jsonRedisSerializer); redisTemplate.setHashValueSerializer(jsonRedisSerializer); return redisTemplate; } }
关于序列化
因为Redis是一个键值存储系统,Java对象不能直接存储在其中,需要转换成字节序列来存储。
什么是序列化呢?
序列化就是把对象的状态信息转换为可以存储或传输的形式,简单来说就是,把对象转换成字节流,以便于网络传输或保存在文件、数据库。反序列化就是恢复这个对象。
查看 redisTemplate.opsForValue().set() 方法发现,它接受任何类型的对象(Object),说明Redis本身并不关心存储的数据的具体类型,它只关心数据的序列化形式
手动序列化
配置 Json序列化器的好处是自动序列化和反序列化,弊端是必须存储一条类型信息,因为在反序列化时必须要知道对象的类型,才能把它还原成 Java对象,所以这条类型信息会造成额外的内存消耗
{
"@class": "priv.wzg.circlebackend.model.entity.User",
"id": 1,
"userAccount": "lisi1234",
"userPassword": "123456",
"nickname": "李四",
}
想节省内存空间,只能手动序列化,统一使用String序列化器。在存储和读取Java对象时,手动完成对象的序列化和反序列化
Java对象—>手动序列化成Json字符串—>写入Redis
从Redis中读取Json字符串—>手动反序列化成Java对象
@Test
void testStringRedisTemplate() {
User user = new User();
user.setId(2L);
user.setUserAccount("wang1234");
user.setUserPassword("123456");
user.setNickname("王五");
String jsonStr = gson.toJson(user);
stringRedisTemplate.opsForValue().set("user:2", jsonStr);
String jsonStrValue = stringRedisTemplate.opsForValue().get("user:2");
User userValue = gson.fromJson(jsonStrValue, User.class);
System.out.println(userValue);
}
Redisson
分布式操作 Redis 的 Java 客户端,让你像在使用本地的集合一样操作 Redis(分布式 Redis 数据网格)
应用场景,比如可以用分布式锁保证多机部署时定时任务不会重复执行,具体使用放在项目中,此处不过多介绍
引入依赖,写配置类
@Configuration
@ConfigurationProperties("spring.data.redis")
@Data
public class RedissonConfig {
private String host;
private String port;
private String password;
@Bean
public RedissonClient redissonClient() {
// 创建配置
Config config = new Config();
String redisAddress = String.format("redis://%s:%s", host, port);
config.useSingleServer()
.setAddress(redisAddress)
.setPassword(password)
.setDatabase(1);
// 创建实例并返回(同步API)
return Redisson.create(config);
}
}
@SpringBootTest
public class RedissonTest {
@Resource
private RedissonClient redissonClient;
@Test
void testRList(){
// 分布式List
RList<Object> rList = redissonClient.getList("myList");
rList.add("test");
System.out.println(rList.get(0));
}
@Test
void testRMap(){
// 分布式Map
RMap<String, String> RMap = redissonClient.getMap("myMap");
RMap.put("key1", "value1");
System.out.println(RMap.get("key1"));
}
}
key 的设计
项目名:业务名:类型:id
对比
- 如果你用的是 Spring,并且没有过多的定制化要求,可以用 Spring Data Redis,最方便
- 如果你用的不是 Spring,并且追求简单,并且没有过高的性能要求,可以用 Jedis + Jedis Pool
- 如果你的项目不是 Spring,并且追求高性能、高定制化,可以用 Lettuce,支持异步、连接池
● 如果你的项目是分布式的,需要用到一些分布式的特性(比如分布式锁、分布式集合),推荐用 redisson