文章目录
- [一、Redis 是什么](#一、Redis 是什么)
- [二、Redis 的使用场景](#二、Redis 的使用场景)
-
-
- [2.1 缓存(Cache)](#2.1 缓存(Cache))
- [2.2 排行榜系统](#2.2 排行榜系统)
- [2.3 计数器应用](#2.3 计数器应用)
- [2.4 社交网络](#2.4 社交网络)
- [2.5 消息队列系统](#2.5 消息队列系统)
-
- [三、Redis 的Java客户端](#三、Redis 的Java客户端)
-
- [1. Jedis客户端](#1. Jedis客户端)
- [2. **SpringDataRedis 客户端**](#2. SpringDataRedis 客户端)
- 四、配置端口转发
-
- [4.3 `application.properties` 配置 `Redis`](#4.3
application.properties配置Redis) - [4.4 idea 中安装 redis 可视化插件(免费)](#4.4 idea 中安装 redis 可视化插件(免费))
- [4.5 测试](#4.5 测试)
- [4.3 `application.properties` 配置 `Redis`](#4.3

一、Redis 是什么
Redis 是一种基于键值对(key-value)的 NoSQL 数据库,与很多键值对数据库不同的是,Redis 中的值可以是由 string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)、Bitmaps(位图)等多种数据结构和算法组成,因此 Redis 可以满足很多的应用场景。
而且因为 Redis 会将所有数据都存放再内存中,所以它的读写性能非常惊人。不仅如此,Redis 还可以将内存的数据利用快照和日志的形式保存到硬盘上,这样在发生类似断电或者机器故障的时候,内存中的数据不会"丢失"。除了上述功能以外,Redis 还提供了键过期、发布订阅、事务、流水线、Lua 脚本等附加功能。总之,如果在合适的场景使用号 Redis,它就会像一把瑞士军刀一样所向披靡。
二、Redis 的使用场景
2.1 缓存(Cache)
缓存机制几乎在所有大型网站都有使用,合理地使用缓存不仅可以加速数据的访问速度,而且能够有效地降低后端数据源的压力。Redis 提供了键值过期时间设置,并且也提供了灵活控制最大内存和内存溢出后的淘汰策略。可以这么说,一个合理的缓存设计能够为一个网站的稳定保驾护航。
2.2 排行榜系统
排行榜系统几乎存在于所有的网站,例如按照热度排名的排行榜,按照发布时间的排行榜,按照各种复杂维度计算出的排行榜,Redis 提供了列表和有序集合的结构,合理地使用这些数据结构可以很方便地构建各种排行榜系统。
2.3 计数器应用
计数器在网站中的作用至关重要,例如视频网站有播放数、电商网站有浏览数,为了保证数据的实时性,每一次播放和浏览都要做加 1 的操作,如果并发量很大对于传统关系型数据的性能是一种挑战。Redis 天然支持计数功能而且计数的性能也非常好,可以说是计数器系统的重要选择。
2.4 社交网络
赞/踩、粉丝、共同好友/喜好、推送、下拉刷新等是社交网站的必备功能,由于社交网站访问量通常比较大,而且传统的关系型数据不太合适保存这种类型的数据,Redis 提供的数据结构可以相对比较容易地实现这些功能。
2.5 消息队列系统
消息队列系统可以说是一个大型网站的必备基础组件,因为其具有业务解耦、非实时业务削峰等特性。Redis 提供了发布订阅功能和阻塞队列的功能,虽然和专业的消息队列比还不够足够强大,但是对于一般的消息队列功能基本可以满足。
三、Redis 的Java客户端
目前主流的 Redis 的 Java 客户端有三种:
Jedis和Lettuce:这两个主要是提供了 Redis 命令对应的 API,方便我们操作 Redis,而 SpringDataRedis 又对这两种做了抽象和封装。Redisson:是在 Redis 基础上实现了分布式的可伸缩的 java 数据结构,例如 Map、Queue 等,而且支持跨进程的同步机制:Lock、Semaphore 等待,比较适合用来实现特殊的功能需求。
1. Jedis客户端
快速入门
-
导入 jedis 依赖:
xml<!--jedis--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.7.0</version> </dependency> -
建立连接:
javaprivate Jedis jedis; @BeforeEach void setUp() { // 1. 建立连接 jedis = new Jedis("127.0.0.1", 6379); // 2. 设置密码 jedis.auth("123123"); // 3. 选择库 jedis.select(0); } -
释放资源:
java@AfterEach void tearDown(){ if (jedis != null){ jedis.close(); } } -
测试:
java@Test void testString(){ jedis.set("name","Kyle"); String name = jedis.get("name"); System.out.println("name = " + name); } @Test void testHash(){ jedis.hset("reggie:user:1","name","Jack"); jedis.hset("reggie:user:2","name","Rose"); jedis.hset("reggie:user:1","age","21"); jedis.hset("reggie:user:2","age","18"); Map<String, String> map = jedis.hgetAll("reggie:user:1"); System.out.println(map); }
连接池
Jedis 本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此我们推荐大家使用 Jedis 连接池代替 Jedis 的直连方式。
下面新建一个 com.blog.util,用于存放我们编写的工具类(后面我们使用SpringDataRedis的时候,可以直接在yml配置文件里配置这些内容,不需要用工具类)
java
public class JedisConnectionFactory {
private static JedisPool jedisPool;
static {
// 配置连接池
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(8);
poolConfig.setMaxIdle(8);
poolConfig.setMinIdle(0);
poolConfig.setMaxWaitMillis(1000);
// 创建连接池对象,参数:连接池配置、服务端ip、服务端端口、超时时间、密码
jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379, 1000, "root");
}
// 封装一个静态接口,提供jedis实例使用
public static Jedis getJedis(){
return jedisPool.getResource();
}
}
测试代码:
java
@SpringBootTest
class RedisTestApplicationTests {
private Jedis jedis = JedisConnectionFactory.getJedis();
@Test
void testString(){
jedis.set("name","Kyle");
String name = jedis.get("name");
System.out.println("name = " + name);
}
@Test
void testHash(){
jedis.hset("reggie:user:1","name","Jack");
jedis.hset("reggie:user:2","name","Rose");
jedis.hset("reggie:user:3","name","Kyle");
jedis.hset("reggie:user:1","age","21");
jedis.hset("reggie:user:2","age","18");
jedis.hset("reggie:user:3","age","18");
Map<String, String> map = jedis.hgetAll("reggie:user:1");
System.out.println(map);
}
@AfterEach
void tearDown(){
if (jedis != null){
jedis.close();
}
}
}
2. SpringDataRedis 客户端
SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis- 官网地址:https://spring.io/projects/spring-data-redis
- 提供了对不同 Redis 客户端的整合(Lettuce 和 Jedis)
- 提供了 RedisTemplate 统一 API 来操作 Redis
- 支持 Redis 的发布订阅模型
- 支持 Redis 哨兵和 Redis 集群
- 支持基于 Lettuce 的响应式编程
- 支持基于 JDK、JSON、字符串、Spring 对象的数据序列化及反序列化
- 支持基于 Redis 的 JDKCollection 实现
- SpringDataRedis 中提供了 RedisTemplate 工具类,其中封装了各种对 Redis 的操作。并且将不同数据类型的操作 API 封装到了不同的类型中:
| API | 返回值类型 | 说明 |
|---|---|---|
| redisTemplate | 通用的命令 | |
| redisTemplate.opsForValue() | ValueOperations | 操作 String 类型数据 |
| redisTemplate.opsForHash() | HashOperations | 操作 Hash 类型数据 |
| redisTemplate.opsForList() | ListOperations | 操作 List 类型数据 |
| redisTemplate.opsForSet() | SetOperations | 操作 Set 类型数据 |
| redisTemplate.opsForzSet() | ZSetOperations | 操作 SortedSet 类型数据 |
快速入门
SpringBoot 已经提供了对 SpringDataRedis 的支持,使用起来非常简单。
-
导入依赖:
xml<!--redis依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!--common-pool--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <!--Jackson依赖--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> -
配置 Redis:
yamlspring: redis: host: 127.0.0.1 port: 6379d password: 123123 lettuce: pool: max-active: 8 max-idle: 8 min-idle: 0 max-wait: 100ms -
注入 RedisTemplate 进行使用:
java@Autowired private RedisTemplate redisTemplate; @Test void stringTest(){ redisTemplate.opsForValue().set("username","David"); String username = (String) redisTemplate.opsForValue().get("username"); System.out.println(username); }
自定义序列化💥
-
RedisTemplate可以接收任意Object作为值写入Redis -
只不过写入前会把
Object序列化为字节形式,默认是采用 JDK 序列化,得到的结果是这样的:java\xAC\xED\x00\x05t\x00\x06\xE5\xBC\xA0\xE4\xB8\x89 -
缺点:
- 可读性差
- 内存占用较大
-
解决方法:
-
我们可以自定义 RedisTemplate 的序列化方式,在
com.blog.config包下编写对应的配置类:java@Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { // 创建RedisTemplate对象 RedisTemplate<String, Object> template = new RedisTemplate<>(); // 设置连接工厂 template.setConnectionFactory(connectionFactory); // 创建JSON序列化工具 GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer(); // 设置Key的序列化💥 template.setKeySerializer(RedisSerializer.string()); template.setHashKeySerializer(RedisSerializer.string()); // 设置Value的序列化💥 template.setValueSerializer(jsonRedisSerializer); template.setHashValueSerializer(jsonRedisSerializer); // 返回 return template; } }
-
问题虽然解决了,但是还有一个问题,序列化后的对象中记录了序列化时对应的 class 名称,目的是为了查询时实现自动反序列化,这带来额外的内存开销,如下所示:
json
{
"@class": "com.blog.entity.User",
"name": "张三",
"age": 18
}
所以下面引入了 StringRedisTemplate。
StringRedisTemplate
-
为了节省内存空间,我们可以不使用
JSON序列化器来处理value,而是统一使用String序列化器 ,要求只能存储String类型的key和value。当需要存储Java对象时,手动完成对象的序列化和反序列化。 -
因为存入和读取时的序列化及反序列化都是我们自己实现的,所以
SpringDataRedis不会将class信息写入Redis,从而节省了空间! -
这种用法比较普遍,因此
SpringDataRedis就提供了RedisTemplate的子类:StringRedisTemplate,它的key和value的序列化方式默认就是String方式。源码如下:javapublic class StringRedisTemplate extends RedisTemplate<String, String> { public StringRedisTemplate() { this.setKeySerializer(RedisSerializer.string()); this.setValueSerializer(RedisSerializer.string()); this.setHashKeySerializer(RedisSerializer.string()); this.setHashValueSerializer(RedisSerializer.string()); } }
所以我们就不需要去自定义字符串的序列化方式了,而是直接使用 StringRedisTemplate,如下所示:
java
@Autowired
private StringRedisTemplate stringRedisTemplate;
private final ObjectMapper mapper = new ObjectMapper();
@Test
void stringTest() throws JsonProcessingException {
// 创建对象
User user = new User("张三", 18);
// 手动序列化
String json = mapper.writeValueAsString(user);
// 写入数据
stringRedisTemplate.opsForValue().set("userdata", json);
// 获取数据
String userdata = stringRedisTemplate.opsForValue().get("userdata");
// 手动反序列化
User readValue = mapper.readValue(userdata, User.class);
System.out.println(readValue);
}
四、配置端口转发
Redis 服务器安装在云服务器上,而我们编写的代码则是在本地主机。
要想让本地主机能访问 redis,需要把 redis 的端口通过云服务器后台页面的 "防火墙"/"安全组" 放开端口到公网上,但是这个操作非常危险。(黑客会顺着 redis 端口进来)
因此我们可以使用端口转发的方式,直接把服务器的 redis 端口映射到本地。
在 xshell 中,进行如下配置:
- 右键云服务器的会话,选择属性
- 找到隧道->配置转移规则

- 使用该会话连接服务器
此时,访问本地的8888,就相当于访问对应服务器的6379。
注意: xshell 和服务器必须处在连接状态,这样的映射才是有效的。
4.3 application.properties 配置 Redis
properties
## redis ##
spring.data.redis.host=localhost
spring.data.redis.port=8888
# 连接空闲超过N(s秒、ms毫秒)后关闭,0为禁用,这里配置值和tcp-keepalive值一致
spring.data.redis.timeout=60s
# 默认使用 lettuce 连接池
# 允许最大连接数,默认8(负值表示没有限制)
spring.data.redis.lettuce.pool.max-active=8
# 最大空闲连接数,默认8
spring.data.redis.lettuce.pool.max-idle=8
# 最小空闲连接数,默认0
spring.data.redis.lettuce.pool.min-idle=0
# 连接用完时,新的请求等待时间(s秒、ms毫秒),超过该时间抛出异常JedisConnectionException,(默认-1,负值表示没有限制)
spring.data.redis.lettuce.pool.max-wait=5s

4.4 idea 中安装 redis 可视化插件(免费)
- 在 File->Settings->Plugins->搜索RedisHelper插件
- 右侧会生成一个页签 RedisHelper
- 然后点击左上角+,登录远程Redis
4.5 测试
java
@SpringBootTest
public class RedisTest {
@Autowired
private StringRedisTemplate redisTemplate;
@Test
public void redisTest() {
redisTemplate.opsForValue().set("key", "value");
String value = redisTemplate.opsForValue().get("key");
System.out.println(value);
//redisTemplate.delete("key");
}
}
