Redis 基础详解:从入门到精通

在当今互联网应用开发领域,数据存储与处理的性能和效率至关重要。Redis(Remote Dictionary Server)作为一款开源的、基于内存的键值存储系统,凭借其出色的性能和丰富的功能,被广泛应用于数据库、缓存、消息中间件等场景。本文将全面深入地讲解 Redis 的核心知识,助力开发者快速掌握 Redis 并应用于实际项目。

一、Redis 简介

Redis 由 Salvatore Sanfilippo 开发,目前由 Redis Labs 维护。它以内存作为数据的主要存储介质,使得数据的读写操作极为迅速,特别适合对性能要求极高的场景。

Redis 的主要特点

  • 内存存储:数据主要存储在内存中,提供极高的读写性能,能在极短时间内完成数据的读取和写入,相比传统磁盘存储数据库,大大提升了响应速度。
  • 持久化支持:支持 RDB(Redis Database)和 AOF(Append Only File)两种持久化方式,确保数据在断电或服务重启后不会丢失,在数据安全性和性能之间取得平衡 。
  • 丰富的数据结构:不仅支持常见的字符串类型,还提供哈希、列表、集合、有序集合等多种数据结构,可满足不同业务场景下的数据存储和处理需求。
  • 原子操作:所有操作都是原子性的,保证了数据操作的完整性和一致性,即使多个客户端同时对同一数据进行操作,也不会出现数据混乱的情况。
  • 发布 / 订阅:支持消息的发布 / 订阅模式,能够实现高效的消息传递,适用于构建实时消息系统。
  • 事务支持:支持简单的事务功能,确保多个命令要么全部执行成功,要么全部不执行,避免部分命令执行导致的数据不一致问题。
  • 高可用:通过主从复制和 Redis Sentinel 机制实现高可用,保障服务的连续性,当主节点出现故障时,能够自动切换到从节点,继续提供服务。
  • 分布式:Redis Cluster 提供分布式解决方案,可实现数据的分片存储和处理,能够轻松应对大规模数据和高并发访问的场景。

二、Redis 安装与配置

1. Linux 下安装 Redis

复制代码
# 下载最新稳定版,这里以6.2.6版本为例,实际可到官网获取最新版本
wget http://download.redis.io/releases/redis-6.2.6.tar.gz
tar xzf redis-6.2.6.tar.gz
cd redis-6.2.6
make

在执行make命令时,可能会出现缺少依赖的情况。常见的依赖包括 gcc、tcl 等,需要提前安装,可使用yum install gcc tcl(CentOS 系统)或apt-get install gcc tcl(Ubuntu 系统)命令进行安装。

2. 启动 Redis 服务器

复制代码
src/redis-server

默认情况下,Redis 会读取当前目录下的redis.conf配置文件启动。如果需要使用自定义的配置文件,可以通过src/redis-server /path/to/redis.conf指定配置文件路径。

3. 使用 Redis 客户端连接

复制代码
src/redis-cli

连接成功后,即可在命令行中输入 Redis 命令与服务器进行交互。如果 Redis 服务器设置了密码,可使用src/redis-cli -a your_password命令进行连接。

三、Redis 基本数据结构与操作

1. 字符串 (String)

字符串是 Redis 最基础的数据类型,可存储文本、数字或二进制数据,适用于存储简单的键值对,如用户 ID 对应的用户名、配置项等。

复制代码
# 设置键值
SET name "Redis"
# 获取值
GET name
# 设置过期时间(秒),这里设置age键30秒后过期
SETEX age 30 "25"
# 递增,常用于计数器场景,如点赞数、浏览量统计
INCR counter
# 递减
DECR counter

对于数值类型的字符串,除了INCRDECR,还支持INCRBYDECRBY命令,可指定递增或递减的步长,如INCRBY counter 5counter的值增加 5。

2. 哈希 (Hash)

哈希适合存储对象,将对象的多个属性以字段和值的形式存储,常用于存储用户信息、商品详情等场景。

复制代码
# 设置哈希字段
HSET user:1000 username antirez password P1pp0 age 34
# 获取所有字段
HGETALL user:1000
# 获取单个字段
HGET user:1000 username
# 设置多个字段
HMSET user:1001 username mike password 123 age 28

在处理大量哈希数据时,HSCAN命令可以用于迭代获取哈希字段,避免一次性获取大量数据导致的性能问题。

3. 列表 (List)

列表是简单的字符串列表,按照插入顺序排序,可用于实现消息队列、任务队列,或者存储用户的浏览历史等。

复制代码
# 从左侧插入
LPUSH mylist "world"
LPUSH mylist "hello"
# 从右侧插入
RPUSH mylist "!"
# 获取列表范围,0表示起始索引,-1表示结束索引
LRANGE mylist 0 -1
# 弹出元素,LPOP从左侧弹出,RPOP从右侧弹出
LPOP mylist
RPOP mylist

BLPOPBRPOP命令可以实现阻塞式弹出,当列表为空时,客户端会阻塞等待,直到列表中有新元素加入,这一特性常用于实现可靠的消息队列。

4. 集合 (Set)

集合是字符串的无序集合,不允许重复元素,适用于去重、交集、并集、差集等操作,比如统计网站的独立访客、获取两个用户的共同关注等。

复制代码
# 添加元素
SADD myset "Hello"
SADD myset "World"
# 获取所有元素
SMEMBERS myset
# 检查元素是否存在
SISMEMBER myset "Hello"
# 移除元素
SREM myset "World"

通过SINTER(交集)、SUNION(并集)、SDIFF(差集)等命令,可以对多个集合进行高效的集合运算。

5. 有序集合 (Sorted Set)

有序集合类似于集合,但每个元素都关联一个分数,用于排序,常用于排行榜、计分系统等场景。

复制代码
# 添加元素,分数在前,元素在后
ZADD myzset 1 "one"
ZADD myzset 2 "two" 3 "three"
# 获取元素范围,并显示分数
ZRANGE myzset 0 -1 WITHSCORES
# 按分数范围获取
ZRANGEBYSCORE myzset 1 2

ZREVRANGE命令可以按照分数从高到低的顺序获取元素,适用于倒序排行榜的展示。

四、Redis 持久化机制

1. RDB(Redis Database)

RDB 是 Redis 默认的持久化方式,它会在指定时间间隔内将内存中的数据快照写入磁盘。
配置示例:

复制代码
save 900 1      # 900秒(15分钟)内有1个更改
save 300 10     # 300秒(5分钟)内有10个更改
save 60 10000   # 60秒内有10000个更改

优点:

  • 适合大规模数据恢复,因为 RDB 文件是一个完整的数据快照,恢复时直接加载即可。
  • 对性能影响小,只有在进行快照操作时会占用一定的系统资源,平时几乎不影响 Redis 的读写性能。
  • 文件紧凑,便于备份和传输,可以方便地将 RDB 文件复制到其他服务器进行数据备份或迁移。

缺点:

  • 可能丢失最后一次快照后的数据,如果在两次快照间隔期间 Redis 发生故障,这期间的数据将无法恢复。
  • 大数据量时保存过程可能较长,因为需要将整个内存数据写入磁盘,可能会导致 Redis 在保存期间响应变慢。

2. AOF(Append Only File)

AOF 持久化记录服务器接收到的所有写操作命令,并在服务器启动时重新执行这些命令来恢复数据。
配置示例:

复制代码
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec  # 每秒同步

优点:

  • 数据安全性更高,因为几乎可以实时记录所有写操作,即使 Redis 突然宕机,最多只会丢失 1 秒的数据(当appendfsync设置为everysec时)。
  • 通过 rewrite 机制压缩文件,Redis 会定期对 AOF 文件进行重写,去除冗余的命令,减少文件体积。
  • 可读性强,便于分析,AOF 文件本质上是一个记录命令的文本文件,可以直接查看其中的操作,方便排查问题。

缺点:

  • 文件体积通常比 RDB 大,因为它记录了每一个写操作命令,随着时间推移,文件会不断增大。
  • 恢复速度比 RDB 慢,因为需要重新执行所有写操作命令来恢复数据,数据量越大,恢复时间越长。
  • 对性能影响相对较大,特别是在appendfsync设置为always时,每次写操作都要同步到磁盘,会降低 Redis 的写入性能。

五、Redis 事务

Redis 事务可以一次执行多个命令,并且保证:

  1. 事务中的所有命令都会序列化并按顺序执行。
  2. 执行过程中不会被其他客户端发送的命令打断。
  3. 没有隔离级别的概念,即事务执行过程中不会出现脏读、不可重复读等问题,但也无法保证数据的并发一致性。

基本命令:

  • MULTI:标记事务开始。
  • EXEC:执行所有事务块内的命令。
  • DISCARD:取消事务,放弃执行事务块内的所有命令。
  • WATCH:监视一个或多个 key,如果在事务执行前这些 key 被其他客户端修改,事务将被中断。

示例:

复制代码
MULTI
SET book-name "Redis in Action"
SET book-author "Josiah L. Carlson"
SADD book-tags "NoSQL" "Redis" "Database"
EXEC

在使用事务时,需要注意,如果事务块中的某个命令在执行时出现错误,后续命令仍然会继续执行。Redis 不会像关系型数据库那样进行回滚,除非使用WATCH命令监视的 key 发生变化导致事务中断。

六、Redis 发布 / 订阅

Redis 发布 / 订阅 (pub/sub) 实现了消息系统,发送者 (pub) 发送消息,订阅者 (sub) 接收消息,适用于实时消息推送、系统通知等场景。

基本命令:

  • SUBSCRIBE:订阅一个或多个频道。
  • PUBLISH:向频道发布消息。
  • UNSUBSCRIBE:退订频道。
  • PSUBSCRIBE:订阅匹配模式的频道,支持通配符,如PSUBSCRIBE news.*可以订阅所有以news.开头的频道。

示例:

复制代码
# 客户端1订阅
SUBSCRIBE news

# 客户端2发布
PUBLISH news "Hello Redis!"

# 客户端1将收到消息

需要注意的是,Redis 的发布 / 订阅是基于内存的,消息不会持久化,如果在发布消息时没有客户端订阅该频道,消息将被丢弃。同时,当有大量订阅者时,发布消息可能会导致 Redis 性能下降,需要谨慎使用。

七、Redis Java 客户端

1. Jedis

Jedis 是 Redis 官方推荐的 Java 客户端,简单易用,适合快速开发和小型项目。
Maven 依赖:

复制代码
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>4.3.1</version>
</dependency>

示例代码:

复制代码
import redis.clients.jedis.Jedis;

public class JedisExample {
    public static void main(String[] args) {
        // 连接Redis,这里假设Redis运行在本地,端口为默认的6379
        Jedis jedis = new Jedis("localhost");
        
        // 字符串操作
        jedis.set("foo", "bar");
        System.out.println(jedis.get("foo"));
        
        // 列表操作
        jedis.lpush("list", "item1", "item2");
        System.out.println(jedis.lrange("list", 0, -1));
        
        // 关闭连接
        jedis.close();
    }
}

在实际应用中,为了提高性能和管理连接,可以使用连接池,如JedisPool。通过连接池可以复用连接,避免频繁创建和销毁连接带来的性能开销。

2. Lettuce

Lettuce 是一个高性能的 Redis 客户端,支持异步和响应式编程,适用于高并发、高性能的场景,特别是在基于 Reactor 模式的项目中。
Maven 依赖:

复制代码
<dependency>
    <groupId>io.lettuce</groupId>
    <artifactId>lettuce-core</artifactId>
    <version>6.2.3.RELEASE</version>
</dependency>

示例代码:

复制代码
import io.lettuce.core.RedisClient;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;

public class LettuceExample {
    public static void main(String[] args) {
        // 创建RedisClient,这里使用默认的连接字符串格式,也可指定密码等参数
        RedisClient client = RedisClient.create("redis://localhost");
        
        // 获取连接
        StatefulRedisConnection<String, String> connection = client.connect();
        RedisCommands<String, String> commands = connection.sync();
        
        // 执行命令
        commands.set("key", "value");
        System.out.println(commands.get("key"));
        
        // 关闭连接
        connection.close();
        client.shutdown();
    }
}

Lettuce 还提供了丰富的异步 API,通过RedisAsyncCommandsRedisReactiveCommands接口,可以实现非阻塞的异步操作,充分利用系统资源,提高应用的并发处理能力。

八、Redis 应用场景

  1. 缓存系统:作为缓存系统,Redis 可以存储热点数据,减轻数据库压力,提高系统响应速度。例如,将频繁访问的商品详情、新闻资讯等数据缓存到 Redis 中,减少对数据库的查询次数。
  2. 计数器 :利用INCR/DECR等命令实现点赞数、浏览量、评论数等统计功能,高效且原子性强。
  3. 排行榜:使用有序集合实现实时排行榜,如游戏排行榜、音乐排行榜等,通过分数进行排序,方便快捷地获取排名信息。
  4. 消息队列 :利用列表的阻塞操作实现简单消息队列,如BLPOPBRPOP,可以实现生产者 - 消费者模型,用于异步任务处理、日志收集等场景。
  5. 会话缓存:存储用户会话信息,如登录状态、购物车数据等,提高用户体验和系统性能。
  6. 分布式锁 :利用SETNX(Set If Not Exists)命令实现分布式锁,确保在分布式环境下多个进程或线程对共享资源的互斥访问,避免数据竞争和不一致问题。
  7. 社交网络:实现关注、粉丝、共同好友等功能,通过集合的交集、并集、差集运算,可以高效地处理社交关系数据。
  8. 实时系统:处理实时数据,如实时分析、实时监控,Redis 的高速读写性能能够满足实时数据处理的需求,例如实时统计网站的访问量、监控系统的指标数据等。

九、Redis 性能优化建议

  1. 合理使用数据结构:根据业务场景选择最适合的数据结构,例如,需要有序存储和排序时使用有序集合,需要去重和集合运算时使用集合,避免因数据结构选择不当导致性能低下。
  2. 批量操作 :使用MSETMGET、Pipeline 等批量操作命令,减少网络开销。Pipeline 可以将多个命令打包发送到 Redis 服务器,一次性执行,大大提高执行效率。
  3. 避免大 Key:单个 Key 的 Value 不宜过大,过大的 Value 会占用过多内存,同时在读取和写入时会增加网络传输时间和处理时间。如果数据量较大,可以考虑将数据进行拆分存储。
  4. 设置合理的内存淘汰策略 :根据业务需求配置maxmemory-policy,当 Redis 内存达到设定的最大值时,会按照指定的策略淘汰数据。常见的策略有volatile-lru(在设置了过期时间的键中,使用最近最少使用算法淘汰数据)、allkeys-lru(在所有键中使用最近最少使用算法淘汰数据)等。
  5. 使用连接池:避免频繁创建和销毁连接,使用连接池可以复用连接,减少连接创建和销毁的开销,提高系统性能。Jedis 和 Lettuce 都提供了连接池的实现。
  6. 合理设置持久化策略 :平衡性能和数据安全性,根据业务对数据丢失的容忍程度选择合适的持久化方式和配置参数。如果对数据安全性要求较高,可以采用 AOF,并设置合适的appendfsync策略;如果更注重性能和恢复速度,可以采用 RDB。
  7. 使用 Lua 脚本 :将多个 Redis 命令编写成 Lua 脚本,通过EVAL命令执行,减少网络往返次数,并且 Lua 脚本在 Redis 中执行是原子性的,保证了操作的一致性。
  8. 监控慢查询 :定期检查并优化慢查询,通过slowlog get命令可以查看 Redis 的慢查询日志,分析执行时间较长的命令,找出性能瓶颈并进行优化。

十、Redis 高可用方案

1. 主从复制

通过主从复制实现数据冗余和读写分离。主节点负责处理所有写操作,并将数据变化同步给从节点,从节点则可以处理读请求 。主从架构能够提升系统的读性能和数据安全性,当主节点发生故障时,虽然无法自动切换,但可以通过手动操作将从节点提升为主节点继续提供服务。

配置方法:

在从节点配置文件redis.conf中添加以下配置,假设主节点 IP 为192.168.1.100,端口为默认的6379

复制代码
replicaof 192.168.1.100 6379

同时,从节点还可以设置只读模式,避免误操作修改数据,在配置文件中添加:

复制代码
readonly yes

工作原理

从节点启动后,会向主节点发送SYNC命令(在 Redis 2.8 之后使用PSYNC命令)进行全量同步,主节点接收到命令后,会执行BGSAVE生成 RDB 文件,并将文件发送给从节点,从节点接收并加载 RDB 文件完成数据初始化。之后,主节点会将新的写命令通过异步方式发送给从节点,实现数据的持续同步。

优缺点

  • 优点:架构简单,易于部署和维护;实现读写分离,分担主节点压力,提升系统整体读性能;数据冗余提高了数据安全性。
  • 缺点:主节点故障时无法自动切换,需要人工介入;从节点复制数据存在一定延迟,在某些对数据一致性要求极高的场景下可能不适用。

2. Redis Sentinel

Redis Sentinel 提供自动故障转移和监控功能,它是一个分布式系统,可以监视一个或多个主从 Redis 服务器,并在主服务器故障时自动将一个从服务器升级为新的主服务器。

配置示例

假设主节点名称为mymaster,IP 为127.0.0.1,端口为6379,在每个 Sentinel 节点的配置文件sentinel.conf中添加以下内容:

复制代码
# 监视名为mymaster的主节点,最后的数字2表示判断主节点失效至少需要2个Sentinel节点同意
sentinel monitor mymaster 127.0.0.1 6379 2 
# 超过5000毫秒未收到主节点响应,就判定主节点下线
sentinel down-after-milliseconds mymaster 5000 
# 故障转移的超时时间,这里设置为60000毫秒
sentinel failover-timeout mymaster 60000 

工作流程

  1. Sentinel 节点会定期向主从节点和其他 Sentinel 节点发送心跳包,检查节点是否正常运行。
  2. 当某个 Sentinel 节点发现主节点无响应超过down-after-milliseconds设置的时间后,会先标记主节点为SDOWN(主观下线)。
  3. 该 Sentinel 节点会与其他 Sentinel 节点进行通信,当有超过配置的quorum(这里是 2)个 Sentinel 节点都认为主节点下线时,会将主节点标记为ODOWN(客观下线)。
  4. 进入故障转移阶段,Sentinel 节点会从从节点中选举一个新的主节点,选举算法基于 Raft 协议,被选举出的从节点会升级为主节点。
  5. 剩余的从节点会重新指向新的主节点,完成故障转移。

优缺点

  • 优点:实现了主节点故障的自动检测和自动故障转移,极大提高了系统的可用性;采用分布式架构,避免单点故障。
  • 缺点:配置相对复杂;Sentinel 节点本身也需要进行高可用部署,否则 Sentinel 节点故障可能影响故障转移功能;故障转移过程中可能存在短暂的服务中断。

3. Redis Cluster

Redis Cluster 提供数据分片和高可用功能,它采用无中心节点的分布式架构,将数据分散存储在多个节点上,每个节点负责一部分数据,节点之间通过 Gossip 协议进行通信和信息交换。

集群创建命令

假设使用 6 个节点构建集群,其中 3 个为主节点,3 个为从节点,执行以下命令创建集群(注意提前启动 6 个 Redis 实例并配置好相关参数):

复制代码
redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 \
127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \
--cluster-replicas 1

--cluster-replicas 1表示为每个主节点分配一个从节点。

工作原理

Redis Cluster 将整个键空间划分为 16384 个哈希槽(Hash Slot),每个节点负责一部分哈希槽。当客户端发送命令时,会根据键计算出对应的哈希槽,然后将命令发送到负责该哈希槽的节点上执行。如果节点发生故障,Redis Cluster 会自动将故障节点负责的哈希槽迁移到其他正常节点上,并进行故障转移,保证服务的可用性。

优缺点

  • 优点:具备强大的水平扩展能力,可以轻松应对大规模数据和高并发访问;数据分片存储,提高了数据处理能力;自动故障转移和哈希槽迁移机制保证了高可用性。
  • 缺点:客户端需要实现哈希槽路由算法,增加了开发复杂度;集群内部节点通信和数据迁移会占用一定的网络带宽;在集群规模较大时,管理和维护难度增加。

结语

Redis 作为高性能的内存数据库,在现代应用开发中扮演着重要角色。掌握 Redis 的基础知识和核心功能,能够帮助开发者构建更高效、更可靠的系统。本文系统介绍了 Redis 的基础知识,涵盖数据结构、持久化、事务、发布订阅等核心功能,以及 Java 客户端的使用、应用场景、性能优化建议和高可用方案。希望这篇指南能助力你快速入门 Redis,并在实际项目中灵活运用,充分发挥 Redis 的强大性能和优势。

相关推荐
NUZGNAW2 分钟前
Ubuntu 安装redis和nginx
redis·nginx·ubuntu
Asu52024 分钟前
思途SQL学习 0729
数据库·sql·学习
北亚数据恢复21 分钟前
服务器数据恢复—RAID上层部署的oracle数据库数据恢复案例
数据库·oracle·服务器数据恢复·北亚数据恢复
不辉放弃1 小时前
kafka的消息存储机制和查询机制
数据库·kafka·pyspark·大数据开发
ZZH1120KQ3 小时前
ORACLE的用户维护与权限操作
数据库·oracle
妮妮喔妮3 小时前
图片上传 el+node后端+数据库
javascript·数据库·vue.js
仰望星空的凡人8 小时前
【JS逆向基础】数据库之MongoDB
javascript·数据库·python·mongodb
duration~10 小时前
PostgreSQL并发控制
数据库·postgresql
给力学长10 小时前
自习室预约小程序的设计与实现
java·数据库·vue.js·elementui·小程序·uni-app·node.js
迷茫运维路11 小时前
MySQL5.7主从延迟高排查优化思路
数据库·主从延时高