暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

原理探究 -- KingbaseES Create Index Concurrently 过程探究

原创 金仓数据库 2023-08-10
7712

前言:

我们知道Oracle 可以通过create index online 在线创建索引,而不影响其他会话并发修改正在创建索引的表的数据。但Oracle 实际在online 创建索引的最后一步,实际还是需要进行锁升级,申请表级的S锁,因此,最后还是有可能堵塞其他会话。而KingbaseES 的两阶段创建索引的机制,则不会对增删改操作有任何影响。以下我们通过观察创建索引过程中会话持有锁及等待的情况,来探究下整个索引创建过程。

一、KingbaseES 非 Concurrently 创建索引

过程及锁申请如下:

 可以看到,正常创建索引的过程需要一直持有 ShareLock 锁,而 ShareLock 与 RowExclusiveLock 冲突,增删改操作无法进行。

二、KingbaseES 以 Concurrently 创建索引

过程及锁持有如下:

Session A Session B Session C Session D
begin transaction isolation level serializable;
  select t2;
begin;
  insert t1;
create index concurrently idx_t1  on t1(id) ;
查询 sys_locks,发现 B 持有ShareUpdateExclusiveLock 对象锁,C 等待 virtualxid ShareLock
commit;
开始扫描 t1 表 查询 sys_locks,发现 B 持有ShareUpdateExclusiveLock 对象锁,C 不再等待 virtualxid ShareLock
begin;  --在开始第2轮扫描前开启新事务
  insert t1;
继续等待会话B 事务结束 查询 sys_locks,发现 B 持有ShareUpdateExclusiveLock 对象锁,C 等待 virtualxid ShareLock。
这里可以看到 virtualxid 已经变化了
commit;
开始第二次扫描 t1 表 查询 sys_locks,发现 B 持有ShareUpdateExclusiveLock 对象锁,C 不再等待 virtualxid ShareLock
过了10秒 (假设扫描一次表,需要10秒左右) 查询 sys_locks,发现 B 持有ShareUpdateExclusiveLock 对象锁,C 等待 virtualxid ShareLock
这里可以看到 virtualxid 已经变化了
rollback;
索引创建立即结束

需要注意的点:

  1. 会话 B 模拟了两次事务(假设扫描一次表需要10秒,第二个事务必须在第二次扫描开始之前),主要是为了验证 concurrently 创建索引的两次读取数据的过程。
  2. 会话 B 在结束第1个事务后,会话C 才能开始扫描表(必须等待所有create index 开始时间点的活动事务结束后,才能开始第1次扫描表),并建立索引。
  3. 会话 B 在结束第1 个事务后,立即开启事务2 , 此时 会话C 才开始建索引,也就是先于 C (在会话C 需要取得snapshot2前)锁住t1表,这样会话C 又得等待。可以看到,在创建过程中会话 C 的 virtualtransaction 会变动(两个值)
  4. 会话 A 的事务是串行化事务,如果是Read Committed 类型事务,则不会堵塞会话C
  5. 会话 C 全程持有的 对象锁类型都是 ShareUpdateExclusiveLock ,不会堵塞其他会话的 DML 操作

在并发索引构建中,需要在两个虚拟事务(因为没有只是扫描表,所以是virtualxid)中发生两次表扫描。在每一次表扫描之前,索引构建必须等待所有修改了表的现有事务终止。在第二次扫描之后,索引构建必须等待任何持有早于第二次扫描的快照的串行事务(不管是否read创建索引的表。read committed事务都无影响)终止。然后该索引最终能被标记为准备好使用,并且CREATE INDEX命令结束。 

具体阶段如下:

  1. 开启 virtualtransaction1,拿到当前snapshot1。
  2. 扫描A表前,等待所有snapshot1前所有修改过A表的事务结束。
  3. 扫描A表,并建立索引。
  4. 结束 virtualtransaction1。
  5. 开启 virtualtransaction2,拿到当前 snapshot2。
  6. 再次扫描A表前,等待snapshot2前所有修改过A表的事务结束。
  7. 在snapshot2之后启动的事务对A表执行的DML,会体现在正在创建的索引。
  8. 再次扫描A 表,根据xmin or xmax ,将snapshot1到snapshot2之间变更的记录,合并到索引。
  9. 上一步更新索引结束后,等待任何持有早于第二次扫描的快照的事务结束(比如某repeatable read事务读取了该表 )。
  10. 结束索引创建。索引可见。

PS:In a concurrent index build, the index is actually entered as an “invalid” index into the system catalogs in one transaction, then two table scans occur in two more transactions. Before each table scan, the index build must wait for existing transactions that have modified the table to terminate. After the second scan, the index build must wait for any transactions that have a snapshot (see Chapter 13) predating the second scan to terminate, including transactions used by any phase of concurrent index builds on other tables, if the indexes involved are partial or have columns that are not simple column references. Then finally the index can be marked “valid” and ready for use, and the CREATE INDEX command terminates. Even then, however, the index may not be immediately usable for queries: in the worst case, it cannot be used as long as transactions exist that predate the start of the index build.

If a problem arises while scanning the table, such as a deadlock or a uniqueness violation in a unique index, the CREATE INDEX command will fail but leave behind an “invalid” index. This index will be ignored for querying purposes because it might be incomplete; however it will still consume update overhead. 

 

最后修改时间:2025-03-26 17:40:20
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论