分享

XX报名程序优化总结

 Joshua 2006-01-23

XX报名程序优化总结

Posted on Wednesday, January 11th, 2006 by yangyiming

1月2号XX省/市开始XX考试网上报名。今年是第一年开始采用网上报名的形式,预计在10天之内XX省/市有大于14万的学生需要在指定的网站中填写完毕个人信息并且确认。本来这个项目并不是我负责的,不过很无奈,正式报名刚开始系统就发生了比较严重的性能问题,因为事情紧急只能取消自己的休假计划,赶到现场调试系统。

到了之后,首先检查了Tomcat的配置文件server.xml,原来一直以为肯定是配置服务器的同学没有把Tomcat中默认的允许访问的最大数75给改掉,后来发现不是这个问题,在配置文件中设置Context的地方有一段非常的诡异:有一个Context的路径是在%Tomcat%/webapps/ROOT中的一个目录AAA,这样的话如果当一个用户访问URL: http://serverip:port/AAA 时,系统是应该使用ROOT/WEB-INF中类还是应该使用ROOT/AAA/WEB-INF中的类?把服务器中两个WEB-INF都下载下来,用JAD反编译其中的部分类,发现并不一致。最后经过分析,得出了TOMCAT在这种Context嵌套在ROOT Context中的情况,TOMCAT会使用ROOT目录中的WEB-INF。

然后为程序加入了数据库连接池配置,实践证明了如果不采用TOMCAT的数据库连接池技术,那么在并发访问数达到4000-5000的时候,Tomcat绝对支撑不到1分钟的。采用TOMCAT的数据库连接池技术后这时的TOMCAT能维持正常运行30分钟左右。数据库连接池配置的方式如下:
修改tomcat配置文件server.xml,在context标签中加上

  1. <Context path="" docBase="ROOT" debug="0">
  2. <Resource name="jdbc/OracleDB" auth="Container"
  3. type="javax.sql.DataSource"/>
  4. <ResourceParams name="jdbc/OracleDB">
  5. <parameter>
  6. <name>factory</name>
  7. <value>org.apache.commons.dbcp.
  8. BasicDataSourceFactory</value>
  9. </parameter>
  10. <parameter>
  11. <name>driverClassName</name>
  12. <value>oracle.jdbc.driver.OracleDriver</value>
  13. </parameter>
  14. <parameter>
  15. <name>url</name>
  16. <value>jdbc:oracle:thin:@10.11.6.1:1521:dbname
  17. </value>
  18. </parameter>
  19. <parameter>
  20. <name>username</name>
  21. <value>yourname</value>
  22. </parameter>
  23. <parameter>
  24. <name>password</name>
  25. <value>yourpasswd</value>
  26. </parameter>
  27. <parameter>
  28. <name>maxActive</name>
  29. <value>1000</value>
  30. </parameter>
  31. <parameter>
  32. <name>maxIdle</name>
  33. <value>20</value> </parameter>
  34. <parameter>
  35. <name>maxWait</name>
  36. <value>-1</value>
  37. </parameter>
  38. </ResourceParams>
  39. </Context>

maxActive是最大激活连接数,这里取值为1000,表示同时最多有1000个数据库连接。maxIdle是最大的空闲连接数,这里取值为20,表示即使没有数据库连接时依然可以保持20空闲的连接,而不被清除,随时处于待命状态。MaxWait是最大等待秒钟数,这里取值-1,表示无限等待,直到超时为止,也可取值9000,表示9秒后超时。

修改web.xml文文件,加入

[待加入的XML片断,竟然被系统过滤掉了,赫赫,留个站位符]

将Oracle的JDBC驱动classes12.jar拷贝到Tomcat安装目录的common/lib下。建立简单的测试页面test2.jsp:

  1. Context initCtx = new InitialContext();
  2. Context envCtx = (Context) initCtx.lookup("java:comp/env");
  3. ds = (DataSource)envCtx.lookup("jdbc/OracleDB");
  4. Connection cn=ds.getConnection();

到这里数据库连接池配置基本完成了。但是如果半个小时服务器死掉一次的话,还是不能令人接受的。开始详细检查tomcat的输出文件%Tomcat%/logs/catalina.out,在这个文件中记录的是所有TOMCAT控制台的输出,一般在JAVA中的抱错信息都会在这里打印出来。自己分析后发现每次TOMCAT死掉之前,都是一堆java.net.SocketException: Too many open files这种错误。这个错误肯定不是程序的问题。

可能是因为每有一个TCP的连接到Linux服务器的80端口,都有一次文件的传输过程,Tomcat服务器会打开一个文件(比如图片文件),传送给请求的客户端浏览器,所以如果访问Tomcat的人数过多,会突破linux系统默认的1024个文件上限。

在/root/.bash_profile文件中加入:

ulimit -n 4096

可以在每次系统启动时候自动执行ulimit命令,把最大文件上传个数调整到4096个。注意ulimit这个命令是针对于一个开打的终端的,比如你在一个终端上面运行ulimit,但是影响到的只是你这个终端上运行的命令和程序的打开的文件数,其他的终端如果再打开,效果仍然是没有运行ulimit -n 4096之前的。大家可以实验一下。

这样之后,服务器大概能维持2小时左右的正常运行,使用TOP命令查看Java进程占用的内存后,发觉一个现象,就是内存使用到256M之后,不会往上增长了(服务器有8G的内存)。上网查了以后,知道了Tomcat默认可以使用的是256M的内存。由于在线人数过多,造成Tomcat默认可以使用的256M的内存不够使用,所以修改Tomcat的启动文件catalina.sh,在“echo “Using JAVA_HOME: $JAVA_HOME”” 后加入JAVA_OPTS=’-Xms256m -Xmx1024m’,256m表示初始化的时候给Tomcat初始内存256M,1024m表示Tomcat最大可使用的内存为1024M。

修改了这个之后,服务器比原先稳定了许多了,可是又发现在有的时候,进行用户登录的时候会报莫名其妙的数据库连接错误。终于在有一次使用ORACLE的客户端程序GOLDEN连接Oracle时,出现了ORA-00020:maximum number of process(150) exceeded 这样一个错误。这是由于数据库服务器访问的连接数过多,会出现ORA-00020:maximum number of process(150) exceeded的错误。Oracle 9i中默认的连接数为150,要修改这个配置文件,需要修改SPFILEORCL.ORA文件中的processes的值。(8.1.5中是init.ora文件,在9i中修改init.ora文件是无效的)这个文件由于是一个二进制的文件,不能直接使用notepad此类的编辑器打开,否则会报错误ORA-27101 Shared memory realm does not exist。使用UltraEdit或者EditPlus之类的可以编辑二进制文件的编辑器打开此文件(直接编辑二进制文件),然后在Windows服务中重新启动Oracle服务器即可。

现在的状况非常好,问题正在逐步的好转之中,不过系统的性能还是非常的差,网页的访问速度很糟糕。到安装Oracle的Win2003服务器上检查了一下Oracle服务器,发现硬盘的占用率很高,基本一致是100% 使用的状况。进入Oracle中进行分析,发现有两条SQL语句非常占用系统的资源,是造成瓶颈的两个SQL。

于是对于原先使用trim的SQL进行优化

由于trim命令会造成大量的硬盘读写(使用trim后造成索引无效),所以把所有使用trim命令的语句修改为使用rpad命令。

例如:

select sfzh from qjgkXSK where trim(sfzh)=:1

改为

select sfzh from qjgkXSK where sfzh=:rpad(1)

 

对于表结构的建议:
表结构中如果长度小于200的字段,建议使用char类型,而不是Varchar类型。

 

然后发现在生成一个唯一序列号的时候,程序中有一个锁表的操作,造成一个最大的瓶颈。
原先的做法是先是锁住一个表,然后取得最大值,最后把最大值加一,写入数据库中。

  1. ps = conn.prepareStatement("LOCK TABLE "+Global.xsk+" IN EXCLUSIVE MODE");
  2. ps.executeUpdate();
  3. ps = conn.prepareStatement("select max(SNO) from "+Global.xsk+" where SNO is not null");
  4. rs = ps.executeQuery();
  5. rs.next();
  6. if (rs.getString(1)!=null)
  7. {
  8.      temp = Global.formatString(""+(Integer.parseInt(rs.getString(1))+1),"0",6);
  9. }
  10. else
  11. { 
  12.      temp = "000001";
  13. }

改进后的做法是加入一个ORACLE的自增1的sequence(Oracle和MySQL、DB2等不同,它没有主键auto-increasement这种功能)

  1. ps = conn.prepareStatement("select seq_qjgksno.nextval from dual");
  2. rs = ps.executeQuery();
  3. rs.next();
  4. temp = Global.formatString("" + (Integer.parseInt(rs.getString(1))), "0", 6);

做完这些时候,感到系统的问题都解决了,不过后来的情况还是几个小时会死机一次,这次是报一个Java虚拟机的错误,估计是因为一开始别人装的是1.5beta版的关系。

总结一下这次的优化的经验:

1.要抓住事物的主要矛盾。其实一开始针对WEB服务器中的很多优化都是次要矛盾,当解决了最后的2个SQL语句的问题后,前面的很多问题都不会出现的,比如那个Linux 1024个默认打开文件数的问题,最后发现如果系统数据库访问的速度足够快,根本不会有那么多打开的文件数的,往往是大家用系统时很慢,所以造成了不断用F5刷新,把Tomcat活活搞死。

2.在这种很重要的场合下改动程序或者配置一定要谨慎谨慎再谨慎。我在修改Oracle的配置文件的时候,一开始没有备份,以后那是一个文本文件,我就是直接开打修改一个数字而已。但是虽然用notepad打开的时候显示出来的是文本,但是这个文件在被notepad打开时一些二进制的东西被破坏了,造成了Oracle服务器启动不起来,那时候自己都要被吓死了,还好有个同学的机器上也有Oracle9i,而且都是按照默认配置的,直接打电话叫他把那个文件通过MSN发了过来。如果没有类似的文件,或者服务器装的比较怪异,那么就惨了,千古罪人的。后来我基本每做一个操作,都不图快,而是随时备份,做的比较稳健了。

3.在改那个程序的时候不断的有考生打电话来咨询服务器的问题,那个时候压力非常大,一定要顶住压力,如果感觉吃不消了,不妨先休息下,然后效率可能会更加高。

4.事前一定要做好测试工作。

最后附上一些Linux中可能会在检测性能时候用到的命令:
A.察看某个进程打开的文件数:
先用ps -aux找到pid,然后运行lsof -p %pid% | wc -l

B.察看80端口的连接数
netstat -nat|grep -i “80″|wc -l

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多