Appearance
Redis及Java操作Redis
1. Redis简介与安装配置
1.1 Redis简介
Redis是一个基于内存的key-value结构数据库。Redis 是互联网技术领域使用最为广泛的存储中间件。
官网: https://redis.io中文网: https://www.redis.net.cn/
key-value结构存储:

主要特点:
- 基于内存存储,读写性能高
- 适合存储热点数据(热点商品、资讯、新闻)
- 企业应用广泛
Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库,官方提供的数据是可以达到100000+的QPS(每秒内查询次数)。它存储的value类型比较丰富,也被称为结构化的NoSql数据库。
1.2 NoSql数据库
NoSql(Not Only SQL),不仅仅是SQL,泛指非关系型数据库。NoSql数据库并不是要取代关系型数据库,而是关系型数据库的补充。
关系型数据库(RDBMS):
- MySQL
- Oracle
- DB2
- SQL Server
非关系型数据库(NoSql):
- Redis
- MongoDB
- MemCached
1.3 Redis配置
1.3.1 修改Redis配置文件
设置Redis服务密码,修改 redis.windows.conf:
ini
requirepass 123456注意:
- 修改密码后需要重启Redis服务才能生效
- Redis配置文件中
#表示注释
1.3.2 连接Redis
重启Redis后,再次连接Redis时,需加上密码,否则连接失败。
bash
redis-cli.exe -h localhost -p 6379 -a 123456
此时,-h 和 -p 参数可省略不写。
1.3.3 Redis客户端图形工具
默认提供的客户端连接工具界面不太友好,同时操作也较为麻烦,可以使用Redis客户端图形工具。
推荐工具:
- Redis Desktop Manager
- Another Redis Desktop Manager
- RedisInsight
新建连接:

连接成功:

2. Redis数据类型
2.1 五种常用数据类型介绍
Redis存储的是key-value结构的数据,其中key是字符串类型,value有5种常用的数据类型:
- 字符串 string
- 哈希 hash
- 列表 list
- 集合 set
- 有序集合 sorted set / zset
2.2 各种数据类型特点

解释说明:
- 字符串(string):普通字符串,Redis中最简单的数据类型
- 哈希(hash):也叫散列,类似于Java中的HashMap结构
- 列表(list):按照插入顺序排序,可以有重复元素,类似于Java中的LinkedList
- 集合(set):无序集合,没有重复元素,类似于Java中的HashSet
- 有序集合(sorted set/zset):集合中每个元素关联一个分数(score),根据分数升序排序,没有重复元素
3. Redis常用命令
3.1 字符串操作命令
Redis 中字符串类型常用命令:
| 命令 | 说明 | 示例 |
|---|---|---|
| SET key value | 设置指定key的值 | SET name "张三" |
| GET key | 获取指定key的值 | GET name |
| SETEX key seconds value | 设置指定key的值,并将 key 的过期时间设为 seconds 秒 | SETEX code 60 "1234" |
| SETNX key value | 只有在 key 不存在时设置 key 的值 | SETNX lock "1" |
更多命令可以参考Redis中文网:https://www.redis.net.cn
3.2 哈希操作命令
Redis hash 是一个string类型的 field 和 value 的映射表,hash特别适合用于存储对象,常用命令:
| 命令 | 说明 | 示例 |
|---|---|---|
| HSET key field value | 将哈希表 key 中的字段 field 的值设为 value | HSET user:100 name "tom" |
| HGET key field | 获取存储在哈希表中指定字段的值 | HGET user:100 name |
| HDEL key field | 删除存储在哈希表中的指定字段 | HDEL user:100 age |
| HKEYS key | 获取哈希表中所有字段 | HKEYS user:100 |
| HVALS key | 获取哈希表中所有值 | HVALS user:100 |

3.3 列表操作命令
Redis 列表是简单的字符串列表,按照插入顺序排序,常用命令:
| 命令 | 说明 | 示例 |
|---|---|---|
| LPUSH key value1 [value2] | 将一个或多个值插入到列表头部 | LPUSH mylist "a" "b" |
| LRANGE key start stop | 获取列表指定范围内的元素 | LRANGE mylist 0 -1 |
| RPOP key | 移除并获取列表最后一个元素 | RPOP mylist |
| LLEN key | 获取列表长度 | LLEN mylist |
| BRPOP key1 [key2] timeout | 移出并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止 | BRPOP mylist 10 |

3.4 集合操作命令
Redis set 是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据,常用命令:
| 命令 | 说明 | 示例 |
|---|---|---|
| SADD key member1 [member2] | 向集合添加一个或多个成员 | SADD set1 "a" "b" "c" |
| SMEMBERS key | 返回集合中的所有成员 | SMEMBERS set1 |
| SCARD key | 获取集合的成员数 | SCARD set1 |
| SINTER key1 [key2] | 返回给定所有集合的交集 | SINTER set1 set2 |
| SUNION key1 [key2] | 返回所有给定集合的并集 | SUNION set1 set2 |
| SREM key member1 [member2] | 移除集合中一个或多个成员 | SREM set1 "a" "b" |

3.5 有序集合操作命令
Redis有序集合是string类型元素的集合,且不允许有重复成员。每个元素都会关联一个double类型的分数。常用命令:
| 命令 | 说明 | 示例 |
|---|---|---|
| ZADD key score1 member1 [score2 member2] | 向有序集合添加一个或多个成员 | ZADD zset1 10 "a" 12 "b" |
| ZRANGE key start stop [WITHSCORES] | 通过索引区间返回有序集合中指定区间内的成员 | ZRANGE zset1 0 -1 WITHSCORES |
| ZINCRBY key increment member | 有序集合中对指定成员的分数加上增量 increment | ZINCRBY zset1 5 "a" |
| ZREM key member [member ...] | 移除有序集合中的一个或多个成员 | ZREM zset1 "a" "b" |

3.6 通用命令
Redis的通用命令是不分数据类型的,都可以使用的命令:
| 命令 | 说明 | 示例 |
|---|---|---|
| KEYS pattern | 查找所有符合给定模式(pattern)的 key | KEYS user:* |
| EXISTS key | 检查给定 key 是否存在 | EXISTS name |
| TYPE key | 返回 key 所储存的值的类型 | TYPE name |
| DEL key | 该命令用于在 key 存在时删除 key | DEL name |
| TTL key | 返回给定 key 的剩余生存时间(秒) | TTL name |
| EXPIRE key seconds | 为给定 key 设置过期时间(秒) | EXPIRE name 60 |
4. 在Java中操作Redis
4.1 Redis的Java客户端
前面我们讲解了Redis的常用命令,这些命令是我们操作Redis的基础,那么我们在Java程序中应该如何操作Redis呢?这就需要使用Redis的Java客户端,就如同我们使用JDBC操作MySQL数据库一样。
Redis 的 Java 客户端很多,常用的几种:
- Jedis:老牌的Redis Java客户端,API简单直接
- Lettuce:基于Netty的异步非阻塞客户端,性能更好
- Spring Data Redis:Spring对Redis客户端的封装,提供了更高级的抽象
Spring 对 Redis 客户端进行了整合,提供了 Spring Data Redis,在Spring Boot项目中还提供了对应的Starter,即 spring-boot-starter-data-redis。
我们重点学习Spring Data Redis。
4.2 Spring Data Redis使用方式
4.2.1 介绍
Spring Data Redis 是 Spring 的一部分,提供了在 Spring 应用中通过简单的配置就可以访问 Redis 服务,对 Redis 底层开发包进行了高度封装。在 Spring 项目中,可以使用Spring Data Redis来简化 Redis 操作。
官网: https://spring.io/projects/spring-data-redis

4.2.2 添加依赖
Spring Boot提供了对应的Starter,Maven坐标:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>如果使用Gradle:
groovy
implementation 'org.springframework.boot:spring-boot-starter-data-redis'4.2.3 配置Redis连接
在 application.yml 或 application.properties 中配置Redis连接信息:
application.yml:
yaml
spring:
redis:
host: localhost
port: 6379
password: 123456
database: 0
timeout: 3000ms
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0application.properties:
properties
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=123456
spring.redis.database=04.2.4 RedisTemplate介绍
Spring Data Redis中提供了一个高度封装的类:RedisTemplate,对相关API进行了归类封装,将同一类型操作封装为operation接口,具体分类如下:
- ValueOperations:string数据操作
- SetOperations:set类型数据操作
- ZSetOperations:zset类型数据操作
- HashOperations:hash类型的数据操作
- ListOperations:list类型的数据操作
4.2.5 编写配置类,创建RedisTemplate对象
java
package com.sky.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
@Slf4j
public class RedisConfiguration {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
log.info("开始创建redis模板对象...");
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
// 设置redis的连接工厂对象
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 设置redis key的序列化器
redisTemplate.setKeySerializer(new StringRedisSerializer());
// 设置redis value的序列化器(可选,根据实际需求设置)
redisTemplate.setValueSerializer(new StringRedisSerializer());
// 设置hash key的序列化器
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
// 设置hash value的序列化器
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
// 初始化RedisTemplate
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}解释说明:
当前配置类不是必须的,因为 Spring Boot 框架会自动装配 RedisTemplate 对象,但是默认的key序列化器为 JdkSerializationRedisSerializer,导致我们存到Redis中后的数据和原始数据有差别(会看到类似 \xac\xed\x00\x05t\x00\x04name 这样的编码),故设置为 StringRedisSerializer 序列化器。
建议:
- 如果存储的是字符串类型,使用
StringRedisSerializer - 如果存储的是对象类型,可以使用
Jackson2JsonRedisSerializer或GenericJackson2JsonRedisSerializer
4.2.6 通过RedisTemplate对象操作Redis
在test下新建测试类
java
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.*;
@SpringBootTest
public class SpringDataRedisTest {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Test
public void testRedisTemplate() {
System.out.println(redisTemplate);
// string数据操作
ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
// hash类型的数据操作
HashOperations<String, Object, Object> hashOperations = redisTemplate.opsForHash();
// list类型的数据操作
ListOperations<String, Object> listOperations = redisTemplate.opsForList();
// set类型数据操作
SetOperations<String, Object> setOperations = redisTemplate.opsForSet();
// zset类型数据操作
ZSetOperations<String, Object> zSetOperations = redisTemplate.opsForZSet();
}
}测试结果:

说明RedisTemplate对象注入成功,并且通过该RedisTemplate对象获取操作5种数据类型相关对象。
上述环境搭建完毕后,接下来,我们就来具体对常见5种数据类型进行操作。
4.2.7 操作常见类型数据
1). 操作字符串类型数据
java
import java.util.concurrent.TimeUnit;
/**
* 操作字符串类型的数据
*/
@Test
public void testString() {
// set get setex setnx
redisTemplate.opsForValue().set("name", "小明");
String name = (String) redisTemplate.opsForValue().get("name");
System.out.println(name); // 输出:小明
// 设置过期时间(3分钟)
redisTemplate.opsForValue().set("code", "1234", 3, TimeUnit.MINUTES);
// setIfAbsent 相当于 SETNX,只有在key不存在时设置
Boolean result1 = redisTemplate.opsForValue().setIfAbsent("lock", "1");
System.out.println(result1); // 输出:true(设置成功)
Boolean result2 = redisTemplate.opsForValue().setIfAbsent("lock", "2");
System.out.println(result2); // 输出:false(key已存在,设置失败)
}2). 操作哈希类型数据
java
import java.util.List;
import java.util.Set;
/**
* 操作哈希类型的数据
*/
@Test
public void testHash() {
// hset hget hdel hkeys hvals
HashOperations<String, Object, Object> hashOperations = redisTemplate.opsForHash();
// 设置hash字段
hashOperations.put("user:100", "name", "tom");
hashOperations.put("user:100", "age", "20");
// 获取hash字段值
String name = (String) hashOperations.get("user:100", "name");
System.out.println(name); // 输出:tom
// 获取所有字段名
Set<Object> keys = hashOperations.keys("user:100");
System.out.println(keys); // 输出:[name, age]
// 获取所有字段值
List<Object> values = hashOperations.values("user:100");
System.out.println(values); // 输出:[tom, 20]
// 删除hash字段
hashOperations.delete("user:100", "age");
}3). 操作列表类型数据
java
import java.util.List;
/**
* 操作列表类型的数据
*/
@Test
public void testList() {
// lpush lrange rpop llen
ListOperations<String, Object> listOperations = redisTemplate.opsForList();
// 从左侧批量插入元素
listOperations.leftPushAll("mylist", "a", "b", "c");
// 从左侧插入单个元素
listOperations.leftPush("mylist", "d");
// 获取列表指定范围内的元素(0到-1表示全部)
List<Object> mylist = listOperations.range("mylist", 0, -1);
System.out.println(mylist); // 输出:[d, c, b, a]
// 从右侧弹出元素
Object popped = listOperations.rightPop("mylist");
System.out.println(popped); // 输出:a
// 获取列表长度
Long size = listOperations.size("mylist");
System.out.println(size); // 输出:3
}4). 操作集合类型数据
java
import java.util.Set;
/**
* 操作集合类型的数据
*/
@Test
public void testSet() {
// sadd smembers scard sinter sunion srem
SetOperations<String, Object> setOperations = redisTemplate.opsForSet();
// 向集合添加元素
setOperations.add("set1", "a", "b", "c", "d");
setOperations.add("set2", "a", "b", "x", "y");
// 获取集合所有成员
Set<Object> members = setOperations.members("set1");
System.out.println(members); // 输出:[a, b, c, d](无序)
// 获取集合成员数
Long size = setOperations.size("set1");
System.out.println(size); // 输出:4
// 求交集
Set<Object> intersect = setOperations.intersect("set1", "set2");
System.out.println(intersect); // 输出:[a, b]
// 求并集
Set<Object> union = setOperations.union("set1", "set2");
System.out.println(union); // 输出:[a, b, c, d, x, y]
// 移除集合中的元素
setOperations.remove("set1", "a", "b");
}5). 操作有序集合类型数据
java
import java.util.Set;
/**
* 操作有序集合类型的数据
*/
@Test
public void testZset() {
// zadd zrange zincrby zrem
ZSetOperations<String, Object> zSetOperations = redisTemplate.opsForZSet();
// 向有序集合添加成员(带分数)
zSetOperations.add("zset1", "a", 10);
zSetOperations.add("zset1", "b", 12);
zSetOperations.add("zset1", "c", 9);
// 获取有序集合指定范围内的成员(按分数升序)
Set<Object> zset1 = zSetOperations.range("zset1", 0, -1);
System.out.println(zset1); // 输出:[c, a, b](按分数排序:9, 10, 12)
// 增加成员的分数
Double newScore = zSetOperations.incrementScore("zset1", "c", 10);
System.out.println(newScore); // 输出:19.0
// 移除有序集合中的成员
zSetOperations.remove("zset1", "a", "b");
}6). 通用命令操作
java
import java.util.Set;
import org.springframework.data.redis.core.DataType;
/**
* 通用命令操作
*/
@Test
public void testCommon() {
// keys exists type del
// 查找所有符合模式的key(注意:生产环境慎用,可能阻塞Redis)
Set<String> keys = redisTemplate.keys("*");
System.out.println(keys);
// 检查key是否存在
Boolean existsName = redisTemplate.hasKey("name");
Boolean existsSet1 = redisTemplate.hasKey("set1");
System.out.println("name exists: " + existsName);
System.out.println("set1 exists: " + existsSet1);
// 获取key的数据类型
for (String key : keys) {
DataType type = redisTemplate.type(key);
System.out.println(key + " type: " + type.name());
}
// 删除key
Boolean deleted = redisTemplate.delete("mylist");
System.out.println("mylist deleted: " + deleted);
// 批量删除key
redisTemplate.delete(keys);
}5 Rdis缓存
5.1 穿透
缓存穿透是指,缓存中没有数据,数据库也没有数据,请求会穿透缓存,请求会直接访问数据库。 缓存穿透的解决方案: 方案一:缓存空数据,空数据可以设置一个过期时间,过期时间过短,可能会导致缓存穿透。 缓存穿透的代码实现:
java
@Override
public User getUser(Integer id) {
String key = "user:" + id;
User user = (User) redisTemplate.opsForValue().get(key);
if (user != null) {
return user;
}
}
@Override
public User getUser(Integer id) {
String key = "user:" + id;
User user = (User) redisTemplate.opsForValue().get(key);
if (user == null) {
user = userMapper.getUser(id);
}
}