CockroachDB学习笔记——[译]在CockroachDB中如何让在线模式更改成为可能

我定期需要对表(tables)进行更改……主要是添加列(columns)。
这种更改其实只是简单的`alter table`操作……
但是我这张表目前有4000万行数据,而且他们正在快速地增长……
所以这种情况下`alter table`操作将会花费数小时时间,
因为我使用的是亚马逊RDS[sic],
我没有办法部署我地从服务器,因而也没有办法改善主服务器。
所以我的问题是,是否有办法在停机时间最小化的情况下做到这一点呢?
我不介意一个小时或甚至几天的操作,如果用户仍然可以使用DB…
(译者:如何在不停机的情况下修改表结构及数据)

—— Stack Overflow serverfault 1

上面的问题在2010年发布,但是关于模式改变的停顿的焦虑仍然存在。

当我们在设计CockroachDB的模式改变引擎(schema change engine)的时候,我们希望更好地构建它。
我们希望提供一种更新表模式(只需运行alter table命令)的简单方法,而不必让应用程序遭受任何负面后果——包括停机时间。
我们希望模式更改是CockroachDB的内置特性,不需要额外的工具、资源或操作的自组织排序;
并且所有这些,对应用程序的读/写延迟没有影响。

在这篇文章中,我将解释我们的在线模式改变策略。
我将讨论如何管理对架构元素(如列和索引)的更改,而不必强制停机。

这就是我们所做的

模式更改涉及更新模式以及添加或删除与该更改相关联的表数据。
两个基本的分布式数据库特性使得这一点特别棘手:

  1. 高性能:为了优化数据库性能,必须采用跨数据库节点缓存架构。保持分布式缓存一致性是有挑战性的。
  2. 大表:数据库中的表可以非常大。与模式改变相关联的回填或清除表数据需要时间,并且正确地执行,而与此同时,不禁用对数据库的访问是具有挑战性的。

我们用于 维护一致的分布式模式缓存以及一致表数据 拥抱着 同时发生的模式的多个版本,我们可以在使用旧模式的同时推出我们的新的模式。
(译者的理解:他可能是先继续维护旧的模式,然后新建一个新的模式,然后再一步步地往新的模式里面填旧的模式里面的数据,当然这个时候新的模式里面的数据也会变动,反正就是一点一点的往旧的模式靠,等到某一个时间点和旧的模式保持同步了,好!立马抛弃旧的模式,启用新的模式。)
它在不持有锁的情况下回填(或删除)基础表数据。
这个解决方案是由谷歌F1团队所做的工作得出的。

安全模式展开(Safe schema rollout)

一个schema元素(它可以是索引或列,虽然在这篇文章的其余部分,我们将重点放在索引的情况下),有相关的数据,这些数据可以被SQL DML命令删除、写入或读取(例如,插入INSERT、更新UPDATE、删除DELETE、选择SELECT)。
CockroachDB采用的策略是首次展现(rollout)一个新的索引,包括对这些DML命令进行删除、写入和读取功能,一个接一个地以分级的方式,而不是同时进行。

添加一个新的索引遵循如下所述的离散阶段:

  1. 授予删除(DELETE)功能。
  2. 授予写入(WRITE)功能。
  3. 回填索引数据。
  4. 授予读取(READ)功能。

在新版本的架构中,一个能力与所有先前授予的能力一起被授予。
为了正确起见,只有在整个集群使用包含所有先前授予的能力的架构之后,才可以授予新的能力。
因此,该过程在每个阶段之前暂停,允许先前授予的功能在下一个功能可以被授予之前传播到整个集群。

删除索引按照相反的方向进行:

  1. 撤销读取(READ)功能。
  2. 撤销写入(WRITE)功能。
  3. 清除索引数据。
  4. 撤销删除(DELETE)功能。

类似地,只有在前一个功能在整个群集上的撤销传播完之后才撤销当前的功能。

删除能力(Delete capability):避免虚假索引条目

通过将索引置于 DELETE_ONLY 状态,来赋予这种功能。
拥有这种能力,SQL DML命令限制了他们自己:

  • 删除(DELETE)是完全有效的删除行连同基础索引数据。
  • 更新(UPDATE)删除旧索引条目,但限制自身不写入新的条目。
  • 插入(INSERT)和选择(SELECT)忽略索引。

在下一阶段授予索引的写入能力的节点可以信任整个集群使用索引的删除能力。
因此,当它接收到插入命令并插入一个行的索引条目时,另一个只有删除能力的节点在看到同一行的删除命令时,将正确删除行的索引条目。
该索引将避免因悬空索引条目而中毒。

当模式改变丢弃索引时,仅在成功地将写入能力撤回群集之后清除关联索引数据;
整个集群只有删除能力,允许安全清除。

写入(Write capability)能力:避免丢失索引条目

这种能力是通过将索引放置在 WRITE_AND_DELETE_ONLY 状态,授予删除和写入能力来实现的。

  • 插入(INSERT)、更新(UPDATE)和删除(DELETE)命令正常运行,根据需要添加或删除索引项。
  • 另一方面,选择(SELECT)需要读取能力,而忽略索引。

索引回填仅在整个群集能够写入之后才运行。
在回填过程中,在任何节点上接收的插入命令创建具有合法索引项的新行,并且不依赖于单独的回填过程来创建行的索引条目。
回填完成后,将不会有缺失的索引条目。

读取能力(Read capability)

这最后一个能力是通过使索引激活而授予的,并通过所有命令打开索引的完整使用。

快速模式展开(Fast schema rollout)

在模式更改过程的每个阶段之间,允许整个集群收敛到表模式的最新版本。
一个简单的模式缓存机制可能会使用5分钟的TTL,但是这会迫使模式改变过程等待许多分钟,然后才相信最新的版本是唯一的一个具有活跃状态的和完全授予/撤销的能力的版本。
在 CockroachDB 中,我们在模式版本上开发了租约(leases),以加快集群收敛到最新的模式版本,这又加速了模式改变过程。

在使用SQL DML命令的表模式之前,运行该命令的节点自一段重要的时间内获得该模式的读取租约(以分钟为单位)。
当更新的表架构版本变为活动时,它将在整个集群上广播,向新版本通知节点并使它们主动释放旧版本上的租约。
如果某些节点不健康并且不主动释放它们的租约,则 rollout 将等待租约过期,并将被延迟。

我们通过遵循以下两条规则来让模式更改策略变得简单:

  • 新租约只授予最新的模式版本。
  • 有效租约只在两个最新的模式版本上。

有关表租约的更详细讨论,请参见我们在Github仓库上的RFC文档。

可靠的模式展开(Reliable schema rollout)

模式更改由执行模式更改SQL命令的节点引导完成。
由于模式更改操作可能长时间运行,所以当执行它们的节点死亡时,它们需要重新启动。
每个节点运行一个模式转换器 goroutine,它能够运行任何不完整的模式更改。
在执行模式更改之前,模式更改器 goroutine 首先获取表上的独占写入租约,使其成为唯一指导模式更改的许可。

总结

在 CockroachDB 中,在线模式更改使用起来是很简单的,模式更改展开的处理也是方便、快速和可靠的。
改变是不可避免的,现在是你不必担心的时候了!
我们要感谢来自谷歌F1团队的人们发布 在线模式更改 的类似实现,从中我们获得了很多灵感。


Apptree. Stack Overflow. “Modifying columns of a very large mysql table with little or no downtime.” August 26, 2010.

猜你喜欢

转载自www.cnblogs.com/zifeiy/p/9315877.html