怎么提升PostgreSQL性能
本篇内容介绍了“怎么提升PostgreSQL性能”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
使用Postgres监测慢的Postgres查询
在这周早些时候,一个用于我们的图形编辑器上的小表(10GB,1500万行)的主键查询,在我们的一个(多个)数据库上发生来大的查询性能问题。
99.9%到查询都是非常迅速流畅的,但是在一些使用大量的枚举值的地方,这些查询会需要20秒。花费如此多到时间在数据库上,意味着使用者必须在浏览器面前等待图形编辑器的响应。很明显只因为这0.01%就会造成很不好到影响。
查询和查询计划
下面是这个出问题的查询
SELECTc.key,c.x_key,c.tags,x.nameFROMcontextcJOINxONc.x_key=x.keyWHEREc.key=ANY(ARRAY[15368196,--11,000otherkeys--)])ANDc.x_key=1ANDc.tags@>ARRAY[E'blah'];
表X有几千行数据,表C有1500万条数据。两张表的主键值“key”都有适当的索引。这是一个非常简单清晰的主键查询。但有趣的是,当增加主键内容的数量,如在主键有11,000个值的时候,通过在查询语句上加上 EXPLAIN (ANALYZE, BUFFERS)我们得到如下的查询计划。
NestedLoop(cost=6923.33..11770.59rows=1width=362)(actualtime=17128.188..22109.283rows=10858loops=1)Buffers:sharedhit=83494->BitmapHeapScanoncontextc(cost=6923.33..11762.31rows=1width=329)(actualtime=17128.121..22031.783rows=10858loops=1)RecheckCond:((tags@>'{blah}'::text[])AND(x_key=1))Filter:(key=ANY('{15368196,(alotmorekeyshere)}'::integer[]))Buffers:sharedhit=50919->BitmapAnd(cost=6923.33..6923.33rows=269width=0)(actualtime=132.910..132.910rows=0loops=1)Buffers:sharedhit=1342->BitmapIndexScanoncontext_tags_idx(cost=0.00..1149.61rows=15891width=0)(actualtime=64.614..64.614rows=264777loops=1)IndexCond:(tags@>'{blah}'::text[])Buffers:sharedhit=401->BitmapIndexScanoncontext_x_id_source_type_id_idx(cost=0.00..5773.47rows=268667width=0)(actualtime=54.648..54.648rows=267659loops=1)IndexCond:(x_id=1)Buffers:sharedhit=941->IndexScanusingx_pkeyonx(cost=0.00..8.27rows=1width=37)(actualtime=0.003..0.004rows=1loops=10858)IndexCond:(x.key=1)Buffers:sharedhit=32575Totalruntime:22117.417ms
在结果的最底部你可以看到,这个查询总共花费22秒。我们可以非常直观的通过下面的CPU使用率图观察到这22秒的花费。大部分的时间花费在 Postgres和 OS 上, 只有很少部分用于I/O .
在最低的层面,这些查询看起来就像是这些CPU利用率的峰值。CPU图很少有用,但是在这种条件下它证实了关键的一点:数据库并没有等待磁盘去读取数据。它在做一些排序,哈希以及行比较之类的事情。
第二个有趣的度量,就是距离这些峰值很近的轨迹,它们是由Postgres“取得”的行数(本例中没有返回,就看看再忽略掉吧)。
显然有些动作在规则的有条不紊的浏览过许多行:我们的查询。
Postgres 的问题所在:位图扫描
下面是行匹配的查询计划
Buffers:sharedhit=83494->BitmapHeapScanoncontextc(cost=6923.33..11762.31rows=1width=329)(actualtime=17128.121..22031.783rows=10858loops=1)RecheckCond:((tags@>'{blah}'::text[])AND(x_key=1))Filter:(key=ANY('{15368196,(alotmorekeyshere)}'::integer[]))Buffers:sharedhit=50919
Postgres 使用位图扫描表C. 当主键的数据量小的时候,它能有效的使用索引在内存里建立位图。如果位图太大,最优查询计划就改变查询方式了。在我们这个查询中,因为主键包含的数据量很大,所以查询就使用最优(系统自己判断的)的方式去检索查询候选行,并且立即查询所有和主键匹配的数据。就是这些¨放入内存¨和¨立即查询¨花费太多的时间(查询计划中的Recheck Cond)。
幸好只有30%的数据被导入到内存中,所以还不至于像从硬盘里读取那么坏。但它仍然对性能有非常明显的影响。记住,查询是非常简单的。这是一个主键查询所以没有很多明了的方式来确定它有没有戏剧性的重新架构数据库或应用程序。PGSQL-Performance mailing list给予了我们很大的帮助.
解决方案
这是我们喜欢开源和喜欢帮助用户的另外一个原因。Tom Lane是开源代码作者中最盛产的程序员之一,他建议我们做如下尝试:
SELECTc.key,c.x_key,c.tags,x.nameFROMcontextcJOINxONc.x_key=x.keyWHEREc.key=ANY(VALUES(15368196),--11,000otherkeys--)ANDc.x_key=1ANDc.tags@>ARRAY[E'blah'];
把ARRAY改成VALUES,你能指出他们的不同点吗?
我们使用ARRAY[...]列举出所有的关键字以用来查询,但是这却欺骗了查询优化器。然而Values(...)却能够让优化器充分使用关键字索引。仅仅是一行代码的改变,并且没有产生任何语义的改变。
下面是新查询语句的写法,差别就在于第三和第十四行。
NestedLoop(cost=168.22..2116.29rows=148width=362)(actualtime=22.134..256.531rows=10858loops=1)Buffers:sharedhit=44967->IndexScanusingx_pkeyonx(cost=0.00..8.27rows=1width=37)(actualtime=0.071..0.073rows=1loops=1)IndexCond:(id=1)Buffers:sharedhit=4->NestedLoop(cost=168.22..2106.54rows=148width=329)(actualtime=22.060..242.406rows=10858loops=1)Buffers:sharedhit=44963->HashAggregate(cost=168.22..170.22rows=200width=4)(actualtime=21.529..32.820rows=11215loops=1)->ValuesScanon"*VALUES*"(cost=0.00..140.19rows=11215width=4)(actualtime=0.005..9.527rows=11215loops=1)->IndexScanusingcontext_pkeyoncontextc(cost=0.00..9.67rows=1width=329)(actualtime=0.015..0.016rows=1loops=11215)IndexCond:(c.key="*VALUES*".column1)Filter:((c.tags@>'{blah}'::text[])AND(c.x_id=1))Buffers:sharedhit=44963Totalruntime:263.639ms
查询时间从22000ms下降到200ms,仅仅一行代码的改变效率就提高了100倍。
在生产中使用的新查询
即将发布的一段代码:
它使数据库看起来更美观轻松.
“怎么提升PostgreSQL性能”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。