阿里云Redis集群配置遇到的一些问题

阿里云Redis(tair)

Redis开源版: 兼容Redis的高性能内存数据库产品
Tair(企业版): 相比redis有更强的性能, 更丰富的数据结构, 更智能的代理等

阿里云Redis的计费模式:

分片: 一个分片就是一个完整的规格, 整体性能=分片数x规格性能
规格: 每个分片的规格, 关联CPU和内存, CPU都是3核心, 规格大小关联内存大小与带宽
节点: 每个分片下的节点数, 指运行 Redis 服务的单个实例(进程),是集群或单机部署的基本组成单位。

分片与节点的关系(以 3 节点为例)

  • 1 个分片:指集群中负责处理某一范围哈希槽(如 0-5460 槽)的节点组,包含主节点和其从节点。
  • 3 个节点
    • 1 个 主节点进程:处理该分片的读写请求,持有完整数据,分配哈希槽。
    • 2 个 从节点进程:从主节点同步数据(只读),主节点故障时可通过选举升级为主节点,实现高可用。
    • 每个节点都是独立的 Redis 进程,有自己的 PID、内存空间和端口(如 6379、6380、6381)

节点与 CPU、内存的关系, 如果1个分片3CPU+2G内存

  • CPU 分配:3 个节点(进程)共享 3 个 CPU 核心。
  • 内存分配:3 个节点共享 2G 内存。

直连与代理模式

直连模式: 需要配置集群以及解析命令中的 Key 到分片节点中的位置。
代理模式: 可以让你像连接单机 Redis 一样使用集群,极大地简化了开发和部署。阿里云 Tair(兼容 Redis)的代理模式对外暴露一个统一的连接地址,内部由 Proxy 节点自动处理数据分片和路由,对客户端透明。

在代理模式下,这些复杂性全部由阿里云的 Proxy 层承担了,你无需关心。

集群模式与高可用模式

在技术选型时, 发现相同配置的 RDS 实例,在“集群模式”下比“高可用模式”便宜很多。原因是: 你所说的“集群模式”很可能指的是 读写分离架构(Proxy + 只读实例),而“高可用模式”指的是 主备架构(HA)的单实例

“高可用模式”虽然只有一个“入口”,但它背后是一个全规格的备实例,相当于你为两个同配置的实例买单。而“集群模式”虽然实例多,但只读实例配置更低,总资源消耗少,所以价格更低。

高可用版(主备架构)

  • 1 个主实例(Primary) 1 个备实例(Standby,用于故障切换), 主备相同配置
  • 所有读写请求都由主实例处理
  • 备实例不提供读服务(只用于灾备)

高可用版也可以配置只读实例, 阿里云对应后台开启 “备节点读能力” 即可。

集群版(读写分离)

  • 1 个主实例(负责写)1~5 个只读实例(负责读)1 个数据库代理(Database Proxy,负责路由)只读实例配置低于主实例
  • 自动读写分离
  • 主实例故障时,可切换

使用阿里云Redis

白名单设置

默认不在白名单的不能访问

访问Redis

  • 推荐在生产缓存中使用内网访问
  • 不推荐在生产环境中使用外网访问, 但测试服可以申请使用
  • 在代理模式中只需要修改项目中的 host/pass 即可像使用单机Redis一样使用集群Redis

集群模式报错 keys not in same slot

Redis 集群模式下的经典问题: 命令中涉及的 Key 必须位于同一个 hash slot(哈希槽)。

Redis 集群将整个 Key 空间划分为 16384 个 hash slots,不同的 Key 根据其名称被分配到不同的 slot。

集群要求:一个命令操作的多个 Key 必须位于同一个节点上(即同一个 slot),否则无法保证原子性。

如果操作了多个Key, 但这些 Key 的名称导致它们被计算到了 不同的 hash slot,Redis 集群拒绝执行。就导致了此错误。

// 校验是否有此错误, 在redis-cli中执行命令
// (error) ERR 'EVAL' command keys must in same slot
eval "return {KEYS[1]}, KEYS[2]" 2 "11" "222" 

哪些操作会出现此问题

  • MGET、MSET、MSETNX:多键批量读写,若键不在同一槽位,集群报错。
  • DEL key1 key2 ...:删除多个键,键需在同一槽位。
  • KEYS pattern:遍历所有键
  • SINTER、SUNION、SDIFF(集合交集 / 并集 / 差集):多集合操作,集合键需在同一槽位。
  • ZUNIONSTORE、ZINTERSTORE(有序集合合并):同上,涉及的键需在同一槽位。
  • 涉及全局状态的命令FLUSHDB/FLUSHALL/DBSIZE/INFO/CONFIG GET,SET
  • Lua 脚本
  • 事务(Transaction)

解决方案一

使用 Hashtag(标签) 强制 Key 落在同一 slot。Redis 支持通过 {} 来定义 Hashtag,只有第一个 {} 内的内容参与 slot 计算

注意如果Hashtag设计不合理可能导致一个slot中有大量数据, 其他slot中无数据的情况, 严重影响性能。

// 如何校验是否在同一个 slot 中, 使用Redis命令
> CLUSTER KEYSLOT user:1001:profile
(integer) 1234

// Hashtag(标签)技巧
Key: user:1001:profile        → 计算整个 Key → slot A
Key: user:1001:settings       → 计算整个 Key → slot B(可能不同)

Key: {user:1001}:profile      → 只计算 {user:1001} → slot X
Key: {user:1001}:settings     → 只计算 {user:1001} → slot X(相同!)

$key1 = "{user:{$uid}}:profile"   // 注意区分 {$uid} 是一个变量, 这个key和用户相关联
$key2 = "{user:{$uid}}:settings" 

// Redis 只会提取第一个 { 和其后的第一个 } 之间的内容作为 hashtag
Key: {key1}:{key2}:{key3}     → 只计算 key1
Key: {{key1}}                 → 只计算 {key1  不推荐
Key: {}                       → 忽略

解决方案二

使用阿里云开源版本的Redis, 集群代理模式. 其可能在代理成针对了 mget等 命令做了多key优化。

解决方案三

使用 Tair 集群的代理模式, 具备智能路由和跨 slot 操作能力。如果 Key 在不同 slot,代理会自动将请求路由到正确的节点,甚至拆分执行。

laravel中queue出现如上错误

真正的罪魁祸首:Laravel 队列的原子操作。

当你把任务推送到 Redis 队列时,Laravel 并不只是简单地 RPUSH 一个任务到 queues:xxx。它使用了一个 Lua 脚本 来保证操作的原子性,这个脚本通常会做以下几件事:

  • 将任务数据 LPUSH 到 queues:xxx队列。
  • 将任务数据 SADD 到 queues:xxx:reserved 集合(用于追踪待处理任务)。
  • 可能还会操作其他 Key,如 queues:xxx:notify。

这三个 Key 极大概率落在不同的 slot,而 Lua 脚本要求所有 Key 必须在同一 slot,因此 Redis 集群拒绝执行,抛出错误。

消费者队列有无大括号的问题

生产者onQueue('{abc}') 和 消费者 --queue=abc 是同一个队列吗?❌ 不是!

生产者生成后的key为
baomingtest_database_queues:{captcha}
baomingtest_database_queues:{captcha}:notify
与消费者的key不同, 所以不是同一个队列

此处评论已关闭