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