分区是将数据拆分为多个Redis实例的过程,因此每个实例只包含key的子集。 本文档的第一部分将向您介绍分区的概念,第二部分将向您展示Redis分区的替代方案。
为什么分区很有用
Redis中的分区有两个主要目标:
- 它允许更大的数据库,使用许多计算机的内存总和。 如果不进行分区,则限制为单台计算机可以支持的内存量。
- 它允许将计算能力扩展到多个核心和多台计算机,并将网络带宽扩展到多台计算机和网络适配器。
分区基础知识
有不同的分区标准。 想象一下,我们有四个Redis实例R0,R1,R2,R3和许多代表用户的key:user:1,user:2,…等等,我们可以找到不同的方法来选择我们存储给定key的实例。 换句话说,有不同的系统将给定的key映射到给定的Redis服务器。
执行分区的最简单方法之一是使用范围分区,并通过将对象范围映射到特定的Redis实例来完成。 例如,我可以说从ID 0到ID 10000的用户将进入实例R0,而用户表单ID 10001到ID 20000将进入实例R1,依此类推。
那样可以工作并且实际上在实践中使用,但是,它具有需要将范围映射到实例的表的缺点。 需要管理此表,并且每种对象都需要一个表,因此Redis中的范围分区通常是不合需要的,因为它比其他替代分区方法效率低得多。
范围分区的替代方法是 散列分区 。 此方案适用于任何键,无需表格object_name:<id>
中的键,并且简单如下:
- 获取key名称并使用散列函数(例如,crc32散列函数)将其转换为数字。 例如,如果键是foobar,crc32(foobar)将输出类似93024922的内容。
- 使用带有此数字的模运算,以便将其转换为0到3之间的数字,以便可以将此数字映射到我的四个Redis实例之一。 93024922 modulo 4等于2,所以我知道我的密钥foobar应该存储在R2实例中。 注意:模运算返回除法运算的余数,并在许多编程语言中使用%运算符实现。
还有许多其他方法可以执行分区,但是有了这两个例子,你应该明白这个想法。 一种高级的散列分区称为一致散列,由一些Redis客户端和代理实现。
不同的分区实现
分区可能负责软件栈的不同部分。
- 客户端分区 意味着客户端直接选择正确的节点在哪里写或读取给定的key。许多Redis客户端实现客户端分区。
- 代理辅助分区 意味着我们的客户端将请求发送到能懂Redis协议的代理,而不是直接向正确的Redis实例发送请求。代理将确保根据配置的分区架构将我们的请求转发到正确的Redis实例,并将回复发送回客户端。 Redis和Memcached代理Twemproxy实现代理辅助分区。
- 查询路由 意味着您可以将查询发送到随机实例,实例将确保将查询转发到正确的节点。 Redis Cluster在客户端的帮助下实现了混合形式的查询路由(请求不是直接从Redis实例转发到另一个实例,而是将客户端重定向到正确的节点)。
分区的缺点
Redis的某些功能在分区方面效果不佳:
- 通常不支持涉及多个键的操作。例如,如果它们存储在映射到不同Redis实例的键中,则无法执行两个集合之间的交集(实际上有方法可以执行此操作,但不能直接执行此操作)。
- 无法使用涉及多个key的Redis事务。
- 分区粒度是关键,因此无法使用单个巨大的key(如非常大的有序集)对数据集进行分片。
- 使用分区时,数据处理更复杂,例如,您必须处理多个RDB / AOF文件,并且需要从多个实例和主机聚合持久性文件来备份数据。
- 添加和删除容量可能很复杂。例如,Redis Cluster支持大多数透明的数据重新平衡,能够在运行时添加和删除节点,但其他系统(如客户端分区和代理)不支持此功能。然而,一种称为预分片的技术在这方面有所帮助。
数据存储还是缓存
虽然无论使用Redis作为数据存储还是作为缓存,Redis中的分区在概念上都是相同的,但在将其用作数据存储时存在很大的局限性。 当Redis用作数据存储时,给定的键必须始终映射到同一个Redis实例。 当Redis用作缓存时,如果给定节点不可用,然后使用不同的节点,则不会出现大问题,因为我们希望改善系统的可用性(即, 系统回复我们的查询)。
如果给定key的首选节点不可用,则一致的散列实现通常能够切换到其他节点。 同样,如果添加新节点,则新键的一部分将开始存储在新节点上。
这里的主要概念如下:
- 如果使用Redis作为缓存,则使用一致性散列可以轻松扩展和缩小。
- 如果将Redis用作存储,则使用固定的键到节点映射,因此节点数必须固定且不能变化。 否则,需要一个能够在添加或删除节点时在节点之间重新平衡密钥的系统,并且目前只有Redis Cluster能够执行此操作 - 从2015年4月1日起,Redis Cluster通常可用并且可以投入生产。
Presharding(预分片)
我们了解到分区的问题在于,除非我们使用Redis作为缓存,否则添加和删除节点可能会非常棘手,并且使用固定的键实例映射会更加简单。
但是,数据存储需求可能随时间而变化。 今天我可以使用10个Redis节点(实例),但明天我可能需要50个节点。
由于Redis的占用空间非常小且轻量级(备用实例使用1 MB内存),因此从一开始就有很多实例可以解决这个问题。 即使您只使用一台服务器,您也可以决定从第一天开始就在分布式环境中生活,并使用分区在单个服务器中运行多个Redis实例。
并且您可以从一开始就选择此数量的实例。 例如,32或64个实例可以为大多数用户提供技巧,并将提供足够的增长空间。
这样,当您的数据存储需求增加并且需要更多Redis服务器时,只需将实例从一个服务器移动到另一个服务器即可。 添加第一个附加服务器后,您需要将一半的Redis实例从第一个服务器移动到第二个服务器,依此类推。
使用Redis复制,您可能可以在用户停机时间最短或无停机的情况下进行移动:
- 在新服务器中启动空实例。
- 将配置这些新实例的数据作为源实例的从属数据移动。
- 阻止你的客户端。
- 使用新服务器IP地址更新已移动实例的配置。
- 将
SLAVEOF NO ONE
命令发送到新服务器中的从站。 - 使用新的更新配置重新启动客户端。
- 最后关闭旧服务器中不再使用的实例。
Redis分区的实现
到目前为止,我们在理论上涵盖了Redis分区,但实践呢? 你应该使用什么系统?
Redis集群
Redis Cluster是获得自动分片和高可用性的首选方式。 从2015年4月1日起,它通常可用并且可以投入生产。您可以在群集教程中获得有关Redis群集的更多信息。
一旦Redis Cluster可用,并且符合Redis Cluster的客户端可用于您的语言,Redis Cluster将成为Redis分区的事实标准。
Redis Cluster是查询路由和客户端分区之间的混合。
Twemproxy
Twemproxy是Twitter在Memcached ASCII和Redis协议上开发的代理。 它是单线程的,用C语言编写,速度极快。 它是根据Apache 2.0许可条款发布的开源软件。
Twemproxy支持多个Redis实例之间的自动分区,如果节点不可用,则可选节点弹出(这将更改keys-instances映射,因此只有在使用Redis作为缓存时才应使用此功能)。
这不是单点故障,因为您可以启动多个代理并指示您的客户端连接到接受连接的第一个代理。
基本上,Twemproxy是客户端和Redis实例之间的中间层,它将以最小的额外复杂性可靠地处理分区。
您可以在此antirez博客文章中阅读有关Twemproxy的更多信息。
支持一致哈希的客户端
Twemproxy的替代方法是使用通过一致散列或其他类似算法实现客户端分区的客户端。 有多个Redis客户端支持一致性哈希,尤其是Redis-rb和Predis。
请检查Redis客户端的完整列表,以检查是否存在具有针对您的语言的一致哈希实现的成熟客户端。