我照看一个Postgres 9.3.3(Amazon RDS实例:db.m3.2xlarge),它是记录传入统计信息并根据这些数据提供报告的系统的后端 - 是的,来自同一个数据库节点。
性能通常非常好,但是在表R上添加额外索引以提高报告性能后,日志记录性能会崩溃,因为INSERT
和UPDATE
位于不同的表L上根据{{1}},虽然没有报告死锁,但是日志记录过程立即开始锁定 - 看起来彼此相似。立即,所有可用连接(根据pg_locks
)以相同方式锁定,DB CPU迅速上升至100%。记录器的负载均衡器使其所有节点无法使用,但由于pg_stat_activity
和INSERT
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和新添加的索引都具有与行数几乎相同的值,再次应该没有...
答案 0 :(得分:0)
虽然我不能确切地说出你的问题可能是什么,但它显然似乎取决于这个新指数。如果我仔细研究一下这些东西肯定会更容易,但我会在黑暗中拍几张照片。
索引和postgres的表现可能是一个很大的主题,但从根本上说,我认为有些事情可能是错误的,你应该检查:
当更改/添加表上的索引时,查询优化器(在查询开始之前触发毫秒,并评估如何最好地执行该查询)当然是以不同的方式查看表。它看到了:&#34;嘿,这里有一个新的索引,也许这比我使用的旧索引更好&#34;在某些情况下,查询优化器可能是错误的。
因此,优化器不是使用过去正常工作的索引,而是开始执行全表扫描或者是愚蠢的事情。这是一种极端情况,但当然可以发生。
另一件事是你可能正在处理很多&#34;死行&#34;。无论何时更新表,它都会创建一个&#34;死行&#34;并插入一个新的(更新的行)。 &#34;死行&#34;并没有真正去任何地方,有时会使你的桌子膨胀,Postgres查询优化器可能会有点乱,试图理解这一点。
有一次,我有一张像这样疯狂的桌子,我不能,因为我的生活,理解为什么它会如此缓慢。然后我查看了表统计信息,看到了数十万个死行。我在黑暗中拍摄了一张照片,然后放弃并重新创建了表格(虽然,这需要在实时数据库上进行一些工作)并且神奇地在此之后一切正常工作(表格结构或表格中的数据没有改变 - 除了在删除该表的实例时完全释放死行时。(
)虽然这有点极端,但我会立即做的是:
a)在桌子上运行真空吸尘器,这样可以很好地慢慢移动,并且可以使用#34;死行&#34;走开
b)然后在桌子上进行分析。这会重新设置表汇总统计信息,这是优化程序获取大量数据以确定查询表的最佳方式。