SELECT/INSERT 在 MYSQL InnoDB 中更新索引列时的性能
SELECT/INSERT performance on an indexed column while it's being updated in MYSQL InnoDB
假设我有一个 table 结构如下:
id (int[11]), name(VARCHAR[255])
name
列有一个 b 树索引。
让我们假设这个 table 的查询顺序是这样的:
- 插入
- SELECT
- 插入
现在,我知道在索引 table 中插入新行时需要更新索引。这需要时间。但是我的问题是:
- 会 SELECT 等待索引更新还是只搜索以前版本的 table 还是有其他问题?
- 第二个 INSERT 会发生什么?它是否会等待索引从之前的 INSERT 完全更新?
谢谢。
UPDATE
假设在发出 SELECT 查询时插入还没有完成更新索引。
我认为这里有一些很好的信息:
https://dev.mysql.com/doc/refman/5.7/en/innodb-locks-set.html
和:
https://dev.mysql.com/doc/refman/5.7/en/innodb-physical-structure.html
但一般来说,Insert 会锁定该行,但不会阻止 Select(甚至不能阻止新的 Insert)。 Select 依赖于索引的查询,由于新插入而当前正在构建,在构建索引之前不会 "see" 新行。
每个插入操作 = 将数据插入行 + 更改受影响的索引。
您拥有的索引越多,插入过程越长,后续 SELECT 查询找到新行所需的时间就越长。
如果您正在编写代码,那么您可以Select等待插入完成。
根据以下评论进行更正和改进
我不是说语句执行时间比较长,我的意思是如果你在时间A插入一行,需要5秒才能完成操作,那么一个select查询运行在 A+2 秒时看不到该行,在 A+6 秒时 select 查询 运行 会看到该行。
更清楚 - 插入不会延迟 Select,除非您将数据库配置为在插入上有 table 锁,但您不会在任何 [=32= 中看到插入] 查询直到Insert完成。
在 table 中插入一行只会在每个二级索引中插入一个新条目。事实证明,插入数据和在索引中插入新条目实际上是相同的操作——两者都向下钻取 BTree(数据由 PRIMARY KEY
列组织,二级索引由二级键列。
INSERT
连同任何索引更新都是原子的。也就是说,条目被添加到索引的同时( 就您所知 SQL)作为插入的行。
在幕后(在 InnoDB 中),有一个 "Change Buffer" 延迟实际将索引更新写入磁盘。但是即使断电也不违反数据+索引更新的'atomic'性质。
事务隔离模式可以让您看到(或阻止您看到)包含 INSERT
的 "transaction" 之前的记录。但那是不同层次的原子性。并且与索引无关。
我意识到只有我的问题的第一部分得到了明确的回答,我的问题的第二部分找到了明确的答案 - 第二个 INSERT 会发生什么?它会等待索引从之前的 INSERT 完全更新吗? - 在已批准的答案提供的 link 中。
Prior to inserting the row, a type of gap lock called an insert
intention gap lock is set. This lock signals the intent to insert in
such a way that multiple transactions inserting into the same index
gap need not wait for each other if they are not inserting at the same
position within the gap.
这是它的总和,但为了深入理解我建议在引用之后阅读整个内容。
再次感谢您认可的回答。
假设我有一个 table 结构如下:
id (int[11]), name(VARCHAR[255])
name
列有一个 b 树索引。
让我们假设这个 table 的查询顺序是这样的:
- 插入
- SELECT
- 插入
现在,我知道在索引 table 中插入新行时需要更新索引。这需要时间。但是我的问题是:
- 会 SELECT 等待索引更新还是只搜索以前版本的 table 还是有其他问题?
- 第二个 INSERT 会发生什么?它是否会等待索引从之前的 INSERT 完全更新?
谢谢。
UPDATE
假设在发出 SELECT 查询时插入还没有完成更新索引。
我认为这里有一些很好的信息: https://dev.mysql.com/doc/refman/5.7/en/innodb-locks-set.html
和: https://dev.mysql.com/doc/refman/5.7/en/innodb-physical-structure.html
但一般来说,Insert 会锁定该行,但不会阻止 Select(甚至不能阻止新的 Insert)。 Select 依赖于索引的查询,由于新插入而当前正在构建,在构建索引之前不会 "see" 新行。
每个插入操作 = 将数据插入行 + 更改受影响的索引。 您拥有的索引越多,插入过程越长,后续 SELECT 查询找到新行所需的时间就越长。
如果您正在编写代码,那么您可以Select等待插入完成。
根据以下评论进行更正和改进
我不是说语句执行时间比较长,我的意思是如果你在时间A插入一行,需要5秒才能完成操作,那么一个select查询运行在 A+2 秒时看不到该行,在 A+6 秒时 select 查询 运行 会看到该行。
更清楚 - 插入不会延迟 Select,除非您将数据库配置为在插入上有 table 锁,但您不会在任何 [=32= 中看到插入] 查询直到Insert完成。
在 table 中插入一行只会在每个二级索引中插入一个新条目。事实证明,插入数据和在索引中插入新条目实际上是相同的操作——两者都向下钻取 BTree(数据由 PRIMARY KEY
列组织,二级索引由二级键列。
INSERT
连同任何索引更新都是原子的。也就是说,条目被添加到索引的同时( 就您所知 SQL)作为插入的行。
在幕后(在 InnoDB 中),有一个 "Change Buffer" 延迟实际将索引更新写入磁盘。但是即使断电也不违反数据+索引更新的'atomic'性质。
事务隔离模式可以让您看到(或阻止您看到)包含 INSERT
的 "transaction" 之前的记录。但那是不同层次的原子性。并且与索引无关。
我意识到只有我的问题的第一部分得到了明确的回答,我的问题的第二部分找到了明确的答案 - 第二个 INSERT 会发生什么?它会等待索引从之前的 INSERT 完全更新吗? - 在已批准的答案提供的 link 中。
Prior to inserting the row, a type of gap lock called an insert intention gap lock is set. This lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap.
这是它的总和,但为了深入理解我建议在引用之后阅读整个内容。
再次感谢您认可的回答。