前言

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. 【Unity3D】相机

    1 简介 ​ 相机用于渲染游戏对象,每个场景中可以有多个相机,每个相机独立成像,每个成像都是一个图层,最后渲染的图层在最前面显示. ​ 相机的属性面板如下: Clear Flags:设置清屏颜色,Sk ...

  2. 【Unity3D】人机交互Input

    1 前言 ​ Input 是 Unity3D 中用于人机交互的工具类,用户可以调用其 GetKey.GetMousePosition.GetMouseButton.GetAxis.GetButton ...

  3. MySQL 8.0.25从节点自动故障转移测试

    从MySQL 8.0.23开始,复制结构中,增加了从节点自动故障转移功能.测试一下起功能: 用dbdeployer快速安装测试环境,我这边安装的是:dbdeployer1.8.5    -- 安装脚本 ...

  4. LVM精简卷(Thinly-Provisioned Logical Volumes)

    可能LVM大家都比较熟悉,那么精简卷又是干什么的呢?相比于普通LVM有什么优势,又会带来哪些新的问题?带着这些我们来一探究竟: 工作原理 在创建Thin"瘦"卷时,预分配一个虚拟的 ...

  5. Java Socket编程系列(二)开发带回声功能的Server和Client

    服务器端: package com.dylan.socket; import java.io.*; import java.net.ServerSocket; import java.net.Sock ...

  6. Vmware中Linux通过NAT设置静态IP实现上网

    1.设置虚拟机上网方式为NAT 2.修改centos网络配置文件,我的是centos7.4,主要网关不能和主机设置的一致 [root@dylan-centos ~]# vi /etc/sysconfi ...

  7. B - Bracket Sequence题解

    B - Bracket Sequence 思路: 用一个flag来标记括号的数目,如果括号数目是个偶数的话,就代表当前要执行'+'操作,反之就是'*'操作.对于最外层的数,是没有计算的. 所以最后要单 ...

  8. 摸鱼快报:golang net/http中的雕虫小技

    以后会开一个板块,摸鱼快报,快速记录这几周开发中雕虫小技. 1. 向开发环境localhost:3000种植cookie 前端使用Create React App脚手架,默认以localhost:30 ...

  9. 对yuv存储格式中的yuv420p和yuv420sp的理解

    一.对yuv的认识 yuv是一种颜色编码系统,它将图像的亮度和色度分离开来.y表示亮度,即黑白信息:uv表示色度,即颜色信息.yuv常用于视频压缩和传输中,因为它可以更有效地表示人眼对亮度和色度的敏感 ...

  10. 记一个 Duplicate class kotlin-stblib vs kotlin-stdlib-jdk7/8 编译问题引发的案例

    某天将项目 kotlin 版本升级到了 1.8.0 ,然后编译报错了, Duplicate class kotlin-stblib vs kotlin-stdlib-jdk7/8 然后开始寻求解决方案 ...