Redis内存数据库:从数据类型到集群部署

文章目录

  • Redis内存数据库:从数据类型到集群部署
    • 前言
    • 一、Redis简介
      • [1.1 什么是Redis?](#1.1 什么是Redis?)
      • [1.2 Redis的优势](#1.2 Redis的优势)
      • [1.3 Redis常见应用场景](#1.3 Redis常见应用场景)
      • [1.4 Redis特性](#1.4 Redis特性)
      • [1.5 单线程模型](#1.5 单线程模型)
      • [1.6 哈希槽的基本概念](#1.6 哈希槽的基本概念)
    • 二、缓存原理
      • [2.1 计算机系统的三层存储结构](#2.1 计算机系统的三层存储结构)
      • [2.2 缓存的两个特征](#2.2 缓存的两个特征)
      • [2.3 Redis缓存处理请求的两种情况](#2.3 Redis缓存处理请求的两种情况)
      • [2.4 Redis作为旁路缓存](#2.4 Redis作为旁路缓存)
      • [2.5 缓存的类型](#2.5 缓存的类型)
      • [2.6 缓存穿透、缓存击穿和缓存雪崩](#2.6 缓存穿透、缓存击穿和缓存雪崩)
    • 三、Redis数据类型
      • [3.1 字符串string](#3.1 字符串string)
      • [3.2 列表list](#3.2 列表list)
      • [3.3 集合set](#3.3 集合set)
      • [3.4 有序集合zset](#3.4 有序集合zset)
      • [3.5 哈希hash](#3.5 哈希hash)
    • 四、Pipeline流水线
    • 五、Redis持久化
      • [5.1 RDB(Redis DataBase)](#5.1 RDB(Redis DataBase))
      • [5.2 AOF(Append-only File)](#5.2 AOF(Append-only File))
      • [5.3 如何选择RDB还是AOF?](#5.3 如何选择RDB还是AOF?)
    • 六、主从复制
    • 七、哨兵模式(Sentinel)
    • [八、Redis Cluster集群(3主3从)](#八、Redis Cluster集群(3主3从))
      • [8.1 核心概念](#8.1 核心概念)
      • [8.2 部署实战(3主3从)](#8.2 部署实战(3主3从))
    • 九、Python连接Redis
    • 十、容器部署Redis
    • 写在最后

Redis内存数据库:从数据类型到集群部署

从缓存原理到五种数据类型,从持久化到主从哨兵集群,一篇文章吃透Redis核心知识

前言

在现代应用架构中,Redis已经成为不可或缺的关键组件。它凭借10万+ QPS的高性能、丰富的数据结构和灵活的部署方式,广泛应用于缓存、分布式锁、计数器、排行榜、消息队列等场景。

但很多开发者对Redis的理解停留在"会set和get"的层面,对其数据类型、持久化机制、高可用架构缺乏系统认识。

本文将系统讲解Redis的完整知识体系,涵盖核心概念、五种数据类型、持久化机制、主从复制、哨兵模式、Cluster集群 以及Python实战,帮助你在生产环境中用好Redis。

一、Redis简介

1.1 什么是Redis?

Redis是完全开源的,遵守BSD协议,是一个高性能的key-value数据库。

Redis与其他key-value缓存产品的三大特点:

  1. 支持持久化:可以将内存中的数据保存在磁盘中,重启时可以再次加载使用
  2. 丰富的数据结构:不仅支持简单的key-value类型,还提供list、set、zset、hash等数据结构的存储
  3. 支持数据备份:即master-slave模式的数据备份

1.2 Redis的优势

优势 说明
性能极高 10万~100万+ QPS,微秒级响应;MySQL通常几千~几万QPS,毫秒级响应
丰富的数据类型 Strings、Lists、Hashes、Sets及Ordered Sets
原子性 所有操作都是原子性的,多个操作也支持事务(MULTI/EXEC)
丰富的特性 publish/subscribe、通知、key过期等

1.3 Redis常见应用场景

一、热点数据缓存

这是Redis最经典的应用场景。将热点数据(如商品详情、用户信息)缓存到Redis,可大幅降低数据库压力。

实现要点:

  • 缓存策略:采用Cache-Aside模式(先查缓存,未命中再查数据库并更新缓存)
  • 过期控制:设置合理的TTL
  • 缓存键设计:使用业务前缀+唯一标识,如product:info:1001
  • 空值处理:对查询不到的数据缓存空值(短TTL),防止缓存穿透

二、分布式锁

Redis的SETNX命令天然适合实现分布式锁。

实现要点:

  • 加锁:使用SET key value NX PX 30000(不存在则设置,过期时间30秒)
  • 解锁:通过Lua脚本原子性判断并删除锁,避免误删
  • 续期:对长时间运行的任务,需定时延长锁的有效期
  • 重试机制:获取锁失败时,实现合理的重试策略

三、计数器与限流

Redis的原子递增命令INCR非常适合实现计数器功能,结合过期时间可实现接口限流。

典型应用:

  • 文章阅读量、视频播放次数统计
  • 接口访问频率限制(如每分钟最多100次请求)
  • 秒杀活动中的库存计数

四、排行榜系统

Redis的SortedSet(有序集合)数据结构完美适配排行榜需求,支持按分数实时排序。

五、分布式会话

在分布式系统中,使用Redis存储用户会话信息,可实现多服务实例间的会话共享。

六、消息队列

利用Redis的List数据结构可实现简单的消息队列,适合对消息可靠性要求不高的场景。

七、位图应用

Redis的Bitmap是一种节省空间的二进制数据结构,适合存储大量布尔值信息。

典型场景:

  • 用户签到记录
  • 活跃用户统计
  • 权限标记

八、地理信息存储

Redis的GEO数据结构专门用于存储和操作地理坐标信息,适合LBS场景。

1.4 Redis特性

  • 速度快:10W QPS,基于内存,C语言实现
  • 单线程:避免线程竞争开销
  • 持久化:支持RDB和AOF
  • 支持多种数据结构
  • 支持多种编程语言
  • 功能丰富:支持Lua脚本、发布订阅、事务、pipeline等
  • 主从复制
  • 支持高可用和分布式

1.5 单线程模型

Redis 6.0版本前一直是单线程方式处理用户的请求。

为什么单线程还这么快?

  1. Redis的操作访问内存,数据库是访问硬盘
  2. Redis核心功能比数据库更简单,避免了复杂的约束和查询
  3. 单线程模型避免线程竞争开销
  4. 使用epoll IO多路复用

epoll的工作原理:

用一个生活例子来理解:你想吃烧烤、饺子和炒菜,有三种方案:

  1. 自己一个个买,一个个等 → 单线程,花的时间最多
  2. 三个人各买各的,分别等 → 多线程,等待时间是三者最大值,系统开销更大
  3. 自己买三份,都告诉老板"好了叫我",一个线程同时等待三份饭 → IO多路复用

epoll相当于事件通知/回调机制,select是你自己在几个摊位之间不停地问,没人提醒你。最关键的是操作系统能通知到应用程序哪个客户端来数据了。

注意事项:

  • 一次只运行一条命令
  • 避免执行长(慢)命令:keys *、flushall、flushdb、slow lua script、mutil/exec、operate big value

1.6 哈希槽的基本概念

槽的数量:Redis集群将整个键空间划分为16384个哈希槽(0-16383)。

数据映射规则 :通过HASH_SLOT = CRC16(key) mod 16384公式计算键对应的槽号,确保相同key会始终映射到同一槽。

数据分布与节点管理:每个集群节点负责部分槽的集合,支持在线调整。

二、缓存原理

2.1 计算机系统的三层存储结构

层级 访问时间 容量
CPU 20~40ns 1~32MB
内存 100ns 32~96GB
磁盘 3~5ms 1~4TB

2.2 缓存的两个特征

  1. 缓存是一个快速子系统:数据存在缓存中时,能避免每次从慢速子系统中存取数据
  2. 缓存容量总是小于后端慢速系统:不可能把所有数据都放在缓存系统中

2.3 Redis缓存处理请求的两种情况

缓存命中:Redis中有相应数据,直接读取Redis,性能非常快。

缓存缺失 :Redis中没有保存相应数据,就从后端数据库中读取数据,性能就会变慢。一旦发生缓存缺失,为了让后续请求能从缓存中读取到数据,需要把缺失的数据写入Redis,这个过程叫作缓存更新。

2.4 Redis作为旁路缓存

Redis是一个独立的系统软件,和业务应用程序是两个软件。应用程序想要使用Redis缓存,需要在程序中增加相应的缓存操作代码。所以,我们也把Redis称为旁路缓存。

需要在应用程序中增加三方面的代码:

  1. 读取数据时,显式调用Redis的GET操作接口
  2. 缓存缺失时,从数据库中读取数据
  3. 缓存更新时,显式调用SET操作接口

2.5 缓存的类型

只读缓存:所有的数据写请求直接发往后端的数据库。对于删改的数据,如果Redis已经缓存了相应的数据,应用需要把这些缓存的数据删除。

读写缓存:所有的写请求也会发送到缓存,在缓存中直接对数据进行增删改操作。

根据业务应用对数据可靠性和缓存性能的不同要求,有两种策略:

策略 说明 特点
同步直写 写请求发给缓存的同时,也会发给后端数据库 优先保证数据可靠性
异步写回 所有写请求都先在缓存中处理 优先提供快速响应

2.6 缓存穿透、缓存击穿和缓存雪崩

缓存雪崩

定义 :指缓存层由于大面积失效(如同一时间批量过期)或缓存服务宕机,导致所有请求瞬间涌入数据库,导致数据库崩溃的现象。

通俗理解:想象一个游乐场,所有出口的闸机同时坏了或者同时过期了,几千人一下子全部冲向检票口,大门瞬间被挤爆。

发生场景:

  • 批量Key同时过期
  • Redis集群宕机

解决方案:

  1. 过期时间加随机值:给Key的过期时间加个随机范围(如10分钟+1~5分钟),打散过期时间
  2. 搭建高可用集群:Redis多主多从,哨兵模式,避免单点故障
  3. 服务限流熔断:使用Sentinel或Hystrix限制并发数
  4. 多级缓存:本地缓存(Guava)+Redis
缓存穿透

定义 :指查询一个根本不存在的数据(缓存中没有,数据库中也没有)。因为缓存不缓存空值,所以每次请求都会绕过缓存直接去查数据库。

通俗理解:你拿着一张假身份证去游乐场检票,检票员看了一眼说没这张票,就让你去后台查。后台查了半天也没有,就让你回去了。结果来了一万个拿着假身份证的人,都去后台折腾。

发生场景:

  • 攻击者恶意查询不存在的ID
  • 业务代码Bug导致反复查无效Key

解决方案:

  1. 接口层增加校验:如用户鉴权校验,ID做基础校验,id<=0的直接拦截
  2. 缓存空结果:对查询的空结果也进行缓存
  3. 布隆过滤器:将所有可能对应的数据为空的key进行统一的存放,并在请求前做拦截
缓存击穿

定义 :指热点Key在某个时刻过期,此时恰好有大量并发请求同时打过来。因为缓存中没有,这些请求都会去查询数据库。

通俗理解:游乐场只有一个VIP通道,这个通道刚好关闭了,但此时有一万人正排队等着通过,结果大家一拥而上,把通道旁边的闸机挤坏了。

发生场景:秒杀活动、热点新闻爆发。

与雪崩的区别:雪崩是大面积(很多Key一起挂),击穿是单点(一个热点Key挂了,但它很重要)。

解决方案:

  1. 加锁排队:在查询数据库的时候加锁排队
  2. 设置永不过时:对于某些经常使用的缓存,设置为永不过期

三、Redis数据类型

Redis支持五种数据类型:string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)。

3.1 字符串string

字符串是一种最基本的Redis值类型。Redis字符串是二进制安全的,一个字符串类型的值最多能存储512M字节的内容。Redis中所有key都是字符串类型的。

常用命令:

命令 说明
`SET key value [EX seconds] [PX milliseconds] [NX XX]`
GET key 获取值
SETEX key seconds value 设置并指定过期时间
SETNX key value 仅当key不存在时设置(用于分布式锁)
INCR key 将key中的数字加1
INCRBY key increment 将key中的数字增加指定值
DECR / DECRBY 减操作
MSET key1 value1 [key2 value2 ...] 批量设置
MGET key1 [key2 ...] 批量获取

SET命令的参数:

  • EX seconds:将键的过期时间设置为seconds秒
  • PX milliseconds:将键的过期时间设置为milliseconds毫秒
  • NX:只在键不存在时,才对键进行设置操作
  • XX:只在键已经存在时,才对键进行设置操作

实操示例:

bash 复制代码
# 设置和获取
127.0.0.1:6379> set key1 value1
OK
127.0.0.1:6379> get key1
"value1"
127.0.0.1:6379> type key1
string

# 设置过期时间
127.0.0.1:6379> set title ceo ex 3
OK
127.0.0.1:6379> get title
(nil)    # 3秒后过期

# NX和XX
127.0.0.1:6379> setnx title coo    # title不存在,设置成功
(integer) 1
127.0.0.1:6379> setnx title ceo    # title已存在,设置失败
(integer) 0
127.0.0.1:6379> set title ceo xx   # title存在,设置成功
OK
127.0.0.1:6379> set age 20 xx      # age不存在,不操作
(nil)

# 批量操作
127.0.0.1:6379> MSET key1 value1 key2 value2
OK
127.0.0.1:6379> MGET key1 key2
1) "value1"
2) "value2"

# 数字递增递减
127.0.0.1:6379> set num 10
OK
127.0.0.1:6379> INCR num
(integer) 11
127.0.0.1:6379> DECR num
(integer) 10
127.0.0.1:6379> INCRBY mykey 5
(integer) 5

# 获取过期时长
127.0.0.1:6379> TTL name
(integer) 97
127.0.0.1:6379> PERSIST name    # 取消过期时间
(integer) 1
127.0.0.1:6379> TTL name
(integer) -1    # -1表示永不过期

3.2 列表list

Redis列表是简单的字符串列表,按照插入顺序排序。支持双向读写,可以添加一个元素到列表的头部(左边)或者尾部(右边)。

特点:有序、可重复、左右都可以操作。

常用命令:

命令 说明
LPUSH key value [value ...] 从左边插入元素
RPUSH key value [value ...] 从右边插入元素
LPOP key 弹出左边第一个元素
RPOP key 弹出右边第一个元素
LRANGE key start stop 获取列表指定范围的元素
LLEN key 获取列表长度
LINDEX key index 获取指定索引的元素
LREM key count value 删除列表中count个等于value的元素

实操示例:

bash 复制代码
# 从左边添加数据
127.0.0.1:6379> LPUSH phone xiaomi apple huawei
(integer) 3
127.0.0.1:6379> TYPE phone
list

# 从右边添加数据
127.0.0.1:6379> RPUSH id 18 19 20
(integer) 3

# 获取列表长度
127.0.0.1:6379> LLEN list1
(integer) 2

# 获取指定位置元素
127.0.0.1:6379> LPUSH list2 a b c d
(integer) 4
127.0.0.1:6379> LINDEX list2 0
"d"
127.0.0.1:6379> LRANGE list2 0 -1
1) "d"
2) "c"
3) "b"
4) "a"

# 修改指定索引值
127.0.0.1:6379> lset list4 2 cloud
OK

# 删除数据
127.0.0.1:6379> lpop list5
"d"
127.0.0.1:6379> RPOP list5
"a"

# 修剪列表(只保留指定区间)
127.0.0.1:6379> LTRIM list6 2 3
OK

3.3 集合set

Set是一个无序的字符串合集,同一个集合中的每个元素是唯一无重复的。支持在两个不同的集合中对数据进行逻辑处理,常用于取交集、并集、统计等场景。

特点:无序、无重复、集合间操作。

常用命令:

命令 说明
SADD key member [member ...] 添加一个或多个成员
SMEMBERS key 获取集合所有成员
SISMEMBER key member 判断成员是否在集合中
SREM key member [member ...] 删除一个或多个成员
SCARD key 获取集合成员数量
SINTER key1 [key2 ...] 求多个集合的交集
SUNION key1 [key2 ...] 求多个集合的并集
SDIFF key1 [key2 ...] 求差集

实操示例:

bash 复制代码
# 创建集合
127.0.0.1:6379> SADD set1 v1
(integer) 1
127.0.0.1:6379> SADD set2 v2 v4
(integer) 2

# 追加数据(重复值无法添加)
127.0.0.1:6379> SADD set1 v2 v3 v4
(integer) 3
127.0.0.1:6379> SADD set1 v2
(integer) 0    # 已存在,添加失败

# 获取所有数据
127.0.0.1:6379> SMEMBERS set1
1) "v4"
2) "v3"
3) "v1"
4) "v2"

# 删除元素
127.0.0.1:6379> srem name1 meiyangyang
(integer) 1

# 集合间操作
127.0.0.1:6379> SINTER set1 set2     # 交集
1) "v4"
2) "v2"
127.0.0.1:6379> SUNION set1 set2     # 并集
1) "v3"
2) "v1"
3) "v2"
4) "v4"
127.0.0.1:6379> SDIFF set1 set2      # 差集
1) "v3"
2) "v1"

3.4 有序集合zset

Redis有序集合和集合类似,是不包含相同字符串的合集。差别是,每个有序集合的成员都关联着一个双精度浮点型的评分(score) ,用于排序。有序集合的成员不能重复,但评分可以重复。常用于排行榜的场景。

特点:有序、无重复元素、每个元素由score和value组成。

常用命令:

命令 说明
ZADD key score member [score member ...] 添加带分数的成员
ZRANGE key start stop [WITHSCORES] 按分数升序返回成员
ZREVRANGE key start stop [WITHSCORES] 按分数降序返回成员
ZREM key member [member ...] 删除成员
ZCARD key 获取成员数量
ZSCORE key member 获取成员的分数
ZRANK key member 获取成员的排名(升序)

实操示例:

bash 复制代码
# 创建有序集合
127.0.0.1:6379> ZADD zset1 1 v1
(integer) 1
127.0.0.1:6379> ZADD zset1 2 v2
(integer) 1
127.0.0.1:6379> ZADD zset1 2 v3
(integer) 1
127.0.0.1:6379> ZADD zset1 3 v4
(integer) 1

# 一次生成多个
127.0.0.1:6379> ZADD zset2 1 v1 2 v2 3 v3 4 v4 5 v5
(integer) 5

# 实现排名
127.0.0.1:6379> ZADD course 90 linux 99 go 60 python 50 cloud
(integer) 4

# 正序排序
127.0.0.1:6379> ZRANGE course 0 -1
1) "cloud"
2) "python"
3) "linux"
4) "go"

# 倒序排序
127.0.0.1:6379> ZREVRANGE course 0 -1
1) "go"
2) "linux"
3) "python"
4) "cloud"

# 带分数显示
127.0.0.1:6379> ZRANGE course 0 -1 WITHSCORES
1) "cloud"
2) "50"
3) "python"
4) "60"
5) "linux"
6) "90"
7) "go"
8) "99"

# 查询排名
127.0.0.1:6379> ZRANK course go
(integer) 3

# 获取分数
127.0.0.1:6379> ZSCORE course cloud
"50"

# 删除元素
127.0.0.1:6379> ZREM course python go
(integer) 2

3.5 哈希hash

hash即字典,用于保存字符串字段field和字符串值value之间的映射。一个hash最多可以包含2^32-1个key/value键值对。

特点:无序、K/V对、适用于存放相关的数据。

常用命令:

命令 说明
HSET key field value 设置hash字段的值
HGET key field 获取hash字段的值
HMSET key field1 value1 [field2 value2 ...] 批量设置字段
HMGET key field1 [field2 ...] 批量获取字段
HGETALL key 获取hash中所有字段和值
HDEL key field [field ...] 删除一个或多个字段
HEXISTS key field 判断字段是否存在

实操示例:

bash 复制代码
# 创建hash
127.0.0.1:6379> HSET 9527 name zhouxingxing age 20
(integer) 2
127.0.0.1:6379> TYPE 9527
hash

# 查看所有字段的值
127.0.0.1:6379> hgetall 9527
1) "name"
2) "zhouxingxing"
3) "age"
4) "20"

# 增加字段
127.0.0.1:6379> HSET 9527 gender male
(integer) 1

# 查看指定field的value
127.0.0.1:6379> HGET 9527 name
"zhouxingxing"

# 获取多个值
127.0.0.1:6379> HMGET 9527 name age
1) "zhouxingxing"
2) "20"

# 删除指定field
127.0.0.1:6379> HDEL 9527 age
(integer) 1

# 批量设置
127.0.0.1:6379> HMSET 9527 name zhouxingxing age 50 city hongkong
OK

# 查看所有field
127.0.0.1:6379> HKEYS 9527
1) "name"
2) "gender"
3) "age"
4) "city"

# 查看所有value
127.0.0.1:6379> HVALS 9527
1) "zhouxingxing"
2) "male"
3) "50"
4) "hongkong"

四、Pipeline流水线

Redis客户端执行一条命令分4个过程:发送命令 → 命令排队 → 命令执行 → 返回结果 ,这个过程称为Round Trip Time(RTT,往返时间)。

Redis提供了批量操作命令(如mget、mset)有效节约RTT。但大部分命令不支持批量操作,例如要执行n次hgetall命令,需要消耗n次RTT。

Pipeline机制能将一组Redis命令进行组装,通过一次RTT传输给Redis,再将这组命令的执行结果按顺序返回。

两者性能对比:

网络延迟 非Pipeline Pipeline
本机 0.17ms 573ms
内网服务器 0.41ms 1610ms
异地机房 7ms 80000ms

结论:使用Pipeline执行速度比逐条执行要快,特别是客户端与服务端的网络延迟越大,性能提升越明显。

五、Redis持久化

Redis作为一个键值对内存数据库,数据都存储在内存当中。存储在内存当中的数据,只要服务器关机或Redis进程退出,内存中的数据就会消失。

为了避免内存中数据丢失,Redis提供了对持久化的支持,我们可以选择不同的方式将数据从内存中保存到硬盘当中。

5.1 RDB(Redis DataBase)

RDB是一种快照存储持久化方式,具体就是将Redis某一时刻的内存数据保存到硬盘的文件当中,默认保存的文件名为dump.rdb。

开启RDB持久化方式:

方式一:save命令(同步操作)

bash 复制代码
127.0.0.1:6379> save
OK

当客户端向服务器发送Save命令请求进行持久化时,服务器会阻塞Save命令之后的其他客户端的请求,直到数据同步完成。如果数据量太大,同步数据会执行很久,所以最好不要在生产环境使用Save命令。

方式二:bgsave命令(异步操作)

bash 复制代码
127.0.0.1:6379> bgsave
Background saving started

Redis服务器主进程会Fork一个子进程来数据同步,在将数据保存到RDB文件之后,子进程会退出。主进程仍然可以接收其他请求,但Fork子进程是同步的。

方式三:服务器配置自动触发

bash 复制代码
# 以下是默认值
save 900 1      # 900秒内修改了1个KEY即触发保存RDB
save 300 10     # 300秒内修改了10个KEY即触发保存RDB
save 60 10000   # 60秒内修改了10000个KEY即触发保存RDB

RDB文件生成过程:

  1. 生成临时RDB文件,并写入数据
  2. 完成数据写入,用临时文件替代正式RDB文件
  3. 删除原来的DB文件

RDB的优点:

  • 与AOF方式相比,通过RDB文件恢复数据比较快
  • RDB文件非常紧凑,适合于数据备份
  • 通过RDB进行数据备份,由于使用子进程生成,对Redis服务器性能影响较小

RDB的缺点:

  • 如果服务器宕机,采用RDB的方式会造成某个时段内数据的丢失
  • 使用Save命令会造成服务器阻塞
  • 使用Bgsave命令在Forks子进程时,如果数据量太大,Forks的过程也会发生阻塞

5.2 AOF(Append-only File)

与RDB存储某个时刻的快照不同,AOF持久化方式会记录客户端对服务器的每一次写操作命令,并将这些写操作以Redis协议追加保存到AOF文件末尾。

开启AOF持久化方式:

bash 复制代码
# 修改配置文件
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec

⚠️ 注意:AOF模式默认是关闭的。第一次开启AOF后,并重启服务生效后,会因为AOF的优先级高于RDB,而AOF默认没有数据文件存在,从而导致所有数据丢失。

正确启用AOF功能,防止数据丢失:

bash 复制代码
# 先动态开启AOF,此时会生成appendonly.aof
127.0.0.1:6379> config set appendonly yes
OK

# 然后修改配置文件
[root@localhost ~]# vim /etc/redis.conf
appendonly yes

三种写入策略:

策略 说明 特点
appendfsync always 每一个写操作都保存到AOF文件 很安全,但很慢
appendfsync everysec 每秒写入一次AOF文件 默认策略,最多丢失1s数据
appendfsync no 交由操作系统决定何时写入 最快,但最不安全

AOF文件重写:

AOF重写是通过读取服务器当前的数据库状态来实现的。例如:

bash 复制代码
rpush list "A" rpush list "B" rpush list "C" rpush list "D" rpush list "E" rpush list "F"

这六条命令可以直接用一条命令代替:

bash 复制代码
rpush list "A" "B" "C" "D" "E" "F"

重写方式:

  • 在配置文件中设置no-appendfsync-on-rewrite
  • 客户端发送bgrewriteaof命令

AOF文件损坏修复:

bash 复制代码
[root@localhost ~]# redis-check-aof --fix /var/lib/redis/appendonly.aof

AOF的优点:

  • AOF只是追加日志文件,对服务器性能影响较小,速度比RDB要快,消耗的内存较少

AOF的缺点:

  • AOF方式生成的日志文件太大
  • 恢复数据的速度比RDB慢

5.3 如何选择RDB还是AOF?

对比项 RDB AOF
恢复速度 快 慢
文件体积 小 大
数据安全性 可能丢失一段时间数据 最多丢失1s数据
性能影响 较小 较小

结论 :如果想要数据足够安全,可以两种方式都开启。当RDB与AOF两种方式都开启时,Redis会优先使用AOF日志来恢复数据,因为AOF保存的文件比RDB文件更完整。

六、主从复制

Redis主从复制(Master-Slave Replication)是Redis实现高可用、读写分离和数据冗余的核心机制之一。

目的:数据冗余和读写分离。

原理:主从复制是Redis高可用性的基础。它允许一个Redis服务器(主节点,Master)将其数据复制到一个或多个Redis服务器(从节点,Slave/Replica)。

主从复制特点:

  • 一个master可以有多个slave
  • 一个slave只能有一个master
  • 数据流向是从master到slave单向的
  • master可读写,slave只读

主从同步工作原理:

  1. slave向master发送sync命令
  2. master启动后台存盘进程,并收集所有修改数据命令
  3. master完成存盘后,传送整个数据文件到slave
  4. slave接受数据文件,加载到内存中完成首次完全同步
  5. 后续有新数据产生时,master继续将新的数据命令传递给slave完成同步

配置从节点:

bash 复制代码
# 修改配置文件
replicaof 192.168.108.10 6379

或者通过命令:

bash 复制代码
127.0.0.1:6379> REPLICAOF MASTER_IP PORT

验证:

bash 复制代码
# master节点
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.108.12,port=6379,state=online,offset=280,lag=0
slave1:ip=192.168.108.11,port=6379,state=online,offset=280,lag=0

# slave节点
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:192.168.108.10
master_port:6379
master_link_status:up

取消主从同步:

bash 复制代码
127.0.0.1:6379> REPLICAOF NO ONE

七、哨兵模式(Sentinel)

Redis哨兵模式是Redis官方提供的高可用性解决方案,用于在主从架构中实现自动故障检测与故障转移。

目的:在主从复制的基础上,实现自动故障转移。

主要功能:

  • 监控:持续检查主节点和从节点是否正常运行
  • 通知:当被监控的Redis实例异常时,可通过API通知管理员或其他系统
  • 自动故障转移:当主节点宕机,Sentinel会自动将一个从节点提升为新主节点
  • 配置提供者:客户端可通过Sentinel获取当前主节点地址

哨兵本身不存储数据,只负责监控和协调。

故障转移流程:

  1. 主观下线(Sdown):某个Sentinel发现主节点无响应
  2. 客观下线(ODOWN):多个Sentinel(≥quorum配置值)达成共识
  3. 选举Leader:Sentinel之间通过Raft算法选举出一个Leader
  4. 提升新主 :Leader选择一个"最优"从节点执行REPLICAOF NO ONE
  5. 重配从节点:其他从节点重新指向新主节点
  6. 通知客户端:通过发布/订阅机制通知客户端主节点变更

配置哨兵:

bash 复制代码
# 复制配置文件
cp sentinel.conf sentinel.conf.bak

# 编辑配置文件
vim sentinel.conf
# sentinel monitor mymaster 192.168.108.10 6379 2
# sentinel down-after-milliseconds mymaster 30000

启动哨兵:

bash 复制代码
redis-sentinel sentinel.conf &

查看哨兵状态:

bash 复制代码
127.0.0.1:26379> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
master0:name=mymaster,status=ok,address=192.168.108.10:6379,slaves=2,sentinels=3

八、Redis Cluster集群(3主3从)

Redis集群是Redis 3.0+官方推出的分布式解决方案,核心是数据分片+主从复制+去中心化故障转移,解决单机Redis的容量、性能、单点故障瓶颈。

8.1 核心概念

关键角色:

  • 主节点(Master):处理读写、管理哈希槽、参与故障投票
  • 从节点(Replica/Slave):异步复制主节点数据,默认只读
  • 哈希槽(Hash Slot):集群固定16384个槽(0~16383),是数据分片的最小单位
  • 节点ID:每个节点唯一标识,用于集群内识别与通信

核心特性:

  • 数据分片:突破单机内存限制,总容量 = 所有主节点内存之和
  • 高可用:部分节点故障不影响整体,自动故障转移
  • 线性扩展:加节点即可提升容量与并发
  • 去中心化:无中心节点,避免单点瓶颈
  • 自动路由:客户端连接任意节点,自动重定向到目标节点

数据分片(哈希槽机制):

text 复制代码
HASH_SLOT = CRC16(key) mod 16384

每个主节点负责一段连续槽位(如3主集群:0-5460、5461-10922、10923-16383)。

节点通信(Gossip协议):

  • 节点间通过集群总线(独立端口,默认+10000)通信
  • 消息类型:PING/PONG(心跳检测)、FAIL(广播节点失效)、UPDATE(槽位映射更新)

故障转移:

  1. 故障检测:某主节点超时无响应,多数主节点标记为FAIL
  2. 从节点选举:该主节点的从节点发起FAILOVER请求
  3. 投票晋升:主节点投票通过,从节点升级为主,接管槽位
  4. 拓扑更新:Gossip同步新状态,集群恢复

8.2 部署实战(3主3从)

环境准备:6台CentOS 7.9虚拟机,IP地址192.168.108.21~26。

所有主机操作:

bash 复制代码
# 关闭防火墙
systemctl stop firewalld

# 修改配置文件
vim redis.conf
bind 0.0.0.0
cluster-enabled yes

# 启动Redis
redis-server redis.conf &

创建集群:

bash 复制代码
redis-cli --cluster create \
    192.168.108.21:6379 192.168.108.22:6379 192.168.108.23:6379 \
    192.168.108.24:6379 192.168.108.25:6379 192.168.108.26:6379 \
    --cluster-replicas 1

查看集群状态:

bash 复制代码
127.0.0.1:6379> cluster nodes
127.0.0.1:6379> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_known_nodes:6
cluster_size:3

创建数据:

bash 复制代码
# 使用-c参数自动重定向
redis-cli -c
127.0.0.1:6379> set age 18
OK
127.0.0.1:6379> get age
"18"

模拟故障:

bash 复制代码
# 关闭redis1节点
[root@redis1 ~]# init 0

# 查看集群信息,redis5被选举为新master
127.0.0.1:6379> cluster nodes

故障前后对比:

项目 故障前 故障后(.21挂)
Master节点 .21, .22, .23 .25, .22, .23
Slave节点 .25, .26, .24 .26, .24
槽覆盖 0-16383 0-16383(完整)
集群状态 ok 短暂fail→恢复为ok
数据完整性 完整 完整(无丢失)

极端情况:如果.21和.25同时挂掉,槽0-5460将无人负责,集群状态变为fail,必须手动恢复才能恢复服务。

九、Python连接Redis

Redis支持多种开发语言访问,下面以Python为例。

安装redis-py库:

bash 复制代码
pip install redis

基本连接:

python 复制代码
import redis

r = redis.Redis(host='192.168.108.172', port=6379, password='123456', db=0)
print(r)

字符串操作:

python 复制代码
# 设置和获取
r.set('color1', 'red')
value = r.get('color1')
print(value)    # b'red'

# 数字递增递减
r.set('int1', 10)
result = r.incr('int1')    # 自增
print(result)              # 11
result = r.decr('int1')    # 自减
print(result)              # 10

列表操作:

python 复制代码
r.lpush('mylist', 'nihao')
r.lpush('mylist', 'laogao')
print(r.lrange('mylist', 0, -1))
# [b'laogao', b'nihao']

哈希操作:

python 复制代码
r.hset('student', 'name', 'lisi')
r.hset('student', 'age', '18')
print(r.hget('student', 'name'))     # b'lisi'
print(r.hget('student', 'age'))      # b'18'
print(r.hgetall('student'))          # {b'name': b'lisi', b'age': b'18'}

⚠️ 注意:文件不要命名为redis.py,会和redis模块名称冲突。

十、容器部署Redis

bash 复制代码
# 使用Docker运行Redis
docker run --name redis -p 6379:6379 -d -v /data/redis:/data redis

# 进入容器操作
docker exec redis redis-cli info server
docker exec redis redis-cli set name ma
docker exec redis redis-cli get name
docker exec redis redis-cli save

💡 默认Redis容器可以支持远程连接。

写在最后

本文系统讲解了Redis的完整知识体系:

章节 核心内容
Redis简介 特点、优势、应用场景、单线程模型
缓存原理 缓存穿透、击穿、雪崩及解决方案
数据类型 string、list、set、zset、hash
Pipeline 批量命令优化RTT
持久化 RDB、AOF及选择策略
主从复制 数据冗余和读写分离
哨兵模式 自动故障转移
Cluster集群 数据分片+高可用
Python实战 redis-py连接操作
容器部署 Docker运行Redis

核心结论:

  1. Redis是单线程的,但通过epoll IO多路复用实现了高并发
  2. 五种数据类型各有适用场景:string做缓存、list做队列、set做去重、zset做排行榜、hash存对象
  3. RDB和AOF可以同时开启,AOF优先级更高,数据更完整
  4. 主从复制 解决读写分离,哨兵模式 解决自动故障转移,Cluster集群解决数据分片
  5. Pipeline能显著提升批量命令的性能,网络延迟越大效果越明显
  6. 缓存三大问题:穿透、击穿、雪崩,各有对应的解决方案

📌 核心命令速查

bash 复制代码
# 字符串
SET key value [EX seconds] [NX|XX]
GET key
INCR/DECR key
MSET/MGET

# 列表
LPUSH/RPUSH key value
LPOP/RPOP key
LRANGE key start stop

# 集合
SADD key member
SMEMBERS key
SINTER/SUNION/SDIFF

# 有序集合
ZADD key score member
ZRANGE/ZREVRANGE key start stop [WITHSCORES]
ZRANK key member

# 哈希
HSET key field value
HGET key field
HGETALL key

# 持久化
SAVE/BGSAVE
BGREWRITEAOF
CONFIG SET appendonly yes

# 主从
REPLICAOF host port
REPLICAOF NO ONE
INFO replication

# 集群
CLUSTER NODES
CLUSTER INFO
redis-cli --cluster create ...

知识点思维导图

📝 本文首发于个人技术博客,欢迎交流讨论。如有错误,恳请指正。

相关推荐
l1t1 小时前
DeepSeek总结的在 NVIDIA Vera 上对 DuckDB v2.0-dev 进行基准测试
服务器·数据库·sql
。1 小时前
【无标题】
linux·服务器·负载均衡·lvs
励志师code1 小时前
Kubernetes 集群节点生命周期管理实战:从扩容、调度管控到证书续期
运维·docker·容器·kubernetes·gpu算力·kubelet
weixin_416667961 小时前
linux ping命令详解
linux·运维·php
润滑卫士1 小时前
NAS 1638 油液清洁度等级:检测方法、影响因素与控制策略
运维
渣渣盟1 小时前
2000万QPS背后的存储引擎博弈:InnoDB、MyRocks、WiredTiger到底谁更强?
数据库·mysql
硅基手札1 小时前
【Linux 内核专栏 17】进程间通信 IPC(上)— 管道·信号·System V IPC·mmap·文件锁
linux·运维·服务器·stm32·单片机·mcu
小能喵1 小时前
kismet -h 帮助信息 中文版
linux·服务器·网络
seuzhouchenglong1 小时前
服务器通过本地电脑连接本地开发板
运维·服务器