YonyouSoftwareCorporationNC性能优化用友软件股份有限公司NC平台技术部代群义YonyouSoftwareCorporation课程对象开发人员、技术人员……YonyouSoftwareCorporation课程内容1.熟悉NC开发性能标准2.熟悉NC性能优化基本方法程序员/技术人员的困惑NC6.3开发性能标准检查某个功能点是否符合标准NC性能优化基本方法程序员/技术人员:使用了最好最新技术…完成了产品功能…用户:抱怨产品太难用了…程序员/技术人员的困惑网络调用标准类型动作启用远程压缩、启用数据缓存模式下数据传输量限制连接数账表(模板)打开界面上行<=10kB,下行<=15kB6账表(无模板)打开界面上行<=10kB,下行<=15kB6账表(查询100行20列)查询上行<=10kB,下行<=15kB6单据(模板)打开界面上行<=10kB,下行<=15kB6单据(无模板)打开界面上行<=10kB,下行<=15kB6单据(单据体10行)保存上行<=10kB,下行<=15kB6单据(单据体100行)保存上行<=10kB,下行<=15kB6查询对话框(模板)打开上行<=10kB,下行<=15kB6查询对话框(无模板)打开上行<=10kB,下行<=15kB6其他功能节点打开上行<=10kB,下行<=15kB6NC6.3开发性能标准:流量标准单点标准业务类型相应时间解释单据的日常业务(打开节点)<=3s单据表体建议为一百行。具体领域可以跟需求确认日常单据的数据量。超过标准需要给出合理性的解释,如果有进度条,标准可以降低。单据的日常业务(单据的增、删、改、查、提交、审批等)<=2s单据表体建议为10行。具体领域可以跟需求确认日常单据的数据量。超过标准需要给出合理性的解释,如果有进度条,标准可以降低。界面不涉及远程调用的操作(参照,修改,返回,退出等)<1s………………NC6.3开发性能标准:单点标准业务系统/业务对象数量级/报表应用场景单用户响应时间范围登陆登陆成功4s-10s打印打印预览1页2s-3s直接打印10页2s-6s华表打印10页2s-6s直接打印100页2s-8s华表打印100页5s-15s单据模板开启一个单据模板1s-3s保存会计科目:新增与公司多少相关(10个公司)2s~4s客商:1s~2s人员档案:存货:1s~2s查询会计科目:10003s-6s客商:30005s-10s人员档案:存货:30005s-10s分配会计科目:1000/公司1m-3m客商:5000/公司<1m存货:10000/公司<1m节点打开<3s权限分配<10s参照首次打开参照:普通参照<3s复合参照<4s参照过滤<1sNC6.3开发性能标准:UAPSQl数量:单个远程调用sql数量要<=1000………NC6.3开发性能标准:SQL数量NClogs目录NC日志文件存放目录如果是单server或nc中间件的话会有一个server1目录,需要进入该目录查看日志如果是集群的话如图:检验某个功能点是否符合标准NC日志收集:NClogs设置sql语句输出把anonymous设置为debug或者all这样就可以在annoy-log下看到sql输出。检验某个功能点是否符合标准NC日志收集:NClogsNC日志收集:NC日志抽取检验某个功能点是否符合标准NC日志收集:NC日志录制日志录制注意事项:1.应用服务器端监控系统启动2.日志级别设置为DEBUG检验某个功能点是否符合标准NC日志收集:NC日志导入检验某个功能点是否符合标准NC日志分析:远程调用检验某个功能点是否符合标准NC日志分析:执行时间检验某个功能点是否符合标准NC日志分析:执行时间检验某个功能点是否符合标准4ncServer………nc.itf.gl.voucher.IVoucher.save制单-保存…………2013-10-2821:54:23323.0244.09.00.02.0…………………53.096.02677.02677.00.0………….NC日志分析:流量检验某个功能点是否符合标准NC日志分析:SQL统计检验某个功能点是否符合标准NC性能优化基本方法•减少远程调用•减少循环中对数据库操作•减少sql数量•批处理•绑定变量•临时表•将产生排他锁的操作放在事务的最后•缓存•sql优化(ORACLE)•…….NC系统优化•NC的访问路径:客户端->网络->应用服务器->数据库客户端应用服务器数据库服务器NC系统优化:减少远程调用数据通过网络在客户端和服务器端交互次数很多,效率很低。NC系统优化:减少SQL数量NC6X单次交易的SQL数量<1000,提高响应时间和系统吞吐量.对于多行增加、删除、修改或者多行数据导入导出,尽量使用批量处理,减少数据库的访问次数。NC系统优化:批处理For(….){MoVO[]movos=service.queryMoVOsBySourceId(srcid);//循环1次,查询1次….判断逻辑….处理逻辑}MapmoMaps=newHashMap();moMaps=service.queryMoVOsBySourceids(srcids);//利用批查询一次性查询出结果For(….){MoVO[]movos=moMaps.get(srcid);….判断逻辑….处理逻辑}例:使用批查询,减少对数据库的查询次数例:减少循环中调用数据库操作,使用批处理BaseDAOdao=newBaseDAO();For(….){booleanneedDel=false;….判断逻辑if(needDel){dao.deleteVO(vo);//循环内不应进行数据库操作}}BaseDAOdao=newBaseDAO();ListdeletedVOs=newArrayList();For(….){booleanneedDel=false;….判断逻辑if(needDel){deletedVOs.add(vo);//加入待删除队列}}dao.deleteVOList(deletedVOs);//一次性删除操作NC系统优化:批处理NC系统优化:绑定变量ORACLE中绑定变量实现原理:SQL共享是实现软解析的方法Oracle将执行过的SQL语句存放在内存的共享池(sharedbufferpool)中,当执行一个SQL语句时,如果与之前的执行语句完全相同,Oracle就能快速获取已经解析的语句及执行计划,同时也节省了内存使用。SQL共享的基本条件1:被执行SQL语句必须与共享池中的语句完全相同,例如大小写、空格、换行等例如:SELECT*FROMtmp_materialid;和下列每一个都不同SELECT*fromtmp_materialid;(大小写)SELECT*FROMtmp_materialid;(空格不对)SELECT*FROMtmp_materialid;(换行)ORACLE中绑定变量实现原理:SQL共享的基本条件2:SQL语句所指的对象必须完全相同例如表tmp_materialid:select…fromtmp_materialid与oracle中同义词tmp_materialid:select…fromtmp_materialid不相同SQL共享的基本条件3:语句中必须使用相同命名的绑定变量例如select„fromtmp_materialidwhereid=:id与select„fromtmp_materialidwhereid=:vid不相同NC系统优化:绑定变量NC系统优化:绑定变量OLTP(联机事务处理)业务系统中尽量使用绑定变量,减少数据库库执行计划的解析时间。OLAP(联机分析处理)业务系统尽量不用绑定变量,要根据业务及数据量分析。绑定变量在代码中实现方法:Statement接口用于执行一个静态SQL语句,得到SQL语句执行后的结果。PreparedStatement类接口:PreparedStatement对象继承Statement接口,因此也是用来执行SQL语句,与Statement接口不同的是,包含于PreparedStatement对象中的SQL语句具有一个或多个参数。每个参数可以用一个“?”作为占位符而代替,但必须在执行该语句之前用适当的setXXX方法来取代“?”。下面为该接口的一个常用的方法:PreparedStatement(“updateemployeesetname=?Whereage=?”);PreparedStatement类接口:NC系统优化:绑定变量StringstrSql=“selectdistinctwlbmidfrombd_bomwheregcbm=?”;//使用绑定变量方式…try{con=getConnection();stmt=con.prepareStatement(strSql);stmt.setString(1,gcbm);ResultSetrs=stmt.executeQuery();}finally{…}catch(Exceptione)(…}StringstrSql=“selectdistinctwlbmidfrombd_bomwheregcbm=‘“+gcbm+”’”;//不建议使用Connectioncon=null;PreparedStatementstmt=null;try{con=getConnection();stmt=con.prepareStatement(strSql);ResultSetrs=stmt.executeQuery();while(rs.next()){Stringwlbmid=rs.getString(1);//后续处理}}finally{…}catch(Exceptione)(…}NC中的临时表使用:SQL语句过长会造成SQL语句解析的成本很高,解析很慢。如在SQL语句中存在条件in时,in语句中的数据多时,建议使用临时表方式。NC中临时表相关类:nc.impl.pubapp.pattern.database.IDQueryBuildernc.impl.pubapp.pattern.database.TempTablenc.impl.pubapp.pattern.database.TempTableDefineNC系统优化—临时表NC中的临时表使用:例子:Select„from„wherecmaterialvid=‘id1’orcmaterialvid=‘id2’„orcmatierlvid=‘id400’„Select„.From„wherecmaterialvidin(‘id1’,„‘id200’)orcmaterialvidin(‘id201’,„’id400’)or„.如果拼接的条件数量多会影响SQL解析时间。使用临时表:Select„.From„wherecmaterialvidin(selectidfromtmp_materialid)NC系统优化—临时表NC中的临时表使用:NC系统优化—临时表StringBuilderstrSql=newStringBuilder();strSql.append(“selectdistinctwlbmidfrombd_bomwheregcbmin(‘“);For(intI=0;I0){strSql.append(“,”);}strSql.append(“’”+ids*i++“’”);}strSql.append(“)”);onnectioncon=null;PreparedStatementstmt=null;try{con=getConnection();stmt=con.prepareStatement(strSql);ResultSetrs=stmt.executeQuery();while(rs.next()){Stringwlbmid=rs.getString(1);//后续处理}}finally{…}catch(Exceptione)(…}StringBuilderstrSql=newStringBuilder();strSql.append(“selectdistinctwlbmidfrombd_bomwheregcbmin(‘“);strSql.append(“selectid1from”+newTempTableDefine().get(ids));//直接使用临时表处理strSql.append(“)”);…将产生排它锁的操作放到事务的最后NC系统优化—将产生排他锁的操作放到事务的最后例:凭证记帐事务开始——更新凭证——检查是否满足控制条件——提交或回滚结束事务锁定时间———————————————————————修改如下:事务开始——检查是否满足控制条件——更新凭证——提交或回滚结束事务锁定时间———————————————NC系统优化—将产生排他锁的操作放到事务的最后死锁:当两个(或多个)用户互相等待被对方加锁的资源时就会发生死锁(deadlock)。死锁将导致相关的事务停止执行。下图演示了产生死锁的两个事务。如图所示,在时间点A,两个事务均获得了更新操作所需数据行上的锁,此时两事务均正常,能够继续执行。接下来,两个事务均要更新当前被对方加锁的数据。因此,在时间点B将发生死锁,因为此时两个事务都不能获得继续执行或终止所需的资源。无论两个事务等待多久,相互冲突的锁都无法被释放,所以此种情况被称为死锁。图:产生死锁的两个事务NC系统优化—将产生排他锁的操作放到事务的最后检测死锁数据库能自动地检测死锁的情况,回滚造成死锁的某个语句,以便释放冲突的行级锁,从而解决死锁问题。向执行了语句级回滚的事务返回一个错误信息。避免死锁如果两个事务需要访问相同的一组表,那么在两个事务中按相同的顺序对这组表加锁通常能避免多表死锁。例如,如果系统中的一个主表及一个明细表都需要更新时,开发者应该遵从一定的规则,如先对主表加锁,再对明细表加锁。如果能够仔细设计类似的规则并严格执行,就能从根本上杜绝死锁的产生。NC系统优化—使用前台数据缓存编程接口DBCacheFacade查询缓存状态:DBCacheFacade.isCacheEnabled();判断当前客户端缓存是否可用(前台数据库是否连接上了)DBCacheFacade.isTableCached(StringtableName,Listcolumns)查询缓存:Stringsql="selectinitcode,valuefrompub_sysinitwherepk_org=?";SQLParameterparam=newSQLParameter();param.addParam(pk_org);Listlist=(List)DBCacheFacade.runQuery(sql,param,ArrayListProcessor(c));注:缓存查询将优先到前台数据库中查询,如果查询结果为空或出错,将到后台数据库中查询。刷新缓存:DBCacheFacade.refreshTable(table);强制刷新缓存表的内容,保证前台数据是最新的。如果表的版本未发生变化,则不刷新。DBCacheFacade.refreshTables(Listtables,booleanrefreshVersion);可以设定是否刷新版本缓存,如果参数为false,则最近5分钟左右变更的数据将不会刷新到本地。但是这会减少一次远程调用。客户端使用内存数据库缓存变化不是很频繁的表,当客户端执行sql查询时,先从前台数据库中查询,查询失败再去后台数据库中查询在模块的tabConfig.xml配置中增加数据库表的配置NC系统优化—使用前台数据缓存禁止在一条SQL语句中使用3层以上的嵌套查询,如果有,请考虑使用临时表或中间结果集。尽量避免在一条SQL语句中从>=4个表中同时取数,对于仅是作为过滤条件关联,但不涉及取数的表,不参与表个数计算;如果必须关联4个或4个以上表,尽量采用子查询的方式。SQL优化—避免太多表关联查询1没有使用到的表不用放到SQL中。Selecta.col1,b.col2fromajoinbona.code1=b.code2leftjoincona.code2=c.code3wherea.col2=xxx2能从一个表中查询出的数据不要关联其它表。selectdistinctto_bill.cbillidfromto_bill,to_bill_bwhereto_bill.dbilldate>=''andto_bill_b.dbilldate>=''andto_bill_b.pk_group=''andto_bill.cbillid=to_bill_b.cbill_b改成selectdistinctto_bill_b.cbillidfromto_bill_bwhereto_bill_b.dbilldate>=''andto_bill_b.pk_group='';SQL优化—SQL中不要关联不必要的表selectp.pk_invbasdoc,p.pk_calbody,p.pk_invmandoc,b.invcode,b.invnamefrombd_invbasdocb,bd_producepwherep.pk_invbasdoc(+)=b.pk_invbasdocand(p.pk_invbasdocin('0001AA1000000047WM16','0001AA1000000047WM1H','0001AA1000000047WM1V','0001AA1000000047X3O4'))这里的外连接是没必要的,加了外连接,oracle选择了2个fulltable+hashouterjoin,把外连接去掉,语句执行非常快selectp.pk_invbasdoc,p.pk_calbody,p.pk_invmandoc,b.invcode,b.invnamefrombd_invbasdocb,bd_producepwherep.pk_invbasdoc=b.pk_invbasdocand(p.pk_invbasdocin('0001AA1000000047WM16','0001AA1000000047WM1H','0001AA1000000047WM1V','0001AA1000000047X3O4'))SQL优化—避免不必要的外连接更新时特别注意:如果不是对全表更新,一定要加where条件,特别是对大表进行更新操作。SQL优化—更新表数据时,不带where条件问题尽量使用where代替having例如:selectpk_accsubj,sum(localcreditamount)fromgl_detailgroupbypk_accsubjhavingpk_accsubj='1003AA1000000000003A'修改为:selectpk_accsubj,sum(localcreditamount)fromufnczz.gl_detailwherepk_accsubj='1017AA1000000000069S'groupbypk_accsubjHAVING中的条件一般用于对一些集合函数的比较,如COUNT()等等.除此而外,一般的条件应该写在WHERE子句中SQL优化--尽量使用where代替havingSelect*fromt1wherecol1in(selectcol1fromt2wheret1.col2=value)修改为:Select*fromt1wherecol1in(selectcol1fromt2)wheret1.col2=valueSQL优化--不要在子查询中引用主查询的条件测试对比SQL优化--不要在子查询中引用主查询的条件测试对比SQL优化--不要在子查询中引用主查询的条件updateia_bill_bsetts='2010-01-265:02:27',ia_bill_b.nsettledretractnum=(selectnvl(ia_bill_b.nsettledretractnum,0)+b.nnumberfromia_bill_bbwhereia_bill_b.cbill_bid=b.cadjustbillitemidandb.cbillid='1035V61000000008XXEQ'andb.nnumber<0--andia_bill_b.dr=0)whereexists(select1fromv_ia_inoutledgerbwhereia_bill_b.cbill_bid=b.cadjustbillitemidandb.cbillid='1035V61000000008XXEQ'andb.nnumber<0)andia_bill_b.dr=0SQL优化--不要在子查询中引用主查询的条件updateia_bill_bsetts='2010-01-265:02:27',ia_bill_b.nsettledretractnum=(selectnvl(ia_bill_b.nsettledretractnum,0)+b.nnumberfromia_bill_bbwhereia_bill_b.cbill_bid=b.cadjustbillitemidandb.cbillid='1035V61000000008XXEQ'andb.nnumber<0andia_bill_b.dr=0)whereexists(select1fromv_ia_inoutledgerbwhereia_bill_b.cbill_bid=b.cadjustbillitemidandb.cbillid='1035V61000000008XXEQ'andb.nnumber<0andia_bill_b.dr=0)YonyouSoftwareCorporation