对于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 | 14
2 | 15
3 | 14
(3 rows) tester=> select * from tab02;
id | value
----+-------
1 | 100
2 | 200
3 | 300
(3 rows)
tester=>

我的测试方法:

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

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

"create index concurrently idx_tab02_id_new on tab02(id)"

结果是:

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

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

 { ECPGdo(__LINE__, , , "db_conn", , ECPGst_normal, "select count ( * ) from tab01 where cd = $1 ",

        ECPGt_char,(vcd),(long) + ,(long),( + )*sizeof(char),

        ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT,

        ECPGt_int,&(vCount),(long),(long),sizeof(int),

        ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);}

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

 { ECPGdo(__LINE__, , , "db_conn", , ECPGst_normal, "select count ( * ) from tab01 where cd = '$1' ",

        ECPGt_char,(vcd),(long) + ,(long),( + )*sizeof(char),

        ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT,

        ECPGt_int,&(vCount),(long),(long),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[+];
EXEC SQL END DECLARE SECTION;
EXEC SQL CONNECT TO 'tester@127.0.0.1:5432' AS db_conn
USER 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(); EXEC SQL DISCONNECT db_conn;
fprintf(stderr,"After disconnect,sleep for 600 seconds\n"); sleep();
return ;
} [root@server soft]#

第二个:

[root@server soft]# cat ./test02/test02.pc

int main()

{
EXEC SQL BEGIN DECLARE SECTION;
int vCount;
char vcd[+];
EXEC SQL END DECLARE SECTION; EXEC SQL CONNECT TO 'tester@127.0.0.1:5432' AS db_conn
USER tester IDENTIFIED BY tester; char *pCd="";
memset(vcd,'\0',);
strncpy(vcd, pCd,); 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(); EXEC SQL DISCONNECT db_conn; fprintf(stderr,"After disconnect,sleep for 600 seconds\n");
sleep(); return ;
} [root@server soft]#

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

我的第三个测试:

客户端1:

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

客户端2:

[postgres@server pgsql]$ ./bin/psql -d tester -U tester
psql (9.1.)
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 = '';
id | cd
----+----
1 | 14
3 | 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 = ; 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 = ;
int index; /* allocate what's certainly enough result space */
vxids = (VirtualTransactionId *)
palloc(sizeof(VirtualTransactionId) * arrayP->maxProcs); LWLockAcquire(ProcArrayLock, LW_SHARED);
for (index = ; 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 sa
on 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 sa
on 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 sa
on 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 sa
on 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 transaction
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)

PostgreSQL的 create index concurrently的更多相关文章

  1. CREATE INDEX - 定义一个新索引

    SYNOPSIS CREATE [ UNIQUE ] INDEX name ON table [ USING method ] ( { column | ( expression ) } [ opcl ...

  2. 在PostgreSQL中CREATE STATISTICS

    如果你用Postgres做了一些性能调优,你可能用过EXPLAIN.EXPLAIN向你展示了PostgreSQL计划器为所提供的语句生成的执行计划,它显示了语句所引用的表如何被扫描(使用顺序扫描.索引 ...

  3. 如何使用CREATE INDEX语句对表增加索引?

    创建和删除索引索引的创建可以在CREATE TABLE语句中进行,也可以单独用CREATE INDEX或ALTER TABLE来给表增加索引.删除索引可以利用ALTER TABLE或DROP INDE ...

  4. CREATE INDEX SELECT COUNT(*)

    CREATE INDEX windex_countrycode ON sales_rank (countrycode); CREATE INDEX windex_grab_amz_date ON sa ...

  5. linux之SQL语句简明教程---CREATE INDEX

    索引 (Index) 可以帮助我们从表格中快速地找到需要的资料.举例来说,假设我们要在一本园艺书中找如何种植青椒的讯息.若这本书没有索引的话,那我们是必须要从头开始读,直到我们找到有关种直青椒的地方为 ...

  6. SQL CREATE INDEX 语句

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

  7. mysql 索引查询 、创建 create index 与 add index 的区别

    1.索引查询 ------TABLE_SCHEMA  库名:TABLE  表名 ------AND UPPER(INDEX_NAME) != 'PRIMARY'  只查询索引,不需要主键 SELECT ...

  8. How MySQL Uses Indexes CREATE INDEX SELECT COUNT(*)

    MySQL :: MySQL 5.7 Reference Manual :: 9.3.1 How MySQL Uses Indexeshttps://dev.mysql.com/doc/refman/ ...

  9. Create Index语句的Include作用

    在 SQL Server 2005 中,可以通过将非键列添加到非聚集索引的叶级别来扩展非聚集索引的功能.通过包含非键列,可以创建覆盖更多查询的非聚集索引.这是因为非键列具有下列优点: 它们可以是不允许 ...

随机推荐

  1. android开发无障碍app

    最近做一些为盲人提供服务的APP,还是挺有感触的,感谢手机和互联网的普及,他们的生活比以前丰富了很多. 通过读屏软件,盲人可以操作手机,上网浏览信息.读屏软件的工作原理很简单,就是读出屏幕上按钮.文本 ...

  2. Android开源滤镜 仿instagram

    前段时间做一个项目的时候发现一个不错的滤镜库,是仿Instagram效果的,能够实现Lomo在内的十几种滤镜效果,git地址是: https://github.com/beartung/insta-f ...

  3. JavaScript DOM高级程序设计 2.4-try{}catch{}--我要坚持到底!

    先看一段有异常的语句 var sound = 'Roar!'; function myOrneryBeast() { this.style.color='green';//window没有style属 ...

  4. 安装安装.net framework过程中出现的问题

    1.安装Microsoft..net framework2.0 SP2 出现 必须使用控制面板中的打开或关闭windows功能,安装或配置.net framework2.0 SP2 原因:可以打开控制 ...

  5. Wince6 RIL层移植

    RIL移植: 因为不同的模组,支持的AT命令有所不同,或是格式不一样,还有就是返回不一样,我们有必要对不同的模组进行RIL驱动移植. 在Response.cpp,Msg.cpp等 文件中ParseXX ...

  6. Android开发UI之开源项目第一篇——个性化控件(View)篇

    原文:http://blog.csdn.net/java886o/article/details/24355907 本文为那些不错的Android开源项目第一篇——个性化控件(View)篇,主要介绍A ...

  7. Ubuntu设置中文

    Ubuntu设置中文:需要联网下载中文包,不然无法设置中文系统. 进去系统后再右上角有个齿轮图标点击,找到系统设置(System Settings)点击弹出一个界面,找到Language Suppor ...

  8. Java [Leetcode 39]Combination Sum

    题目描述: Given a set of candidate numbers (C) and a target number (T), find all unique combinations in  ...

  9. ORACLE DATAGURARD 折腾记二

    前文再续,书接上一回,这次折腾Data Guard的一个重要目的是利用switchover实现机器的升级,怎么switchover呢?按照我的理解,Data Guard的角色切换是这样一个过程: (1 ...

  10. 彩色网页变黑白色CSS代码变黑白色调!

    <style> html { -webkit-filter: grayscale(%); -moz-filter: grayscale(%); -ms-filter: grayscale( ...