这是CSDN上的一篇文章:http://www.csdn.net/article/2014-04-28/2819531/3
我觉得很不错,所以摘了一部分过来,以后可以经常看看。
如何才能提高Java Web性能?
1.使用Nginx作为前端接入
用Nginx进行动静分离。这个不用多讲,新浪、网易、淘宝、腾讯等巨头的使用已经说明了一切。
2.保持最简单的架构
遵守KISS原则(Keepitsimpleandstupid)。尽量不要考虑项目外的重用,过多的考虑项目外的重用,必然会增加项目的复杂度。避免过度集成,让每个模块只做自己的事,这对于日后的维护和模块复用都有好处。
3.精心设计缓存处理、毫不吝啬代码(对象、列表、片段)
对于门户网站的首页来说,往往可能会有近百个SQL。用户并发上去以后,光首页就足以让服务器down掉。缓存不但有利于降低负载,而且还能提高响应速度。
4.调整使用聚集索引
对于每个表来讲,聚集索引只有一个,利用好了,查询速度会有意想不到的提升效果。
以MySql为例,InnoDB选取聚集索引参照列的顺序是
1)如果声声明了主键(primarykey),则这个列会被做为聚集索引;
2)如果没有声明主键,则会用一个唯一且不为空的索引列做为主键,成为此表的聚集索引;
3)上面二个条件都不满足,InnoDB会自己产生一个虚拟的聚集索引。
- CREATETABLE`timeline_raw`(
- `rawId`bigint(20)NOTNULLAUTO_INCREMENT,
- `uid`bigint(20)DEFAULTNULL,
- `did`bigint(20)DEFAULTNULL,
- `channelId`char(1)NOTNULLDEFAULT'1'COMMENT'1:qvga;2:720p',
- `fileId`bigint(20)DEFAULTNULL,
- `sectionId`bigint(20)DEFAULTNULL,
- `headerFilePath`varchar(120)DEFAULTNULL,
- `startTime`bigint(20)DEFAULTNULL,
- `endTime`bigint(20)DEFAULTNULL,
- `updateTime`datetimeDEFAULTNULL,
- `createTime`datetimeDEFAULTNULL,
- PRIMARYKEY(`rawId`),
- KEY`index_uid_did_startTime`(`uid`,`did`,`startTime`)USINGBTREE,
- KEY`index_uid_did_endTime`(`uid`,`did`,`endTime`)USINGBTREE,
- KEY`index_time`(`startTime`)USINGBTREE,
- KEY`index_uid_did_fileId`(`uid`,`did`,`sectionId`)USINGBTREE,
- KEY`index_sectionId`(`sectionId`)
- )ENGINE=InnoDBAUTO_INCREMENT=1DEFAULTCHARSET=utf8
这个表有四个索引:主键rawId、sectionId、`uid`,`did`、startTime。
项目的iBatis2中有这样一条查询语句:
- <selectid="getRawFileList"parameterClass="java.util.HashMap"resultClass="com.defonds.mysql.raw.entity.TimelineRaw">
- SELECT*FROMtimeline_raw_
- WHEREuid=#uid#
- ANDdid=#did#
- ANDchannelId=#channelId#
- <isNotNullproperty="sectionId">ANDsectionId=#sectionId#</isNotNull>
- AND
- (
- (startTimeBETWEEN#startTime#and#endTime#)
- OR
- (endTimeBETWEEN#startTime#and#endTime#)
- OR
- (
- <![CDATA[
- startTime<=#startTime#
- ]]>
- AND
- <![CDATA[
- endTime>=#endTime#
- ]]>
- )
- )
- ORDERBYstartTime;
- </select>
根据实际业务向timeline_raw表注入一千万条数据,进行模拟测试,发现getRawFileList的执行平均时间为160ms以上。这是不能接受的。
考虑到实际业务中对于主键rawId查询条件甚少,我们把rawId主键索引取消掉,改为唯一约束,却把sectionId+startTime+endTime作为主键(业务上能够保证其唯一性,根据InnoDB索引规则,这个索引将成为我们新表的聚集索引)。然后把sectionId、startTime两个索引也取消掉,仅保留`uid`,`did`索引。
这样子,我们新表的索引实际上只有两个了:一个聚集索引(sectionId+startTime+endTime)一个非聚集索引(`uid`,`did`)。
再次进行模拟测试,同样的数据、数据量,同样的查询结果集,getRawFileList执行平均时间已经降到了11ms。结果是令人振奋的,不是么?
5.使用/dev/shm来存储缓存的磁盘文件
在网站运维中,利用好了这一点,往往有意想不到的收获。以tomcat为例,可以通过修改catalina.sh中的CATALINA_TMPDIR值的路径来将缓存设置为/dev/shm。
以OSC为例,他们就是纯Java写的,部署在tomcat下。在长时间的在线运行之后,管理员发现网站响应速度奇慢,服务器负载正常,又找不出是哪里的问题。后来df一下,发现tomcat临时目录下的文件足足有8G之多,原来是CPU等待磁盘操作造成响应速度加长。于是他们将临时目录映射到/dev/shm,网站响应速度从此奇快。
6.分析系统中每一个SQL的执行效率
以MySql为例,对于每个SQL最好都explain一下。对于有明显效率问题的,通过sql优化、调索引等方法进行改进。
7.健康慢查询日志,检查所有执行超过100毫秒的SQL
对于上线了的项目,健康慢查询日志,检查所有执行超过100毫秒的SQL,看看有没有优化余地。对于没有上线的项目,可以进行场景模拟对嫌疑SQL,或者对频繁使用的SQL进行性能测试,统计它们执行时间,得出平均值,画出曲线分析图,对于单表千万数据,执行时间超过50ms的SQL要重点关注。
在Java Web项目架构设计上有没有什么经验可以分享?
- 拆分(软件部署方式(业务、架构(展现层、业务逻辑层、持久层)))软件包括自己的、第三方的(DB、MQ...);
- 抽取通用服务,形成自己应用的中间线;
- 负载均衡(LVS、HProxy、读写分离、切片);
- 高可用\故障转移(heartbeat、keepalive、MasterSlave);
- 共享存储(nfs、iscsi);
- 在业务和技术之间做出最佳的选择。
CSDN:有人说,初学Java Web开发的人,要远离各种框架,对此,你怎么看?
孔德芳:我支持这种说法。框架把程序员要做的很多事情封装起来,让我们能够专注于企业业务开发,能显著提高开发效率。但是一开始应该注重基础和原理,初学Java Web开发,一开始就上手框架,很容易忽略掉那些基础知识。很多人认为Java Web开发就是SSH,一系列的配置文件拷来拷去;很多程序员做了几年Java Web,你问他定义的一个对象的生命周期描述一下,以及为什么要用spring管理,他都答不出来;还有少数一部分人更是连js、jsp哪个是在服务器端执行哪个是在客户端执行都分不清楚,弄出来很多本来期望在客户端弹出的窗口却在服务器端弹出来的笑话…这些都是一开始学习上手框架,没有注重基础学习的后果。可以说成也框架,败也框架。
CSDN:在Java Web学习上,有没有什么学习难点?如何解决这些难点?
孔德芳:难点是Web的MVC实现、项目的测试和调试。项目的测试和调试是学习难点,这个没得说,这是每个程序员的基本功底,从某种意义上来讲能够代表一个程序员水平的高低,需要我们在项目实践中多动手、多思考,不断自我积累和提高。那么MVC这种大家耳熟能详的东东,怎么也成了难点了?事实上,我经常在Java Web版看到网友把SSH进行MVC的三层对号入座。这就暴露出来他根本就没有理解MVC。事实上,MVC的三层和所谓SSH不是一个概念,不要硬性按号入座。SSH体系的分工是为纵向分工是线性分布,而MVC是面性的三者之间都能互相合作,虽然ssh目的和MVC一样,都是支持可维护性可扩展性为目的。
CSDN:Java Web程序员在熟练使用一些框架之后,如何进一步提高技术?
孔德芳:Java Web程序员在熟练使用一些框架之后,要想避免成为熟练工,突破现有瓶颈,应该多关注以下这些问题:
- 海量数据的处理
- 数据并发的处理
- 文件存贮
- 数据关系的处理
- 数据索引
- 分布式处理
- 安全防御
- 数据同步和集群的处理