關于 Java Web 項目性能提升的一些思路
使用 Nginx 作為前端接入
用 Nginx 進行動靜分離。這個不用多講,新浪、網易、淘寶、騰訊等巨頭的使用已經說明了一切。
保持最簡單的架構
遵守 KISS 原則(Keep it simple and stupid)。盡量不要考慮項目外的重用。過多的考慮項目外的重用,必然會增加項目的復雜度。避免過度集成,讓每個模塊只做自己的事,這對于日后的維護和模塊復用都有好處。
精心設計緩存處理、毫不吝嗇代碼(對象、列表、片段)
對于門戶網站的首頁來說,往往可能會有近百個 SQL。用戶并發上去以后,光首頁就足以讓服務器 down 掉。緩存不但有利于降低負載,而且還能提高響應速度。
調整使用聚集索引
對于每個表來講,聚集索引只有一個,利用好了,查詢速度會有意想不到的提升效果。
以 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 來存儲緩存的磁盤文件
在網站運維中,利用好了這一點,往往有意想不到的收獲。以 tomcat 為例,可以通過修改 catalina.sh 中的 CATALINA_TMPDIR 值的路徑來將緩存設置為 /dev/shm。
以 OSC 為例,他們就是純 Java 寫的,部署在 tomcat 下。在長時間的在線運行之后,管理員發現網站響應速度奇慢,服務器負載正常,又找不出是哪里的問題。后來 df 一下,發現 tomcat 臨時目錄下的文件足足有 8G 之多,原來是 CPU 等待磁盤操作造成響應速度加長。于是他們將臨時目錄映射到 /dev/shm,網站響應速度從此奇快。
分析系統中每一個 SQL 的執行效率
以 MySql 為例,對于每個 SQL 最好都 explain 一下。對于有明顯效率問題的,通過 sql 優化、調索引等方法進行改進。
健康慢查詢日志,檢查所有執行超過 100 毫秒的 SQL
對于上線了的項目,健康慢查詢日志,檢查所有執行超過 100 毫秒的 SQL,看看有沒有優化余地。對于沒有上線的項目,可以進行場景模擬對嫌疑 SQL,或者對頻繁使用的 SQL 進行性能測試,統計它們執行時間,得出平均值,畫出曲線分析圖,對于單表千萬數據,執行時間超過 50ms 的 SQL 要重點關注。參考《sql 性能測試例子》。
posted on 2013-12-23 09:59 順其自然EVO 閱讀(701) 評論(0) 編輯 收藏 所屬分類: 數據庫