MySQL 的事务性 DDL 工作流

sen*_*ett 25 mysql transaction ddl rollback

我有点惊讶地发现,DDL语句(alter tablecreate index等)隐含在MySQL提交当前事务。来自 MS SQL Server,在本地事务(然后回滚)中进行数据库更改的能力是我工作流程的重要组成部分。对于持续集成,如果迁移因任何原因而中断,则使用回滚,这样至少我们不会让数据库处于半迁移状态。

人们在使用 MySQL 进行迁移和持续集成时如何解决这两个问题?

Rol*_*DBA 11

对于许多人来说,MySQL 的致命弱点是隐式提交。

根据本书第 418 页第 3 段

MySQL 5.0 认证学习指南

以下命令可以并且将破坏交易

  • ALTER TABLE
  • BEGIN
  • CREATE INDEX
  • DROP DATABASE
  • DROP INDEX
  • DROP TABLE
  • RENAME TABLE
  • TRUNCATE TABLE
  • LOCK TABLES
  • UNLOCK TABLES
  • SET AUTOCOMMIT = 1
  • START TRANSACTION

建议

对于 MySQL,您构建的任何 ContinuousIntegration (CI)/SelfService 作业都应始终使事务性作业和 DDL 脚本互斥。

这使您有机会创建范式

  • 支持与START TRANSACTION/COMMIT块正确隔离的事务
  • 通过自己编写 DDL 脚本来控制 DDL,运行这样的 DDL 作为构造函数或析构函数
    • 构造函数:使用新设计制作表格的 DDL
    • 析构函数:DDL 使表恢复到以前的设计
  • 永远不要将这些操作合并在一项工作中

警告:如果您为此使用 MyISAM,您可以(不)友好地将 MyISAM 添加到可能破坏事务的事物列表中,可能不是隐式提交,但绝对是在数据一致性方面应该回滚需要。

为什么不是 LVM?

LVM 快照很棒,无需执行繁重的 SQL 处理即可恢复数据库的整个实例是理想的选择。但是,当谈到 MySQL 时,您必须考虑两个存储引擎:InnoDB 和 MyISAM。

全 InnoDB 数据库

查看 InnoDB 的架构(图片由 Percona CTO Vadim Tkachenko 提供)

InnoDB 管道

InnoDB 有许多活动部件

  • 系统表空间
    • 数据字典
    • 双写缓冲区(支持数据一致性;用于崩溃恢复)
    • 插入缓冲区(缓冲区更改为辅助非唯一索引)
    • 回滚段
    • 撤消空间(最不受控制的增长可能发生的地方)
  • InnoDB 缓冲池
    • 脏数据页
    • 脏索引页
    • 对非唯一索引的更改
  • 其他重要的内存缓存

获取所有 InnoDB 数据库的 LVM 快照,其中未提交的更改漂浮在缓冲池和内存缓存中,将产生一个数据集,一旦 LUN 恢复和 mysqld 启动,该数据集将需要 InnoDB 崩溃恢复。

对 ALL-InnoDB 的建议

如果您可以在拍摄快照之前关闭 MySQL

    1. SET GLOBAL innodb_fast_shutdown = 0;
    1. SET GLOBAL innodb_max_dirty_pages_pct = 0;
    1. SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_dirty';
    1. 重复步骤 3 直到Innodb_buffer_pool_pages_dirty为 0 或尽可能接近 0
    1. service mysql stop
    1. 拍摄 LVM 快照
    1. service mysql stop

如果您无法关闭但使用 MySQL Live 拍摄快照

    1. SET GLOBAL innodb_max_dirty_pages_pct = 0;
    1. SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_dirty';
    1. 重复步骤 2 直到Innodb_buffer_pool_pages_dirty为 0 或尽可能接近 0
    1. 拍摄 LVM 快照
    1. SET GLOBAL innodb_max_dirty_pages_pct = 75;

All-MyISAM 数据库或 InnoDB/MyISAM Mix

MyISAM 在访问时会维护一个针对它的打开文件句柄的计数。如果 MySQL 崩溃,任何打开文件句柄计数 > 0 的 MyISAM 表都将被标记为崩溃并需要修复(即使数据没有任何问题)。

当快照恢复并启动 mysqld 时,对具有正在使用的 MyISAM 表的数据库进行 LVM 快照将有一个或多个 MyISAM 表需要修复。

对 All-MyISAM 或 InnoDB/MyISAM 混合的建议

如果您可以在拍摄快照之前关闭 MySQL

    1. SET GLOBAL innodb_fast_shutdown = 0;
    1. SET GLOBAL innodb_max_dirty_pages_pct = 0;
    1. SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_dirty';
    1. 重复步骤 3 直到Innodb_buffer_pool_pages_dirty为 0 或尽可能接近 0
    1. service mysql stop
    1. 拍摄 LVM 快照
    1. service mysql stop

如果您无法关闭但使用 MySQL Live 拍摄快照

您可以强制刷新某些 InnoDB 表

    1. SET GLOBAL innodb_max_dirty_pages_pct = 0;
    1. SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_dirty';
    1. 重复步骤 2 直到Innodb_buffer_pool_pages_dirty为 0 或尽可能接近 0
    1. FLUSH TABLES innodb_tbl1,... FOR EXPORT;在关键 InnoDB 表上运行
    1. FLUSH TABLES WITH READ LOCK;
    1. 拍摄 LVM 快照
    1. UNLOCK TABLES;
    1. SET GLOBAL innodb_max_dirty_pages_pct = 75;

MySQL 复制有帮助吗?

虽然您可以将一个 LVM 快照还原到两台服务器并设置 MySQL 主/从复制,但在还原快照时,这会成为清理工作的额外来源。

如果您在 Master 上运行 CI 作业并且这些作业很小,则在某些情况下复制可能会节省时间。您可以STOP SLAVE;在 Slave 上运行START SLAVE;,在Master 上启动 CI 作业,并在 Master 的数据得到认证后在 Slave 上运行。

如果 CI 作业提醒过多数据,您可以从头开始恢复 LVM 快照并设置复制。如果您发现自己经常这样做,您可能可以通过设置 MySQL 复制来完成。

最后的想法

  • 最好使用多个数据库服务器(3 个或更多)来执行恢复和回归测试。
  • 如果这些表不需要保留 MyISAM,则将剩余的 MyISAM 表转换为 InnoDB。
  • 如果您的数据内容是敏感的,您应该在恢复快照后在启动任何测试之前执行 CI 作业来清理数据。作为替代方案,您可能希望使用已经清理过的数据拍摄 MySQL 的快照。


phi*_*l_w 4

如果您谈论持续集成,那么我认为它是一个开发环境。在这种情况下,我想说,进行结构更改的人必须对其进行测试,以确保不会破坏其他人的东西,就像更新公共库的人一样:在提交此类更改之前在自己的沙箱中进行测试。

在生产部署过程中,您通常会通过开发、质量检查甚至预生产环境来测试您的更改,就像任何代码更改一样。

请注意,这不是 MySQL 特有的:Oracle 数据库在发出“alter table”等时也会隐式执行 COMMIT。

现在,如果您想保护自己,您当然可以提前进行备份,或者创建 LVM 或文件系统快照(如果您的系统可以做到这一点)。您可能还有一个从站,您可以在敏感操作之前延迟/停止它作为安全措施。