PostgreSQL 查询涉及分区表过多导致的性能问题 - 性能诊断与优化(大量BIND, spin lock, SLEEP进程)

摘要: 标签 PostgreSQL , 分区表 , bind , spin lock , 性能分析 , sleep 进程 , CPU空转 , cache 背景 实际上我写过很多文档,关于分区表的优化: 《PostgreSQL 商用版本EPAS(阿里云ppas) - 分区表性能优化 (堪比pg_pathman)》 《PostgreSQL 传统 hash 分区方法和性能》 《PostgreSQL 10 内置分区 vs pg_pathman perf profiling》 实际上native分区表的性能问题主要还是在于分区表过多的时候,执行计划需要耗时很久。

点此查看原文:https://yq.aliyun.com/articles/405176?spm=a2c4e.11153959.teamhomeleft.44.8WKxt7

实际上native分区表的性能问题主要还是在于分区表过多的时候,执行计划需要耗时很久。

因此有了

1、PPAS的edb_enable_pruning参数,可以在生成执行计划前,使用简单SQL的话,直接过滤到目标分区,从而不需要的分区不需要进入执行计划的环节。

2、pg_pathman则支持的更全面,除了简单SQL,复杂的表达式,immutable都可以进行过滤,过滤到目标分区,从而不需要的分区不需要进入执行计划的环节。

因分区表过多引发的问题通常出现在OLTP系统(主要是OLTP系统的并发高,更容易把这种小问题放大),本来一次请求只需要1毫秒的,但是执行计划可能需要上百毫秒,也就是说执行耗时变成了小头,而执行计划(SPIN LOCK)变成了大头。

下面这个例子也是OLTP系统相关的,有具体的原因分析。

SQL访问的分区表过多,并发高时CPU负载高,但是大量的是SLEEP状态的BIND进程。

某个业务系统,单次SQL请求很快,几十毫秒,但是并发一高,QPS并没有线性的增长。

而且大量的进程处于BIND,SLEEP的状态。

图片描述

经过诊断,

《PostgreSQL 源码性能诊断(perf profiling)指南》

《Linux 性能诊断 perf使用指南》

主要的原因是大量的SPIN LOCK,导致CPU空转。

perf record -ag    perf report -g

图片描述

比如某个进程BIND时的pstack

#pstack  18423    
#0  0x00002ad051f3ef67 in semop () from /lib64/libc.so.6  -- 这边到了内核,上spin lock    
#1  0x0000000000656117 in PGSemaphoreLock ()    
#2  0x00000000006c274a in LWLockAcquire ()      
#3  0x00000000006bd136 in LockAcquireExtended ()      
#4  0x00000000006b8768 in LockRelationOid ()  -- 对所有的子表都会调用这个函数,导致spinlock    
#5  0x000000000050c10a in find_inheritance_children ()      
#6  0x000000000050c212 in find_all_inheritors ()  -- 找到所有子表    
#7  0x0000000000645e4e in expand_inherited_tables ()    
#8  0x000000000063a6e8 in subquery_planner ()    
#9  0x0000000000618c4f in set_rel_size ()    
#10 0x0000000000618e7c in set_rel_size ()    
#11 0x0000000000619587 in make_one_rel ()    
#12 0x0000000000636bd1 in query_planner ()    
#13 0x000000000063862c in grouping_planner ()    
#14 0x000000000063a9c4 in subquery_planner ()    
#15 0x0000000000618c4f in set_rel_size ()    
#16 0x0000000000619587 in make_one_rel ()    
#17 0x0000000000636bd1 in query_planner ()    
#18 0x000000000063862c in grouping_planner ()    
#19 0x000000000063a9c4 in subquery_planner ()    
#20 0x0000000000618c4f in set_rel_size ()    
#21 0x0000000000619587 in make_one_rel ()    
#22 0x0000000000636bd1 in query_planner ()    
#23 0x000000000063862c in grouping_planner ()    
#24 0x000000000063b0d0 in standard_planner ()    
#25 0x00000000006d1597 in pg_plan_queries ()    
#26 0x00000000007ca156 in BuildCachedPlan ()    
#27 0x00000000007ca525 in GetCachedPlan ()    
#28 0x00000000006d1d07 in exec_bind_message ()    
#29 0x00000000006d44de in PostgresMain ()    
#30 0x000000000066bd5f in PostmasterMain ()    
#31 0x00000000005f474c in main ()    

由于业务使用了prepared statement,所以过程会变成bind 过程

1、prepare statement

2、bind parameters

3、代入参数、(设置了constraint_exclusion时)判断哪些分区需要被过滤

4、execute prepared statement

在find_all_inheritors过程中,涉及的分区表过多,最后每个分区都要取LOCK(后面加载了系统的spin lock),所以我们会看到CPU很高,同时大量的BIND,进程处于SLEEP状态,也就是CPU空转,CPU时间片被独占的状态。

spinlock (自旋锁) 
自旋锁是专为防止多处理器并发而引入的一种锁,它在内核中大量应用于中断处理等部分(对于单处理器来说,防止中断处理中的并发可简单采用关闭中断的方式,不需要自旋锁)。

自旋锁最多只能被一个内核任务持有,如果一个内核任务试图请求一个已被争用(已经被持有)的自旋锁,那么这个任务就会一直进行忙循环——旋转——等待锁重新可用。

要是锁未被争用,请求它的内核任务便能立刻得到它并且继续进行。自旋锁可以在任何时刻防止多于一个的内核任务同时进入临界区,因此这种锁可有效地避免多处理器上并发运行的内核任务竞争共享资源。

事实上,自旋锁的初衷就是:

在短期间内进行轻量级的锁定。一个进程去获取被争用的自旋锁时,请求它的线程在等待锁重新可用的期间进行自旋(特别浪费处理器时间),所以自旋锁不应该被持有时间过长(等待时CPU被独占)。如果需要长时间锁定的话, 最好使用信号量(睡眠,CPU资源可出让) 

简单的说,自旋锁在内核中主要用来防止多处理器中并发访问临界区,防止内核抢占造成的竞争。另外自旋锁不允许任务睡眠(持有自旋锁的任务睡眠会造成自死锁——因为睡眠有可能造成持有锁的内核任务被重新调度,而再次申请自己已持有的锁),它能够在中断上下文使用。

死锁:假设有一个或多个内核任务和一个或多个资源,每个内核都在等待其中的一个资源,但所有的资源都已经被占用了。这便会发生所有内核任务都在相互等待,但它们永远不会释放已经占有的资源,于是任何内核任务都无法获得所需要的资源,无法继续运行,这便 
意味着死锁发生了。自死琐是说自己占有了某个资源,然后自己又申请自己已占有的资源,显然不可能再获得该资源,因此就自缚手脚了。

spinlock特性:

防止多处理器并发访问临界区,

1、非睡眠(该进程/LWP(Light Weight Process)始终处于Running的状态)

2、忙等 (cpu一直检测锁是否已经被其他cpu释放)

3、短期(低开销)加锁

4、适合中断上下文锁定

5、多cpu的机器才有意义(需要等待其他cpu释放锁)

以下截取自

http://blog.sina.com.cn/s/blog_458d6ed5010110hv.html

Spinlock的目的是用来同步SMP中会被多个CPU同时存取的变量。在Linux中,普通的spinlock由于不带额外的语义,是用起来反而要非 常小心。 在Linux kernel中执行的代码大体分normal和interrupt context两种。tasklet/softirq可以归为normal因为他们可以进入等待

Spinlock的目的是用来同步SMP中会被多个CPU同时存取的变量。在Linux中,普通的spinlock由于不带额外的语义,是用起来反而要非常小心。

在Linux kernel中执行的代码大体分normal和interrupt context两种。tasklet/softirq可以归为normal因为他们可以进入等待;nested interrupt是interrupt context的一种特殊情况,当然也是interrupt context。Normal级别可以被interrupt抢断,interrupt会被另一个interrupt抢断,但不会被normal中断。各个 interrupt之间没有优先级关系,只要有可能,每个interrupt都会被其他interrupt中断。

我们先考虑单CPU的情况。在这样情况下,不管在什么执行级别,我们只要简单地把CPU的中断关掉就可以达到独占处理的目的。从这个角度来说,spinlock的实现简单地令人乍舌:cli/sti。只要这样,我们就关闭了preemption带来的复杂之门。

单CPU的情况很简单,多CPU就不那么简单了。单纯地关掉当前CPU的中断并不会给我们带来好运。当我们的代码存取一个shared variable时,另一颗CPU随时会把数据改得面目全非。我们需要有手段通知它(或它们,你知道我的意思)——spinlock正为此设。这个例子是 我们的第一次尝试:

extern spinlock_t lock;  
// ...  
spin_lock(&lock);  
// do something  
spin_unlock(&lock);  

他能正常工作吗?答案是有可能。在某些情况下,这段代码可以正常工作,但想一想会不会发生这样的事:

// in normal run level  
extern spinlock_t lock;  
// ...  
spin_lock(&lock);  
// do something  
// interrupted by IRQ ...  // in IRQ  
extern spinlock_t lock;  
spin_lock(&lock); 

喔,我们在normal级别下获得了一个spinlock,正当我们想做什么的时候,我们被interrupt打断了,CPU转而执行interrupt level的代码,它也想获得这个lock,于是“死锁”发生了!解决方法很简单,看看我们第二次尝试:

extern spinlock_t lock;  
// ...  
cli; // disable interrupt on current CPU  
spin_lock(&lock);  
// do something  
spin_unlock(&lock);  
sti; // enable interrupt on current CPU  

在获得spinlock之前,我们先把当前CPU的中断禁止掉,然后获得一个lock;在释放lock之后再把中断打开。这样,我们就防止了死锁。事实上,Linux提供了一个更为快捷的方式来实现这个功能:

extern spinlock_t lock;  
// ...  
spin_lock_irq(&lock);  
// do something  
spin_unlock_irq(&lock);  

如果没有nested interrupt,所有这一切都很好。加上nested interrupt,我们再来看看这个例子:

// code 1  
extern spinlock_t lock;  
// ...  
spin_lock_irq(&lock);  
// do something  
spin_unlock_irq(&lock);  // code 2  
extern spinlock_t lock;  
// ...  
spin_lock_irq(&lock);  
// do something  
spin_unlock_irq(&lock);  

Code 1和code 2都运行在interrupt context下,由于中断可以嵌套执行,我们很容易就可以想到这样的运行次序:

Code 1  
extern spinlock_t lock;  
// ...  
spin_lock_irq(&lock);      Code 2  
extern spinlock_t lock;  
// ...  
spin_lock_irq(&lock);  
// do something  
spin_unlock_irq(&lock);  Code 1  
// do something  
spin_unlock_irq(&lock); 

问题是在第一个spin_unlock_irq后这个CPU的中断已经被打开,“死锁”的问题又会回到我们身边!

解决方法是我们在每次关闭中断前纪录当前中断的状态,然后恢复它而不是直接把中断打开。

unsigned long flags;  
local_irq_save(flags);  
spin_lock(&lock);  
// do something  
spin_unlock(&lock);  
local_irq_restore(flags);  

Linux同样提供了更为简便的方式:

unsigned long flags;  
spin_lock_irqsave(&lock, flags);  
// do something  
spin_unlock_irqrestore(&lock, flags);  

小结

优化方法:

1、假设我们的QUERY进程要查询多个分区(指很多个分区),那么建议把分区的粒度降低,尽量让QUERY减少真正被访问的分区数,从而减少LWLockAcquire次数。

2、如果我们的分区很多,但是通过QUERY的WHERE条件过滤后实际被访问的分区不多,那么分区表的选择就非常重要。(目前尽量不要使用NATIVE分区)。尽量使用PPAS的edb_enable_pruning。对于PostgreSQL社区版本用户,在社区优化这部分代码前,请尽量使用pg_pathman分区功能。

扫描二维码获取更多消息:

图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/522806.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

flowable实战(九)flowable数据库表中流程实例、活动实例、任务实例三者之间关系分析

场景模拟(请假流程): 员工申请请假 部门领导审批 人事审批 员工销假 本文用次例介绍在工作流中出现的几个对象及其之间的关系,以及在Activiti中各个对象是如何关联的。 在线演示实例:http://aws.kafeitu.me:8080/kft…

看懂云计算、虚拟化和容器,这一篇就够啦!

戳蓝字“CSDN云计算”关注我们哦!作者 | 小枣君来源 | 鲜枣课堂“云计算”这个词,相信大家都非常熟悉。作为信息科技发展的主流趋势,它频繁地出现在我们的眼前。伴随它一起出现的,还有这些概念名词——OpenStack、Hypervisor、KVM…

一张图读懂阿里巴巴一站式研发协同云——云效

摘要: 程序员、测试员、项目经理,你们有freestyle吗?阿里云云效,一站式企业协同研发云,“需求->开发->测试->发布->运维->运营”端到端的协同服务和研发工具支撑,助力企业快速创新迭代和研发…

flowable实战(十)flowable 启动流程到完成所有任务之间的数据库变化

来写一下Activiti 5.18版本从启动流程到整个流程结束之间数据库表的变化 先给出流程图,很简单的流程,就是两个UserTask: 代码如下: DeploymentBuilder builderrepositoryService.createDeployment(); Deployment deploymentbui…

阿里敏捷教练如何优化优酷需求分析流程?

摘要: 如何帮助优酷迅速融合到阿里研发体系?如何优化优酷的需求分析流程?针对需求信息不明确,开发出来的功能不是产品想要的痛点如何解决? 点此查看原文:http://click.aliyun.com/m/41381/ 导读&#xff1a…

java float 高效加减_java Double 进行加减乘除

Double 工具类package org.fh.util;import java.io.Serializable;import java.math.BigDecimal;import java.math.RoundingMode;/*** double的计算不精确,会有类似0.0000000000000002的误差,正确的方法是使用BigDecimal或者用整型* 整型地方法适合于货币…

Gartner预计2019年全球半导体收入将下滑9.6%;苹果中国用户正流向华为;Facebook将支付50亿美元与FTC和解...

戳蓝字“CSDN云计算”关注我们哦!嗨,大家好,重磅君带来的【云重磅】特别栏目,如期而至,每周五第一时间为大家带来重磅新闻。把握技术风向标,了解行业应用与实践,就交给我重磅君吧!重…

PostgreSQL 多重含义数组检索与条件过滤 (标签1:属性, 标签n:属性) - 包括UPSERT操作如何修改数组、追加数组元素

摘要: 标签 PostgreSQL , 多重函数数组 , UDF索引 , 过滤 , 文本处理 背景 PG的数组类型,被广泛应用于 画像系统 , 标签系统。 在一些业务重建中,对数组内容的定义往往包含了多重含义,例如即包含了标签本身&#xff0c…

从MapReduce的执行来看如何优化MaxCompute(原ODPS) SQL

摘要: SQL基础有这些操作(按照执行顺序来排列): from join(left join, right join, inner join, outer join ,semi join) where group by select sum distinct count order by 如果我们能理解mapreduce是怎么实现这些SQL中的基本操…

flowable实战(十二)flowable 核心表ACT_RU_EXECUTION 详解(初学者误解的一张表)

一、ACT_RU_EXECUTION 表(很多初学者迷惑的一张表,以为是流程实例表,其实它叫执行实例表):这个表和act_run_task表,一起控制了用户任务的产生与完成等。 这个表是工作流程的核心表,这个表会体现主干与分支…

阿里云大数据MaxCompute计算资源分布以及LogView分析优化

摘要: MaxCompute(原ODPS)的概念 海量数据处理平台,服务于批量结构化数据的存储和计算,提供海量数据仓库的解决方案以及针对大数据的分析建模服务.(官方文档有这里就不多做介绍了)官方文档链接 优势 用户不必关心分布式计算细节&a…

计算机视觉领域还能耍什么花样?

从移动支付的自动贩卖机到刷脸支付的智能货柜;从亲自到柜台验证到人脸核身远程开卡;从排队买票、排队进门的糟糕旅游体验到提前预约,刷脸入园的智慧旅游;……从计算机视觉应用的产业板块上分析,以视频应用为基础的视频…

MaxCompute MapReduce

摘要: 大数据计算服务(MaxCompute)的功能详解和使用心得 点此查看原文:http://click.aliyun.com/m/41384/ 前言 MapReduce已经有文档,用户可以参考文档使用。本文是在文档的基础上做一些类似注解及细节解释上的工作。 功能介绍 MapReduce 说起…

Flowable springboot项目自定义中文字体

Flowable springboot项目自定义中文字体 摘要:在flowable框架中,当我们想要集成springboot框架的时候,可能要设置中文字体,flowable6.4之前的版本因为没有可以设置字体的属性,所以我们没法进行中文字体的设置&#xff…

漫画 | Kubernetes带你一帆风顺去远航

戳蓝字“CSDN云计算”关注我们哦!来源 | Google Cloud如果你是一个狂立学习flag却屡屡打脸的懒癌晚期,或者是一个对云计算方面云里雾里,不知所措的好学者,亦或是一位资深行业专家,都欢迎关注【CSDN云计算公众号】&…

Kubernetes与Docker基本概念与常用命令对照

摘要: Docker是众多用户上手入门的基础容器和编排工具,提供了良好的开发者体验。Kubernetes是强大的容器编排平台,功能丰富。它们有很多概念和操作都有类似之处。我们今天会和大家对比基本概念与常用命令,可以方便熟悉Docker的用户…

flowable 设置流程跟踪高亮线的颜色

背景:在实际情况下,很多人对这个红色的高亮有意见,所以这里我把我的修改颜色的代码分享出来,希望对大家有帮助。(如果有问题可以加QQ群:633168411 里面很多高手,人也都非常善良) 效果…

连续启动 crash 自修复技术实现与原理解析

摘要: 如果 app 连续 crash 两次无法启动,用户往往会选择卸载。本文介绍如何该类 crash 的自修复技术。 点此查看原文:http://click.aliyun.com/m/41487/ 作者:阿里云-移动云-大前端团队 前言 如果 app 连续 crash 两次无法启动…

舞动的桥 阿里云首个百万IOPS云盘的背后

摘要: 近日,阿里云推出了首个百万IOPS的ESSD云盘服务,性能上有50倍的飞跃,同时还具备超高吞吐、超低时延等特性,在真实业务场景中,PostgreSQL数据库的写入速度快了26倍。 如此超高的性能,有人会…

Kubernetes上的服务网格 Istio - 分布式追踪篇

摘要: 2017年5月,Google、IBM和Lyft发布了开源服务网格框架Istio,提供微服务的连接、管理、监控和安全保护。Istio提供了一个服务间通信的基础设施层,解耦了应用逻辑和服务访问中版本管理、安全防护、故障转移、监控遥测等切面的问…