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)
相关推荐
程序员杰哥3 小时前
接口测试之文件上传
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·接口测试
2401_841495643 小时前
【自然语言处理】单字与双字字频统计算法设计
人工智能·python·算法·自然语言处理·单字·双字·字频统计
fegggye3 小时前
创建一个rust写的python库[signatures和错误处理]
开发语言·python·rust
拉姆哥的小屋3 小时前
从400维向量到160000维矩阵:基于深度学习的火焰参数预测系统全解析
开发语言·人工智能·python·深度学习·线性代数·算法·矩阵
矢鱼3 小时前
python中对应c++容器的结构
开发语言·c++·python·算法
古城小栈3 小时前
Java 内存优化:JDK 22 ZGC 垃圾收集器调优
java·python·算法
serve the people3 小时前
tensorflow 零基础吃透:tf.sparse.SparseTensor 与核心 TensorFlow API 的协同使用
人工智能·python·tensorflow
SoleMotive.3 小时前
redis和mysql有什么区别,以及redis和mysql都有什么缺点,以及什么地方redis不如mysql?
数据库·redis·mysql
雍凉明月夜3 小时前
视觉opencv学习笔记Ⅴ-数据增强(2)
人工智能·python·opencv·计算机视觉