杂谈影响性能的一些因素

前几天发现一个3点开始的job(调用了一个数据库过程T)执行到了10点还没有执行完(到了11点才执行完毕).其间使用dbms_monitor 10046了一段时间,然后使用tkprof查看数据,发现在trace的这个时间段里几乎是没有什么等待事件的,主要的流逝时间消耗在了cpu time上,而cpu time,逻辑IO按每个sql的执行次数平均的话,没有一个明显偏高的,主要的问题就是执行次数偏高。因为会话还在执行着,就检查了一下v$session_event,发现也是不存在什么显著的等待事件的(8个小时消耗在等待的时间总共也就是7分钟),v$sesstat也反映出10046 trace文件同样的问题,总的cpu time偏高,逻辑IO偏高,总的sql执行次数明显偏多.v$active_session_history反应3-11点,基本上就是占用CPU执行任务,少有等待.主要执行的sql语句执行计划上都不存在问题.(当然,我们过程里普遍存在的一个问题是没有日志记录,包括这样的job调用的执行统计工作的过程也是这样,不要说具体的日志信息,就连开始时间,结束时间这样基本的信息也没有,所以不要说过程执行的时间长短,甚至是过程成功执行没有,都没有记录的.所以我无从知道这个过程是以前一直执行很快,现在才开始慢的,还是一直都是这么慢的.不过从现在for循环的处理和我对他们数据库编程能力的了解来看,应该说以前运行也很慢的,因为这里的处理是一开始select bulk collect into到一个table类型的变量里,然后for循环这个table类型变量的元素来实现的,而不是他们一贯使用的for x in(select)来实现的,但我估计一开始也是for x in(select)来实现的,但因为执行时间太长了,导致了ORA-01555快照太老的错误,所以才改成了现在的这种实现形式.所以我估计这个程序以前执行也很慢的.另外说到过程里的日志记录,说白了就是插入数据到日志表里去,它不像java程序写日志到应用服务器端的文件里一样是非事务性的,数据库里写日志表是事务性的,可能会影响到现有的事务单元,其实这个很好解决,就是把写日志的操作封装到一个自治事务的过程里调用就可以了).
仔细检查过程逻辑,两个for循环,最里面调用了一个过程S,完成一些更新操作,和开发人员讨论了一下,这个最里面调用的过程S直接对应于前台的一个功能,来完成一个报价的更新及其相关操作,而这个job调用的过程T完成的实际上是一个批量更新的操作,所以这里开发人员图省事,直接就是for循环来调用这个过程S.这样的弊端是显而易见的:for循环调用一个sql和通过一个sql来完成同样的功能,for循环调用sql的形式相当于走了一个nl的连接方式,而对于驱动结果集很大的情况下,这种实现形式很可能是不好的,而如果用一个sql来实现的话,nl的实现形式优化器是可以考虑的,其它的实现形式优化器也是可以考虑的,最终它会选择一个它认为最优的方案来执行,而for循环的实现形式则把执行计划给固定为nl了.我问开发人员是否可以不用这种嵌套循环的形式,而使用过程S的基本形式使用几个sql来实现批量更新呢,开发人员想了想,说应该是可以的.
就像tom说的那样:如果可能的话,应该用单条sql语句完成;如果不能用单条sql语句完成,那么就用pl/sql完成工作;如果不能用pl/sql完成......这里是程序实现逻辑上的问题导致的性能问题.

也是前几天,晚上7点开始的一个静态发布三个热点小类下的产品的应用,一般是到8:30就发布完了,这次却因为数据库方面的原因导致相关的sql逻辑读偏大(这里不讨论这个问题了),cpu负载偏高,导致一直到了凌晨0:30才发布完毕.我查看awr报表的时候,发现一个sql在8点到9点这个时间段里执行了近1W次,而这个sql是在4个函数里调用的,这4个函数分别是某个小类下的top10新品,top10热品,某个品牌下的top10新品,top10热品.而这个时间段上应用主要就是在发布那3个小类下的产品,而那3个小类下的品牌一共不到400个,那怎么会在8-9点这个时间段里被调用了1W次呢(而实际的静态发布时间更长,也就是说这4个函数被调用的次数还要多得多)?和开发人员讨论这个问题的时候,才发现是为什么:实际上这里是静态发布,可以认为在发布的这个时间段里,某个小类,品牌下的top10新品,top10热品都已经确定下来了,那么完全可以实现为:小类,品牌的top10新品,top10热品这些频繁变更的内容作为一个块来发布,变更不频繁的内容作为一个块来发布,比如说这里的某款产品的具体内容作为一个块来发布(因为一天内新增修改的产品是很少的,很多的产品已经很长时间里没有变更内容了).也就是说发布某个小类下的产品的时候,先单独发布这个小类和它下面的品牌的top10新品,top10热品块,产品块直接就是引用这些块;而发布产品的时候,根据静态发布的频率,比如说这里的一天设置,只发布最近2天(这样虽然也存在重复,但至少不会遗漏)内新增和修改的产品,其它根本就没有变更过的产品就不要重复发布了.但这里的页面在设计之初,或者说后来由于新增功能,偏偏没有区分频繁变更的内容和不频繁变更的内容,而变成了这样的设计:就是一个页面块,包括产品的内容和它同类的小类,品牌的top10新品,top10热品,这里top10是频繁变更的,所以导致某个产品的页面在发布的周期内始终是在变化的,即使这个产品的内容已经很久没有更新了,而且最要命的是top10被重复不必要的发布了,而10g的时候是不存在sql查询结果集缓存的,也就是说数据库层面上是做不了结果缓存的,一个语句重复执行,物理读可以避免,但逻辑读却避免不了,这样大量不必要的调用导致了性能上的问题.这里是页面元素的设计不当导致了不必要的调用导致的资源上的浪费,导致了性能上的问题.

之前一次,一个图片按说应该只能属于一个产品的,也就是图片url在某个表中应该是唯一的,但当初数据库,应用上都没有限制这一点(其实限制也应该要在数据库端限制,而不应该像很多应用那样没有在数据库端限制,而只是在应用逻辑里去控制这一点),所以编辑们有时候会把一个图片关联给多个产品,也就是说在这个表中图片url不再唯一了,但现在另一个应用需要产品的图片数据的时候,出现问题了,这里要求图片url必须唯一,于是同事要求我做个视图,一个图片url只能属于一个最新的产品,其它的就不要显示了,我说既然是这样,为什么不从根源上也就是说数据上去处理(然后添加相应的数据库约束,并修改应用),保证一个图片只能属于一个产品呢,为什么老要采用这样一种讨巧的方式去实现呢,同事说数据上不好处理,主要是怕这样去除多余的图片数据的话,可能会给其它应用带来一些问题,没办法,我只能用分析函数实现了这样的功能,我认为它们是一次性查询这个视图,把其中的数据拷贝到另外一个数据库,然后查询的呢,后来才知道它们就是通过这个视图查询的,而且查询的时候都是针对特定的产品id去查询的,但因为我这里视图实现是row_number()over(partition by 图片url order by productid desc) r,然后r=1过滤的,导致这个产品id不能谓词内推了,导致实际上每次查询某个产品的时候,都是把这个视图执行了一遍(这里的分析函数要排序,会大量消耗cpu资源的),然后从它的结果集里去查询某个产品的信息的,这样自然性能上是无法忍受的.幸好它们对于数据的实时性要求不高,后来就改为一个物化视图查询我定义的这个视图每天完全刷新一遍,然后应用查询这个物化视图来实现了.其实这样也是存在问题的,就是某个产品的图片都被其它产品给占据了,而且这个产品的productid相对其它产品来说都比它们低,导致在这个物化视图(视图实现也是存在这个问题的)中这个产品消失了,我跟同事说了这一点,同事丢失些数据就丢了吧,也没什么问题的.很后来的时候,我问同事数据处理了没有,同事说没有处理.我也没什么可说的,为什么不能从数据根源上去解决问题,而老是采用讨巧的方式去实现,从而给性能带来很坏的影响呢?!这里是存在不符合应用需求的数据,却因为各种原因无法从数据根源上去处理,而采用些讨巧的方式导致的性能上的问题.

很早的时候做的一个联盟系统的广告防作弊日志分析,分析的是apache日志文件,基本是用awk实现的,需要区分广告位,也就是说需要按照广告位元素来进行排序,而日志文件里是通过6个元素(4个字符串元素,比如site,node,和2个数字元素)来唯一的标示一个广告位的,也就是说我需要按照6个字段来排序,这消耗了大量的cpu资源,虽然我后来分解任务,并行的来完成任务,利用起了多个cpu资源,但总感觉性能上是个问题,最关键的是,之后我把分析的结果(当然包括6个广告位元素了),导入到数据库中,然后通过这6个广告位元素和数据库中的多个表关联得到一个广告位id(number类型,和那6个广告位元素是等价的,唯一的标示一个广告位),然后关联计算每个联盟的收入情况.也就是说6个元素可以唯一的标示一个广告位,一个number类型的数字也可以唯一的标示一个广告位,还有我分析的结果还是要关联得到广告位id才能关联其它表得到最终的结果,那为什么不在apachelog中记录这个广告位id呢?这样的话,我在shell中排序的话,我只需要这一个排序字段,而不是6个排序字段了,这样在cpu资源的消耗上会大大减少的,导入数据库的时候导入广告位id而不是6个广告位元素,导入数据库的数据量也会大大减少,而且避免了关联得到广告位id这一步骤(要继续关联表的话只能通过广告位id去关联,而不能通过那6个元素来关联了),无论怎么看都是一个很好的方案呀,可为什么当初没有把广告位id这个字段记录到apachelog中去呢?!虽然说我以前也是搞开发的,但我那时是VC开发,并没有接触过apache之类的应用服务器,所以一开始我还认为是不能记录这个变量到apachelog中去呢,后来和同事讨论这个事情的时候,才知道是可以在Apache日志中记录这个字段的.后来开发新的防作弊日志分析的时候,在apache日志中记录了广告位id这个字段,按照我前面所描述的方法去实现的防作弊分析,性能上有了极大的提示.这是当初设计的时候没有考虑后来的应用或者说后来应用的扩展却没有修改当初的设计导致的性能上的问题.

所以说,数据库的性能问题,有时候不仅仅是数据库层面的问题引入的,也很可能是应用设计,业务逻辑,逻辑实现等方面引入的问题,所以就需要我们在各个方面都要多加考虑.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值