【问题标题】:Will a table lock prevent row locking表锁会阻止行锁定吗
【发布时间】:2014-03-19 15:00:21
【问题描述】:

我有一个包含超过 1.6 亿个条目的表,它的表引擎有误,所以我要更换引擎。 当我在没有任何准备的情况下执行此操作时,由于我的缓冲区大小而出现错误,因为行锁太多

mysql> ALTER TABLE foobar ENGINE=MyISAM;
ERROR 1206 (HY000): The total number of locks exceeds the lock table size

我现在想在此操作之前锁定整个表并在之后解锁整个表。

mysql> LOCK TABLES foobar WRITE;

我的问题:mysql 服务器是否注意到表锁已经处于活动状态并跳过行锁,或者它锁定了表并且现在还会再次锁定每一行,我会再次遇到同样的错误?

我也愿意接受任何其他建议如何以最快的方式更改如此大(和更大)表的引擎:)

【问题讨论】:

  • 检查 pt-online-schema-change 工具。它也应该可以解决您的问题。但是 MyISAM……你不关心你的数据,是吗? :)
  • 感谢您提供有关该工具的建议!我更喜欢在没有任何第三方工具的情况下更换引擎,但也许没有其他方法可以解决这个问题。顺便说一句:每个工具都有它的工作 ey ;) - MyISAM 也是如此。所以回答你的问题:不,在这种情况下我实际上没有,它实际上是一个缓存表,但无论如何......问题更多的是关于后台进程和理解如果表锁定已经激活,mysql是否跳过行锁定: )

标签: mysql sql locking alter-table


【解决方案1】:

你可以运行一个实验:

在第一次会话运行中:

mysql> LOCK TABLE foobar WRITE;
mysql> ALTER TABLE foobar ENGINE=MyISAM;

在第二个会话中(即打开第二个终端窗口),运行:

mysql> SHOW ENGINE INNODB STATUS\G

在 TRANSACTIONS 部分,您会发现您的 ALTER 线程正在进行中:

---TRANSACTION 69638, ACTIVE 45 sec fetching rows
mysql tables in use 1, locked 1
14626 lock struct(s), heap size 1898936, 5145657 row lock(s)

哇!尽管 LOCK TABLE 有效,但它会创建许多单独的行锁。

(这只是一个示例;随着 ALTER TABLE 在您的表中工作,您会看到行锁的数量随着时间的推移而增加。)


所以需要为大量的行锁保证空间。

https://dev.mysql.com/doc/refman/5.6/en/innodb-error-codes.html 说:

  • 1206 (ER_LOCK_TABLE_FULL)

    锁的总数超过了 InnoDB 用于管理锁的内存量。为避免此错误,增加innodb_buffer_pool_size 的值。在单个应用程序中,解决方法可能是将大型操作分解为更小的部分。例如,如果较大的INSERT 发生错误,请执行几个较小的INSERT 操作。

(强调我的)

另请阅读How much memory Innodb locks really take?

Innodb 行级锁是通过具有特殊的锁表来实现的,该表位于缓冲池中,可以设置为每个哈希分配的小记录和锁定在该页位上的每一行。这在理论上可以提供低至每行几位的开销。

因此锁定 1.6 亿行需要锁定表中大约 60-80MB 的空间。您的缓冲池可能太小而无法容纳它。

在 MySQL 5.1.27 及更早版本中,InnoDB 缓冲池的默认大小仅为 8MB。在 MySQL 5.1.28 中,默认值增加到 128MB。


你的评论:

每个工具都各司其职 ;) - MyISAM 也是如此

MyISAM 不支持原子性、一致性、隔离性或持久性。除此之外,它很棒。 :-)

【讨论】:

  • 感谢您的帮助!我的服务器版本是5.5.35-0+wheezy1-log,所以应该受够了……不过我刚刚分配了1G,让我们看看它是如何工作的。关于评论:是的,我们真的不需要 ACID 这个表,如果这不起作用,我们甚至可能清空它并重建缓存,所以对我们来说 MyISAM 很棒:)
  • 好吧,如果你不需要事务并且数据损坏时很容易重建,并且你喜欢MyISAM has worse performance,那就去吧!
  • Bill,您是否先尝试了 auto_commit = 0 以免立即释放表锁?
  • @MarcusAdams,显式表锁不限于单个事务。它们一直有效,直到您明确 UNLOCK TABLES 或您的会话结束。
  • 我想我对此感到困惑:“事务持有的所有 InnoDB 锁都在事务提交或中止时释放。因此,在 InnoDB 表上调用 LOCK TABLES 没有多大意义autocommit=1 模式,因为获取的 InnoDB 表锁将立即释放。”来自here
【解决方案2】:

通常,如果您的 InnoDB 缓冲池不足以为表中的所有行保存行锁(有时是必要的),您可以增加 InnoDB 缓冲池大小 (innodb_buffer_pool_size)。

尝试将您的 innodb_buffer_pool_size 增加到 128 MB。

如果您仍然遇到问题,您可以随时尝试创建新的 MyISAM 表,然后执行 INSERT INTO ... SELECT FROM ... 将您的数据从 InnoDB 表复制到 MyISAM 表。您可以分块复制行以减少行锁。

【讨论】:

  • 感谢您的建议,如果ALTER 仍然失败,我将尝试将数据从旧表复制到新表(使用正确的引擎)。我只是认为 mysql 在内部做同样的事情(通过ALTER 上的临时表复制),那么手工做的重点在哪里......但也许我错了,这就是解决方案;)我'明天见。
  • InnoDB 在INSERT INTO ... SELECT FROM ... 期间确实在原始表的行上创建了 shared 锁。例如,当您将大量行插入临时表时,这可能会导致从属服务器上的复制线程被阻塞。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-18
  • 2014-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-31
相关资源
最近更新 更多