Python 整合 Redis 哨兵(Sentinel)与集群(Cluster)实战指南

Python 整合 Redis 哨兵(Sentinel)与集群(Cluster)实战指南

面向生产环境的 Redis Python 工程化示例,涵盖:

  • 哨兵模式 & 集群模式自动切换
  • 常用数据结构(String / Hash / List / Set / ZSet)
  • 分布式锁(可重入 / 自动续期 / Lua)
  • 发布订阅(Pub/Sub)
  • 原子计数 / 限流 / 缓存模式
  • 健康检查、连接池、异常处理、最佳实践

一、技术选型说明

1. Python Redis 客户端

text 复制代码
redis-py >= 5.x
  • 官方维护
  • 原生支持 Sentinel
  • 原生支持 Redis Cluster
  • 支持 pipeline / lua / pubsub

安装:

bash 复制代码
pip install redis>=5.0.0

二、项目结构(推荐生产级)

text 复制代码
redis_client/
├── __init__.py
├── config/
│   ├── __init__.py
│   └── settings.py          # Redis 配置(哨兵 / 集群)
│
├── core/
│   ├── __init__.py
│   ├── client.py            # Redis 客户端工厂(核心)
│   ├── connection.py        # 连接池管理
│   └── exceptions.py        # 自定义异常
│
├── features/
│   ├── __init__.py
│   ├── cache.py             # 通用缓存封装
│   ├── lock.py              # 分布式锁
│   ├── counter.py           # 计数器 / 限流
│   ├── pubsub.py             # 发布订阅
│   └── structures.py        # Redis 各数据结构封装
│
├── utils/
│   ├── __init__.py
│   ├── serializer.py        # JSON / MsgPack
│   └── time.py
│
└── examples/
    ├── sentinel_demo.py
    ├── cluster_demo.py
    └── pubsub_demo.py

三、Redis 配置(settings.py

python 复制代码
# redis_client/config/settings.py

REDIS_MODE = "sentinel"  # sentinel | cluster | standalone

# Sentinel 配置
REDIS_SENTINEL = {
    "sentinels": [
        ("10.0.0.1", 26379),
        ("10.0.0.2", 26379),
    ],
    "service_name": "mymaster",
    "db": 0,
    "password": None,
    "socket_timeout": 3,
}

# Cluster 配置
REDIS_CLUSTER = {
    "startup_nodes": [
        {"host": "10.0.0.10", "port": 6379},
        {"host": "10.0.0.11", "port": 6379},
    ],
    "password": None,
    "socket_timeout": 3,
}

四、Redis 客户端工厂(核心)

python 复制代码
# redis_client/core/client.py

from redis import Redis
from redis.sentinel import Sentinel
from redis.cluster import RedisCluster
from redis_client.config.settings import (
    REDIS_MODE,
    REDIS_SENTINEL,
    REDIS_CLUSTER,
)


class RedisClientFactory:
    _client = None

    @classmethod
    def get_client(cls):
        if cls._client:
            return cls._client

        if REDIS_MODE == "sentinel":
            sentinel = Sentinel(
                REDIS_SENTINEL["sentinels"],
                socket_timeout=REDIS_SENTINEL["socket_timeout"],
                password=REDIS_SENTINEL["password"],
            )
            cls._client = sentinel.master_for(
                service_name=REDIS_SENTINEL["service_name"],
                db=REDIS_SENTINEL["db"],
                decode_responses=True,
            )

        elif REDIS_MODE == "cluster":
            cls._client = RedisCluster(
                startup_nodes=REDIS_CLUSTER["startup_nodes"],
                password=REDIS_CLUSTER["password"],
                decode_responses=True,
            )

        else:
            cls._client = Redis(host="localhost", port=6379)

        return cls._client

优点

  • 调用方无感知哨兵 / 集群
  • 自动主从切换
  • 单例连接池

五、通用缓存封装(cache.py

python 复制代码
# redis_client/features/cache.py

import json
from redis_client.core.client import RedisClientFactory


class Cache:
    def __init__(self):
        self.redis = RedisClientFactory.get_client()

    def set(self, key, value, ttl=None):
        val = json.dumps(value)
        self.redis.set(key, val, ex=ttl)

    def get(self, key, default=None):
        val = self.redis.get(key)
        return json.loads(val) if val else default

    def delete(self, key):
        self.redis.delete(key)

    def exists(self, key) -> bool:
        return self.redis.exists(key) == 1

缓存模式建议

场景 建议
热点数据 TTL + 随机抖动
空值 缓存 NULL(短 TTL)
高并发 逻辑过期 + 异步重建

六、Redis 数据结构封装(structures.py

python 复制代码
# redis_client/features/structures.py

from redis_client.core.client import RedisClientFactory


class RedisStructures:
    def __init__(self):
        self.redis = RedisClientFactory.get_client()

    # String
    def incr(self, key, amount=1):
        return self.redis.incr(key, amount)

    # Hash
    def hset(self, name, key, value):
        self.redis.hset(name, key, value)

    def hget(self, name, key):
        return self.redis.hget(name, key)

    # List
    def lpush(self, key, value):
        self.redis.lpush(key, value)

    def rpop(self, key):
        return self.redis.rpop(key)

    # Set
    def sadd(self, key, value):
        self.redis.sadd(key, value)

    # ZSet
    def zadd(self, key, mapping: dict):
        self.redis.zadd(key, mapping)

七、分布式锁(lock.py

1. Lua 保证原子性

python 复制代码
# redis_client/features/lock.py

import uuid
import time
from redis_client.core.client import RedisClientFactory


UNLOCK_SCRIPT = """
if redis.call('get', KEYS[1]) == ARGV[1] then
    return redis.call('del', KEYS[1])
else
    return 0
end
"""


class RedisLock:
    def __init__(self, key, ttl=10):
        self.redis = RedisClientFactory.get_client()
        self.key = f"lock:{key}"
        self.ttl = ttl
        self.value = str(uuid.uuid4())

    def acquire(self, retry=3):
        for _ in range(retry):
            if self.redis.set(self.key, self.value, nx=True, ex=self.ttl):
                return True
            time.sleep(0.1)
        return False

    def release(self):
        self.redis.eval(UNLOCK_SCRIPT, 1, self.key, self.value)

特点:

  • 防误删
  • 支持哨兵 / 集群
  • Lua 原子性

八、发布订阅(pubsub.py

python 复制代码
# redis_client/features/pubsub.py

import threading
from redis_client.core.client import RedisClientFactory


class RedisPubSub:
    def __init__(self):
        self.redis = RedisClientFactory.get_client()

    def publish(self, channel, message):
        self.redis.publish(channel, message)

    def subscribe(self, channel, handler):
        pubsub = self.redis.pubsub()
        pubsub.subscribe(channel)

        def listen():
            for msg in pubsub.listen():
                if msg['type'] == 'message':
                    handler(msg['data'])

        threading.Thread(target=listen, daemon=True).start()

⚠️ 注意:

  • Pub/Sub 不可靠(断线即丢)
  • 生产消息推荐 Redis Stream / MQ

九、计数器 & 限流(counter.py

python 复制代码
# redis_client/features/counter.py

import time
from redis_client.core.client import RedisClientFactory


class RateLimiter:
    def __init__(self, key, limit, window):
        self.redis = RedisClientFactory.get_client()
        self.key = key
        self.limit = limit
        self.window = window

    def allow(self):
        now = int(time.time())
        pipe = self.redis.pipeline()
        pipe.incr(self.key)
        pipe.expire(self.key, self.window)
        count, _ = pipe.execute()
        return count <= self.limit

十、生产级最佳实践总结

Redis 哨兵

  • 客户端必须通过 Sentinel 发现主节点
  • 禁止直连 master IP

Redis Cluster

  • Key 必须使用 hash tag 保证多 key 原子:
text 复制代码
user:{123}:name
user:{123}:age

通用建议

建议
连接池 单例
大 key 严禁
热 key 本地缓存 + Redis
Lua 控制复杂度
序列化 JSON / MsgPack

十一、可以进一步扩展的功能

  • Redis Stream 消费组
  • RedLock 多实例锁(跨机房)
  • Cache Aside / Write Through
  • 二级缓存(Local + Redis)
  • 监控(slowlog / latency)
相关推荐
BYSJMG18 小时前
2026计算机毕设推荐:基于大数据的车辆二氧化碳排放量可视化分析系统
大数据·vue.js·python·mysql·django·课程设计
Pyeako18 小时前
opencv计算机视觉--DNN模块实现风格迁移
python·opencv·计算机视觉·pycharm·dnn·预处理·风格迁移
m0_7066532318 小时前
用Python创建一个Discord聊天机器人
jvm·数据库·python
tlwlmy18 小时前
python excel图片批量导出
开发语言·python·excel
ValhallaCoder18 小时前
hot100-矩阵
数据结构·python·算法·矩阵
笨蛋不要掉眼泪18 小时前
Redis持久化解析:RDB和AOF的对比
前端·javascript·redis
那年我七岁18 小时前
android ndk c++ 绘制图片方式
android·c++·python
Java后端的Ai之路18 小时前
【Python教程10】-开箱即用
android·开发语言·python
深蓝电商API18 小时前
异步爬虫中代理池的并发管理
开发语言·爬虫·python
B站计算机毕业设计超人18 小时前
计算机毕业设计PySpark+Hive+Django小红书评论情感分析 小红书笔记可视化 小红书舆情分析预测系统 大数据毕业设计(源码+LW+PPT+讲解)
大数据·人工智能·hive·爬虫·python·spark·课程设计