高并发架构背后的秘密

Redis 编程操作与
高可用生产环境部署

从代码连接到企业级集群架构的演进之旅

本节课程导航

1

Python 编程操作

如何用代码与 Redis 对话
(连接、连接池、数据操作)

2

主从复制模式

解决单机瓶颈的第一步
(老板与助理的协作艺术)

3

哨兵高可用模式

让系统学会自我修复
(永不疲倦的监控保安)

4

分布式集群模式

突破内存极限的终极形态
(人多力量大的分工哲学)

第一部分

Python 编程操作 Redis

让代码成为我们指挥 Redis 数据库的“指挥棒”

为什么使用 Python 操作 Redis?

无缝衔接大数据生态

  • Python 是目前大数据和人工智能领域的首选语言
  • 语法简洁,学习曲线平缓,适合快速开发。
  • 拥有极度丰富的第三方生态库。

在之前的学习中,我们都是在黑漆漆的命令行里手敲命令(比如输入 SET key value)。

但在真实的企业开发中,Redis 是在后台默默服务的,我们必须通过程序代码来自动写入和读取数据。

什么是数据库驱动?

我们要用 Python 操控 Redis,必须先引入一个概念:驱动(Driver)

驱动 = 贴身翻译官

Python 只懂 Python 语法,Redis 只懂 Redis 协议。它们俩就像一个只懂中文,一个只懂英语,没法直接沟通。

驱动程序就是它们之间的翻译官,它负责把 Python 代码翻译成 Redis 能听懂的网络指令。

Python 程序

调用方法
Redis 驱动程序
(redis-py)
网络指令

Redis 服务器

下载与安装 Redis 驱动

在 Python 的世界里,最官方、最常用的 Redis 驱动库叫做 redis-py

如何安装?

我们使用 Python 的包管理工具 pip 来进行安装。在企业开发环境中,这通常是部署项目的第一步。

Windows CMD / Linux Terminal
C:\Users\Admin> pip install redis
Collecting redis
Downloading redis-5.0.1-py3-none-any.whl
Installing collected packages: redis
Successfully installed redis-5.0.1

编程操作:建立连接

安装好驱动后,第一件事就是“打电话”给 Redis 服务器,建立连接。

连接参数(四大金刚)

  • host:目标主机的 IP 地址(例如 127.0.0.1 或云服务器公网 IP)。
  • port:端口号,Redis 默认是 6379
  • db:数据库编号,Redis 默认有 16 个库(0-15),通常用 0 库。
  • password:Redis 的密码,如果没有设置则不需要填。

StrictRedis vs Redis

redis-py 库中,推荐使用 StrictRedis 对象来连接。

因为它完全遵循官方 Redis 命令的语法和参数顺序,让我们写代码就像在命令行敲命令一样自然!

第一个 Python 操作 Redis 代码

理论结合代码,我们来看看在 Python 中如何写入和读取一个字符串(String)。

hello_redis.py
import redis

# 1. 创建连接对象 (给 Redis 打电话)
client = redis.StrictRedis(host='127.0.0.1', port=6379, db=0, decode_responses=True)

# 2. 操作数据:写入 (SET)
client.set('student:001:name', '张三')

# 3. 操作数据:读取 (GET)
name = client.get('student:001:name')
print(f"获取到的名字是: {name}")

提示:代码中的 decode_responses=True 非常关键,它能让返回的数据自动变成 Python 字符串,而不是难懂的字节码(bytes)。

进阶:什么是连接池 (Connection Pool)?

上面那种每次要用就“创建连接”的方式,在学习时没问题,但在生产环境(真实应用中)会引发大灾难!

生活中的比喻:共享单车 vs 买自行车

每次操作都新建连接,就像每次出门办事都要买一辆新自行车,办完事就把车扔了。非常浪费时间(建立连接耗时)和资源(内存)。

连接池(Connection Pool)就像是一个共享单车停放点

  • 提前准备好一批建立好的连接放在池子里。
  • 需要操作时,从池子里一个连接。
  • 用完后不销毁,而是回池子里给别人用。

使用连接池优化代码

在企业级开发中,我们必须使用连接池来管理 Redis 连接。

pool_redis.py
import redis

# 1. 建立一个连接池,最大连接数设为 10
pool = redis.ConnectionPool(host='127.0.0.1', port=6379, db=0, max_connections=10)

# 2. 从连接池中获取一个连接对象
client = redis.StrictRedis(connection_pool=pool)

# 3. 后续操作完全一样
client.set('login:count', 100)
client.incr('login:count') # 访问量加 1

常见数据类型操作示例 (1)

操作 List (列表)

List 适合做消息队列、最新评论列表等。

# 从左侧推入数据
client.lpush('tasks', 'task1', 'task2')

# 从右侧弹出一个数据
task = client.rpop('tasks')

# 获取列表长度
length = client.llen('tasks')

操作 Hash (哈希)

Hash 非常适合存储对象结构,比如用户信息、商品属性。

# 存入对象的多个属性
client.hset('user:01', mapping={'name':'李四', 'age':20})

# 获取单个属性
age = client.hget('user:01', 'age')

# 获取所有属性
info = client.hgetall('user:01')

异常处理机制

网络通信随时可能中断(比如网线被老鼠咬断了,或者服务器停电了)。

在编写生产环境代码时,绝对不能假定网络永远通畅,必须加上异常捕获!

Try-Except 保护装甲

通常我们会捕获 redis.exceptions.RedisError(所有Redis错误的父类)。

try:
    client.ping() # 测试连接是否存活
    client.set('key', 'value')
except redis.exceptions.RedisError as e:
    print(f"糟糕!连接 Redis 发生错误: {e}")
    # 这里可以加入报警邮件、重试机制等

第二部分

Redis 主从复制模式

当一台服务器扛不住时,我们要开始找“小弟”了

单机 Redis 的三大危机

在刚才的编程中,我们一直连的都是 127.0.0.1 这一台电脑。但真实世界里,只有一台 Redis 会面临三个致命问题:


内存容量危机

一台机器的内存是有限的(比如最多128GB)。如果大数据业务有 500GB 的缓存数据,一台机器根本装不下!


性能并发危机

虽然 Redis 很快(单机十万次并发),但在双十一大促时,如果有上百万的用户同时疯狂访问,单台机器 CPU 会被瞬间打满。


单点故障危机

最可怕的危机!如果这唯一的服务器主板烧了,或者停电了,整个公司的缓存系统直接瘫痪,后果不堪设想。

什么是主从复制模式?

为了解决单机问题,Redis 提供了主从复制(Master-Slave)机制。

老板与助理的故事

你可以把 Master 节点看作是“老板”,把 Slave 节点看作是“助理”

  • 老板负责核心决策和签字(处理写操作)。
  • 老板做完决策后,立刻把文件复印一份给助理(数据同步)。
  • 当有人来查资料时,助理就可以直接翻文件回答,不用事事麻烦老板(分担读操作)。
Master (主节点)
数据复制
Slave 1 (从节点)
Slave 2 (从节点)

主从架构的核心设计:读写分离

主从模式最重要的应用场景就是实现读写分离,极大地提升系统的并发能力。

规则一:主写从读

  • 所有的 写操作(如 SET, HSET, LPUSH)必须只能发送给 Master 节点
  • 所有的 读操作(如 GET, HGET, LRANGE)可以分配给多个 Slave 节点

禁忌:默认情况下,Slave 节点是“只读”的,如果你强行往 Slave 节点写数据,Redis 会直接报错拒绝!

为什么这很有用?
在真实的互联网应用中(比如刷微博、看淘宝),用户看(读)的操作往往是写(发)的操作的 10 倍甚至 100 倍!增加从节点就能轻松扛住巨大的访问量。

数据是如何同步的?(核心原理)

既然主节点和从节点分开了,那么主节点刚写入的数据,从节点是怎么知道的呢?

Redis 的同步机制分为两种:全量同步增量同步

全量同步

当一个从节点刚刚加入(或断开很久重新连上)时触发。

  • 主节点会把内存中所有的数据,打包生成一个 RDB 快照文件。
  • 把这个大文件一次性丢给从节点。
  • 从节点清空自己旧数据,加载这个新文件。

代价高昂,比较慢。

增量同步

当从节点完成全量同步后,进入日常工作状态触发。

  • 主节点没收到一条写命令,除了自己执行,还会记录到一个小本本(缓冲区)里。
  • 然后把这些新的小命令,像流水一样源源不断地发送给从节点。
  • 从节点照着命令再执行一遍。

轻量级,实时性高。

如何部署主从模式?(概念篇)

部署 Redis 主从模式非常简单,可以说是一键搞定。主节点不需要做任何配置,默认大家生下来都是主节点。

关键在于从节点,我们只需要告诉它:“去,给 XX 当小弟”。

核心配置指令:replicaof (或早期版本的 slaveof)

在从节点的 Redis 配置文件(redis.conf)中,加入一行代码:

replicaof <master的IP地址> <master的端口号>

例如:replicaof 192.168.1.100 6379。重启从节点后,它就会自动去找主节点认大哥,并开始同步数据。

互动思考:主从模式完美吗?

假设我们部署了 1主 2从 的架构。

如果某天晚上,主节点(Master)的服务器突然宕机(死机)了,会发生什么?

点击刮开查看真相 👆
真相是:系统直接瘫痪一半!

1. 从节点们会变成“孤儿”,它们只能提供读服务,无法接受任何写数据
2. 主从模式没有自动故障转移的能力。
3. 必须半夜把你(程序员)叫醒,手动登录服务器,把其中一个从节点强行提拔为主节点,然后修改所有代码里的IP配置......

结论:主从模式解决了性能瓶颈,但依然存在“单点故障”的隐患!

第三部分

Redis 哨兵模式 (Sentinel)

给数据库请几位 24 小时巡逻的“智能保安”

什么是哨兵模式?

为了解决主从模式下主节点挂掉无人接管的问题,Redis 官方引入了 哨兵 (Sentinel) 架构

独立的监控者

哨兵其实也是一个特殊的 Redis 节点,但它不存储业务数据(不存缓存)。

它的唯一任务就是:盯着主从集群看! 就像医院心电监护仪一样,时刻检测主节点的心跳。

Sentinel (哨兵集群)

监控
Master (主)
Slave 1
Slave 2

哨兵的四大核心职责

1

监控 (Monitoring)

哨兵会不断地检查主节点和从节点是否按预期正常工作。就像查寝的宿管阿姨。

2

通知 (Notification)

当被监控的某个 Redis 节点出现问题时,哨兵可以通过 API 向管理员或其他应用程序发送通知。

3

自动故障转移 (Failover)

最核心功能! 如果主节点宕机,哨兵会自动选出一个聪明的从节点,把它升职为新的主节点。

4

配置提供者 (Configuration)

客户端(我们的 Python 代码)连接时,先问哨兵:“现在谁是老大?”哨兵会把最新主节点的 IP 告诉代码。

心跳机制与下线判定

哨兵是怎么知道主节点死没死的呢?靠的是心跳包(PING指令)

主观下线 (SDOWN)

一个哨兵节点每秒钟向主节点发送 PING。如果主节点在设定的时间内心虚没回信(PONG),这个哨兵就会心里想:“哎呀,老板是不是跑路了?”

但这只是它个人的主观判断,万一是它自己的网线松了呢?

客观下线 (ODOWN)

为了防止误判,哨兵们会互相拉个群开会(Gossip协议交流)。

当认为老板死掉的哨兵数量达到了法定人数(Quorum),大家就达成共识:“不是我的网络问题,是老板真挂了!”。此时正式宣布主节点客观下线。

哨兵领导者选举

当确认主节点彻底死亡后,需要执行故障转移。但这群哨兵里,谁去负责执行这个提拔任务呢?

如果大家都抢着去提拔自己的小弟,系统就乱套了。所以哨兵之间要先选出一个班长(Leader)

Raft 选举算法(简化版)

  • 每个哨兵都有权要求别人选自己当班长。
  • 通常来说,谁最先发现主节点挂了,谁就最先发起拉票请求。
  • 其他哨兵遵循“先到先得”的原则,谁先拉票我就投给谁。
  • 当某个哨兵获得超过半数(N/2 + 1)的选票时,它就光荣当选 Leader。

这就是为什么在生产环境中,哨兵节点的数量必须是奇数(例如 3、5、7),以防投票平局死锁!

故障转移过程 (Failover)

Leader 哨兵选出来后,它开始主持大局,执行以下三步操作:

第一步:选出新主 (选太子)

从剩下的存活从节点中,挑选一个最优秀的。挑选标准包括:网络最稳的、数据同步最完整的、配置优先级最高的。

第二步:黄袍加身 (提拔)

哨兵向选中的从节点发送命令 slaveof no one,让它立刻摆脱从属身份,成为新的真正的主节点。

第三步:昭告天下 (重置)

让其他从节点认新的主节点做大哥;同时更新配置,告诉外面的 Python 代码新主节点的 IP;甚至如果老主节点诈尸复活了,也会被强行变成新主节点的小弟。

哨兵模式的部署概念

哨兵的部署需要独立的配置文件 sentinel.conf

sentinel.conf 核心配置
# 格式:sentinel monitor <主节点起个名字> <IP> <端口> <法定人数(quorum)>
sentinel monitor mymaster 192.168.1.100 6379 2

# 判断失联的时间(毫秒),这里是 30 秒无响应则认为是主观下线
sentinel down-after-milliseconds mymaster 30000

# 故障转移失败的超时时间(毫秒)
sentinel failover-timeout mymaster 180000

注:法定人数 2 意味着,至少需要 2 个哨兵都同意主节点挂了,才能执行故障转移。如果是 3 台哨兵的集群,设为 2 最合理。

哨兵模式的局限性

有了哨兵,似乎高可用的问题(自动救火)解决了,但是...

容量和写并发的终极天花板并没有打破!

在哨兵模式下,不论你有多少个从节点,真正能进行写操作的,永远只有那 1 台主节点

同时,每一个节点内部都保存了 100% 的全量数据。如果公司有 1TB 的数据,由于单机内存限制根本装不下,哨兵模式依然束手无策。

第四部分

Redis Cluster 集群模式

人多力量大,打破单机内存极限的终极武器

什么是 Redis Cluster (集群)?

面对海量数据(比如微信上亿用户的登录状态),唯一的方法就是分而治之(分布式架构)

去中心化设计

在之前的模式里,不管怎样都有一个最高统治者(唯一的 Master)。

但在 Cluster 模式下,没有绝对的老大,大家都是平等的。有多台机器共同担任 Master 的角色,每个人只负责管理总体数据的一部分。

数据分片 (Sharding) 示意图

主节点 A
33%数据
主节点 B
33%数据
主节点 C
33%数据

总容量 = A + B + C。无限水平扩展!

核心魔法:哈希槽 (Hash Slot)

既然数据被分给了好几个主节点,那当我们想存一个叫做 student:name 的数据时,怎么知道该去哪台机器存呢?

Redis 引入了 哈希槽(Hash Slot) 的概念。

16384 个神奇的柜子

  • Redis 集群固定将整个数据存储空间划分为 16384 个槽(Slot编号从 0 到 16383)。
  • 这就像储物柜里有 16384 个小格子。
  • 集群中的每个主节点,会被分配负责其中一段槽位。
  • 例如:节点A负责 0~5000 槽,节点B负责 5001~10000 槽,节点C负责 10001~16383 槽。

数据是如何路由的?

当 Python 客户端向集群发送指令 SET name 张三 时:

1

计算哈希值

Redis 会把你的 key (也就是 'name') 放进一个叫 CRC16 的算法公式里,计算出一个整数结果。

2

取模定槽位

把这个整数对 16384 求余数(% 16384)。得到的结果一定是 0 到 16383 之间的一个数字。这就是这个数据归属的槽位。

3

精准投递

系统查看这个槽位当前被哪个主节点管辖,就把这条数据准确地保存到对应的服务器上。

节点通信:Gossip 协议

集群里有这么多节点,它们怎么知道彼此的死活,怎么知道哪个槽归谁管呢?

它们使用的是 Gossip(八卦/流言)协议

村口大妈式通信机制

Gossip 协议就像村口大妈聊天一样。节点 A 随机挑几个邻居节点,把自己的近况和听来的八卦(集群状态)告诉他们。这些邻居听完后,再分别告诉他们的邻居...

结果:不出几秒钟,一传十,十传百,整个集群的所有节点都同步了最新的全局信息。非常高效,且不需要一个中央服务器来协调!

请求重定向 (MOVED)

如果客户端不小心把读写请求发给了错误的主节点,会发生什么?

客户端:“节点A,我要获取 name 的值!”

节点A内部运算发现:“哎呀,name 经过计算属于 8800 号槽,这个槽目前归节点B管啊。”

节点A回复客户端:“MOVED 8800 192.168.1.101:6379” (拒绝执行,并告诉你去找B节点)。

客户端的驱动程序:默默擦干眼泪,自动带着请求去连接节点B重新拿数据(过程对我们写代码的人是透明的)。

集群的高可用保障

虽然集群把数据分片了,解决了容量问题。但如果某个管着 33% 数据的主节点硬件坏了,那这部分数据岂不是全丢了?

所以,集群模式内嵌了主从复制和哨兵的功能!

每个主节点的背后,都有备胎

  • 在真实的集群部署中,要求最少准备 6 台服务器(3主 3从)。
  • 主节点负责处理槽位和读写操作。
  • 每个主节点至少绑定一个从节点在背后默默同步数据。
  • 当某个主节点宕机,集群内的其他主节点通过 Gossip 协议互相确认后,会立刻自动将其从节点提拔为新的主节点接管槽位。无需借助外部的哨兵进程!

部署集群的命令行一瞥

虽然本节是理论课不要求实操,但大家可以看看现代 Redis 部署集群是多么的一键化:

使用 redis-cli 创建集群
# 将 6 台预先配置好的节点联合成集群,且自动按 1主1从(--cluster-replicas 1) 搭配
$ redis-cli --cluster create \
  192.168.1.11:6379 192.168.1.12:6379 \
  192.168.1.13:6379 192.168.1.14:6379 \
  192.168.1.15:6379 192.168.1.16:6379 \
  --cluster-replicas 1

>>> Performing Cluster Check (using node 192.168.1.11:6379)
>>> M: ...
[OK] All 16384 slots covered.

总结与对比

回顾四大阶段的演进

架构没有最好,只有最适合当前业务的

架构演进全景对比

部署模式 优点 致命缺点 适合场景
单机模式 最简单,零配置 无任何保障,容易死机 个人学习、早期测试
主从模式 读写分离,读性能翻倍 主节点挂了需要半夜人工抢修 读多写少的轻量级业务
哨兵模式 自动化故障转移,高可用 依然无法突破单台机器的内存上限 中型企业,对稳定性要求高
集群模式 (Cluster) 容量和性能无限扩展,自带高可用 配置最复杂,维护成本高 海量大数据、超高并发的大厂核心业务

本节理论课结束

下节课我们将前往实训机房,亲手在 Linux 系统上敲入命令,将今天学到的架构一步步搭建出来!大家做好准备!

感谢聆听

期待在下一节的实践操作课中与大家再见!