378 lines
14 KiB
Markdown
378 lines
14 KiB
Markdown
|
|
---
|
|||
|
|
tags: [cachecloud, redis, infra, self-hosted]
|
|||
|
|
create time: 2026-07-04 12:00
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# CacheCloud Redis 管理台
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
CacheCloud(搜狐开源)是一个 **Redis 私有云平台**,支持 Standalone、Sentinel、Cluster 三种架构的一站式高效管理。xinfra 中所有需要 Redis 服务的场景都通过 CacheCloud 进行**统一的实例申请和管理**——开发同学不再需要手动安装部署 Redis,只需在平台上提交工单即可获取可用实例。
|
|||
|
|
|
|||
|
|
核心目标:**降低大规模 Redis 运维成本,提升资源管控能力和利用率**。平台提供快速搭建/迁移、运维管理、弹性伸缩、统计监控、客户端整合接入等功能。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 系统架构
|
|||
|
|
|
|||
|
|
### 整体拓扑
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
graph TB
|
|||
|
|
subgraph "用户层"
|
|||
|
|
Dev[开发同学 / 运维人员]
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
subgraph "接入层 (Nginx 双机房)"
|
|||
|
|
NG1[Nginx 机房 A]
|
|||
|
|
NG2[Nginx 机房 B]
|
|||
|
|
VIP[Virtual IP → 双向漂移]
|
|||
|
|
VIP --> NG1
|
|||
|
|
VIP --> NG2
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
subgraph "应用层 (Spring Boot)"
|
|||
|
|
CC[CacheCloud Web Server]
|
|||
|
|
DB[(MySQL 元数据)]
|
|||
|
|
AGT_Mgr[Agent 管理器]
|
|||
|
|
ALERT[报警组件<br/>邮件 / 微信 / HTTP]
|
|||
|
|
CUSTOM[自定义扩展模块]
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
subgraph "执行层 (Agent 代理)"
|
|||
|
|
AGT1[Agent 宿主机 A<br/>Redis Standalone / Sentinel]
|
|||
|
|
AGT2[Agent 宿主机 B<br/>Redis Cluster Shard 1]
|
|||
|
|
AGT3[Agent 宿主机 C<br/>Redis Cluster Shard 2]
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
Dev -->|cc.company.com| VIP
|
|||
|
|
VIP --> NG1 & NG2
|
|||
|
|
NG1 & NG2 --> CC
|
|||
|
|
CC <--> DB
|
|||
|
|
CC --> AGT_Mgr
|
|||
|
|
CC --> ALERT
|
|||
|
|
CC --> CUSTOM
|
|||
|
|
AGT_Mgr -->|SSH + 心跳| AGT1 & AGT2 & AGT3
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> **关键设计决策**:CacheCloud 采用 **Agent 模式**而非直接 SSH 连接 Redis 实例。每个宿主机部署一个 Agent 进程,负责该机器上所有 Redis 实例的生命周期管理(启停、升级、备份恢复)。这样做的好处是 Agent 可复用 SSH 连接、缓存本地状态,大幅减少管理平台到 Redis 主机的网络开销。
|
|||
|
|
|
|||
|
|
### 核心组件
|
|||
|
|
|
|||
|
|
| 组件 | 职责 |
|
|||
|
|
|------|------|
|
|||
|
|
| **Web UI / REST API** | 自助申请实例、配置修改、工单审批、监控大屏 |
|
|||
|
|
| **Agent 代理** | 部署在每个宿主机上,负责 Redis 进程生命周期管理、状态上报、远程命令执行 |
|
|||
|
|
| **元数据库 (MySQL)** | 存储实例拓扑、配置模板、账号权限、工单流转记录 |
|
|||
|
|
| **报警模块** | 内置邮件和微信报警,同时暴露 HTTP 接口供任意语言集成 |
|
|||
|
|
| **扩展模块** | 登录组件 (`LoginComponent`)、报警组件 (`EmailComponent`/`WeChatComponent`) 均可自行实现 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 实例类型与选型指南
|
|||
|
|
|
|||
|
|
### 三种架构对比
|
|||
|
|
|
|||
|
|
| 维度 | Standalone | Sentinel (Master-Slave) | Cluster |
|
|||
|
|
|------|-----------|------------------------|---------|
|
|||
|
|
| **高可用** | ❌ 单机,无 Failover | ✅ 主从 + Sentinel 自动故障转移 | ✅ 多分片,自动槽迁移 |
|
|||
|
|
| **横向扩展** | ❌ 受单机容量限制 | ❌ 受单机容量限制 | ✅ 新增节点即扩容 |
|
|||
|
|
| **复杂度** | 简单 | 中等 | 较高 |
|
|||
|
|
| **推荐场景** | 测试环境、低频读写缓存 | 生产环境常规业务、内存需求 ≤ 6GB | 大数据量、高并发、内存需求 > 6GB |
|
|||
|
|
|
|||
|
|
> **建议**:并不是 Cluster 越好。如果当前或未来所需内存不超过 6GB 且要求高可用,选择 Sentinel 就足够了。
|
|||
|
|
|
|||
|
|
### 选型决策树
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart TD
|
|||
|
|
Start{是否需要高可用?}
|
|||
|
|
Start -->|否: 测试/灰度| Standalone[Standalone]
|
|||
|
|
Start -->|是| NeedScale{是否需要横向扩展?}
|
|||
|
|
NeedScale -->|否≤6GB| Sentinel[Sentinel 主从]
|
|||
|
|
NeedScale -->|是>6GB| Cluster[Redis Cluster]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 客户端接入方式
|
|||
|
|
|
|||
|
|
CacheCloud 提供了多种客户端 SDK 及 REST API,覆盖 Java、Python 等主流语言。
|
|||
|
|
|
|||
|
|
### REST API(最通用)
|
|||
|
|
|
|||
|
|
通过简单 HTTP 请求即可获取实例连接信息,适用于任何语言:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
GET http://{domain}/cache/client/redis/{appType}/{appId}.json?clientVersion={version}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**参数说明**:
|
|||
|
|
|
|||
|
|
| 参数 | 含义 | 枚举值 |
|
|||
|
|
|------|------|--------|
|
|||
|
|
| `appType` | 实例类型 | `cluster` / `sentinel` / `standalone` |
|
|||
|
|
| `appId` | 应用 ID | 平台分配的数字 ID |
|
|||
|
|
|
|||
|
|
**响应示例**:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"message": "client is up to date, Cheers!",
|
|||
|
|
"shardNum": 10,
|
|||
|
|
"appId": 10192,
|
|||
|
|
"status": 1,
|
|||
|
|
"shardInfo": "10.10.xx.xx:6390,10.10.xx.xx:6382 10.10.xx.xx:6387,10.10.xx.xx:6379 ..."
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> `shardInfo` 字段以空格分隔每个分片,逗号分隔同一个分片的主从地址。解析后即可直连。
|
|||
|
|
|
|||
|
|
### Java — cachecloud-client-redis(Jedis 封装)
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
@Configuration
|
|||
|
|
public class RedisConfiguration {
|
|||
|
|
|
|||
|
|
@Bean(destroyMethod = "close")
|
|||
|
|
public PipelineCluster pipelineCluster(@Value("${cachecloud.demo.appId}") long appId) {
|
|||
|
|
return ClientBuilder.redisCluster(appId).build();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@Bean(destroyMethod = "destroy")
|
|||
|
|
public JedisSentinelPool jedisSentinelPool(@Value("${cachecloud.demo.appId}") long appId) {
|
|||
|
|
return ClientBuilder.redisSentinel(appId).build();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@Bean(destroyMethod = "destroy")
|
|||
|
|
public JedisPool jedisPool(@Value("${cachecloud.demo.appId}") long appId) {
|
|||
|
|
return ClientBuilder.redisStandalone(appId).build();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**使用方式**:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
@Autowired private PipelineCluster pipelineCluster;
|
|||
|
|
|
|||
|
|
public String get(String key) {
|
|||
|
|
return pipelineCluster.get(key);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> 所有 `...Pool.getResource()` 使用后必须调用 `jedis.close()` —— 它并非真正关闭连接,而是将连接归还给连接池(内部判断连接是否损坏后决定 `returnResource` 还是 `returnBrokenResource`)。
|
|||
|
|
|
|||
|
|
### Java — cachecloud-client-lettuce(Lettuce 封装)
|
|||
|
|
|
|||
|
|
适合需要异步/响应式场景的应用:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
@Bean(destroyMethod = "shutdown")
|
|||
|
|
public RedisClusterClient redisClusterClient(long appId, String password) {
|
|||
|
|
return LettuceClientBuilder.redisCluster(appId, password).build();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@Bean(destroyMethod = "close")
|
|||
|
|
public StatefulRedisClusterConnection<String, String> clusterConnection(RedisClusterClient client) {
|
|||
|
|
StatefulRedisClusterConnection<String, String> conn = client.connect();
|
|||
|
|
conn.setReadFrom(ReadFrom.REPLICA_PREFERRED); // 读操作优先走从节点
|
|||
|
|
return conn;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Python 接入(REST API 动态拉取连接信息)
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
import requests
|
|||
|
|
from rediscluster import RedisCluster
|
|||
|
|
|
|||
|
|
app_id = 10192
|
|||
|
|
url = f'http://cc.company.com/cache/client/redis/cluster/{app_id}.json'
|
|||
|
|
resp = requests.get(url).json()
|
|||
|
|
|
|||
|
|
startup_nodes = [
|
|||
|
|
dict(zip(['host', 'port'], addr.split(':')))
|
|||
|
|
for shard in resp['shardInfo'].split(' ')
|
|||
|
|
for addr in shard.split(',')
|
|||
|
|
]
|
|||
|
|
rc = RedisCluster(startup_nodes=startup_nodes, password='your-pass')
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 跨机房部署(Cross-Room)
|
|||
|
|
|
|||
|
|
对于容灾要求高的业务,CacheCloud 支持**跨机房双活**:同一业务在两个机房分别部署应用实例,客户端 SDK 自动做双写双读和机房切换。原理是两个 `PipelineCluster` 实例被包装进一个 `RedisCrossRoomClient`。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 系统功能全景
|
|||
|
|
|
|||
|
|
### 用户端功能
|
|||
|
|
|
|||
|
|
| 功能 | 说明 |
|
|||
|
|
|------|------|
|
|||
|
|
| **应用管理** | 查看统计信息、实例列表、应用拓扑、连接信息 |
|
|||
|
|
| **监控面板** | 命令曲线、延迟监控、日报统计 |
|
|||
|
|
| **命令执行** | 在线执行 Redis 命令用于排查 |
|
|||
|
|
| **键值分析** | 分析 bigkey、hotkey 分布 |
|
|||
|
|
|
|||
|
|
### 运维端功能
|
|||
|
|
|
|||
|
|
| 功能分类 | 具体能力 |
|
|||
|
|
|----------|---------|
|
|||
|
|
| **数据统计** | 全局统计、client 统计、server 统计 |
|
|||
|
|
| **工单审批** | 实例申请、配置修改、数据清理等操作需管理员审批 |
|
|||
|
|
| **应用运维** | 应用维度的启停、配置下发、日志查看 |
|
|||
|
|
| **实例运维** | 实例维度的启停、配置查询、数据清理 |
|
|||
|
|
| **数据迁移** | 跨实例数据迁移工具 |
|
|||
|
|
| **诊断工具** | 慢查询分析、连接数诊断等 |
|
|||
|
|
| **模板管理** | 按规格预设 Redis 配置模板(maxmemory、持久化策略等) |
|
|||
|
|
| **任务流** | 编排运维操作流程(如批量升级) |
|
|||
|
|
|
|||
|
|
### 报警配置
|
|||
|
|
|
|||
|
|
CacheCloud 内置的报警覆盖了 Redis 机器级别和实例级别的重要指标,支持的报警渠道:
|
|||
|
|
|
|||
|
|
- **邮件报警** — 默认实现,也可替换为 HTTP 回调
|
|||
|
|
- **微信报警** — 企业微信/钉钉 webhook 风格
|
|||
|
|
|
|||
|
|
报警可通过 HTTP 接口自定义,格式如下:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
POST www.xxx.com/emailAlert?title=xx&content=xx&receiver=x&cc=x
|
|||
|
|
POST www.xxx.com/weChatAlert?title=xx&message=xx&weChatList=xx
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 基础运维实践
|
|||
|
|
|
|||
|
|
### maxmemory-policy 策略选择
|
|||
|
|
|
|||
|
|
| 策略 | 行为 | 适用场景 |
|
|||
|
|
|------|------|---------|
|
|||
|
|
| `volatile-lru` | 删除有过期时间的 key,LRU 淘汰 | 默认策略,有 TTL 的缓存 |
|
|||
|
|
| `allkeys-lru` | 对所有 key 做 LRU 淘汰 | 纯缓存场景,不设 TTL |
|
|||
|
|
| `volatile-ttl` | 删除即将过期的 key | 希望按过期时间优先淘汰 |
|
|||
|
|
| `noeviction` | 不淘汰,写操作返回错误 | 需要绝对保证数据完整性 |
|
|||
|
|
|
|||
|
|
> **注意**:修改的配置会对应用的所有节点生效,因为所有节点的配置是统一的。
|
|||
|
|
|
|||
|
|
### Jedis 连接池调优参考
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
|
|||
|
|
poolConfig.setMaxTotal(DEFAULT_MAX_TOTAL * 5); // 根据实际 QPS 调整
|
|||
|
|
poolConfig.setMaxIdle(DEFAULT_MAX_IDLE * 3);
|
|||
|
|
poolConfig.setMinIdle(DEFAULT_MIN_IDLE * 2);
|
|||
|
|
poolConfig.setMaxWaitMillis(3000); // 连接耗尽时最多等待 3s
|
|||
|
|
poolConfig.setJmxEnabled(true); // 开启 JMX 便于观察
|
|||
|
|
poolConfig.setTestWhileIdle(true); // 空闲时定期检查连接有效性
|
|||
|
|
poolConfig.setTimeBetweenEvictionRunsMillis(60000); // 每分钟检查一次
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
关键字段解读:
|
|||
|
|
|
|||
|
|
| 配置项 | 默认值 | 调优建议 |
|
|||
|
|
|--------|--------|---------|
|
|||
|
|
| `maxTotal` | 8 | 根据峰值连接数估算,通常 × 3~5 |
|
|||
|
|
| `testOnBorrow` | false | 不建议设为 true,会显著增加延迟 |
|
|||
|
|
| `testOnReturn` | false | 一般保持 false,由 `testWhileIdle` 兜底 |
|
|||
|
|
| `whenExhaustedAction` | 1 (阻塞) | 配合 `maxWaitMillis` 使用,避免无限等待 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 常见陷阱与最佳实践
|
|||
|
|
|
|||
|
|
### 1. Bigkey 的寻找与优化
|
|||
|
|
|
|||
|
|
**什么是 bigkey**:value 所占内存空间较大的 key。字符串类型超过 100KB 即视为 bigkey;非字符串类型(Hash/List/Set/ZSet)则以元素数量过多为准。
|
|||
|
|
|
|||
|
|
**危害**:
|
|||
|
|
- 内存不均匀:Cluster 中造成部分节点内存暴增
|
|||
|
|
- 超时阻塞:Redis 单线程特性下,大 key 操作耗时 > 客户端超时
|
|||
|
|
- 网络拥塞:单次大流量冲击网卡,影响同机其他实例
|
|||
|
|
- 过期删除阻塞:未启用 lazyfree 时阻塞主线程
|
|||
|
|
- 碎片整理冲突:Redis 4.0+ activeDefrag 对超大 key 可能导致周期性延迟
|
|||
|
|
|
|||
|
|
**发现手段**:
|
|||
|
|
|
|||
|
|
| 方法 | 命令 | 特点 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| redis-cli | `redis-cli --bigkeys` | 全量扫描,建议在从节点执行 |
|
|||
|
|
| DEBUG OBJECT | `DEBUG OBJECT key` | 获知序列化长度 `serializedlength` |
|
|||
|
|
| MEMORY USAGE | `MEMORY USAGE key` | 仅 Redis 4.0+,返回精确内存占用 |
|
|||
|
|
| 监控输出缓冲区 | `info clients` 关注 `client_biggest_input_buf` / `client_recent_max_output_buffer` | 间接判断是否存在大 key 读取 |
|
|||
|
|
|
|||
|
|
**优雅删除方案**:
|
|||
|
|
|
|||
|
|
- **String**:直接使用 `DEL`,通常不会阻塞
|
|||
|
|
- **Hash/List/Set/ZSet**:使用 `HSCAN/SSCAN/ZSCAN` 分批获取元素 + `HDEL/SREM/ZREM` 逐个删除,或使用 Redis 4.0+ 的 `UNLINK`(异步删除)
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def del_big_hash(r, key):
|
|||
|
|
cursor = 0
|
|||
|
|
while True:
|
|||
|
|
cursor, members = r.hscan(key, cursor, count=100)
|
|||
|
|
if not members:
|
|||
|
|
break
|
|||
|
|
r.hdel(key, *members) # pipeline 批量更高效
|
|||
|
|
if cursor == 0:
|
|||
|
|
break
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. Hotkey 的处理方向
|
|||
|
|
|
|||
|
|
当某个 key 的访问量远超平均水平时:
|
|||
|
|
- **本地缓存**:在应用侧加一层 Guava/Caffeine 缓存
|
|||
|
|
- **key 拆分**:将一个大 key 拆成多个子 key(如 `user:1001:friends` → `user:1001:friends:1`, `user:1001:friends:2`)
|
|||
|
|
- **读写分离**:读操作通过 Sentinel 路由到从节点
|
|||
|
|
|
|||
|
|
### 3. 实例规格匹配
|
|||
|
|
|
|||
|
|
不要盲目选大规格。建议先通过 Prometheus/Grafana 观察历史 QPS、内存使用和连接数,再确定实例规格。过度配置会在资源看板上体现为浪费。
|
|||
|
|
|
|||
|
|
### 4. 临时 vs 长期实例
|
|||
|
|
|
|||
|
|
- **临时实例**(测试/灰度用)应设置自动回收策略——超出保留期后系统自动销毁
|
|||
|
|
- **长期实例**则需要完善监控、定期巡检配置变更
|
|||
|
|
|
|||
|
|
### 5. 机房就近原则
|
|||
|
|
|
|||
|
|
Redis 对网络延迟非常敏感。跨机房访问比同机房慢数倍,因此应用申请时应填写服务所在机房,确保实例分配到最近的机器。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Redis 版本演进速览
|
|||
|
|
|
|||
|
|
CacheCloud 管理的 Redis 实例可能运行在不同版本,了解各版本的关键特性有助于理解平台行为差异:
|
|||
|
|
|
|||
|
|
| 版本 | 核心新特性 | CacheCloud 相关 |
|
|||
|
|
|------|-----------|----------------|
|
|||
|
|
| **3.x** | 原生 Cluster | 最早支持的 Cluster 形态 |
|
|||
|
|
| **4.0** | Lazyfree 异步删除、AOF/RDB 混合持久化、内存碎片整理 | 支持 UNLINK 删除 bigkey |
|
|||
|
|
| **5.0** | Stream 数据类型、RESP3 协议、Dynamic HZ | 客户端兼容性更好 |
|
|||
|
|
| **6.0** | 多线程 IO、ACL、SSL 加密、协助客户端缓存 | 多线程 IO 提升非 Pipeline 场景 2 倍性能 |
|
|||
|
|
|
|||
|
|
> **启发问题**:为什么 Redis 6.0 的多线程只处理网络 IO 而不涉及命令执行?这背后有什么权衡?
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 与 xinfra 的对接方向
|
|||
|
|
|
|||
|
|
进阶目标中提到要「基于 CacheCloud API 实现页面直接创建 Redis Cluster」,这意味着:
|
|||
|
|
|
|||
|
|
1. **阅读 Open API 文档**:梳理可用的创建/配置/查询接口
|
|||
|
|
2. **封装创建流程**:选择规格 → 调用 API → 等待就绪 → 返回连接信息
|
|||
|
|
3. **集成到 xinfra 统一服务管理页面**:让开发者在 xinfra 内即可完成 Redis 实例的全生命周期操作
|
|||
|
|
4. **可选增强**:对接 xinfra 的审批流、用量统计、成本分摊等模块
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 关联笔记
|
|||
|
|
|
|||
|
|
- [[technical/xinfra-preview/cloud-dm-overview]] — SQL 审核与 Redis 访问同属数据层基础设施
|
|||
|
|
- [[technical/xinfra-preview/k8s-rke2-fundamentals]] — CacheCloud 的宿主机未来可能迁移至 K8s 部署
|
|||
|
|
- https://github.com/sohutv/cachecloud — CacheCloud GitHub 仓库(含完整 Wiki 和代码)
|
|||
|
|
- https://github.com/antirez/redis — Redis 官方仓库
|