11redis搭建与入门
一、Redis简介
Redis诞生于2009年,全称是 Remote Dictionary Server(远程词典服务器),是一个基于内存的键值型 NoSQL 数据库。key 一般是 String 类型,value 支持多种不同数据结构,功能丰富。
Redis 的特征
- 键值(key-value)型,value 支持多种不同数据结构,功能丰富
- 单线程,每个命令具备原子性
- 低延迟,速度快(基于内存、IO 多路复用、良好的编码)
- 支持数据持久化
- 支持主从集群、分片集群
- 支持多语言客户端

NoSQL 与 SQL 对比
SQL 是关系型数据库,NoSQL 是非关系型数据库,二者在多个维度存在差异:
| 维度 | SQL | NoSQL |
|---|---|---|
| 数据结构 | 结构化(Structured) | 非结构化 |
| 数据关联 | 关联的(Relational) | 无关联的 |
| 查询方式 | SQL 查询 | 非 SQL |
| 事务特性 | ACID | BASE |
| 存储方式 | 磁盘 | 内存 |
| 扩展性 | 垂直扩展 | 水平扩展 |

事务方面,SQL 满足 ACID(原子性、一致性、隔离性、持久性),NoSQL 满足 BASE(基本可用、软状态、最终一致)。

NoSQL 常见分类:
- 键值类型(Redis)
- 文档类型(MongoDB)
- 列类型(HBase)
- Graph 类型(Neo4j)
使用场景上,SQL 适合数据结构固定、对数据安全性和一致性要求较高的业务;NoSQL 适合数据结构不固定、对一致性和安全性要求不高、但对性能要求较高的场景。
二、安装Redis
可使用 Docker 快速部署 Redis,并开启 AOF 持久化:
docker run \
--restart=always \
--log-opt max-size=100m \
--log-opt max-file=2 \
-p 6379:6379 \
--name redis \
-v /root/redis/conf/redis.conf:/etc/redis/redis.conf \
-v /root/redis/data:/data \
-d redis redis-server /etc/redis/redis.conf \
--appendonly yes \
--requirepass 123456三、Redis数据结构
Redis 是 key-value 数据库,key 一般是 String 类型,value 的类型多种多样,可分为基本类型和特殊类型:
- 基本类型:String、Hash、List、Set、SortedSet
- 特殊类型:GEO、BitMap、HyperLogLog

通用命令
通用指令是部分数据类型都可以使用的指令,常见的有:
KEYS:查看符合模板的所有 keyDEL:删除一个指定的 keyEXISTS:判断 key 是否存在EXPIRE:给一个 key 设置有效期,到期时该 key 会被自动删除TTL:查看一个 key 的剩余有效期
通过 help [command] 可以查看一个命令的具体用法。

String 类型
String 是 Redis 中最简单的存储类型,value 是字符串,根据格式可分为 3 类:
string:普通字符串int:整数类型,可以做自增、自减操作float:浮点类型,可以做自增、自减操作
不管是哪种格式,底层都是字节数组形式存储,只不过是编码方式不同。字符串类型的最大空间不能超过 512M。
常见命令:
SET:添加或修改已存在的 String 键值对GET:根据 key 获取 String 类型的 valueMSET:批量添加多个 String 键值对MGET:根据多个 key 获取多个 valueINCR:让整型 key 自增 1INCRBY:让整型 key 自增并指定步长INCRBYFLOAT:让浮点类型数字自增并指定步长SETNX:添加 String 键值对,前提是 key 不存在SETEX:添加 String 键值对并指定有效期
Key 的层级结构:Redis 的 key 允许有多个单词形成层级结构,多个单词之间用 : 隔开,格式为 项目名:业务名:类型:id。例如项目名为 heima 时:
- 用户 key:
heima:user:1 - 商品 key:
heima:product:1
如果 Value 是 Java 对象,可将对象序列化为 JSON 字符串后存储。
Hash 类型
Hash 也叫散列,value 是一个无序字典,类似于 Java 中的 HashMap 结构。String 结构将对象序列化为 JSON 字符串后存储,修改单个字段时很不方便;而 Hash 结构可以将对象中的每个字段独立存储,可以针对单个字段做 CRUD。
常见命令:
HSET key field value:添加或修改 hash 类型 key 的 field 值HGET key field:获取一个 hash 类型 key 的 field 值HMSET:批量添加多个 hash 类型 key 的 field 值HMGET:批量获取多个 hash 类型 key 的 field 值HGETALL:获取一个 hash 类型 key 中所有的 field 和 valueHKEYS:获取一个 hash 类型 key 中所有的 fieldHVALS:获取一个 hash 类型 key 中所有的 valueHINCRBY:让一个 hash 类型 key 的字段值自增并指定步长HSETNX:添加 hash 类型 key 的 field 值,前提是 field 不存在
List 类型
Redis 中的 List 类型与 Java 中的 LinkedList 类似,可以看做是双向链表结构,既支持正向检索也支持反向检索。特征:
- 有序
- 元素可以重复
- 插入和删除快
- 查询速度一般
常用来存储一个有序数据,例如朋友圈点赞列表、评论列表等。
常见命令:
LPUSH key element ...:向列表左侧插入一个或多个元素LPOP key:移除并返回列表左侧的第一个元素,没有则返回 nilRPUSH key element ...:向列表右侧插入一个或多个元素RPOP key:移除并返回列表右侧的第一个元素LRANGE key start end:返回一段角标范围内的所有元素BLPOP和BRPOP:与 LPOP 和 RPOP 类似,没有元素时等待指定时间而不是直接返回 nil
利用 List 结构可以模拟:
- 栈:入口和出口在同一边
- 队列:入口和出口在不同边
- 阻塞队列:入口和出口在不同边,出队时采用 BLPOP 或 BRPOP
Set 类型
Redis 的 Set 结构与 Java 中的 HashSet 类似,可以看做是一个 value 为 null 的 HashMap,特征:
- 无序
- 元素不可重复
- 查找快
- 支持交集、并集、差集等功能
常见命令:
SADD key member ...:向 set 中添加一个或多个元素SREM key member ...:移除 set 中的指定元素SCARD key:返回 set 中元素的个数SISMEMBER key member:判断一个元素是否存在于 set 中SMEMBERS:获取 set 中的所有元素SINTER key1 key2 ...:求 key1 与 key2 的交集SDIFF key1 key2 ...:求 key1 与 key2 的差集SUNION key1 key2 ...:求 key1 和 key2 的并集
SortedSet 类型
Redis 的 SortedSet 是一个可排序的 set 集合,与 Java 中的 TreeSet 有些类似,但底层数据结构差别很大。SortedSet 中的每一个元素都带有一个 score 属性,可以基于 score 属性对元素排序,底层实现是跳表(SkipList)加 hash 表。
SortedSet 特性:
- 可排序
- 元素不重复
- 查询速度快
因为可排序特性,SortedSet 经常被用来实现排行榜这样的功能。
常见命令:
ZADD key score member:添加一个或多个元素到 sorted set,已存在则更新 scoreZREM key member:删除 sorted set 中的指定元素ZSCORE key member:获取指定元素的 score 值ZRANK key member:获取指定元素的排名ZCARD key:获取 sorted set 中的元素个数ZCOUNT key min max:统计 score 值在给定范围内的所有元素的个数ZINCRBY key increment member:让指定元素自增,步长为指定的 incrementZRANGE key min max:按 score 排序后,获取指定排名范围内的元素ZRANGEBYSCORE key min max:按 score 排序后,获取指定 score 范围内的元素ZDIFF、ZINTER、ZUNION:求差集、交集、并集
注意:所有排名默认都是升序,如果要降序则在命令的 Z 后面添加 REV 即可。
四、持久化
Redis 虽然是基于内存的数据库,但支持数据持久化,主要包含两种方式:
- RDB(Redis Database):将内存中的数据以快照形式保存到磁盘,默认生成
dump.rdb文件。可以通过配置save规则触发自动快照,或在命令行执行BGSAVE手动触发。RDB 文件紧凑、恢复速度快,但可能丢失最后一次快照之后的数据。 - AOF(Append Only File):将所有写命令追加到磁盘文件
appendonly.aof中。可在配置中通过appendonly yes开启。相比 RDB,AOF 数据安全性更高,但文件体积较大、恢复速度相对较慢。
五、Java客户端
Redis 官网提供了各种语言的客户端。Java 端常见的客户端包括:
- Jedis:以 Redis 命令作为方法名称,学习成本低,简单实用。但 Jedis 实例是线程不安全的,多线程环境下需要基于连接池来使用。
- Lettuce:基于 Netty 实现,支持同步、异步和响应式编程方式,线程安全,支持 Redis 哨兵模式、集群模式和管道模式。
- Redisson:基于 Redis 实现的分布式、可伸缩的 Java 数据结构集合,包含 Map、Queue、Lock、Semaphore、AtomicLong 等强大功能。

Jedis 客户端
引入依赖:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.7.0</version>
</dependency>建立连接、测试与释放资源:
private Jedis jedis;
@BeforeEach
void setUp() {
// 建立连接
jedis = new Jedis("192.168.150.101", 6379);
// 设置密码
jedis.auth("123321");
// 选择库
jedis.select(0);
}
@Test
void testString() {
// 插入数据,方法名称就是redis命令名称
String result = jedis.set("name", "张三");
System.out.println("result = " + result);
// 获取数据
String name = jedis.get("name");
System.out.println("name = " + name);
}
@AfterEach
void tearDown() {
// 释放资源
if (jedis != null) {
jedis.close();
}
}Jedis 连接池:Jedis 本身线程不安全,并且频繁创建和销毁连接会有性能损耗,推荐使用连接池代替直连方式。
public class JedisConnectionFactory {
private static final JedisPool jedisPool;
static {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
// 最大连接
jedisPoolConfig.setMaxTotal(8);
// 最大空闲连接
jedisPoolConfig.setMaxIdle(8);
// 最小空闲连接
jedisPoolConfig.setMinIdle(0);
// 设置最长等待时间,ms
jedisPoolConfig.setMaxWaitMillis(200);
jedisPool = new JedisPool(jedisPoolConfig, "192.168.150.101", 6379,
1000, "123321");
}
// 获取Jedis对象
public static Jedis getJedis(){
return jedisPool.getResource();
}
}SpringDataRedis 客户端
SpringData 是 Spring 中数据操作的模块,其中对 Redis 的集成模块叫做 SpringDataRedis。它提供了 RedisTemplate 统一 API 来操作 Redis。

特点:
- 提供了对不同 Redis 客户端的整合(Lettuce 和 Jedis)
- 提供了 RedisTemplate 统一 API 来操作 Redis
- 支持 Redis 的发布订阅模型
- 支持 Redis 哨兵和 Redis 集群
- 支持基于 Lettuce 的响应式编程
- 支持基于 JDK、JSON、字符串、Spring 对象的数据序列化及反序列化
- 支持基于 Redis 的 JDK Collection 实现
RedisTemplate 将不同数据类型的操作 API 封装到了不同的类型中:
| API | 返回值类型 | 说明 |
|---|---|---|
redisTemplate.opsForValue() |
ValueOperations | 操作 String 类型数据 |
redisTemplate.opsForHash() |
HashOperations | 操作 Hash 类型数据 |
redisTemplate.opsForList() |
ListOperations | 操作 List 类型数据 |
redisTemplate.opsForSet() |
SetOperations | 操作 Set 类型数据 |
redisTemplate.opsForZSet() |
ZSetOperations | 操作 SortedSet 类型数据 |
redisTemplate |
通用的命令 |
引入依赖:
<!--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:
redis:
host: 192.168.150.101
port: 6379
password: 123321
lettuce:
pool:
max-active: 8 # 最大连接
max-idle: 8 # 最大空闲连接
min-idle: 0 # 最小空闲连接
max-wait: 100 # 连接等待时间注入 RedisTemplate 并测试:
@SpringBootTest
public class RedisTest {
@Autowired
private RedisTemplate redisTemplate;
@Test
void testString() {
// 插入一条 string 类型数据
redisTemplate.opsForValue().set("name", "李四");
// 读取一条 string 类型数据
Object name = redisTemplate.opsForValue().get("name");
System.out.println("name = " + name);
}
}序列化方式优化
RedisTemplate 可以接收任意 Object 作为值写入 Redis,写入前会把 Object 序化为字节形式,默认采用 JDK 序列化。其缺点是可读性差、内存占用较大。

方案一:自定义 RedisTemplate
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
// 创建Template
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
// 设置连接工厂
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 设置序列化工具
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;
}JSON 序列化方式虽然能满足需求,但为了在反序列化时知道对象的类型,JSON 序列化器会将类的 class 类型写入 json 结果中存入 Redis,会带来额外的内存开销。

方案二:使用 StringRedisTemplate
为了节省内存空间,统一使用 String 序列化器,要求只能存储 String 类型的 key 和 value。当需要存储 Java 对象时,手动完成对象的序列化和反序列化。

Spring 默认提供了 StringRedisTemplate 类,它的 key 和 value 的序列化方式默认就是 String 方式,省去了自定义 RedisTemplate 的过程:
@Autowired
private StringRedisTemplate stringRedisTemplate;
// JSON工具
private static final ObjectMapper mapper = new ObjectMapper();
@Test
void testStringTemplate() throws JsonProcessingException {
// 准备对象
User user = new User("虎哥", 18);
// 手动序列化
String json = mapper.writeValueAsString(user);
// 写入一条数据到redis
stringRedisTemplate.opsForValue().set("user:200", json);
// 读取数据
String val = stringRedisTemplate.opsForValue().get("user:200");
// 反序列化
User user1 = mapper.readValue(val, User.class);
System.out.println("user1 = " + user1);
}两种方案对比:
- 方案一:自定义 RedisTemplate,修改序列化器为
GenericJackson2JsonRedisSerializer,使用方便但会额外存储 class 信息。 - 方案二:使用 StringRedisTemplate,写入时手动把对象序列化为 JSON,读取时手动反序列化为对象,更节省内存空间。