最近客户在使用我司开发的数据库时,报告了如下问题(也不能算是问题,就是疑惑吧),环境如下:

OS : Red Hat Enterprise Linux Server release 6.7 (Santiago)
Kernel : 2.6.32-573.el6.x86_64
PostgreSQL : PostgreSQL 9.6.2

执行准备工作:

postgres=# create table test (id int, data text);
CREATE TABLE
postgres=# insert into test select a, 'Test data No ' || a from
generate_series(1,10000000) as a;
INSERT 0 10000000
postgres=# create index on test (id);
CREATE INDEX
postgres=# vacuum analyze verbose test;
......

然后,开启一个事务,在该事务中查询test表:

postgres=# begin ;
BEGIN
postgres=# select data from test where data = 'Test data No 1';
data
----------------
Test data No 1
(1 row)

在开另一个psql console,查询锁的情况:

postgres=# select c.oid, c.relname, l.locktype, l.pid, l.mode ,a.query
from (pg_class as c inner join pg_locks as l on (c.oid = l.relation))
inner join pg_stat_activity as a on (l.pid = a.pid) where relname like 'test%'
order by pid;
oid | relname | locktype | pid | mode |
query
-------+-------------+----------+-------+-----------------+----------
---
-------+-------------+----------+-------+-----------------+----------
---
-------+-------------+----------+-------+-----------------+----------
---
-------+-------------+----------+-------+-----------------+----------
---
-------+-------------+----------+-------+-----------------+--
19412 | test_id_idx | relation | 27612 | AccessShareLock | select data
from test where data = 'Test data No 1';
19405 | test | relation | 27612 | AccessShareLock | select
data from test where data = 'Test data No 1';
(2 rows)

发现在执行select期间对表上的所有的索引都加上了AccessShareLock锁,但是查询并没有走索引。这让客户非常奇怪。

没办法,带着这个问题调查了一番。大概有了以下的两点粗浅的认识和理解。

1.在执行一条select文时,数据库后端会对select文进行查询分析,查询重写,查询规划和查询执行四个阶段。在查询规划阶段,需要生成一个RelOptInfo结构存储优化的查询路径,该结构中存储了表上的索引信息。此处调用get_relation_info函数打开并锁定表上所有的索引,并将索引的信息写入到RelOptInfo结构体中。

具体到代码里,我们可以看到:

typedef struct RelOptInfo
{
NodeTag type; RelOptKind reloptkind; /* all relations included in this RelOptInfo */
Relids relids; /* set of base relids (rangetable indexes) */
.
.
.
List *indexlist; /* list of IndexOptInfo */ <------- here
.
.
.
bool has_eclass_joins; /* T means joininfo is incomplete */
} RelOptInfo;

这里indexlist就是查询的表上的所有的index的列表;

我们再看看get_relation_info函数,它获取该表上的“catalog information”,详情可以看看这段注释:


##############################################################
src/backend/optimizer/util/plancat.c: /*
* get_relation_info -
* Retrieves catalog information for a given relation.
*
* Given the Oid of the relation, return the following info into fields
* of the RelOptInfo struct:
*
* min_attr lowest valid AttrNumber
* max_attr highest valid AttrNumber
* indexlist list of IndexOptInfos for relation's indexes
* serverid if it's a foreign table, the server OID
* fdwroutine if it's a foreign table, the FDW function pointers
* pages number of pages
* tuples number of tuples
*
* Also, initialize the attr_needed[] and attr_widths[] arrays. In most
* cases these are left as zeroes, but sometimes we need to compute attr
* widths here, and we may as well cache the results for costsize.c.
*
* If inhparent is true, all we need to do is set up the attr arrays:
* the RelOptInfo actually represents the appendrel formed by an inheritance
* tree, and so the parent rel's physical size and index information isn't
* important for it.
*/
void
get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
RelOptInfo *rel) ###################################################################

而由于该查询是放在一个事务中的,我们发现:

2.一个事务内申请的锁会在事务结束时调用函数LockReleaseAll统一释放,在事务结束之前,该事务仍然保持对锁的持有。

代码如下:

src/backend/storage/lmgr/lock.c:

/*
* LockRelease -- look up 'locktag' and release one 'lockmode' lock on it.
* Release a session lock if 'sessionLock' is true, else release a
* regular transaction lock.
*
* Side Effects: find any waiting processes that are now wakable,
* grant them their requested locks and awaken them.
* (We have to grant the lock here to avoid a race between
* the waking process and any new process to
* come along and request the lock.)
*/
bool
LockRelease(const LOCKTAG *locktag, LOCKMODE lockmode, bool sessionLock)

最后我们打开lock log,再模拟下客户现场。贴一下执行结果:

postgres=# begin;
BEGIN
postgres=# select data from test where data = 'Test data No 1';
LOG: LockAcquire: lock [12373,16386] AccessShareLock at character 18
STATEMENT: select data from test where data = 'Test data No 1';
LOG: LockAcquire: lock [12373,16392] AccessShareLock
STATEMENT: select data from test where data = 'Test data No 1';
LOG: LockAcquire: lock [12373,16386] AccessShareLock
STATEMENT: select data from test where data = 'Test data No 1';
data
----------------
Test data No 1
(1 row) postgres=# end;
LOG: LockReleaseAll: lockmethod=1
STATEMENT: end;
LOG: LockReleaseAll done
STATEMENT: end;

果然,锁是在事务最后释放的。

综合1,2两点,我们向客户解释了这个问题是PostgreSQL的式样,不是bug。

Over~

从一个实例谈谈postgresql索引锁的更多相关文章

  1. C#实现的内存分页机制的一个实例

    C#实现的内存分页机制的一个实例 //多页索引表管理类(全局主索引表管理类) public class MuliPageIndexFeatureClass : IDisposable { protec ...

  2. 6. 将单独表空间(File-Per-Table Tablespaces)复制到另一个实例

    6. 将单独表空间复制到另一个实例 本节介绍如何将单独表空间从一个MySQL实例复制 到另一个MySQL实例,也称为可传输表空间功能. 将InnoDB单独表空间复制到其他实例的原因有很多: - 在不对 ...

  3. Java程序只运行一个实例[转]

    如果希望你的Java程序只能存在一个实例,可以参考下面的用法. 原文链接:http://blog.csdn.net/yaerfeng/article/details/7264729 Java没有提供这 ...

  4. PostgreSQL索引介绍

    h1, h2, h3, h4, h5, h6, p, blockquote { margin: 5px; padding: 5; } body { font-family: "Helveti ...

  5. 事务(进程 ID 64)与另一个进程被死锁在 锁 资源上,并且已被选作死锁牺牲品。

    访问频率比较高的app接口,在后台写的异常日志会偶尔出现以下错误. 事务(进程 ID 64)与另一个进程被死锁在 锁 资源上,并且已被选作死锁牺牲品.请重新运行该事务 实所有的死锁最深层的原因就是一个 ...

  6. 从pg_hba.conf文件谈谈postgresql的连接认证

    最近一直在弄postgresql的东西,搭建postgresql数据库集群环境什么的.操作数据库少不得要从远程主机访问数据库环境,例如数据库管理员的远程管理数据库,远程的客户存取数据库文件. 而在po ...

  7. C# 最基本的涉及模式(单例模式) C#种死锁:事务(进程 ID 112)与另一个进程被死锁在 锁 | 通信缓冲区 资源上,并且已被选作死锁牺牲品。请重新运行该事务,解决方案: C#关闭应用程序时如何关闭子线程 C#中 ThreadStart和ParameterizedThreadStart区别

    C# 最基本的涉及模式(单例模式) //密封,保证不能继承 public sealed class Xiaohouye    { //私有的构造函数,保证外部不能实例化        private  ...

  8. Postgresql 解决锁表

    转载地址:https://blog.csdn.net/cicon/article/details/68068462##一.postgresql解决锁表--查询是否锁表了select oid from ...

  9. initdb - 创建一个新的 PostgreSQL数据库集群

    SYNOPSIS initdb [ option...] --pgdata | -D directory DESCRIPTION 描述 initdb 创建一个新的 PostgreSQL 数据库集群. ...

随机推荐

  1. jquery.i18n.properties前端国际化解决方案“填坑日记”

    但现在的情况是老的项目并没有使用这类架构.说起国际化,博主几年前就做过,在MVC里面实现国际化有通用的解决方案,主要就是通过资源文件的方式定义多语言.最初接到这个任务,并没有太多顾虑,毕竟这种东西有很 ...

  2. Spring3.2不支持jdk8

    解决方案: http://stackoverflow.com/questions/24128045/spring-context-initialization-failed-with-java-lan ...

  3. JSP入门 导出文件

    1.图片校验码 <img  src="captcha.jpg"  /> web.xml配置 <servlet>      <servlet-name& ...

  4. 从头编写 asp.net core 2.0 web api 基础框架 (2)

    上一篇是: http://www.cnblogs.com/cgzl/p/7637250.html Github源码地址是: https://github.com/solenovex/Building- ...

  5. python urllib、urlparse、urllib2、cookielib

    1.urllib模块 1.urllib.urlopen(url[,data[,proxies]]) 打开一个url的方法,返回一个文件对象,然后可以进行类似文件对象的操作.本例试着打开google i ...

  6. Sublime Text保存文件时自动去掉行末空格

    修改一个Sublime Text的用户配置,其中这个配置就是"保存文件时自动去掉每行结束后多余的空格",具体操作如下: 在Sublime Text菜单栏中找到preferences ...

  7. 【Vue】浅谈Vue(一):从模板语法数据绑定、指令到计算属性

    写在前面 今年前端届比较有意思,从大漠穷秋发表文章比较angular和vue,继而致歉vue作者.社区,从谷歌辞去Angular Developer PM in China一职并且呼吁大家停止各种无谓 ...

  8. jquery层次选择器:空格 > next + nextAll ~ siblings

    全栈工程师开发手册 (作者:栾鹏) jquery系列教程1-选择器全解 jquery层次选择器 jquery层次选择器,包括空格.>.next.+.nextAll.~.siblings等函数或表 ...

  9. 基于EF Core的Code First模式的DotNetCore快速开发框架

    前言 最近接了几个小单子,因为是小单子,项目规模都比较小,业务相对来说,也比较简单.所以在选择架构的时候,考虑到效率方面的因素,就采取了asp.net+entity framework中的code f ...

  10. 【特效】单选按钮和复选框的美化(只用css)

    表单的默认样式都是比较朴素的,实际页面中往往需要美化他们.这里先说说单选按钮和复选框,有了css3,这个问题就变的好解决了.利用input与label相关联,对label进行美化并使其覆盖掉原本的in ...