对于PostgreSQL的 "create index concurrently". 我个人认为其中存在一个bug。

我的验证过程如下:

我有两个表,tab01和 tab02,这两个表之间没有任何关联。

我认为 对 tab02执行 "create index concurrently" 不会对 访问tab01的事务有任何影响,然而事实并非尽然。

我第一程序的表现: 通过ecpg执行事务,再通过 "create index concurrently" 给tab02建立索引,成功。

我第二程序的表现:通过ecpg执行事务,再通过 "create index concurrently" 给tab02建立索引,被阻塞。

我第三个测试:      通过psql发起事务,  另一个psql客户端执行 "create index concurrently" 成功。

我第四个测试:    通过psql发起事务 另一个psql客户端执行 "create index concurrently",被阻塞。

无论 PostgreSQL9.1.2,还是PostgreSQL9.2.4,结果是一样的。

数据准备:

[postgres@server bin]$ ./psql -U tester -d tester
psql (9.1.2)
Type "help" for help.
tester=> \d tab01;Table "public.tab01"Column |         Type         | Modifiers
--------+----------------------+-----------
 id     | integer              |cd     | character varying(4) |tester=> \d tab02;Table "public.tab02"Column |  Type   | Modifiers
--------+---------+-----------
 id     | integer |value  | integer |tester=> select * from tab01;id | cd
----+----1 | 142 | 153 | 14
(3 rows)tester=> select * from tab02;id | value
----+-------1 |   1002 |   2003 |   300
(3 rows)
tester=>

我的测试方法:

对第一个程序和第二个程序:

当我的eccp程序正在睡眠的时候,我另外开一个终端,执行:

"create index concurrently idx_tab02_id_new on tab02(id)"

结果是:

第一个程序执行中,我可成功建立索引。 
第二个程序执行中,我无法建立索引,会被阻塞
而我的tab01和tab02之间,没有任何关联。而且我也不认为我的ecpg程序会有潜在的可能去使用tab02的索引。

事实上,如果我去看ecpg预编译后得到的c程序,我可以看到:

 { ECPGdo(__LINE__, 0, 1, "db_conn", 0, ECPGst_normal, "select count ( * ) from tab01 where cd = $1 ",ECPGt_char,(vcd),(long)4 + 1,(long)1,(4 + 1)*sizeof(char),ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT,ECPGt_int,&(vCount),(long)1,(long)1,sizeof(int),ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);}

当我给$1加入引号后,我就可以成功地建立索引了。

 { ECPGdo(__LINE__, 0, 1, "db_conn", 0, ECPGst_normal, "select count ( * ) from tab01 where cd = '$1' ",ECPGt_char,(vcd),(long)4 + 1,(long)1,(4 + 1)*sizeof(char),ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT,ECPGt_int,&(vCount),(long)1,(long)1,sizeof(int),ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);}

下面是我测试程序的源代码:

第一个:

[root@server soft]# cat ./test01/test01.pc
int main()
{EXEC SQL BEGIN DECLARE SECTION;int vCount;char vcd[4+1]; EXEC SQL END DECLARE SECTION;EXEC SQL CONNECT TO 'tester@127.0.0.1:5432' AS db_connUSER tester IDENTIFIED BY tester;                            EXEC SQL AT db_conn SELECT COUNT(*) INTO :vCount FROM tab01;                                      fprintf(stderr,"count is:%d\n",vCount); fprintf(stderr,"Before disconnect,sleep for 500 seconds\n");sleep(500);EXEC SQL DISCONNECT db_conn;fprintf(stderr,"After disconnect,sleep for 600 seconds\n");sleep(600);return 0;
}[root@server soft]#

第二个:

[root@server soft]# cat ./test02/test02.pcint main(){EXEC SQL BEGIN DECLARE SECTION;int vCount;char vcd[4+1];                        EXEC SQL END DECLARE SECTION;EXEC SQL CONNECT TO 'tester@127.0.0.1:5432' AS db_connUSER tester IDENTIFIED BY tester;char *pCd="14";memset(vcd,'\0',5);strncpy(vcd, pCd,4);             EXEC SQL AT db_conn SELECT COUNT(*)            INTO :vCount FROM tab01 WHERE cd = :vcd;fprintf(stderr,"count is:%d\n",vCount);fprintf(stderr,"Before disconnect,sleep for 500 seconds\n");sleep(500);EXEC SQL DISCONNECT db_conn;fprintf(stderr,"After disconnect,sleep for 600 seconds\n");sleep(600);return 0;
}[root@server soft]#

而且,通过 psql,还可以发现一个与 create index concurrently 相关的现象:

我的第三个测试:

客户端1:

[postgres@server pgsql]$ ./bin/psql -d tester -U tester
psql (9.1.2)
Type "help" for help.tester=> begin;
BEGIN
tester=> select * from tab01 where cd = '14';id | cd
----+----1 | 143 | 14
(2 rows)
tester=>

客户端2:

[postgres@server pgsql]$ ./bin/psql -d tester -U tester
psql (9.1.2)
Type "help" for help.tester=> create index concurrently idx_tab02_id_new on tab02(id);

可以很快就成功创建索引。

我的第四个测试:

客户端1:

[postgres@server pgsql]$ ./bin/psql -d tester -U tester
psql (9.1.2)
Type "help" for help.
tester=> begin;
BEGIN
tester=> select * from tab01 where cd = '14';id | cd
----+----1 | 143 | 14
(2 rows)tester=> select pg_sleep(500);pg_sleep
----------
(1 row)
tester=>

客户端2:

[postgres@server pgsql]$ ./bin/psql -d tester -U tester
psql (9.1.2)
Type "help" for help.tester=> create index concurrently idx_tab02_id_new on tab02(id);

客户端2的创建索引会被阻塞

根据我对PostgreSQL的源代码的跟踪,可以看到有如下的调用关系:

PortalRunMulti--> PortalRunUtility-->Standard_ProcessUtility-->DefineIndex

而我对DefineIndex作简化后,可以看到:

{           …old_snapshots = GetCurrentVirtualXIDs(snapshot->xmin, true, false,PROC_IS_AUTOVACUUM | PROC_IN_VACUUM, &n_old_snapshots);for (i = 0; i < n_old_snapshots; i++){     …if (VirtualTransactionIdIsValid(old_snapshots[i]))VirtualXactLockTableWait(old_snapshots[i]);}…
} 

对于我的第一个测试程序,GetCurrentVirtualXIDs 函数执行后,n_old_snapshots 的值为0 ,
for (i = 0; i < n_old_snapshots; i++) 循环不会被执行,索引的生成不会被阻塞。

对我的第二个测试程序,GetCurrentVirtualXIDs 函数执行后,n_old_snapshots 的值为1, 
for (i = 0; i < n_old_snapshots; i++) 循环会被执行。
VirtualXactLockTableWait(old_snapshots[i]) 的执行,导致等待一个锁,所以索引生成被阻塞。

再往下分析:

VirtualTransactionId *
GetCurrentVirtualXIDs(TransactionId limitXmin, bool excludeXmin0,bool allDbs, int excludeVacuum,int *nvxids)
{VirtualTransactionId *vxids;ProcArrayStruct *arrayP = procArray;int            count = 0;int            index;/* allocate what's certainly enough result space */vxids = (VirtualTransactionId *)palloc(sizeof(VirtualTransactionId) * arrayP->maxProcs);LWLockAcquire(ProcArrayLock, LW_SHARED);
for (index = 0; index < arrayP->numProcs; index++){
volatile PGPROC *proc = arrayP->procs[index];if (proc == MyProc)continue;if (excludeVacuum & proc->vacuumFlags)continue;if (allDbs || proc->databaseId == MyDatabaseId){/* Fetch xmin just once - might change on us */TransactionId pxmin = proc->xmin;if (excludeXmin0 && !TransactionIdIsValid(pxmin))continue;
/** InvalidTransactionId precedes all other XIDs, so a proc that* hasn't set xmin yet will not be rejected by this test.*/if (!TransactionIdIsValid(limitXmin) ||TransactionIdPrecedesOrEquals(pxmin, limitXmin)){
                VirtualTransactionId vxid;GET_VXID_FROM_PGPROC(vxid, *proc);if (VirtualTransactionIdIsValid(vxid))vxids[count++] = vxid;}}}LWLockRelease(ProcArrayLock);*nvxids = count;return vxids;
}

对于我的第一个程序,测试结果显示:pxmin 为零,TransactionIdIsValid(pxmin) 为假。所以如下代码导致跳过循环一次。

if (excludeXmin0 && !TransactionIdIsValid(pxmin))continue;

没有机会执行 vxids[count++]=vxid 这一行。

那么pxmin是如何来的?

看这句: TransactionId pxmin = proc->xmin;

而xmin的含义是:当我们执行程序中对数据进行增删改的时候,会将当前transaction id 赋予给 xmin。

写记录的时候,把这个xmin写入该行记录头。

如此,每个进程看来,它只关心 xmin 小于自己的transaction id的。PostgreSQL用这种方式来保证MVCC。

但此处,proc->xmin为零是很不合理的。

此时,

if (allDbs || proc->databaseId == MyDatabaseId) 里的:
if (excludeXmin0 && !TransactionIdIsValid(pxmin))就会成立,所以会直接continue调回循环开始处,也就没有机会去
vxids[count++] = vxid;

在我的第二个程序里,proc->xmin根本就不为零。故此说,这是一个bug。

另外的佐证:对我的三个测试,运行下列SQL文:

pgsql=# select l.pid, l.mode, sa.procpid, sa.current_query
from pg_locks l
inner join pg_stat_activity saon l.pid = sa.procpid
where l.mode like '%xclusive%';

一开始在pg_sleep(100)执行期间,可以看到:

pgsql=# select l.pid, l.mode, sa.procpid, sa.current_query
from pg_locks l
inner join pg_stat_activity saon l.pid = sa.procpid
where l.mode like '%xclusive%';pid  |     mode      | procpid |                   current_query
------+---------------+---------+----------------------------------------------------5356 | ExclusiveLock |    5356 | select l.pid, l.mode, sa.procpid, sa.current_query+|               |         | from pg_locks l                                   +|               |         | inner join pg_stat_activity sa                    +|               |         |         on l.pid = sa.procpid                     +|               |         | where l.mode like '%xclusive%';5517 | ExclusiveLock |    5517 | select pg_sleep(100);
(2 rows)

我开另外的终端,执行 "create index concurrently"的时候,再看:

pgsql=# select l.pid, l.mode, sa.procpid, sa.current_query
from pg_locks l
inner join pg_stat_activity saon l.pid = sa.procpid
where l.mode like '%xclusive%';pid  |           mode           | procpid |                      current_query
------+--------------------------+---------+----------------------------------------------------------5356 | ExclusiveLock            |    5356 | select l.pid, l.mode, sa.procpid, sa.current_query      +|                          |         | from pg_locks l                                         +|                          |         | inner join pg_stat_activity sa                          +|                          |         |         on l.pid = sa.procpid                           +|                          |         | where l.mode like '%xclusive%';5517 | ExclusiveLock            |    5517 | select pg_sleep(100);5527 | ExclusiveLock            |    5527 | create index concurrently idx_tab02_id_new on tab02(id);5527 | RowExclusiveLock         |    5527 | create index concurrently idx_tab02_id_new on tab02(id);5527 | ShareUpdateExclusiveLock |    5527 | create index concurrently idx_tab02_id_new on tab02(id);
(5 rows)

等到 pg_sleep执行完毕的时候:

pgsql=# select l.pid, l.mode, sa.procpid, sa.current_query
from pg_locks l
inner join pg_stat_activity saon l.pid = sa.procpid
where l.mode like '%xclusive%';pid  |           mode           | procpid |                      current_query
------+--------------------------+---------+----------------------------------------------------------5356 | ExclusiveLock            |    5356 | select l.pid, l.mode, sa.procpid, sa.current_query      +|                          |         | from pg_locks l                                         +|                          |         | inner join pg_stat_activity sa                          +|                          |         |         on l.pid = sa.procpid                           +|                          |         | where l.mode like '%xclusive%';5517 | ExclusiveLock            |    5517 | <IDLE> in transaction5527 | ExclusiveLock            |    5527 | create index concurrently idx_tab02_id_new on tab02(id);5527 | RowExclusiveLock         |    5527 | create index concurrently idx_tab02_id_new on tab02(id);5527 | ShareUpdateExclusiveLock |    5527 | create index concurrently idx_tab02_id_new on tab02(id);
(5 rows)

转载于:https://www.cnblogs.com/gaojian/p/3154211.html

PostgreSQL的 create index concurrently相关推荐

  1. 从Oracle到PostgreSQL:Storage Index 特性 vs BRIN 索引

    墨墨导读:本文介绍 PostgreSQL 中的BRIN索引.为什么引人注意专门单独讲述这个性能?因为这就是活脱脱的 Oracle Exadata 中的 Storage Index 和 Oracle D ...

  2. oracle 创建索引 CREATE INDEX

    CREATE INDEX -- 定义一个新索引 Synopsis CREATE [ UNIQUE ] INDEX name ON table [ USING method ]( { column | ...

  3. PostgreSQL DBA(49) - Index(SP-GiST)

    本节简单介绍了PostgreSQL中的SP-GiST索引,包括SP-GiST索引的基础知识和结构等. 简介 上一节介绍了GiST索引,SP-GiST基于GiST,其中SP的意思是 space part ...

  4. sql 高级 (五)(create index(创建索引) drop)

    2019独角兽企业重金招聘Python工程师标准>>> SQL create index 语句 create  index语句用于在表中创建索引. 在不读取整个表的情况下,索引使数据 ...

  5. MySQL创建索引(CREATE INDEX)

    索引的建立对于 MySQL 数据库的高效运行是很重要的,索引可以大大提升 MySQL 的检索速度. 基本语法 MySQL 提供了三种创建索引的方法: 1) 使用 CREATE INDEX 语句 可以使 ...

  6. 9、创建索引(CREATE INDEX)

    创建索引是指在某个表的一列或多列上建立一个索引,可以提高对表的访问速度.创建索引对 MySQL 数据库的高效运行来说是很重要的. 基本语法 MySQL 提供了三种创建索引的方法: 1) 使用 CREA ...

  7. MySQL使用CREATE INDEX创建索引

    如果,在一个已经存在的表上创建索引,可以使用CREATE INDEX语句,语法格式 CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX 索引名 ON 表名(字段名[(长度)] ...

  8. Kibana 创建索引 POST 403 (forbidden) on create index

    一.问题描述: Kibana创建索引:kibana > management > index patterns > create index pattern 索引名称: mercha ...

  9. 索引 CREATE INDEX

    CREATE INDEX 语句用于在表中创建索引. 在不读取整个表的情况下,索引使数据库应用程序可以更快地查找数据. 索引 您可以在表中创建索引,以便更加快速高效地查询数据. 用户无法看到索引,它们只 ...

最新文章

  1. TestInside640-801 v11(神州testv11)题库视频讲解(全部上传完毕)
  2. EL之GB(GBC):利用GB对多分类问题进行建模(分层抽样+调1参)并评估
  3. 请问在FOB条件下,订舱的具体流程是怎样的?
  4. python扩展库简介_python非官方扩展库
  5. 树莓派内核开发准备(内核源码获取、启动过程、源码目录树)
  6. 如何平衡存储系统的一致性和可用性?
  7. 文思创新面试总结(1)
  8. saltstack2 grains模块
  9. C#.Net工作笔记015---C#中Decimal类型四舍五入_小数点截位
  10. [转载]github在线更改mysql表结构工具gh-ost
  11. Quick_Cocos2d_x V3.3 Protobuf Android
  12. Qt之布局管理——分割窗口
  13. 枯燥的计算机组成原理课.....!!! 看来只能自己看书消化了...!!!
  14. 用计算机写短文教学反思,《阿西莫夫短文两篇》教学反思6则
  15. TIM ETR 配置
  16. R485集线器定协议有多少种能否抗干扰?
  17. Affine-Transformation Parameters Regression for Face Alignment
  18. 微众银行张开翔: FISCO BCOS - 开放的区块链实践之道 | 11月25日【区块链技术与实践】论坛...
  19. 使用python来实现零售行业的数据分析 : EDA+TF-IDF+t-SNE+K-Means+LDA(干货)
  20. mssql 中msdb 权限说明

热门文章

  1. linus开启snmp_【snmp】Linux开启snmp及查询
  2. python模拟手机充值场景_appium+Python实例(二)模拟新建场景表(辅助测试执行)...
  3. Windows 安装 Redis
  4. 十大WordPress安全设置技巧
  5. Linux学习资源汇总
  6. sc925 文档服务器,dell服务器磁盘阵列配置手册.docx
  7. Java概述标识符 、常量、关键字、数据类型
  8. Delphi 的信息框相关函数
  9. Python(37)_字典嵌套
  10. 计算机网络基础-目录