关于 Java Web 项目性能提升的一些思路
- 使用 Nginx 作为前端接入
- 保持最简单的架构
- 精心设计缓存处理、毫不吝啬代码(对象、列表、片段)
- 调整使用聚集索引
以 MySql 为例,InnoDB选取聚集索引参照列的顺序是
1. 如果声声明了主键(primary key),则这个列会被做为聚集索引;
2. 如果没有声明主键,则会用一个唯一且不为空的索引列做为主键,成为此表的聚集索引;
3. 上面二个条件都不满足,InnoDB会自己产生一个虚拟的聚集索引。
CREATE TABLE `timeline_raw` (
`rawId` bigint(20) NOT NULL AUTO_INCREMENT,
`uid` bigint(20) DEFAULT NULL,
`did` bigint(20) DEFAULT NULL,
`channelId` char(1) NOT NULL DEFAULT '1' COMMENT '1:qvga; 2:720p',
`fileId` bigint(20) DEFAULT NULL,
`sectionId` bigint(20) DEFAULT NULL,
`headerFilePath` varchar(120) DEFAULT NULL,
`startTime` bigint(20) DEFAULT NULL,
`endTime` bigint(20) DEFAULT NULL,
`updateTime` datetime DEFAULT NULL,
`createTime` datetime DEFAULT NULL,
PRIMARY KEY (`rawId`),
KEY `index_uid_did_startTime` (`uid`,`did`,`startTime`) USING BTREE,
KEY `index_uid_did_endTime` (`uid`,`did`,`endTime`) USING BTREE,
KEY `index_time` (`startTime`) USING BTREE,
KEY `index_uid_did_fileId` (`uid`,`did`,`sectionId`) USING BTREE,
KEY `index_sectionId` (`sectionId`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
这个表有四个索引:主键 rawId、sectionId、`uid`,`did`、startTime。
项目的 iBatis2 中有这样一条查询语句:
<select id="getRawFileList" parameterClass="java.util.HashMap" resultClass="com.defonds.mysql.raw.entity.TimelineRaw">
SELECT * FROM timeline_raw_
WHERE uid=#uid#
AND did=#did#
AND channelId=#channelId#
<isNotNull property="sectionId"> AND sectionId = #sectionId#</isNotNull>
AND
(
(startTime BETWEEN #startTime# and #endTime#)
OR
(endTime BETWEEN #startTime# and #endTime#)
OR
(
<![CDATA[
startTime<=#startTime#
]]>
AND
<![CDATA[
endTime>=#endTime#
]]>
)
)
ORDER BY startTime;
</select>
根据实际业务向 timeline_raw 表注入一千万条数据,进行模拟测试(参考《 sql 性能测试例子》),发现 getRawFileList 的执行平均时间为 160 ms 以上。这是不能接受的。
考虑到实际业务中对于主键 rawId 查询条件甚少,我们把rawId主键索引取消掉,改为唯一约束,却把sectionId+startTime+endTime作为主键(业务上能够保证其唯一性,根据InnoDB索引规则,这个索引将成为我们新表的聚集索引)。然后把sectionId、startTime两个索引也取消掉,仅保留`uid`,`did`索引。
这样子,我们新表的索引实际上只有两个了:一个聚集索引(sectionId+startTime+endTime)一个非聚集索引(`uid`,`did`)。
再次进行模拟测试,同样的数据、数据量,同样的查询结果集,getRawFileList 执行平均时间已经降到了 11 ms。结果是令人振奋的,不是么?
- 使用 /dev/shm 来存储缓存的磁盘文件
以 OSC 为例,他们就是纯 Java 写的,部署在 tomcat 下。在长时间的在线运行之后,管理员发现网站响应速度奇慢,服务器负载正常,又找不出是哪里的问题。后来 df 一下,发现 tomcat 临时目录下的文件足足有 8G 之多,原来是 CPU 等待磁盘操作造成响应速度加长。于是他们将临时目录映射到 /dev/shm,网站响应速度从此奇快。
- 分析系统中每一个 SQL 的执行效率
- 健康慢查询日志,检查所有执行超过 100 毫秒的 SQL