【问题标题】:How to disable index in innodb如何在innodb中禁用索引
【发布时间】:2012-03-20 11:06:32
【问题描述】:

我正在尝试通过临时禁用索引来加速 InnoDB 表中的批量插入:

ALTER TABLE mytable DISABLE KEYS;

但它给出了一个警告

+-------+------+-------------------------------------------------------------+
| Level | Code | Message                                                     |
+-------+------+-------------------------------------------------------------+
| Note  | 1031 | Table storage engine for 'mytable' doesn't have this option |
+-------+------+-------------------------------------------------------------+
1 row in set (0.00 sec)

我们如何禁用索引?

在进行批量插入时,有哪些替代方法可以避免使用索引?

我们怎样才能加快这个过程?

【问题讨论】:

  • 您有多少数据(以行数和千兆字节计)?以及如何加载它?
  • 大约 500 万行和大约 1.2GB。谢谢。
  • 您要重新加载整个表吗?如果不是,占表格的百分比是多少?

标签: mysql indexing innodb


【解决方案1】:

您是否尝试过以下方法?

    SET autocommit=0; 
    SET unique_checks=0; 
    SET foreign_key_checks=0;

来自 MySQL 参考资料https://dev.mysql.com/doc/refman/8.0/en/optimizing-innodb-bulk-data-loading.html

请参阅“批量数据加载提示”部分

【讨论】:

  • 我试图这样做。但关键是如何知道索引是启用还是禁用?
  • 我知道“EXPLAIN”语句可以帮助理解“SELECT”语句,但是“LOAD DATA LOCAL INFILE”语句呢?我如何知道运行该命令时是否启用了索引?谢谢。
  • @fanchyna,禁用/启用键是全局设置,而不是每个会话。使用show keys in my_table 查看索引是被禁用还是启用。
  • 关闭这些可能会在以后造成麻烦。
【解决方案2】:

你不能在 InnoDB 表上执行 DISABLE KEYS 有一个很好的理由; InnoDB 不是为使用它而设计的,而 MyISAM 是。

事实上,当你重新加载一个 mysqldump 时会发生以下情况:

您将看到一个 MyISAM 表的 CREATE TABLE,后面跟着一个写锁。

在运行所有批量插入之前,已完成对 ALTER TABLE ... DISABLE KEYS 的调用。

这样做是关闭 MyISAM 表中的二级索引。

然后,完成批量插入。在完成此操作时,正在重建 MyISAM 表中的主键和所有唯一键。在UNLOCK TABLEs 之前,调用ALTER TABLE ... ENABLE KEYS 以线性重建所有非唯一索引。

恕我直言,此操作未编码到 InnoDB 存储引擎中,因为非唯一索引中的所有键都带有来自 gen_clust_index(又名聚集索引)的主键条目。这将是一项非常昂贵的操作,因为构建非唯一索引需要 O(n log n) 运行时间来检索每个唯一键以附加到非唯一键。

有鉴于此,对于任何涉及非 MyISAM 存储引擎的特殊情况,发布有关尝试在 InnoDB 表上 DISABLE KEYS/ENABLE KEYS 的警告远比为 mysqldump 编写异常要容易得多。

【讨论】:

  • 感谢您的解释,但问题仍然存在:如何避免在上传数据时使用索引以减少导入时间。谢谢。
  • @RolandoMySQLDBA 的意思是,您不会通过禁用 INNODB 数据库上的索引来显着减少加载时间。聚集索引本身就是基于树(= 日志定时)的数据结构;您的选择是在 nlogn 处构建整个索引,或者运行 n logn 插入。您可能会节省一些每次插入成本,但实际上我敢打赌它很少;已经有顺序插入优化。
  • 我经常构建非常大的 innodb 表,其中包含大量索引(数百 GB/10 多个不同的索引)。当我 INSERT 进入没有索引的表时,创建时间会大大减少。显然,“巨大”很大程度上取决于表的最终大小、索引的数量和类型、数据元素的类型等。我通常只用主键来构建表(如果你只是加载则不需要已经有主键 id 的数据),然后在最后建立索引。您可能不会注意到 5MM 行和 1.5GB 的差异,但随着表的增长,您会注意到。
  • 只删除所有二级索引,不删除主键。然后以完美的 PK 顺序插入新数据。在可行的情况下,对每个分区使用单独的(并行)插入。然后在单个 ALTER TABLE 语句中添加所有索引,其中包括短语 ALGORITHM=INPLACE 以避免重写数据。
【解决方案3】:

有点晚了但是...不管...忘记这里的所有答案,不要禁用索引,没有办法,只需删除它们 ALTER TABLE tablename DROP INDEX whatever,批量插入数据,然后 ALTER TABLE 表名 ADD INDEX whatever (whatever);重新创建索引的时间是带有索引的批量插入的 1%,比如 400000 行有索引需要 10 分钟,没有索引需要 2 秒......,干杯......

【讨论】:

  • 好点!谁降级了这篇文章。我想知道为什么。
  • 插入后重新创建索引所花费的时间呢?我会假设索引的存在是有原因的,因此删除它们、插入记录、然后创建相同的索引都需要时间。考虑到删除和创建索引所花费的时间,它是否仍然更快?
  • 当你创建一个索引时,速度快得惊人。它可能在将索引写入数据库之前对内存中的索引进行排序,然后它可以顺序写入整个索引,而不必在为主表创建每条记录时将记录插入索引的随机区域。我有一个数据导入实用程序,在导入之前删除索引,然后重新创建它们要快得多。它可能更有效地存储,因为页面拆分将处于最佳位置。
  • 开销取决于innodb_buffer_pool_size。另请参阅“更改缓冲”。
  • 请记住,删除索引后,对键的任何约束都不会被强制执行,因此后续的CREATE INDEX 很可能会失败。
【解决方案4】:

为了减少重新计算索引的成本,您应该使用 DATA INFILE 或使用 Mysql Multi Row Inserts 插入数据,例如

INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);

-> 所以用一个语句插入几行。

一个语句可以插入多少行取决于max_allowed_packet mysql 设置。

【讨论】:

  • 你确定吗?那么,您是在告诉我们使用 single 插入语句插入一百万行吗?
  • 我不是告诉你用一个语句插入“所有数据”,而是一次插入几行。在我的应用程序中,我每次插入使用了 500-1000 行,没有问题。这主要取决于行数和每行插入的数据量。
  • 那么什么是好的数字?你是通过反复试验来决定的吗?
  • 见我之前的评论。我并没有在一份声明中说要做事情。 如果您的服务器有无限量的内存 - 您可以使用无限大/长/大语句
  • @Drew 我就是这么说的。无论如何我都会更新答案,因为您发布的链接很有用。
猜你喜欢
  • 1970-01-01
  • 2012-09-23
  • 1970-01-01
  • 1970-01-01
  • 2019-08-18
  • 1970-01-01
  • 1970-01-01
  • 2015-05-12
  • 1970-01-01
相关资源
最近更新 更多