Vitess支持基于VReplication的管理的、非阻塞的模式迁移,这一点很贴切vitess迁移。Vitess迁移是强大的、可恢复的。他们拿了一个异步方法,在数据库服务器上更轻量级。异步方法带来了一个实现挑战:如何在对用户/应用程序的影响最小的情况下进行切换,同时避免数据丢失。在这篇文章中,我们将深入探讨vitess迁移中使用的切换逻辑。
Vitess迁移切换的特点:
- 快速
- vitess客户端核心
- 避免数据丢失/数据漂移问题
为了更好地理解这意味着什么,让我们首先回顾一下什么是割接。
切换步骤
这是在线模式迁移过程中,以及所有实现和工具中最关键的一步:从pt联机架构更改 , fb osc公司,至 GHOST公司最后呢 维蒂斯.联机模式迁移(又称联机DDL)过程创建一个卷影表,填充它,使其与原始表保持最新,然后通过重命名原始表并替换阴影表来进行切换。
切换是两个表必须在整个切换过程中保持完全同步的点。无论实现是什么,这都涉及到锁,这会影响用户/应用程序。Vitess迁移使用异步更改传播:它们在二进制日志中查找对原始表的更改,然后将这些更改应用于卷影表。这意味着在将这些更改应用到阴影表之前,会有一些延迟,无论多么小。在任何时候,这两个表都可能不同步,阴影表稍微落后于原始表。
但在切换时,阴影表必须与原始表完全同步。这是怎么做到的?
短暂的悬念
所有模式迁移技术在切换时都使用某种形式的锁。这种锁会导致查询暂停和连接激增。
为了使影像表与原始表同步,vitess运行以下通用流:
- 验证迁移处于适当的状态
- 防止写入原始表
- 标记已禁用写入的时间点
- 使用二进制日志直到标记的时间点,并应用到影像表上
- 表现在同步及切换
vitess如何阻止对原始表的写入是本文的重点。值得注意的是,流可能会失败或超时,在这种情况下,vitess会继续写操作,并在稍后阶段重试。
防止写入
有一些技术可以防止对迁移的表进行写操作。它们在几个方面有所不同:这些技术是否足够安全?它们是可逆的吗?对应用程序有什么影响?
接受fb-osc的方法:它运行两步切换,首先重命名原始表,从而在数据库中创建一个穿孔,然后在其位置重命名影子表。在某个时间点上,表就是不存在的。对于用户和应用程序来说,这将显示为意外错误。突然有一堆查询失败,声称没有这样的表。如果工具在这两个重命名之间崩溃了呢?
gh-ost的方法是通过精巧的机制这样可以确保在发生错误/超时时进行回滚。对于用户/应用程序来说,它看起来是原子的。查询将被阻塞和堆积,然后被释放以对新表进行操作。这种逻辑依赖于内部MySQL锁的优先级划分,一些用户已经展示了预期优先级划分的特定场景可能会失败 .
实施维蒂斯的切换,我们想要最好的世界。我们希望切换对应用程序来说是原子的,也就是说,应用程序不应该出现意外错误,最坏的情况是会阻塞几秒钟。我们还希望完全确定数据完整性:当我们加快影子表的速度时,不可能对原始表进行写入。崩溃随之而来。
缓存写入
Vitess的优势是流量通过VTGate,它的MySQL兼容代理。普通用户和应用程序不直接与MySQL通信(尽管如下文所述,切换逻辑还包括直接通信场景)。查询通常被发送到VTGate,VTGate会将它们路由到相应的碎片以及这些碎片上的vtablet服务器。然后vtablet将在MySQL上运行查询。
vtablet有acl(访问控制列表)的概念。这主要是为了让管理员拒绝对表的写入。
通过vtablet的每个查询都会得到一个查询计划。该计划包括与查询中引用的任何表关联的所有ACL。在执行时,vtablet首先评估acl是否允许查询通过,然后继续在MySQL上执行查询并返回结果。
Vitess迁移引入了一种新的ACL形式:限时缓存。这些都是过期的规则。您可以在某个表上设置一个缓冲规则,然后取消该规则,或者它最终自动过期。
当规则处于活动状态时,查询将被缓存,或者基本上只是被隔离起来,等待进一步的通知。或者:
- 规则将被主动取消,这意味着缓存已完成,查询继续执行(假设没有其他ACL冲突),或者
- 规则将自行过期,在这种情况下查询将被拒绝。
我们预计切换总时间为几秒,通常为两三秒。当切换开始时,vitess设置10s正在缓存迁移表上的ACL。因此,任何新的查询进入缓存区长达10秒。如果迁移在那时完成,那就太好了,查询将解除阻塞并继续在新表上运行。如果没有,则查询错误。
缓冲只能等待一定数量(可配置)的查询。在重负载下,应用程序可能最终耗尽缓存区容量,此时查询将出错。这就是为什么必须尽快完成切换。
但是,缓存并不是一个完整的解决方案:
执行的查询会发生什么情况就在之前切换,并验证了它的ACL?如果我们现在放置缓冲acl,那就太晚了。它继续执行。我们怎么知道?
如果某些自动化在MySQL服务器上运行直接查询,会发生什么情况?这不是正常的维生素流动,但它可能发生,我们都曾经历过。
失速
作为对潜在挂起查询的一种表示,流将暂停以获取额外的100ms. 这段时间很可能足以让任何已经通过预剪切acl的查询在MySQL服务器上开始执行,也可能足够让它们完成执行。
这一步增加了所有人的总切换时间,但允许在引入任何锁定之前执行剩余的查询。
这一步并不能保证什么,真的。我们坚信它可以给查询足够的时间来完成,但是随着竞争条件的发展,一个sleep从来没有答案
此外,这对直接在MySQL上运行的任何可能的查询都没有影响。
穿刺
下一步是RENAME original_table TO somewhere_else. 值得注意的是重命名将等待任何挂起的查询完成;因此任何UPDATE仍在进行中的将完成而不是失败。
但是一旦RENAME完成了,我们有一个穿刺。原来的表已不在原来的位置。现在已经不可能有任何查询修改原始表了。当然,正常的应用程序查询并没有意识到这个漏洞:它们仍然通过acl进行缓冲。
我们现在标记我们的时间点(MySQL的gtid_executed值)
完成
Vitess继续读取标记时间点之前的任何剩余二进制日志条目,并将它们应用到影子表中。我们不指望有那么多。只有当二进制日志处理处于良好状态并且紧跟在实际写入之后时,我们才进入切换过程。我们希望有一两秒钟的最后追赶时间。
当事件被消费时,我们知道原始表和影子表是完全同步的。我们现在RENAME替换原始表的阴影表。我们有一张新桌子了!穿刺处被修正。
最后,我们清除缓冲ACL。然后,允许缓冲查询继续在表(新表)上执行,而不知道发生了什么。
失败
当vTablet运行时,它能够在任何时候回滚切换操作:
缓冲开始前发生故障?没问题,没有伤害。
重命名原始表失败?没问题,撤消ACL,稍后再试
重命名卷影表代替原始表失败?没问题,重新命名原始表,删除缓冲区,稍后再试
但是,如果VTTablet的过程在穿刺时失败了,会发生什么呢?这就是vitess框架的好处所在。新的vTablet将运行。无论我们是否故障转移到一个新的MySQL服务器,我们都希望最终有一个vtablet进程负责。该进程将为过早中断的迁移运行恢复步骤。实际上,它将从中断点恢复任何中断的迁移。
在重命名原始表之前,vtablet会审核预期的操作。如果出现故障,新的vtablet将处理中断迁移的状态,并查看审计结果。然后,它将原始表恢复原位,从而回滚整个切换操作。ACL在内存中,因此新的vTablet不需要删除缓冲ACL。然后,它采用迁移并让它运行,并按照事物的自然顺序在适当的时候尝试切换。
总结
通过利用vitess框架本身,vitess迁移能够提供一种多层切换机制,包括acl和MySQL原语,这样用户和应用程序可以获得最佳体验,同时仍然可以完全控制数据的准确性。