前言

CREATE INDEX CONCURRENTLY(CIC)是DBA们最常用的语句之一,它的好处是不阻塞DML语句。

但在大事务、长事务较多的系统,它可能被阻塞得很久。

本篇就从这个阻塞的案例开始,学习CIC的过程、原理以及注意事项。

测试CREATE INDEX CONCURRENTLY被阻塞

create table test(id int);
INSERT INTO test(id) VALUES (generate_series(1, 100000)); create table tmp(a int);
insert into tmp values(1); 会话1: TEST=# select sys_backend_pid();
sys_backend_pid
-----------------
11860
(1 row)
select count(*) from test a, test b; 会话2:
create index concurrently ind_02 on tmp(a); 可以看到,即使test和tmp都不是同一个表,会话1执行的不是dml语句,tmp的索引创建依然被阻塞了。如果会话1中是要执行漫长的查询,会话2的索引创建也将一直被阻塞。
那么为什么被阻塞呢?
查看等待事件:
TEST=# SELECT pid, locktype,virtualxid,relation::regclass, mode FROM sys_locks where granted='f' order by pid;
pid | locktype | virtualxid | relation | mode
-------+------------+------------+----------+-----------
15150 | virtualxid | 6/5220 | | ShareLock
(1 row)
TEST=#
TEST=# SELECT pid, locktype,virtualxid,relation::regclass, mode FROM sys_locks where granted='t' order by pid;
pid | locktype | virtualxid | relation | mode
-------+------------+------------+-----------+--------------------------
11860 | relation | | test | AccessShareLock
11860 | virtualxid | 6/5220 | | ExclusiveLock
15150 | virtualxid | 7/1604 | | ExclusiveLock
15150 | relation | | tmp | ShareUpdateExclusiveLock
15440 | virtualxid | 8/659 | | ExclusiveLock
15440 | relation | | pg_locks | AccessShareLock
15440 | relation | | sys_locks | AccessShareLock
(7 rows) 当执行查询语句或dml时会获取一个virtualxid,但为什么创建索引要跟它获取同一个virtualxid?
先看看执行的函数堆栈,pid15150是被阻塞的pid,从堆栈中看到它正常获取一个锁WaitOnLock,锁类型是VirtualXactLock,并发现DefineIndex需要调用一个函数叫WaitForOlderSnapshots,它在等更旧的快照。 pid 15150:
gdb)
#0 0x00007f3f980917d3 in __epoll_wait_nocancel () from /lib64/libc.so.6
#1 0x00000000007f5ed3 in WaitEventSetWait ()
#2 0x00000000007f67b4 in WaitLatchOrSocket ()
#3 0x0000000000803ac5 in ProcSleep ()
#4 0x0000000000801362 in WaitOnLock ()
#5 0x0000000000802abf in LockAcquireExtended ()
#6 0x0000000000803242 in VirtualXactLock ()
#7 0x000000000062bd90 in WaitForOlderSnapshots ()
#8 0x000000000062fbf4 in DefineIndex ()
#9 0x000000000081ed29 in ProcessUtilitySlow ()
#10 0x000000000081f5ed in standard_ProcessUtility ()
#11 0x00007f3f909a95f9 in synonym_ProcessUtility () from /home/kingbase7/KESRealPro/V008R006C007B0012/Server/lib/synonym.so
#12 0x00007f3f907346a2 in plsql_utility_command () from /home/kingbase7/KESRealPro/V008R006C007B0012/Server/lib/plsql.so
#13 0x00007f3f8ffd70d4 in forceview_ProcessUtility () from /home/kingbase7/KESRealPro/V008R006C007B0012/Server/lib/force_view.so
#14 0x00007f3f8fdca8d6 in flashback_ProcessUtility () from /home/kingbase7/KESRealPro/V008R006C007B0012/Server/lib/kdb_flashback.so
#15 0x00007f3f8e73886b in pgss_ProcessUtility () from /home/kingbase7/KESRealPro/V008R006C007B0012/Server/lib/sys_stat_statements.so
#16 0x000000000081ac06 in PortalRunUtility ()
#17 0x000000000081c0bf in PortalRunMulti ()
#18 0x000000000081c979 in PortalRun ()
#19 0x00000000008174e2 in exec_simple_query ()
#20 0x000000000081a486 in PostgresMain ()
#21 0x000000000079fd2a in PostmasterMain ()
#22 0x00000000006eb5bf in main () pid 11860:
(gdb) bt
#0 0x00007f3f980917d3 in __epoll_wait_nocancel () from /lib64/libc.so.6
#1 0x00000000007f5ed3 in WaitEventSetWait ()
#2 0x00000000006da195 in secure_read ()
#3 0x00000000006e5824 in pq_recvbuf ()
#4 0x00000000006e5cb7 in pq_getbyte ()
#5 0x0000000000819a16 in PostgresMain ()
#6 0x000000000079fd2a in PostmasterMain ()
#7 0x00000000006eb5bf in main ()
可以看出进行select读操作,并获取buffer信息等,pq_getbyte描述了I/O读的操作。

sys_index表中的字段含义

indislive为true:表示索引可见,新事务知道这个索引存在。

indisready为true:表示该索引可写,新事务的DML操作需要维护该索引。

indisvalid 为true:表示改索引可读,新事务可以使用此索引进行查询。

CIC创建过程

阶段1

语法解析和预检查

构建catalog元数据信息, 主要包括 relcache,sys_class, sys_index,此时的状态是(indislive=true 索引可见、indisready=false不能被写入、indisvalid= false不能被查询)

获取一个锁(ShareUpdateExclusiveLock),避免创建阶段,表被删除。此阶段后,新事务会看到表中有一个invalid索引(但此时不可读写),

阶段2

1、获取ShareLock,等待此创建索引的表上所有的dml事务结束。

2、获取快照,对该表进行全表扫描,将对此快照可见的所有元组构建索引。

在这个阶段,其它事务对该表进行写入时,并不维护索引(因为索引还不能写入),仅保证HOT更新满足新索引定义,因此会有索引和表数据不一致的情况。

3、更新sys_index中indisready=true,此阶段后,索引可写入但不能查询(因为数据还不一致),其他事务修改该表时,需要维护新索引。

阶段3

第三阶段就是保证数据一致性。

使用ShareLock等待表上所有的dml事务结束,等待原因:阶段2中结束前开始的事务,无法看到新索引已变为可写状态,修改基表时并不维护新索引。

再次获取快照,进行一次全表扫描,将Phase2事务开始到现在索引中缺少的元组添加到索引中。

记下当前快照的xmin,获取所有早于当前快照xmin的快照的virtualxid,等待所有旧读写事务结束(我们的例子就卡在这步)

等待原因:旧事务的快照可以看到比构建索引时的快照更旧的行,如果它们使用新索引进行查询,会发生索引中查不到想要的旧数据,导致数据不一致。

因此,第3阶段必须等所有旧读写事务结束,才能将新索引置为可读状态。而后,更新relcache,释放锁ShareUpdateExclusiveLock。

CIC的注意事项

不要在有长事务时执行此操作,否则会等待很久。

CIC需要扫描两遍表,如果原表很大,耗时会更长,资源消耗更多。

分区表不支持在主表CIC创建索引,在子分区创建支持。

KingbaseES V8R6 创建索引create index concurrently被阻塞的更多相关文章

  1. MySQL 创建索引(Create Index)的方法和语法结构及例子

    MySQL 创建索引(Create Index)的方法和语法结构及例子 MySQL 创建索引(Create Index)的方法和语法结构及例子   CREATE INDEX Syntax CREATE ...

  2. PostgreSQL的 create index concurrently

    对于PostgreSQL的 "create index concurrently". 我个人认为其中存在一个bug. 我的验证过程如下: 我有两个表,tab01和 tab02,这两 ...

  3. SQL Server 创建索引(index)

    索引的简介: 索引分为聚集索引和非聚集索引,数据库中的索引类似于一本书的目录,在一本书中通过目录可以快速找到你想要的信息,而不需要读完全书. 索引主要目的是提高了SQL Server系统的性能,加快数 ...

  4. Oracle常用操作——创建表空间、临时表空间、创建表分区、创建索引、锁表处理

    摘要:Oracle数据库的库表常用操作:创建与添加表空间.临时表空间.创建表分区.创建索引.锁表处理 1.表空间 ■  详细查看表空间使用状况,包括总大小,使用空间,使用率,剩余空间 --详细查看表空 ...

  5. SQL 创建索引的作用以及如何创建索引

    SQL 创建索引的作用以及如何创建索引 SQL 创建索引的作用 一.使用索引的优点: 1.通过唯一性索引(unique)可确保数据的唯一性 2.加快数据的检索速度 3.加快表之间的连接 4.减少分组和 ...

  6. SQL创建索引和删除索引

    使用CREATE 语句创建索引 CREATE INDEX index_name ON table_name(column_name,column_name) include(score) 普通索引 C ...

  7. Oracle数据库查看已添加的索引和创建索引

    /** *查看目标表中已添加的索引 * */ --在数据库中查找表名 select * from user_tables where table_name like 'tablename%'; --查 ...

  8. Oracle创建索引;查询索引

    1.创建索引 create index 索引名 on 表名(列名); 2.删除索引 drop index 索引名; 3.创建组合索引 create index 索引名 on 表名(列名1,,列名2); ...

  9. SQL语句-创建索引

    语法:CREATE [索引类型] INDEX 索引名称ON 表名(列名)WITH FILLFACTOR = 填充因子值0~100 GO USE 库名GO IF EXISTS (SELECT * FRO ...

  10. SQLServer 语句-创建索引

    语法:CREATE [索引类型] INDEX 索引名称ON 表名(列名)WITH FILLFACTOR = 填充因子值0~100GO /*实例*/USE 库名GOIF EXISTS (SELECT * ...

随机推荐

  1. 斐讯K3C改散热

    斐讯K3C改散热 斐讯K3C日常使用还是不错的,就是日常的温度还是比较高的,不过冬天用来当暖手宝还是不错的. 这个改散热的方法是跟贴吧老哥学的,不得不说贴吧老哥还是牛皮,原贴在这,我当时拍的照片不够, ...

  2. el-dialog关闭后重置表单和校验提示

    问题说明 最近测试反馈操作某新增/修改表单,点击[取消]或[关闭]窗口后再次点击[新增]或[修改]发现校验提示仍然存在! 问题原因 项目采用Vue+ElementUI,修改表单的窗口控件采用el-di ...

  3. ORA-12514问题解决

    版本:11.2.0.1.0 - 64bit 本机安装Oracle后链接测试发现以下情况: sqlplus scott/tiger 正常登陆 sqlplus scott/tiger@orcl  登陆失败 ...

  4. ftp 出现Passive mode refused 解决办法

    在shell中调用FTP出现下面错误时, Permission denied. Passive mode refused. Permission denied. Passive mode refuse ...

  5. Java集合框架学习(八) HashMap详解

    HashMap介绍 HashMap是一个基于Map的集合类,用于存储Key和Value的键值对. 通常用HashMap<Key, Value> or HashMap<K, V> ...

  6. deque双端队列

    # 支持从任意一端增加和删除元素 d = collections.deque() d.extend('abcdefg') d.append('h') d.extendleft(range(6)) # ...

  7. tox运行报C901错误解决办法

    # 报C901表示该函数太复杂! 解决办法 在函数上添加如下注释即可 # flake8: noqa: C901

  8. 【LeetCode二叉树#05】平衡二叉树

    力扣题目链接(opens new window)](https://leetcode.cn/problems/balanced-binary-tree/) 给定一个二叉树,判断它是否是高度平衡的二叉树 ...

  9. 面试官上来就让手撕HashMap的7种遍历方式,当场愣住,最后只写出了3种

    写在开头 今天有个小伙伴私信诉苦,说面试官上来就让他手撕HashMap的7种遍历方式,最终只写出3种常用的,怀疑面试官是在故意***难.这个问题大家怎么看? 反正我个人感觉这肯定不是***难,&quo ...

  10. Java 辨析之实例化和初始化

    在面向对象编程中,实例化和初始化是两个相关但不同的概念: 实例化(Instantiation): 实例化是指创建一个类的新的具体对象的过程.当程序运行时,通过 new 关键字调用类的构造函数来创建该类 ...