首页 技术 正文
技术 2022年11月10日
0 收藏 734 点赞 4,890 浏览 4120 个字

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

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
{
NodeTagtype;RelOptKindreloptkind;/* all relations included in this RelOptInfo */
Relidsrelids;/* set of base relids (rangetable indexes) */
.
.
.
List *indexlist;/* list of IndexOptInfo */ <------- here
.
.
.
boolhas_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_attrlowest valid AttrNumber
*max_attrhighest valid AttrNumber
*indexlistlist of IndexOptInfos for relation's indexes
*serveridif it's a foreign table, the server OID
*fdwroutineif it's a foreign table, the FDW function pointers
*pagesnumber of pages
*tuplesnumber 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~

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,492
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,907
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,740
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,494
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:8,132
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:5,295