将Postgres索引添加到一个表会锁定另一个表

时间:2014-05-19 08:51:31

标签: sql postgresql amazon-web-services

我照看一个Postgres 9.3.3(Amazon RDS实例:db.m3.2xlarge),它是记录传入统计信息并根据这些数据提供报告的系统的后端 - 是的,来自同一个数据库节点。

性能通常非常好,但是在表R上添加额外索引以提高报告性能后,日志记录性能会崩溃,因为INSERTUPDATE位于不同的表L上根据{{​​1}},虽然没有报告死锁,但是日志记录过程立即开始锁定 - 看起来彼此相似。立即,所有可用连接(根据pg_locks)以相同方式锁定,DB CPU迅速上升至100%。记录器的负载均衡器使其所有节点无法使用,但由于pg_stat_activityINSERT s拒绝完成或超时,所有连接都保持锁定状态。

请注意,仅在使用期间,这不是索引创建期间的问题。这也不是负载问题:将日志记录限制90%并重新启动系统再次立即将其锁定。没有任何报道同时发生。

立即删除R索引 会释放所有L锁。

我用:

创建索引
UPDATE

列是:

CREATE INDEX idxForGroup ON R (group,article_id,month);

已经有一个复合主键,其中上面只是一个子集:

'group' type: VARCHAR(64) defaultValue: "" nullable: false
'month' type: TIMESTAMP nullable: false
'article_id' type: BIGINT defaultValue: 0 nullable: false

我应该补充说R和L之间存在关系:触发器根据对L的更新来更新报告表R:

customer_resource_id (a FK), subtype (a VARCHAR), group, article_id, month

我接受添加任何索引会产生一个小的(微秒?)成本/负载,但R上已有索引,所以我不明白一个小小的' R上的额外索引可能会产生如此巨大的影响,导致L的锁定。


更新

如果我调查被锁定的L个查询:

CREATE TRIGGER on_event_report AFTER INSERT OR UPDATE ON L FOR EACH ROW EXECUTE PROCEDURE resource_event_trigger();

所以,你认为更新R的触发器必定是问题 - 然而当我EXPLAIN (analyze,buffers) update L set count=count+1 where customer_resource_id=911657 and item_type_id='type' and event_subtype='subtype' and reporting_date='2014-04-13 00:00:00' AND group=''; Update on L (cost=0.57..20.18 rows=5 width=49) (actual time=70.968..70.968 rows=0 loops=1) Buffers: shared hit=170 read=16 dirtied=15 -> Index Scan using L_pkey on L (cost=0.57..20.18 rows=5 width=49) (actual time=0.067..0.525 rows=19 loops=1) Index Cond: ((customer_resource_id = 911657) AND ((group)::text = ''::text) AND ((item_type_id)::text = 'type'::text) AND ((event_subtype)::text = 'subtype'::text) AND (article_id = 0)) Buffers: shared hit=24 Trigger on_L: time=11626.219 calls=19 <--- Total runtime: 11697.285 ms 触发器查询时,它们都检查得很好:索引命中,没有扫描等等。


更新2:

不确定这是否真的是一个锁定问题,或者仅仅是性能大幅下降,但这里的EXPLAIN存在索引:

pg_locks

删除索引,并在几秒钟内完成:

SELECT mode,COUNT(*) FROM pg_locks GROUP BY mode;

       mode       | granted | count 
------------------+---------+-------
 AccessShareLock  | t       | 24715
 ExclusiveLock    | t       |  1504
 ExclusiveLock    | f       |   138
 RowExclusiveLock | t       |  5901
 RowShareLock     | t       |   185
 ShareLock        | f       |    95

更新3:

这是记录表L上更新报告表R的触发源:

      mode       | count 
-----------------+-------
 ExclusiveLock   |     3
 AccessShareLock |    31

它很长,但很简单。当单独解析时,所有单个查询都使用主键/索引,使用少量缓冲区等。


更新4:

如果我检查创建的索引,我会注意到:

SELECT tablename,attname,n_distinct,pg_stats的相关性,其中tablename =&#39; R&#39; AND attname IN(&#39; group&#39;,&#39; article_id&#39;,&#39; date&#39;,&#39; customer_resource_id&#39;,&#39; subtype&#39;)订购BY attname;

CREATE OR REPLACE FUNCTION resource_event_trigger()
RETURNS TRIGGER AS $$
DECLARE
cre_row R%ROWTYPE;
delta INTEGER;
BEGIN
SELECT * INTO cre_row FROM R cre WHERE cre.customer_resource_id = NEW.customer_resource_id AND cre.group = NEW.group_id AND cre.subtype = NEW.event_subtype AND cre.date = date_trunc('month', NEW.date) AND cre.article_id = NEW.article_id;

IF cre_row IS null THEN
INSERT INTO R (customer_resource_id, group, subtype, article_id, date) VALUES (NEW.customer_resource_id, NEW.group_id, NEW.event_subtype, NEW.article_id, date_trunc('month', NEW.date));
END IF;

IF TG_OP = 'INSERT' THEN
delta = NEW.event_count;
ELSE
delta = NEW.event_count - OLD.event_count;
END IF;

CASE
WHEN NEW.item_type_id = 'typeA' THEN
UPDATE R SET count_A = count_A + delta WHERE customer_resource_id = NEW.customer_resource_id AND group = NEW.group_id AND subtype = NEW.event_subtype AND article_id = NEW.article_id AND date = date_trunc('month', NEW.date);
[...]
END CASE;

RETURN NEW;
END;
$$
LANGUAGE plpgsql;

......看起来似乎有道理。如果我看基数,我会得到:

 tablename |       attname        | n_distinct | correlation 
-----------+----------------------+------------+-------------
 R         | article_id           |      25886 |    0.756468
 R         | group                |        165 |    0.227023
 R         | customer_resource_id |  -0.304469 |    0.729134
 R         | date                 |         53 |    0.943593
 R         | subtype              |          2 |    0.657429

PK和新添加的索引都具有与行数几乎相同的值,再次应该没有...

1 个答案:

答案 0 :(得分:0)

虽然我不能确切地说出你的问题可能是什么,但它显然似乎取决于这个新指数。如果我仔细研究一下这些东西肯定会更容易,但我会在黑暗中拍几张照片。

索引和postgres的表现可能是一个很大的主题,但从根本上说,我认为有些事情可能是错误的,你应该检查:

当更改/添加表上的索引时,查询优化器(在查询开始之前触发毫秒,并评估如何最好地执行该查询)当然是以不同的方式查看表。它看到了:&#34;嘿,这里有一个新的索引,也许这比我使用的旧索引更好&#34;在某些情况下,查询优化器可能是错误的。

因此,优化器不是使用过去正常工作的索引,而是开始执行全表扫描或者是愚蠢的事情。这是一种极端情况,但当然可以发生。

另一件事是你可能正在处理很多&#34;死行&#34;。无论何时更新表,它都会创建一个&#34;死行&#34;并插入一个新的(更新的行)。 &#34;死行&#34;并没有真正去任何地方,有时会使你的桌子膨胀,Postgres查询优化器可能会有点乱,试图理解这一点。

有一次,我有一张像这样疯狂的桌子,我不能,因为我的生活,理解为什么它会如此缓慢。然后我查看了表统计信息,看到了数十万个死行。我在黑暗中拍摄了一张照片,然后放弃并重新创建了表格(虽然,这需要在实时数据库上进行一些工作)并且神奇地在此之后一切正常工作(表格结构或表格中的数据没有改变 - 除了在删除该表的实例时完全释放死行时。(

虽然这有点极端,但我会立即做的是:

a)在桌子上运行真空吸尘器,这样可以很好地慢慢移动,并且可以使用#34;死行&#34;走开

b)然后在桌子上进行分析。这会重新设置表汇总统计信息,这是优化程序获取大量数据以确定查询表的最佳方式。