从一个实例谈谈postgresql索引锁
最近客户在使用我司开发的数据库时,报告了如下问题(也不能算是问题,就是疑惑吧),环境如下:
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索引锁的更多相关文章
- C#实现的内存分页机制的一个实例
		C#实现的内存分页机制的一个实例 //多页索引表管理类(全局主索引表管理类) public class MuliPageIndexFeatureClass : IDisposable { protec ... 
- 6. 将单独表空间(File-Per-Table Tablespaces)复制到另一个实例
		6. 将单独表空间复制到另一个实例 本节介绍如何将单独表空间从一个MySQL实例复制 到另一个MySQL实例,也称为可传输表空间功能. 将InnoDB单独表空间复制到其他实例的原因有很多: - 在不对 ... 
- Java程序只运行一个实例[转]
		如果希望你的Java程序只能存在一个实例,可以参考下面的用法. 原文链接:http://blog.csdn.net/yaerfeng/article/details/7264729 Java没有提供这 ... 
- PostgreSQL索引介绍
		h1, h2, h3, h4, h5, h6, p, blockquote { margin: 5px; padding: 5; } body { font-family: "Helveti ... 
- 事务(进程 ID 64)与另一个进程被死锁在 锁 资源上,并且已被选作死锁牺牲品。
		访问频率比较高的app接口,在后台写的异常日志会偶尔出现以下错误. 事务(进程 ID 64)与另一个进程被死锁在 锁 资源上,并且已被选作死锁牺牲品.请重新运行该事务 实所有的死锁最深层的原因就是一个 ... 
- 从pg_hba.conf文件谈谈postgresql的连接认证
		最近一直在弄postgresql的东西,搭建postgresql数据库集群环境什么的.操作数据库少不得要从远程主机访问数据库环境,例如数据库管理员的远程管理数据库,远程的客户存取数据库文件. 而在po ... 
- C# 最基本的涉及模式(单例模式)    C#种死锁:事务(进程 ID 112)与另一个进程被死锁在 锁 | 通信缓冲区 资源上,并且已被选作死锁牺牲品。请重新运行该事务,解决方案:    C#关闭应用程序时如何关闭子线程      C#中 ThreadStart和ParameterizedThreadStart区别
		C# 最基本的涉及模式(单例模式) //密封,保证不能继承 public sealed class Xiaohouye { //私有的构造函数,保证外部不能实例化 private ... 
- Postgresql 解决锁表
		转载地址:https://blog.csdn.net/cicon/article/details/68068462##一.postgresql解决锁表--查询是否锁表了select oid from ... 
- initdb - 创建一个新的 PostgreSQL数据库集群
		SYNOPSIS initdb [ option...] --pgdata | -D directory DESCRIPTION 描述 initdb 创建一个新的 PostgreSQL 数据库集群. ... 
随机推荐
- TETELaser Cutting System 不连续吹起的问题
			TETELaser Cutting System 不连续吹起的问题 :配置 加工参数-->机器参数-->信号灯和激光器报警:黄灯索引==EX14 红灯索引==EX15 绿灯索引= ... 
- JSP入门3 Servlet
			需要继承类HttpServlet 服务器在获得请求的时候会先根据jsp页面生成一个java文件,然后使用jdk的编译器将此文件编译,最后运行得到的class文件处理用户的请求返回响应.如果再有请求访问 ... 
- UI自动化测试(二)浏览器操作及对元素的定位方法(xpath定位和css定位详解)
			Selenium下的Webdriver工具支持FireFox(geckodriver). IE(InternetExplorerDriver).Chrome(ChromeDriver). Opera( ... 
- Twitter的分布式系统中ID生成方法——Snowflake
			Twitter-Snowflake算法产生的背景相当简单,为了满足Twitter每秒上万条消息的请求,每条消息都必须分配一条唯一的id,这些id还需要一些大致的顺序(方便客户端排序),并且在分布式系统 ... 
- JavaWeb(四)EL表达式
			前言 前面详细的说明了什么是JSP和它的一些元素,这篇给大家介绍一下的是EL表达式. 用EL表达式,能更好的使用JSP中的各种内置对象和作用域. 楼主作为大四狗马上要出去面试了,内心很紧张!!! 一. ... 
- 冒泡排序(Bubble Sort)
			冒泡排序的基本思路 冒泡排序是一种效率极低的排序,首先它需要知道数组的有效数据长度,再对数据第一个和第二个两两比较,按照比较规则进行交换,然后第二个数据和第三个数据进行比较,按照比较规则进行交换:第一 ... 
- ORALCE  PL/SQL学习笔记
			ORALCE PL/SQL学习笔记 详情见自己电脑的备份数据资料 
- Java web JavaScript DOM 编程
			 JavaScript DOM 编程 (1).DOM概述及分类 (2).DOM结构模型:XML DOM 和 HTML DOM 关系? (3).结点,结点树,结点属性与方法? 1.DOM是什么? d ... 
- dom4j之小小工具
			dom4j经常不用,方法忘了又记,故做出读取xml和把document写入xml的小小工具~~~ /** * 读取document和将document对象写入到xml的小工具 * 使用该类必须给出do ... 
- 读Zepto源码之Stack模块
			Stack 模块为 Zepto 添加了 addSelf 和 end 方法. 读 Zepto 源码系列文章已经放到了github上,欢迎star: reading-zepto 源码版本 本文阅读的源码为 ... 
