项目优化方案

1.程序开发优化

1.循代码编写约定及规范

1.2基础代码优化

1.2.1字符串优化

1.2.1.1使用StringBuffter代替String

例如:

String s ="a"+"b"+"c"+"d"+"...";

替换为:

StringBuffer sb= new StringBuffer();
sb.append("a").append("b").append("c").append("d").append("...");

1.2.1.2 区别StringBuffer 和StringBuilder

StringBuffer线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。StringBuilder与该类相比,通常应该优先使用 java.lang.StringBuilder类,因为它支持所有相同的操作,但由于它不执行同步,所以速度更快。

例如:

StringBuffer sb = new StringBuffer();①
//StringBuilder sb = newStringBuilder();②
        Long t1 = new Date().getTime();
        for(int i =0;i<10000000;i++){
            sb.append("a");
        }
    Long t2 = new Date().getTime();
    System.out.println("耗时:"+(t2-t1)+"ms");

①  输出结果:耗时: 549ms

②  输出结果:耗时: 349ms

 

1.2.2对象的创建

尽最大努力少New新对象,用new关键词创建类的实例时,构造函数链中的所有构造函数都会被自动调用。但如果一个对象实现了Cloneable接口,我们可以调用它的clone()方法。clone()方法不会调用任何类构造函数。

在使用设计模式(Design Pattern)的场合,如果用Factory模式创建对象,则改用clone()方法创建新的对象实例非常简单。例如,下面是Factory模式的一个典型实现:

public static Credit getNewCredit() { returnnew Credit(); } 

改进后的代码使用clone()方法,如下所示:

private static Credit BaseCredit = newCredit();
   public static Credit getNewCredit() {
     return (Credit)BaseCredit.clone(); 
}


1.2.3资源关闭

程序中使用到的资源应当被释放,以避免资源泄漏。这最好在finally块中去做。不管程序执行的结果如何,finally块总是会执行的,以确保资源的正确关闭。对于稀缺资源的使用问题,像流、数据库连接这种资源使用后一定要关闭。

1.2.4优化循环体

1.2.4.1终止条件先定义

例如:将

for(int i=0; i<collection.size();i++){... }

  替换为:

     尽量减少对变量的重复计算

 

int n=collection.size();
for(int i=0; i<n;i++){...}

1.2.4.2大循环放里,小循环放外

例如:将

for(int j=0; j<1000000;j++){
 for(int i=0; i<10;i++){…}
 }

替换为:

  for(int i=0; i<10;i++){
     for(int j=0; j<1000000;j++){…}
    }

1.2.4.3循环体内不相关计算移到外面

for(int i=0;terminal=x.length;i<terminal;i++){
x = x/scaleA *scaleB;
}

替换为:

Double scale = scaleB*scaleA;
for(int i=0;terminal=x.length;i<terminal;i++){
x = x/scale ;
}

1.2.4.4循环内不要创建对象缺

例如:

for(int i=1;i<=100;i++){.           
AuditResult auditResult = new AuditResult();
}

这种做法会在内存中保存N份这个对象的引用//会浪费大量的内存空间,

替换为:

AuditResult auditResult;
for(int i=1;i<=100;i++){
auditResult=new AuditResult();
}

1.2.4.5避免循环里面进行数据库操作
1.2.4.6 避免循环里面进行文件读写操作
1.2.4.7将try/catch块移出循环

把try/catch块放入循环体内,会极大的影响性能,如果编译JIT(Just-In-TimeCompiler,即时编译器。)被关闭或者你所使用的是一个不带JIT的JVM,性能会将下降21%之多!        

例子:        

import java.io.FileInputStream;
public class Test {
   void method (FileInputStream fis) {
       for (int i = 0; i < size; i++) {
           try {
                _sum += fis.read();
           } catch (Exception e) {}
       }
    }
   private int _sum;
} 

       

替换为:        

将try/catch块移出循环        

  void method (FileInputStream fis) {
       try {
           for (int i = 0; i < size; i++) {
                _sum += fis.read();
           }
       } catch (Exception e) {}
    }

1.2.4.8不要在循环中调用synchronized(同步)方法

方法的同步需要消耗相当大的资料,在一个循环中调用它绝对不是一个好主意。

例子:

import java.util.Vector;
public class Test {
   public synchronized void method (Object o) {
    }

   private void test () {
       for (int i = 0; i < vector.size(); i++) {
           method (vector.elementAt(i));   // violation
       }
    }
   private Vector vector = new Vector (5, 5);

}

更正:

不要在循环体中调用同步方法,如果必须同步的话,推荐以下方式:

import java.util.Vector;

public class SYN {

   public void method (Object o) {

    }

private void test () {
   synchronized{//在一个同步块中执行非同步方法
           for (int i = 0; i < vector.size(); i++) {
               method(vector.elementAt(i));  
           }
       }
    }
   private Vector vector = new Vector (5, 5);}

 

1.2.5生成对象时,分配合理的空间和大小

例如:

StringBuffer sb = new StringBuffer();①
//StringBuffer sb = new StringBuffer(10000000); ②
        Long t1 = new Date().getTime();
        for(int i =0;i<10000000;i++){
            sb.append("a");
        }
        Long t2 = new Date().getTime();
        System.out.println("耗时:"+(t2-t1)+"ms");

输出结果:耗时:563ms

输出结果:耗时:480ms

1.2.6慎用异常处理

抛出一个异常和执行一个catch代码块花费是很高的(主要由于当创建一个异常时要获得线程栈的一个快照) 抛出异常首先要创建一个新的对象。Throwable接口的构造函数调用名为fillInStackTrace()的本地(Native)方法,fillInStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,JVM就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。使编译器和运行时最优化,将几个方法调用放在一个try/catch块中,而不是为每个方法调用实现几个try/catch块。

例如:

try{
  Some.method1();
}catch(method1Exception e){
 handle exception 1
}

try{
  Some.method2();
}catch(method2Exception e){
 handle exception 2
}

  替换为:

try{
  Some.method1();
  Some.method2();
}catch(method1Exception e){
  handle exception 1
}catch(method2Exception e){
  handle exception 2
}

1.2.7乘法和除法

考虑下面的代码:

for (val = 0; val < 100000; val +=5) {alterX = val * 8; myResult = val * 2; }

替换为:

for (val = 0; val < 100000; val += 5) {alterX = val << 3; myResult = val << 1; }//

修改后的代码不再做乘以8的操作,而是改用等价的左移3位操作,每左移1位相当于乘以2。相应地,右移1位操作相当于除以2。值得一提的是,虽然移位操作速度快,但可能使代码比较难于理解,所以最好加上一些注释。

1.2.8数组复制

'System.arraycopy ()' 要比通过循环来复制数组快的多。          

例如:

public class Test {
    voidmethod() {
        int[]array1 = new int[100];
        for(int i = 0; i <array1.length; i++) {
            array1[i] = i;
        }
        int[] array2 = new int[100];
       for (int i = 0; i < array2.length; i++){
           array2[i] = array1[i];
       }
    }
}    

    

替换为:

public class Test {
    voidmethod() {
        int[]array1 = new int[100];
        for(int i = 0; i <array1.length; i++) {
            array1[i] = i;
        }
        int[] array2 = new int[100];
       System.arraycopy(array1, 0, array2, 0, 100);
    }
}

1.2.9如果只是查找单个字符,用charAt()代替startsWith()

用一个字符作为参数调用startsWith()也会工作的很好,但从性能角度上来看,调用用String API无疑是错误的!

例子:

public class Test {
   private void method(String s) {
       if (s.startsWith("A")) {
           // do something…
       }
    }
}     

   

替换为: 

将'startsWith()' 替换成'charAt()'.

public class Test {
   private void method(String s) {
       if ('A' == s.charAt(0)) {
           // do something…
       }
    }
}

1.3框架优化

1.3.1优化struts

1.3.2优化spring

1.3.3优化hibernate

 

2数据库优化

2.1SQL语句

2.1.1针对select  t.*  from tablename t 优化

替换为:

Select t.id ,t.name  from tablename t

2.1.2大写SQL语句

在JAVA + ORACLE 的应用系统开发中,java中内嵌的SQL语句尽量使用大写的形式,以减轻ORACLE解析器的解析负担。

2.1.3 IN操作符优化

优化器把使用IN比较符的检索表达式替换为等价的使用“=”和“OR”操作符的检索表达式。

例如,优化器会把表达式enameIN ('SMITH','KING','JONES')替换为ename = 'SMITH' ORename = 'KING' OR ename = 'JONES‘

2.1.4BETWEEN 操作符优化:

    优化器总是用“>=”和“<=”比较符来等价的代替BETWEEN操作符。

例如:优化器会把表达式sal BETWEEN 2000 AND 3000用sal >= 2000 AND sal <= 3000来代替。

2.1.5 NOT 操作符优化:

    优化器总是试图简化检索条件以消除“NOT”逻辑操作符的影响,这将涉及到“NOT”操作符的消除以及代以相应的比较运算符。

    例如,优化器将下面的第一条语句用第二条语句代替:NOT deptno =(SELECT deptno FROM emp WHERE ename = 'TAYLOR'); deptno <> (SELECT deptnoFROM emp WHERE ename = 'TAYLOR')

    通常情况下一个含有NOT操作符的语句有很多不同的写法,优化器的转换原则是使“NOT”操作符后边的子句尽可能的简单,即使可能会使结果表达式包含了更多的“NOT”操作符。

    例如,优化器将如下所示的第一条语句用第二条语句代替。NOT (sal <1000 OR comm IS NULL); NOT sal < 1000 AND comm IS NOT NULL sal >= 1000AND comm IS NOT NULL

 

2.1.6 IN和EXISTS

EXISTS要远比IN的效率高。里面关系到full table scan和range scan。几乎将所有的IN操作符子查询改写为使用EXISTS的子查询。

NOT(sal < 1000 OR comm IS NULL)

NOTsal < 1000 AND comm IS NOT NULL sal >= 1000 AND comm IS NOT NULL

2.2降低与数据库创建连接的频率

2.2.1jdbc连接池

连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用。使用JDBC直接访问数据库中的数据,每一次数据访问请求都必须经历建立数据库连接、打开数据库、存取数据和关闭数据库连接等步骤,而连接并打开数据库是一件既消耗资源又费时的工作,如果频繁发生这种数据库操作,系统的性能必然会急剧下降,甚至会导致系统崩溃。数据库连接池技术是解决这个问题最常用的方法。项目中常用的连接池的方式有dbcp , c3p0, proxool 等。

2.2.2大数据量插入使用批量操作

例如:

PreparedStatement  预先准备语句

String sql = "INSERT INTO Users VALUES(?,?)";

PreparedStatement stmt = conn.prepareStatement(sql);

User[ ] users = ...;

for(int i=0; i<users.length; i++) {

stmt.setInt(1, users[i].getName());

stmt.setInt(2, users[i].getAge());

stmt.addBatch( );

}

int[ ] counts = stmt.executeBatch();

同时,如果使用参数化的预先准备语句,就可以提高数据库和你的服务器端的代码的效率。这些提高都会允许你的应用程序提高性能。

2.2.3多表关联查询代替多表分批查询

例如:

select t1.id  from tablename1 t1 wheret1.name=’NAME’;

select t2.* from tablename2 t2 where t2.tablename1_id=’t1.id’ ;

 替换为:

Select t2.* from tablename2 t2

left join tablename1 t1 on t1.id=t2.tablename1_id

where t1.name=’NAME’;

2.2.4数据缓存机制

缓存是介与应用程序和物理数据源之间,其作用是为了降低应用程序对物理数据源访问的频次,从而提高了应用的运行性能。在开发中我们可以采用的方式框架(如:hibernate)缓存机制或第三方(memcachedehcacheredis等)缓存方式,将查询的数据缓存在内存中。

2.3索引

2.3.1 使用索引的优点

1、通过唯一性索引(unique)可确保数据的唯一性

2、加快数据的检索速度

3、加快表之间的连接

4、减少分组和排序时间

5、使用优化隐藏器提高系统性能

2.3.2使用索引的原则

1、在需要经常搜索的列上创建索引

2、主键上创建索引

3、经常用于连接的列上创建索引

4、经常需要根据范围进行搜索的列上创建索引

5、经常需要排序的列上创建索引

6、经常用于where子句的列上创建索引

2.3.3不创建索引的原则

1、查询很少使用和参考的列不建索引

2、对只有少数值的列不建索引

3、定义为text、image、bit的列不建索引

4、当需要update性能远远高于select性能时不应建索引

2.4定期清洗数据库

在一定程度上,数据库表中数据量过多时会影响到数据操作性能,减缓响应速度。对于一些非实时性的数据库表,我们可以选择定期清洗来降低数据量,从而提高查询性能。清洗的方式将数据库表中的数据迁移到历史表(新建表)中存放。

2.5数据库集群和库表散列

大型网站都有复杂的应用,这些应用必须使用数据库,那么在面对大量访问的时候,数据库的瓶颈很快就能显现出来,这时一台数据库将很快无法满足应用,于是我们需要使用数据库集群或者库表散列。

在数据库集群方面,很多数据库都有自己的解决方案,Oracle、Sybase等都有很好的方案,常用的MySQL提供的Master/Slave也是类似的方案,您使用了什么样的DB,就参考相应的解决方案来实施即可。

上面提到的数据库集群由于在架构、成本、扩张性方面都会受到所采用DB类型的限制,于是我们需要从应用程序的角度来考虑改善系统架构,库表散列是常用并且最有效的解决方案。我们在应用程序中安装业务和应用或者功能模块将数据库进行分离,不同的模块对应不同的数据库或者表,再按照一定的策略对某个页面或者功能进行更小的数据库散列,比如用户表,按照用户ID进行表散列,这样就能够低成本的提升系统的性能并且有很好的扩展性。sohu的论坛就是采用了这样的架构,将论坛的用户、设置、帖子等信息进行数据库分离,然后对帖子、用户按照板块和ID进行散列数据库和表,最终可以在配置文件中进行简单的配置便能让系统随时增加一台低成本的数据库进来补充系统性能。

3前端页面优化:

3.1减少HTTP请求数量

这条策略基本上所有前端人都知道,而且也是最重要最有效的。都说要减少HTTP请求,那请求多了到底会怎么样呢?首先,每个请求都是有成本的,既包含时间成本也包含资源成本。一个完整的请求都需要经过DNS寻址、与服务器建立连接、发送数据、等待服务器响应、接收数据这样一个漫长而复杂的过程。时间成本就是用户需要看到或者感受到这个资源是必须要等待这个过程结束的,资源上由于每个请求都需要携带数据,因此每个请求都需要占用带宽。另外,由于浏览器进行并发请求的请求数是有上限的,因此请求数多了以后,浏览器需要分批进行请求,因此会增加用户的等待时间,会给用户造成站点速度慢这样一个印象,即使可能用户能看到的第一屏的资源都已经请求完了,但是浏览器的进度条会一直存在。

3.2资源合并

定义全局公共文件比如:图片文件、JS文件、CSS样式文件。

将页面中散列公共的图片合并为一张图片从而达到减少图片资源大小的目的,javascript文件、CSS文件同理。并且由专门人员编写javascipt脚本文件将其组件化。

3.3将CSS放在head中

如果将CSS放在其他地方比如BODY中,则浏览器有可能还未下载和解析到CSS就已经开始渲染页面了,这就导致页面由无CSS状态跳转到CSS状态,用户体验比较糟糕。除此之外,有些浏览器会在CSS下载完成后才开始渲染页面,如果CSS放在靠下的位置则会导致浏览器将渲染时间推迟。

 

3.4将外部Javascript脚本置底

浏览器是可以并发请求的,这一特点使得其能够更快的加载资源,然而外链脚本在加载时却会阻塞其他资源,例如在脚本加载完成之前,它后面的图片、样式以及其他脚本都处于阻塞状态,直到脚本加载完成后才会开始加载。如果将脚本放在比较靠前的位置,则会影响整个页面的加载速度从而影响用户体验。解决这一问题的方法有很多,在这里有比较详细的介绍(这里是译文和更详细的例子),而最简单可依赖的方法就是将脚本尽可能的往后挪,减少对并发下载的影响。

3.5延迟加载 Javascript脚本文件

随着Javascript框架的流行,一种是为那些流量特别大的页面专门定制一个专用的mini版框架,另一种则是Lazy Load,最初只加载核心模块,其他模块可以等到需要使用的时候才加载。

 

3.6页面数据静态化

可以直接使用Html静态化页面,或通过freemarker模板技术生成静态化页面。

3.7数据压缩GZIP

HTTP协议上的GZIP编码是一种用来改进WEB应用程序性能的技术。大流量的WEB站点常常使用GZIP压缩技术来让用户感受更快的速度。这一般是指WWW服务器中安装的一个功能,当有人来访问这个服务器中的网站时,服务器中的这个功能就将网页内容压缩后传输到来访的电脑浏览器中显示出来.一般对纯文本内容可压缩到原大小的40%.这样传输就快了,效果就是你点击网址后会很快的显示出来.当然这也会增加服务器的负载. 一般服务器中都安装有这个功能模块的。 

4服务器优化:

4.1服务器硬件优化

4.2负载均衡

1、DNS负载均衡

2、代理(反代理)服务器负载均衡

4.3 图片服务器分离  

大家知道,对于Web服务器来说,不管是ApacheIIS还是其他容器,图片是最消耗资源的,于是我们有必要将图片与页面进行分离,这是基本上大型网站都会采用的策略,他们都有独立的图片服务器,甚至很多台图片服务器。这样的架构可以降低提供页面访问请求的服务器系统压力,并且可以保证系统不会因为图片问题而崩溃,在应用服务器和图片服务器上,可以进行不同的配置优化,比如apache在配置ContentType的时候可以尽量少支持,尽可能少的LoadModule,保证更高的系统消耗和执行效率。

4.4设计服务镜像

镜像是大型网站常采用的提高性能和数据安全性的方式,镜像的技术可以解决不同网络接入商和地域带来的用户访问速度差异,比如ChinaNet和EduNet之间的差异就促使了很多网站在教育网内搭建镜像站点,数据进行定时更新或者实时更新。在镜像的细节技术方面,这里不阐述太深,有很多专业的现成的解决架构和产品可选。也有廉价的通过软件实现的思路,比如Linux上的rsync等工具。

 




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值