为什么 MySQL 中的 Insert-Ignore 如此昂贵?

Mar*_*tos 7 mysql index upsert aurora bulk-insert

TL; DR: 插入大量的行的无冲突快得多比(不)重新插入相同的行,BOTH使用INSERT IGNORE语法。

为什么是这样?我假设插入和“忽略”插入的索引查找成本相同,因为 MySQL 不知道传入的数据是否具有重复/冲突的数据(因此需要被忽略)......因此,索引发生在初始插入和被忽略的插入运行中。

此外,我认为“忽略”行应该是便宜的,因为它不需要任何磁盘写入。

但这绝对不是这种情况。

长版: 在这个问题中,我们使用 AWS 的 Aurora/MySQL 和LOAD DATA FROM S3 FILE语法来删除任何传输或性能变量。我们加载了一个与下面架构相对应的 4 兆行 CSV 文件,并加载了两次,每次都使用LOAD ... IGNORE.

请注意,该问题也发生在标准INSERT ... IGNORE,但使用批量行插入时。LOAD ... IGNORE这里的用途是将讨论引向测量结果的反直觉性质,而不是“如何执行大量被忽略的插入”。这不是这里的问题,因为已经制定了特定于域的方法。

在被测试的模型中,有一个三层索引:前两个是基数非常低的可枚举分类列,第三个列本质上是“实际”数据。为了简化这个问题,我只是坚持我目前的设置。

假设以下简单模式:

our_table:

id: basic auto-increment bigint, primary key
constkey1: varchar(50) -- this is a constant in the insert
constkey2: varchar(50) -- this is a constant in the insert
datakey:   varchar(50) -- this is pulled in from the CSV file
datafield: varchar(50) -- this is pulled in from the CSV file
consttime: datetime    -- this is a constant in the insert

Unique Index: (constkey1, constkey2, datakey)
Run Code Online (Sandbox Code Playgroud)

和以下查询,运行两次:

LOAD DATA FROM S3 FILE  's3://...'
    IGNORE
    INTO TABLE our_table
    COLUMNS TERMINATED BY ','
    (datakey, datafield)
    SET constkey1 = 'some string',
        constkey2 = 'some other string',
        consttime = CURRENT_TIMESTAMP  -- so that I can verify changes
Run Code Online (Sandbox Code Playgroud)

此外,假设our_table已经包含了数百万行,没有一个是有具有既不相同constkey1 NOR constkey2作为查询给出。

  • 第一次运行上面的查询,4个megarows插入需要3到5分钟(大概是基于运行时当前数据库环境的vagrancies),所有4个megarows都正确插入。这是一个可接受的基线。

  • 我第二次运行查询,4个megarows需要很长时间才能(不)插入一个疯狂的金额,并如预期,所有4个megarows正确更新。时间足够长,甚至不值得在这里排位(一个跑近一个小时)。

这个测试运行了很多次,总是产生相同的结果。

更新: 在修改此问题时,SE 建议了以下问题

在几百万条记录后,mysql插入索引表需要很长时间

虽然我不认为它在这里完全通用,但它可能与索引范围的内存大小有关?在这里的示例中,('some string', 'some other string', * )运行 1 中的“子索引”大小为零,而运行 2 中的“子索引”大小为 4 个。也许这是一个问题?

另请注意,最终解决方案采用以下问题中给出的形式。这对手头的问题无关紧要,因为我想知道为什么原始解决方案不起作用。这只是给那些好奇的人。

我可以识别大型 MySQL 插入中的重复项吗?

mus*_*cio 4

从源代码中可以看出,当向InnoDB表中插入一行时,它会先添加到聚集索引中,然后依次添加到所有二级索引中。在您的情况下,聚集索引基于自动增量列,因此第一个操作始终会成功。由于重复的键值,对辅助唯一索引的下一个操作失败。然后引擎继续回滚之前的操作,从而导致不必要的性能损失。

该逻辑显然针对“快乐路径”进行了优化——无冲突插入,但代价是次优异常路径。如果他们在应用任何更改之前验证约束,冲突解决成本将会降低,但“快乐路径”将变得更加昂贵,使更多用户不满意。

您的实例是 AWS Aurora 实例,由于这两个更改都需要写入更改日志的法定数量以复制到辅助节点,以及它可能对快速速度产生负面影响,因此问题可能会变得更糟。插入加速度