Garnet 架构设计文档
概述
Garnet 是微软研究院开发的新一代远程缓存存储系统,采用 Redis RESP(Redis Serialization Protocol)协议,与 Redis 完全兼容。基于 .NET 技术栈构建,具有高性能、可扩展性强、跨平台等特点。
核心特性
- Redis 协议兼容:完全支持 Redis RESP 协议,可使用现有 Redis 客户端
- 高性能:极低延迟(99.9% 请求小于 300 微秒),高吞吐量
- 现代化设计:基于 .NET,跨平台,易于开发和扩展
- 集群支持:内置分片、复制和动态迁移功能
- 可扩展性:支持自定义命令、数据类型和存储过程
整体架构
Garnet 的架构设计重新思考了整个缓存存储栈,从网络数据包接收、解析处理数据库操作,到执行存储交互。
┌─────────────────────────────────────────────────────────────┐
│ Client Applications │
└─────────────────────┬───────────────────────────────────────┘│ RESP Protocol
┌─────────────────────▼───────────────────────────────────────┐
│ Network Layer │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ TCP Handler │ │ TLS/SSL │ │ Connection │ │
│ │ │ │ Support │ │ Management │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────┬───────────────────────────────────────┘│
┌─────────────────────▼───────────────────────────────────────┐
│ Session Management │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ RESP Server │ │ Authentication│ │ Command │ │
│ │ Session │ │ & ACL │ │ Parsing │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────┬───────────────────────────────────────┘│
┌─────────────────────▼───────────────────────────────────────┐
│ Command Processing │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Basic │ │ Custom │ │ Transaction │ │
│ │ Commands │ │ Commands │ │ Manager │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────┬───────────────────────────────────────┘│
┌─────────────────────▼───────────────────────────────────────┐
│ Storage Layer │
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
│ │ Main Store │ │ Object Store │ │
│ │ (Raw Strings) │ │ (Complex Objects) │ │
│ │ │ │ │ │
│ │ ┌─────────────────┐ │ │ ┌─────────────────┐ │ │
│ │ │ Tsavorite KV │ │ │ │ Tsavorite KV │ │ │
│ │ │ (Memory) │ │ │ │ (.NET Objects)│ │ │
│ │ └─────────────────┘ │ │ └─────────────────┘ │ │
│ │ ┌─────────────────┐ │ │ ┌─────────────────┐ │ │
│ │ │ Tiered │ │ │ │ Serialization │ │ │
│ │ │ Storage │ │ │ │ (Disk/Cloud) │ │ │
│ │ └─────────────────┘ │ │ └─────────────────┘ │ │
│ └─────────────────────────┘ └─────────────────────────┘ │
└─────────────────────┬───────────────────────────────────────┘│
┌─────────────────────▼───────────────────────────────────────┐
│ Infrastructure │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ AOF │ │ Checkpoints │ │ Metrics │ │
│ │ Logging │ │ & Recovery │ │ & Monitoring│ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└───────────────────────────────────────────────────────────────┘
核心组件详解
1. 网络层 (Network Layer)
1.1 设计理念
Garnet 的网络层基于共享内存设计,TLS 处理和存储交互在网络 I/O 完成线程上执行,避免了线程切换开销。
1.2 核心组件
TCP 网络处理器 (TcpNetworkHandler)
public abstract class TcpNetworkHandler<TServerHook> : TcpNetworkHandlerBase<TServerHook, GarnetTcpNetworkSender>
- 管理 TCP 连接生命周期
- 处理网络 I/O 操作
- 支持 TLS/SSL 加密传输
- 实现连接池和缓冲区管理
网络发送器 (GarnetTcpNetworkSender)
- 负责数据包发送
- 支持批量发送优化
- 实现发送限流控制
缓冲区管理
- 使用固定缓冲池 (LimitedFixedBufferPool)
- 减少内存分配和垃圾回收
- 支持可配置的缓冲区大小
2. 会话管理层 (Session Management)
2.1 RESP 服务器会话 (RespServerSession)
internal sealed unsafe partial class RespServerSession : ServerSessionBase
核心职责:
- RESP 协议解析和处理
- 命令路由和执行
- 用户认证和授权
- 数据库会话管理
关键特性:
- 支持多数据库切换
- 集成性能监控和指标收集
- 实现命令解析状态机
- 支持事务处理
2.2 会话状态管理
- parseState: 主命令解析状态
- customCommandParseState: 自定义命令解析状态
- activeDbId: 当前活动数据库 ID
- userHandle: 当前认证用户句柄
3. 存储层 (Storage Layer)
3.1 双存储架构
Garnet 采用创新的双存储设计:
主存储 (Main Store)
- 专门优化原始字符串操作
- 基于 Tsavorite KV 引擎
- 精心管理内存以避免 GC 压力
- 支持内存分层存储
对象存储 (Object Store)
- 优化复杂对象和自定义数据类型
- 支持 Redis 数据类型(Set、Hash、List、ZSet 等)
- 利用 .NET 库生态系统
- 内存中存储堆对象,磁盘上序列化存储
3.2 Tsavorite 存储引擎
核心特性:
- 高性能并发键值存储
- 支持分层存储(内存、SSD、云存储)
- 快速非阻塞检查点
- 操作日志持久化
- 多键事务支持
存储 API 模型:
public interface IGarnetApi : IGarnetReadApi, IGarnetAdvancedApi
{GarnetStatus SET(ref SpanByte key, ref SpanByte value);GarnetStatus GET(ref SpanByte key, ref SpanByteAndMemory output);// ... 更多 API
}
3.3 数据库抽象 (GarnetDatabase)
public class GarnetDatabase : IDisposable
{public TsavoriteKV<SpanByte, SpanByte> MainStore { get; }public TsavoriteKV<byte[], IGarnetObject> ObjectStore { get; }public LightEpoch Epoch { get; }public StateMachineDriver StateMachineDriver { get; }
}
4. 命令处理系统
4.1 命令解析和路由
- RespCommandsInfo: 命令元数据定义
- CommandType: 命令类型分类(Read、Write、ReadModifyWrite)
- RespCommand: 内置命令枚举
4.2 命令执行流程
- 解析阶段: RESP 协议解析
- 验证阶段: 权限检查和参数验证
- 路由阶段: 确定目标存储和处理逻辑
- 执行阶段: 调用相应的存储 API
- 响应阶段: 构造和发送响应
5. 扩展性系统
5.1 自定义命令管理器 (CustomCommandManager)
public class CustomCommandManager
{internal int Register(string name, CommandType type, CustomRawStringFunctions customFunctions,RespCommandsInfo commandInfo, RespCommandDocs commandDocs, long expirationTicks)
}
支持的扩展类型:
- 原始字符串命令: 操作字符串数据
- 自定义对象命令: 操作复杂数据结构
- 事务存储过程: 原子性多命令操作
- 自定义过程: 复杂业务逻辑
5.2 模块化架构
- ModuleLoadContext: 模块加载上下文
- CustomObjectFactory: 自定义对象工厂
- CustomTransaction: 自定义事务
6. 集群支持
6.1 集群架构
- 被动集群设计: 响应用户控制平面命令
- Gossip 协议: 节点状态同步
- 动态分片: 支持键迁移和重新平衡
6.2 集群组件
- ClusterProvider: 集群状态管理
- ClusterFactory: 集群组件工厂
- IClusterSession: 集群会话接口
6.3 多节点分片机制
哈希槽分配
- 使用16384个固定哈希槽进行数据分片
- 通过CRC16算法将键映射到槽位:
slot = crc16(key) & 16383
- 支持键标签机制:
{tag}key
确保相关键在同一槽位
槽位状态管理
public enum SlotState : byte
{STABLE, // 稳定状态,正常服务MIGRATING, // 迁移中,数据正在移出 IMPORTING, // 导入中,数据正在移入OFFLINE // 离线状态,不可用
}
分片策略
- 槽位均匀分配给主节点
- 交错分配算法确保负载均衡
- 支持动态槽位重分配
6.4 动态键迁移实现
迁移流程
- 源节点设置槽位为MIGRATING状态
- 目标节点设置槽位为IMPORTING状态
- 使用MIGRATE命令原子性传输数据
- 更新集群配置,设置槽位为STABLE
状态转换保证
- Epoch机制确保配置变更原子性
- 状态机严格控制迁移流程
- Gossip协议同步配置变更
迁移优化
- 批量槽位迁移提高效率
- 后台自动迁移任务
- 支持SLOTSRANGE批量操作
6.5 负载重平衡机制
自动重平衡
- 基于负载监控的动态调整
- 可配置的迁移频率和批量大小
- 随机或基于策略的槽位选择
一致性保证
- 迁移过程中的数据可用性
- 原子性的槽位所有权转移
- 故障时的自动回滚机制
性能优化
- 并发迁移支持
- 增量数据同步
- 最小化服务中断时间
7. 持久化和恢复
7.1 AOF (Append-Only File)
- 实时日志记录: 所有写操作
- 可配置缓冲区: 内存缓冲后写入磁盘
- 压缩和重写: 优化日志文件大小
7.2 检查点机制
- 非阻塞检查点: 不影响正常操作
- 增量检查点: 减少 I/O 开销
- 一致性保证: 确保数据完整性
8. 监控和指标
8.1 性能指标
- GarnetSessionMetrics: 会话级别指标
- GarnetLatencyMetricsSession: 延迟指标
- CacheSizeTracker: 缓存大小跟踪
8.2 指标类型
- 延迟分布 (HdrHistogram)
- 吞吐量统计
- 内存使用情况
- 错误率统计
性能优化策略
1. 内存管理优化
- 零拷贝设计: 减少内存拷贝操作
- 池化技术: 缓冲区和对象池
- GC 优化: 减少垃圾回收压力
2. 并发优化
- 无锁数据结构: 减少线程同步开销
- LightEpoch: 轻量级时代管理
- 细粒度锁: 降低锁冲突
3. I/O 优化
- 异步 I/O: 全异步操作模型
- 批量操作: 减少系统调用
- 预读和预写: 优化磁盘访问模式
安全性设计
1. 认证和授权
- IGarnetAuthenticator: 认证接口
- UserHandle: 用户会话管理
- ACL 支持: 精细化权限控制
2. 网络安全
- TLS/SSL 支持: 加密传输
- 连接限制: 防止 DoS 攻击
- 访问控制: IP 白名单/黑名单
配置和部署
1. 服务器配置 (GarnetServerOptions)
public class GarnetServerOptions : ServerOptions
{public bool DisableObjects = false;public string ObjectStoreHeapMemorySize = "";public bool EnableCluster = false;public bool EnableAOF = false;public bool EnableLua = false;
}
2. 部署选项
- 单机模式: 适用于小型应用
- 集群模式: 适用于大规模分布式场景
- 容器化部署: 支持 Docker 和 Kubernetes
总结
Garnet 通过以下关键设计实现了高性能和可扩展性:
- 现代化架构: 基于 .NET 的跨平台设计
- 双存储引擎: 针对不同数据类型的优化存储
- 网络层优化: 共享内存设计减少线程切换
- 强大的扩展性: 支持自定义命令和数据类型
- 企业级特性: 集群、持久化、监控等完整功能
这种设计使得 Garnet 能够在保持 Redis 兼容性的同时,提供更好的性能、可扩展性和现代化的开发体验。
线程模型详解
Garnet 的线程架构
Garnet 是多线程架构,而非单线程模型:
1. 多层次的并发设计
网络层并发
- 支持多个客户端并发连接
- 每个连接可以在独立的线程上处理
- 网络 I/O 采用异步处理模型
会话层并发
- 每个
RespServerSession
独立处理客户端请求 - 多个会话可以并行执行,但单个会话内部是线程安全的
- 会话注释明确说明:"expects mono-threaded client access"(期望单线程客户端访问)
存储层并发
- Tsavorite 存储引擎设计为高并发
- 支持多个线程同时读写
- 使用"Thread-independent session interface"
2. 具体的并发特性
ClientSession 线程独立性
/// <summary>
/// Thread-independent session interface to Tsavorite
/// </summary>
public sealed class ClientSession<TKey, TValue, TInput, TOutput, TContext, TFunctions, TStoreFunctions, TAllocator>
并发操作支持
ConcurrentWriter
: 支持并发写操作ConcurrentReader
: 支持并发读操作- 使用 Light Epoch Protection 进行无锁并发控制
基准测试验证
// 从测试代码可以看到支持多线程压测
Thread[] workers = new Thread[NumThreads];
for (int idx = 0; idx < NumThreads; ++idx)
{workers[idx] = new Thread(() => OpRunnerLightClient(x));
}
3. 线程模型对比
特性 | Redis | Garnet |
---|---|---|
主处理模型 | 单线程事件循环 | 多线程并发 |
网络处理 | 单线程 | 多线程异步 |
并发连接 | 通过事件循环 | 真正的并行处理 |
数据操作 | 串行执行 | 并发执行(带锁控制) |
分布式锁能力
为什么 Garnet 能作为分布式锁
1. 强大的锁机制
多级锁系统
- LockTable: 哈希桶级别的锁
- Transient Locking: 临时锁,用于短期操作
- Manual Locking: 手动锁,用于事务和复杂操作
锁类型支持
public interface ILockTable<TKey>
{bool TryLockShared(ref HashEntryInfo hei); // 共享锁bool TryLockExclusive(ref HashEntryInfo hei); // 排他锁bool TryPromoteLock(ref HashEntryInfo hei); // 锁升级void UnlockShared(ref HashEntryInfo hei); // 释放共享锁void UnlockExclusive(ref HashEntryInfo hei); // 释放排他锁
}
2. 事务级锁管理
TransactionManager
public sealed class TransactionManager
{internal bool Run(bool internal_txn = false, bool fail_fast_on_lock = false, TimeSpan lock_timeout = default){// 获取事务版本txnVersion = stateMachineDriver.AcquireTransactionVersion();// 开始锁定会话BeginLockable(transactionStoreType);// 尝试锁定所有键bool lockSuccess = keyEntries.TryLockAllKeys(lock_timeout);if (!lockSuccess) {// 锁获取失败,回滚Reset(true);return false;}// 执行事务逻辑return true;}
}
3. 分布式锁的实现原理
键级锁定
- 每个键都可以被独立锁定
- 支持超时机制避免死锁
- 自动死锁检测和恢复
集群级一致性
- 通过 Gossip 协议同步锁状态
- 支持跨节点的锁协调
- 实现最终一致性
原子操作保证
// 典型的分布式锁使用模式
var keys = new[] {new FixedLengthLockableKeyStruct<long>(lockKey, LockType.Exclusive, context)
};context.SortKeyHashes(keys); // 避免死锁
if (context.TryLock(keys)) {try {// 执行临界区代码context.Upsert(dataKey, newValue);}finally {context.Unlock(keys); // 确保锁被释放}
}
4. 分布式锁的优势
与 Redis 分布式锁对比
特性 | Redis | Garnet |
---|---|---|
锁类型 | 简单字符串锁 | 多种锁类型(共享/排他) |
死锁避免 | 手动实现 | 内置死锁检测 |
事务支持 | 有限 | 完整的 ACID 支持 |
性能 | 单线程限制 | 多线程并发优化 |
超时处理 | 需要外部实现 | 内置超时机制 |
性能优势
- 更低的延迟(<300μs)
- 更高的并发处理能力
- 更好的锁争用处理
可靠性保证
- Strong consistency through ACID transactions
- Automatic lock cleanup on session timeout
- Built-in deadlock detection and recovery
- Cluster-wide lock coordination
5. 使用场景
适合的分布式锁场景
- 高频率的锁获取/释放
- 需要读写锁的场景
- 要求低延迟的分布式协调
- 复杂的多键事务锁定
最佳实践
- 使用事务管理器处理复杂锁定逻辑
- 利用键排序避免死锁
- 设置合理的锁超时时间
- 在集群环境中利用一致性哈希