我知道正确的方法是备份所有计划的停机时间,然后进行更改。
但是...当前的数据库系统是否支持“在线”执行这些操作而不停止任何操作? (也许只是延迟引用刚被更改/删除的列的查询)
当我在实时运行的数据库上执行
ALTER TABLE...
时会发生什么?是否一切都停止了?什么时候发生?
数据会损坏吗?
等。再次,我主要是指Postgres或MySQL,因为这是我遇到的问题。
(是的,是的,我必须在做“正确的方式”之前做任何事情,备份事情,安排例行工作等等。。。但是我只是想知道是否有可能做这种事情“快速且肮脏的”或是否有任何数据库系统实际上支持“快速,肮脏的”架构更改)
有人刚刚建议从Facebook脚本中为MySQL进行在线架构更改(此处有教程,并在此处提供源代码)...似乎是一种自动化“黑客”方式的好方法,...有人在类似的产品中使用过它吗?
#1 楼
当您在PostgreSQL中发布ALTER TABLE
时,它将获得一个ACCESS EXCLUSIVE
锁,该锁会阻止包括SELECT
在内的所有内容。但是,如果表不需要重写,没有新的UNIQUE
,CHECK
或FOREIGN KEY
约束都不需要昂贵的全表扫描来进行验证,则此锁可能很短。如果有疑问,您通常可以尝试一下! PostgreSQL中的所有DDL都是事务性的,因此如果花费很长时间取消
ALTER TABLE
并开始保留其他查询,则可以取消。锁定页面中记录了各种命令所需的锁定级别。可以加快某些通常为慢速的操作的安全性,而不会造成停机。例如,如果您具有表
t
,并且由于客户已决定所有客户代码现在必须以customercode integer NOT NULL
开头而希望将列text
更改为X
,则可以编写:ALTER TABLE t ALTER COLUMN customercode TYPE text USING ( 'X'||customercode::text );
...但这会锁定整个表以进行重新写入。添加带有
DEFAULT
的列也是如此。可以通过几个步骤来避免长时间锁定,但是应用程序必须能够应对临时复制:ALTER TABLE t ADD COLUMN customercode_new text;
BEGIN;
LOCK TABLE t IN EXCLUSIVE MODE;
UPDATE t SET customercode_new = 'X'||customercode::text;
ALTER TABLE t DROP COLUMN customercode;
ALTER TABLE t RENAME COLUMN customercode_new TO customercode;
COMMIT;
这只会阻止写入
t
在此过程中;锁名EXCLUSIVE
在某种程度上具有欺骗性,因为它排除了SELECT
以外的所有内容; ACCESS EXCLUSIVE
模式是唯一不包含绝对内容的模式。请参阅锁定模式。由于ALTER TABLE
要求进行锁升级,因此该操作可能会死锁回滚,但在最坏的情况下,您只需再次执行此操作即可。您甚至可以避免使用该锁并执行通过在
t
上创建触发函数使整个过程变得生动,无论何时出现INSERT
或UPDATE
,都会自动从customercode_new
中填充customercode
。还内置了诸如CREATE INDEX CONCURRENTLY
和ALTER TABLE ... ADD table_constraint_using_index
之类的内置工具,这些工具旨在允许DBA通过以并发友好的方式更慢地完成工作来减少排他锁定时间。pg_reorg
工具或其后续产品。也可以用于某些表重组操作。评论
@Craig所说的关键是,“如果不需要重写的话”。一旦获得锁定,使用ALTER TABLE t ADD COLUMN i INT是一种快速操作(通常<1ms)。但是,获得锁可以使连接排队,因此它不是“免费的”……尽管它比您在MySQL中要做的要好得多。添加NOT NULL约束更加困难,而不是出于心脏假装。
– Sean
2012年10月19日5:16
pg_repack是pg_reorg的改进后继者似乎已达成共识。
–欧文·布兰德斯特(Erwin Brandstetter)
2013年4月9日23:47
关于添加具有默认值(或计算值)的列的好答案,一种较少“阻塞”的方式是创建整个新表,阻塞旧表以进行插入/更新/删除,但允许选择并填充新表。最后,在旧表上发出一个简短的排他锁以供选择,删除它并将新名称重命名为old。根据您的情况,您甚至可以开始填充新文件而不会在旧文件中阻塞插入,并在解决差异时发出排他锁(希望只是插入一些新记录)
–让
18年1月29日在13:04
#2 楼
Percona拥有自己的用于执行在线模式更改的工具该工具称为pt-online-schema-change
,它涉及触发器,因此请仔细阅读文档。
根据文档,已完成的主要操作是
健全性检查
分类
在线模式更改
创建和更改临时表
将表中的更改捕获到临时表中
将表中的行复制到临时表中
同步表和临时表
交换/重命名表和临时表
清理
评论
谢谢,似乎是Facebook方法的“强化”版本,我可以信任更多...
– NeuronQ
2012年10月18日16:12
如果您正在运行自己的MySQL服务器,那么pt-online-schema-change绝对是首选的方法。从Percona Tools 2.2开始,(很遗憾)它们不支持AWS上的RDS / Aurora。 pt-online-schema-change在源表上插入一个触发器,以将行(MyISAM的低优先级)复制到目标table_temp,并在所有行在源和目标之间同步时执行一次快速锁定删除并在最后重命名表。
– phpguru
16 Mar 21 '16 at 18:46
#3 楼
关闭系统并立即进行所有更改可能会非常危险。如果出了问题,而且经常发生,就没有简单的方法。作为一名敏捷开发人员,有时我需要重构表而没有任何停机时间,因为这些表正在被修改和读取。
以下方法风险低,因为更改是通过几个很容易回滚的低风险步骤完成的:
请确保访问该表的所有模块都已被自动化测试很好地覆盖。
创建一个新表。更改所有修改旧表的过程,以便它们同时修改旧表和新表。
将现有数据迁移到新结构中。批量进行,以免严重影响服务器的整体性能。
验证数据迁移是否成功。
将某些选择过程从旧表重定向到新表。使用自动化测试来确保更改后的模块仍然正确。确保其性能可接受。部署更改后的过程。
重复上一步,直到所有报表都使用新表。
更改修改表的过程,以便它们只能访问新表。
归档旧表表并将其从系统中删除。
我们已经多次使用这种方法来更改大型现场生产表,而无需停机,也没有任何问题。
评论
很好...但这正是我要避免的“疼痛”类型:)
– NeuronQ
2012年10月18日在16:14
@NeuronQ“没有简单的方法可以回去”-Postgres中有这样的方法:只需将所有内容放入事务中,然后在出现问题时进行回滚。
– a_horse_with_no_name
18-09-25在16:14
#4 楼
是的,许多现代数据库将允许您仅添加列或更改列的特性,例如添加或删除可为空的内容。如果删除列,数据将丢失,但不会丢失非常担心腐败。
#5 楼
Percona工具使用触发器来帮助进行更改,如果您的表已具有现有触发器,则它的效果不佳。我最终不得不编写一个可以很好地处理现有触发器的触发器,因为它们对我们的数据库非常重要https://github.com/StirlingMarketingGroup/smg-live-alter#6 楼
要解决有关ALTER TABLE
语句会发生什么的问题,这取决于更改的程度。在特定情况下,如果至少在MS SQL Server中添加新列,则引擎将在创建新表定义的同时创建表的临时副本,然后将数据插入该表中。因此,在更改期间,用户将无法访问该表。 此处是针对MSSQL服务器的特定操作的示例:http://support.microsoft.com/kb/956176/zh-cn
我认为其他RMDB具有类似的方法,尽管确切的实现将需要您与供应商的文档进行验证。
评论
-1这对于SQL Server是完全错误的:“如果至少在MS SQL Server中添加新列,则引擎将在创建新表定义的同时创建表的临时副本,然后将数据插入回去。在那里”
–A-K
2012年10月18日14:09
@AlexKuznetsov-我想出了上一行,以及列出的一些案例的链接将阐明这种情况并不总是发生。我修改了句子以更好地反映这一点。
–SchmitzIT
2012年10月18日14:17
您提到的是GUI,SSMS的行为,而不是SQL Server本身的行为。按照您的链接,建议是直接使用T-SQL进行DDL更改。 SSMS并不是更改DDL的很好工具。
–A-K
2012年10月18日15:05
@AlexKuznetsov-我读这篇文章时说的是有风险,但没有气disc。无论如何,我没有为GUI位链接文章,而是指示由于底层数据结构的更改而导致ALTER语句导致创建临时表的某些操作。我没有测试直接从T-SQL发出语句时是否适用完全相同的方法,但是我认为该过程非常相似,并且SL Server在后台进行了繁琐的工作。
–SchmitzIT
2012年10月18日在16:18
您可以启动Profiler,直接执行ALTER TABLE语句,然后看看发生了什么。然后,您可以通过对话框更改表,并亲自查看正在执行的命令。
–A-K
2012年10月19日,下午1:54
评论
注意:指定的“正确方法”是相对于MySQL而不是PostgreSQL。 PostgreSQL中的“正确方法”通常很容易,尽管可能会涉及到。使用pg_reorg可以帮助解决更困难的情况。我希望有一个详细的视频,有人可以解释尽可能多的策略。