目录
单实例(Standalone)
概括
- 最简单的部署方式,单个redis实例,所有数据存储到单个redis实例中
- 优势:部署简单。性能最高,单点写入,无数据一致性问题,无网络开销,延迟最低,调试和监控也相对简单
- 劣势:单点故障,无法扩展
- 适用于开发测试或者小规模的应用
介绍
- 单点redis的读速度是110000次/s,写速度大概是80000多次/s
主从(Master-Slave)
概括
- 一主多从,读写分离
- 优势:提高读性能,数据备份
- 劣势:手动故障转移、写入单点
- 适用于读多写少的场景
介绍
- 这种模式下,如果主节点挂了,从节点晋升为主节点,客户端访问redis实例的连接地址需要修改。由此引入了哨兵模式

哨兵(Sentinel)
概括
- 主从基础上增加哨兵节点
- 优势:自动故障转移,服务发现
- 功能:监控、通知、自动切换、配置提供
- 适用于需要高可用的中型项目
介绍
- 哨兵集群不存储数据,只负责监控数据面实例和建立连接,哨兵节点会不停的确认下面的数据面节点是否正常(PING),这样相当于加了一层,可以实现故障转移

集群(Cluster)
概括
- Redis官方分布式方案
- 特性:16384个哈希槽分片、水平扩展
- 优势:自动分片、故障转移、线性扩展
- 适用于大规模、高并发场景
介绍

- redis集群的键空间被分成16384个哈希槽,key经过
CRC16算法模16384,得到实际存储的槽位置,在映射到具体的集群节点
为什么是16384?
- 这个数是 2 14 2^{14} 214,2的幂次,方便进行位运算和分片计算
- 大小适中,既不会太少导致分片粒度太粗,也不会太多导致元数据开销过大
- 对于大型集群(100+节点),仍然能提供良好的分片粒度
- 16384bits = 2048 bytes = 2KB,集群节点间交换的槽位信息只有2KB,网络开销小
- // Redis源码中的注释(CRC16的输出是16位结果,使用低14位正好对应16384)
/* We have 16384 hash slots. The hash slot of a given key is obtained
/* as the least significant 14 bits of the crc16 of the key. */为什么是CRC16?
- 计算速度快,现代CPU有专门的CRC指令
- 分布均匀
- 冲突概率适中
- 读分配给slave节点,写分配给master节点,读写分离增加了并发能力

- 水平扩容的场景中,哈希槽会重新计算,并将旧的数据迁移到新节点的哈希槽上
go-redis
-
在go语言中,可以使用go-redis来集成redis客户端
go get github.com/redis/go-redis/v9
-
使用也非常简单,建议使用
NewUniversalClient来创建一个客户端,它会根据配置来选择合适的创建出来的客户端实例,如单机模式、集群模式等
go
// NewUniversalClient returns a new multi client. The type of the returned client depends
// on the following conditions:
//
// 1. If the MasterName option is specified with RouteByLatency, RouteRandomly or IsClusterMode,
// a FailoverClusterClient is returned.
// 2. If the MasterName option is specified without RouteByLatency, RouteRandomly or IsClusterMode,
// a sentinel-backed FailoverClient is returned.
// 3. If the number of Addrs is two or more, or IsClusterMode option is specified,
// a ClusterClient is returned.
// 4. Otherwise, a single-node Client is returned.
func NewUniversalClient(opts *UniversalOptions) UniversalClient {
if opts == nil {
panic("redis: NewUniversalClient nil options")
}
switch {
case opts.MasterName != "" && (opts.RouteByLatency || opts.RouteRandomly || opts.IsClusterMode):
return NewFailoverClusterClient(opts.Failover())
case opts.MasterName != "":
return NewFailoverClient(opts.Failover())
case len(opts.Addrs) > 1 || opts.IsClusterMode:
return NewClusterClient(opts.Cluster())
default:
return NewClient(opts.Simple())
}
}