Redis入门训练营:课时7:Tair生态及开源module的使用
Tair生态及开源module的使用
内容介绍
一、 Tair生态简介
二、 TairStack最佳实践
三、RedisShake使用方法
一、Tair生态简介
今天的课程主题是Tair生态及开源方式的使用。课程分为三个主要部分。首先,我们将对生态进行整体概念简介,帮助大家建立基本认识。这包括Tair家目前开源的一些项目,以及参与的开放项目。这将使大家对Tair生态有一个大致了解。正如前面的课程所介绍的,我们的生态与Redis密切合作,涵盖了Redis生态的各个领域。
除此之外,还有许多其他开源项目,例如SDK和各种模块以及相关工具。第二部分将专注介绍我们生态的核心项目之一,即测试栈(test stack)。这是基于Redis扩展的各种模块系统。我们将重点介绍三个已经开源的模块,并结合案例和最佳实践帮助大家理解它们的用途和适用场景。第三部分将涵盖我们的生态工具RedisShake使用方法,它是用于Redis之间数据同步的迁移工具。它已经在GitHub上开源,并且有许多用户在使用,甚至一些其他厂商也在采用,在这部分,我们将介绍其原理和基本使用方法。
我们首先来看一下Tair的生态简介。
Tair的生态覆盖了整个Redis生态系统,从Redis引擎本身一直到各种周边的客户端SDK和各类工具。因此,我们的Tair团队自2015年以来一直深度参与了Redis社区的开发工作,累计贡献已经超过200项。这包括了Redis 7.0等版本的贡献。社区对我们在Redis方面的贡献也给予了高度认可,目前有两位核心开发者是属于我们团队的。他们对Redis社区未来的发展产生了很大的影响力。
在SDK方面,我们也一直积极跟进。毕竟,在业务中要使用Redis,必然需要客户端来连接。而且客户端的生态非常丰富繁杂,同时也伴随着各种各样的挑战。比如,可能会遇到配置错误、网络问题,甚至是客户端对Redis协议解析的错误。当我们面对这些问题时,我们一方面会帮助用户解决,同时也积极与社区合作,以做出改进和修复。目前,我们团队在SDK生态中有两位成员,他们也在社区有很好的声誉。
当然,客户端相对较新,虽然有很多出色的功能,但也存在一些问题,比如断线重连方面需要改进。我们也正在与社区合作,共同致力于改进和发展。此外,还有许多其他的客户端,如h,pi,i等,我们也积极推动这些开源客户端的发展。我们不仅提供稳定的引擎端服务给用户。
当然,我们也需要提供稳定的客户端服务给用户,另外,让我们继续谈谈我们的Test Stack。Test Stack是我们基于系统开发的众多数据结构模块之一。在Redis出现之前,用户主要使用的缓存系统是... 但当Redis问世时,它引入了丰富的数据结构。起初,它只支持基本的数据类型,而Redis自带的数据结构,如列表、哈希、集合等,拥有更强大的表达能力,更便于使用。业务不再需要复杂的编解码工作,可以直接使用这些更强大的数据结构来满足自己的需求,因此使用场景也变得更多样化。
所以,Test Stack的核心思路是开发新的数据结构来扩展Redis的使用场景。我们已经开源了其中一些模块,用户的反馈都相当不错。在接下来的部分,我们将介绍已经开源的一些模块。
最后,让我们谈谈Red。Red是一款用于Redis数据迁移的工具,主要应用于用户上云、机房之间的数据同步和数据迁移等各种场景。在GitHub上开源后,它已经获得了超过2400个star的关注,相当受欢迎。而且,不断有新的需求被提出。可以说,现在国内进行Redis数据迁移时,Red是一个标准的实时工具。
这就是我们整个生态的介绍。
二、TairStack最佳实践
首先,让我们看一下Test Stack的模块。
整个Test Stack就像一个家族,它包括了对Redis原生数据结构的扩展,以及广泛应用的过滤器等等。还有我们最新推出的全文搜索引擎等等。
另外,我们还提供了配套的SDK,支持主流编程语言如Go、Java、Python等等。而且,云上数据库的周边系统,包括数据管理、数据传输、审计日志和大数据备份等,我们也都全面支持。当然,有这么多模块并不是随意创建的,它们都是基于用户需求而实现的。就像Redis的原作者曾说过的那样,Redis的使用场景不是由他来决定的,而是由用户来决定的,这点非常激发人的创造力。
因此,我们开发了这些模块系统,让更多的开发者可以基于Redis来定制满足他们需求的解决方案。
这也极大地推动了Redis和生态的发展,我们要非常感谢社区的大力支持。这些模块共同组成了Test Stack,而且在云上,我们是云原生数据库,完全支持Test Stack,因此有很多用户都在使用它。
我们进行了一项统计,目前Test Stack中各个模块的使用情况如下图所示。可以看到,有些模块的使用率相对较高,与其他项目相比也很大,例如哈希(Hash)模块等。这说明了Test Stack在各种使用场景中都有广泛的应用。
我们已经开源了其中一部分模块,这些模块由于其出色的技术和广泛的应用场景而备受欢迎。目前,有三个模块是特别值得关注的,它们分别是哈希(Hash)、对Redis原生的Ring哈希(Ring Hash)、以及增量分享(Incremental Share)。您可以在GitHub上找到它们的代码,并下载、编译以及使用。
首先,让我们来看一下Test String的一个案例。在介绍String模块的具体命令之前,我们先来讨论一个关于并发更新的问题。Redis本身采用单线程运行模式。
"Redis的命令执行本身是原子的,不涉及锁的问题。但是在实际业务中,通常会有多个客户端同时连接到Redis,并执行多个命令。如果业务逻辑需要执行多个命令,那么就会出现无法保证业务逻辑的原子性的情况。下面的示意图展示了一个典型的并发情况,导致了资源竞争的问题:
一开始,key(key)k的值是"hello"。然后在t1时刻,应用1尝试读取k,读到的值仍然是"hello"。然后在本地进行更新,但尚未写回Redis。然后,在t10时刻,应用2也尝试读取k,它也读到的值是"hello"。然后它将值更改为"universe",然后在t3时刻,应用1尝试将k的值更改为"world"。
在t4时刻,应用2尝试将k的值更改回"universe"。最终,k的值变成了"universe",但应用1仍然认为它是"world"。这可能导致后续操作出现问题。
为了解决这个问题,需要确保访问和更新的原则性。这就是Tair Stack的String模块可以做到的事情。它的实现方式很容易理解,就是在原生的String数据类型上增加了版本号信息。每个key都有对应的版本号,用于表示key的当前版本。
在读取时,可以获取值和版本号两个信息,然后在更新时,带上之前获取的版本号,进行操作。Test Stack在后端会进行校验,如果给定的版本号与当前版本号相同,那么更新将成功,并且版本号会加一。如果给定的版本号不同,更新将失败,并返回错误,客户端会知道数据已经被其他人更新。
需要再重新进行一轮获取和更新的操作。左侧是Test Stack提供的这些命令,除了增强版本控制之外,与原生的String操作方法基本相同。但需要注意的是它们并不是相同的数据结构,这些命令不能混用。
例如,Set和ExSet是不能同时操作同一个key的。ExSet是一个更新操作,它的参数中包括版本号。如果要创建一个新的key,版本号默认为1。在这种情况下,指定版本号参数是无效的,因为默认是1。如果想要创建新key并指定版本号,需要使用Abs参数来设置绝对版本。
Abs参数,以及类似于ExS和ExC的这些特殊命令和参数,可以强制更改key的版本号。通常只在特殊场景下使用。除了这些特殊情况,还支持ExGet命令,它用于计数器,只有在版本号相同时才会执行加减操作。浮点数计数器也类似。其他还有ExS、ExC等,它们是ExGet和Ex结合在一起的一些简单场景的优化命令,可以减少网络交互次数。
有了这些特性,实现乐观锁就变得非常方便了。让我们看一下示例代码。这是一个循环,首先使用ExGet来获取keyt的值和版本号,然后准备要更新的数据,接着使用ExEx来尝试更新。这个命令会带上之前获取的版本号。如果ExEx返回OK。
如果返回的是OK,那么数据更新成功,然后跳出循环。如果返回的是错误,说明期间有其他人更新了版本,那么数据版本就不匹配了,需要尝试重新运行这个流程,整个过程其实相当容易实现。
这时候就可以应用在许多场景中,一个简单的例子是抢购系统,我们可以用一个计数器来代表库存,使用ExGet获取库存和版本号,判断如果库存大于零,就表示还有库存,然后在本地减少库存,并使用ExEx和Set命令来更新,或者直接使用ExEx去扣减库存。如果返回OK,就告诉用户抢购成功,但如果返回错误,说明其他人已经抢购了,这时候需要重新尝试,Test Stack的应用场景非常广泛,包括乐观锁等。这里只是提供了一些简单的示例,您可以在课后继续探索更多的业务场景。
接下来,让我们继续讨论哈希(Hash)模块。其中一个主要特点是它支持不同级别的过期时间。如果您熟悉Redis,您可能知道Redis支持key级别的过期时间,但原生的哈希表只能设置key的过期时间。如果您有一个哈希表,其中包含了多个字段,每个字段需要不同的过期时间,原生的哈希表无法满足这个需求。但Test Stack的哈希模块可以做到这一点。
此外,Test Stack的哈希模块还支持级别的版本号设置功能,非常强大,模块的格式基本上与原生的哈希表一致,只是增加了版本号和过期时间。
在这里,我列出了一些与过期时间相关的新增命令:Ex、ExPI。Ex的作用是给Tair哈希中的指定字段设置过期时间,而ExPI用于获取指定字段的过期时间。
需要注意的是,虽然哈希模块支持版本号,但并不是强制的,也就是说,您可以选择是否使用版本号,也可以选择不使用,只使用级别的过期时间也是可以的。因为哈希模块的主要特点还是级别的过期时间。允许您更灵活地管理数据。
一个典型的哈希模块应用案例是用户登录会话管理。我们可以从最简单的用户会话管理开始。使用原生数据结构,您可以使用用户名作为key来存储用户信息,并设置过期时间来管理登录会话的周期。然而,现代用户通常在多个设备上登录,如手机、电脑、平板等。在这种情况下,您可能需要修改key的结构,例如将用户名与设备信息拼接在一起。虽然这种方法也可以工作,但需要在业务中实现拼接逻辑,不够直观,并且可能导致大量的重复用户名前缀,浪费存储空间。
使用Test Stack的哈希模块,您可以更好地解决这个问题。key仍然是用户名,然后可以使用哈希表来记录不同设备的登录会话,每个设备对应一个字段。这样不仅更直观,还可以节省存储空间。
这个多维排序集合模块非常有用,因为在排序场景中,经常需要多个维度的排序。
原生的Set数据结构只支持单一维度的double类型排序。在需要多维度排序的情况下,原生的Set就无法满足需求,例如,在排行榜中需要根据金牌数、银牌数和同排数量来排序,这就涉及到多个维度的排序。传统的方法是使用多个Set来实现,但这会带来一些复杂性和性能问题。
Test Stack的多维排序集合模块支持多个维度的分值,并且可以对指定维度的分值进行加减操作。它最大支持256个维度的分值,并且几乎涵盖了所有原生Set的命令,除了加入了维度前缀以外。如果不使用维度前缀,它的使用方式基本与原生Set一致。
右侧是一个典型的排行榜示例,将参赛者的金牌数、银牌数和同排数量写入Tair排序集合中,然后可以轻松获取希望的多维排序排名。
除了多维排序集合,Redis生态中还有其他模块系统,如Redis Bloom Filter等。这些模块系统为Redis提供了更丰富的功能,可以用于各种不同的应用,包括文档存储等。
这些模块不仅完全兼容Redis,还提供了丰富的功能,并支持动态扩容以降低内存消耗。
其中,我们特别提到了位图模块的升级,它具有丰富的位预算功能,底层采用了两层索引和多种存储容器,同时还可以监控性能和提高效率。
尽管本次课程时间有限,无法详细介绍所有模块,但您可以在官方文档中找到更多信息。
三、RedisShake使用方法
接下来,让我们深入了解一下 Redis 数据迁移工具 Red。
其主要用途是将数据从一个 Redis 实例迁移到另一个 Redis 实例,无论是单机还是集群模式,Red 都提供了支持。
尽管看似简单,即数据迁移,但实际上实现起来并不容易,因为存在各种复杂的场景和需求。有些用户只拥有 RDB 文件,无法连接到实例,而其他用户不仅需要进行全量数据迁移,还希望进行实时的增量数据同步。此外,还有权限问题、断点重传等挑战需要应对。在 Redis 的不断发展中,我们总结出了一些常见场景,并提供了多种同步方法,包括 MPK(从 Redis 实例到 Redis 实例)、RK(从 RDB 文件到 Redis 实例)、RKDB(从 RDB 文件到 Redis RDB 文件)、RKAWS(从 AWS 到 Redis),以及更多,这使得 Red 成为了一个通用的数据迁移工具,可以用于各种场景。
然而,在数据迁移过程中,还存在一些挑战,例如大体积的拆分。Redis 的原生协议对于参数的大小有限制,最大为 512 兆。举个例子,如果有一个哈希,每个值都只有几 KB,但总数量非常庞大,可能达到百万、上千万甚至上亿。在将其导出为 RDB 文件后,整个 RDB 文件的大小可能超过 512 兆。在这种情况下,直接将这个 512 兆的 RDB 文件同步到目标端是不符合 Redis 协议的。因此,我们需要对 RDB 文件进行拆分,解析其格式,并将其转化为逐条执行的命令,例如将哈希拆分成多个 HSET 命令。这有助于防止由于大数据量导致的同步失败。Shake 现在支持从 2.6 版本到最新的 7.0 版本的数据迁移,包括字符串、哈希、有序集合等各种数据类型的同步。
在我们之前提到的五种工作模式中,有三种是最常用的。
我将重点介绍这三种同步模式的原理和适用场景,因为很多用户常常困惑于在数据同步时应该选择哪种模式。今天,我们将从原理出发,为大家提供更清晰的认识。
首先,让我们来看看 Red-MP 模式。这是一种非常通用且广泛适用的同步模式,因为它支持全量和实时增量同步。其工作原理相对复杂,Redis 在同步链路中伪装成一个 Redis 实例,然后从源端获取数据并将其同步到目标 Redis。在这个过程中,Redis 实际上实现了完整的 Redis 复制协议,并模拟成一个 Redis 实例,向源端发送 PSYNC 或者 SYNC 命令。源端收到这些命令后,会生成一个快照并生成 RDB 文件,然后将 RDB 文件传输给目标端。在传输 RDB 文件后,源端会缓存增量数据,然后逐条实时发送增量数据。目标端首先接收并解析 RDB 文件,将其中的 key-value 数据转化为 RESTORE 命令,然后发送给目标 Redis。如果存在大型key值对,会拆分为特定数据结构的命令。这个全量同步过程需要一些时间来进行数据转换。
一旦完成全量同步,增量同步就开始了,目标端会接收并转发来自源端的 Redis 命令,直到源端满足某些条件或用户终止同步。需要注意的是,Redis 复制协议相对复杂,这里我已经简化了整个流程。这种 MP 模式的优点是明显的,它支持全量和实时增量同步,可以最大程度满足数据一致性的要求。但是,前提条件是源端必须开启高级别的复制和限制命令。有一些用户反馈说 AWS 不支持 PSYNC 命令,但我们也可以根据配置来适配命令,使其与 AWS 兼容。
然而,如果源端完全不支持 PSYNC,该怎么办呢?这时就需要考虑 Red-RK 模式。我们开发了这个模式,它更容易理解。
相对简化的模式是 Red-RK 模式。它的工作方式相对简单:直接读取本地的 RDB 文件,然后将 RDB 文件中的数据转化为一条一条的命令,最后将这些命令发送给目标实例。这类似于 MP 模式中的全量同步阶段,但不需要连接到原实例执行 PSYNC 命令以获取 RDB 文件。这是一种离线的全量同步方式,使用成本较低且不会影响原始实例的性能。它适用于那些仅需要进行全量数据同步且可以获取 RDB 备份的用户。通常情况下,RDB 备份是可以获得的。然而,也存在一些情况,无法获得 RDB 备份,这时就需要考虑 RA 模式。
RA 模式相对复杂一些,因为它在没有 RDB 文件的情况下需要连接到原实例来进行数据同步。它会模拟 Redis 生成 RDB 文件的过程,不断发送命令来扫描原实例的数据。针对扫描到的key执行命令,获取单个key的 RDB 格式数据,然后将其发送给目标实例。这个过程将重复进行,直到扫描完所有数据。RA 模式主要使用两个命令:SCAN 和 DUMP。这两个命令在大多数情况下都得到支持。需要注意的是,RA 模式只能进行全量同步,不支持像 PSYNC 这样的增量同步,因为增量同步依赖于 Redis 原生的复制协议,而 STA DUMP 不包括在复制协议中。
现在,您可以根据自己的需求来选择这三种模式中的一种。它们基本上可以满足大多数数据同步的场景。
最后,让我们来看一下配置文件中的一些关key配置项。在源端,有三个关健的配置项,其中一个是源端类型,可以是 SA(Stand Alone)点"。
在配置文件中,首先需要指定源端的类型,即是主从(SA:Stand Alone)还是集群(Cluster)。如果选择集群模式,那么 Shake 会自动获取源端集群的拓扑结构、地址和密码信息。同样,您也需要配置目标端的类型、地址和密码,如果是目标集群模式,Shake 会自动获取目标集群的拓扑。
配置文件准备好后,您可以按照上述提到的几种工作模式来启动 Shake,选择适当的工作模式,然后启动同步过程。同步过程的详细信息将会记录在运行日志中。
除了上述必须的基础配置项之外,Shake 还支持一些高级配置选项。例如,'key exist' 选项定义了在同步过程中遇到重复key时的处理方式,可以选择覆盖、终止同步或忽略。
另一个高级配置选项是数据库(DB)的黑白名单,您可以指定要同步哪些数据库,以及哪些数据库不需要同步。这在实例拆分等场景中很有用,例如将一个实例中的数据导入到多个不同的实例中。