前兩天網站訪問慢的問題定位過程以及最終解決辦法 原 薦

前提說明:目前開源中國社區在做改版,因此網站同時存在新舊兩種不同的版面,例如個人空間就是新的系統。因此整個網站包含兩套系統,改版前和改版後,我們把改版前叫老系統,把改版後叫新系統。

大概一週多的時間,經常會感覺網站,包括客戶端使用過程中有卡頓的情況,沒太在意。但是前天這個卡頓就變得比較嚴重,動彈裏開始出現各種反應訪問慢的信息。

檢查所有的機器的 CPU 使用率都在一個正常的狀態,Tomcat 無特別的異常日誌,應用的數據庫連接池的忙連接比平時要高好多倍,但是數據庫操作一些正常。

檢查數據庫慢查詢日誌,比平時多了一些,基本上集中在近期改版新的 SQL 上,把這些慢查詢通過索引調整、SQL 優化方式解決後,慢查詢不再出現,但是系統響應速度仍舊沒有提升。

我們一度懷疑是數據庫響應慢,想重啓一下社區的 MySQL 數據庫,這個數據庫已經運行超過 5 年沒重啓過了!!!但還是忍住了!再試試其他不行再說。

然後發現了一個非常非常慢的操作,就是在客戶端上收藏某篇文章的時候,完全沒有響應,或者是要十幾秒纔有反應。

於是在這個收藏功能對應的代碼增加了數據監控點,在日誌中記錄該方法幾個邏輯的執行時間。更新到線上進行測試發現,從在界面上點擊收藏,到日誌的輸出中間隔了好幾十秒,而真正功能的執行時間很短。看似有很多請求在排隊等 Tomcat 處理導致的堵塞,但是 Tomcat 的日誌沒有關於處理請求隊列慢的日誌信息。這是其中一個疑點。

日誌顯示數據庫操作執行十幾毫秒,緩存操作一兩百毫秒,所以可以確定數據庫是沒問題的(幸虧沒重啓)。而緩存操作就算是一兩百毫秒也不應該導致這個系統拖得那麼慢,但是可以明確的是緩存是肯定有問題的。沒準其他更復雜的緩存操作耗時要大很多,導致請求處理的卡頓。

OSChina 一直在用 J2Cache 的兩級緩存框架,這個框架有很多人在噴,但是噴的人都沒用過它。由於 OSChina 現在整個網站同時運行新老兩個系統,因爲很多頁面還沒改版完成。老的系統基於 J2Cache 1.x 運行,新的系統基於 J2Cache 2.x。連接的同一個 Redis 服務,使用不同的 database 進行隔離。

所以我們決定搞一個全新的乾淨的 Redis ,先把老系統切到這個新的 Redis 服務上,切換後先內部測試,首次訪問慢一點,再次訪問速度就很快了。切到生產上,所有的用戶請求的轉到這個系統上,訪問速度很快,請求的處理速度一般只有幾十毫秒。

運行了半天多,速度仍然很穩定。

但是個人空間依然很慢,因爲個人空間是新的系統,接到還是之前的 Redis。於是我們又使用了另外一個獨立的全新的 Redis 服務重新部署了一套空間,剛部署上線完後速度很快,但是過了半個小時一個小時候,就開始感覺沒那麼順暢了。

在觀察系統日誌時,同時也發現了我們新的業務代碼在緩存處理邏輯上的嚴重問題,例如生成大量動態的 Region ,而實際使用 Region 不應該這樣使用,Region 應該事先在配置文件中定義好的。因爲在 J2Cache 中動態創建 Region 是一個線程同步的方法,大量併發請求勢必導致等待。

於是我們修改了這部分處理邏輯,不再動態創建大量的 Region ,再次更新後訪問速度終於恢復了,注意這裏是有兩個系統。

新系統運行改版後的頁面,老系統運行尚未改版的頁面,新老系統通過 Nginx 進行路由。

經過一晚上運行之後,新系統感覺雖然沒有之前慢,但是也時常感覺不順暢!!!但是老系統依然很快。

於是現在的問題是:爲什麼老系統很快,但是新系統運行一段時間後會變慢。

禮拜二早上醒來覺得不服氣,在牀上操起電腦繼續看各種系統日誌和運行狀態 —— 發現了兩者在使用 Redis 服務上的一個區別:

老系統使用 J2Cache 的 hash 模式在 Redis 存儲數據,而新系統使用 J2Cache 的 generic 模式記錄數據。也就是說老系統操作 Redis 使用 hget/hset 這些方法,Region 對應的是 Redis 的一個 key,而具體的緩存數據是 key 對應的子 key 。而新系統直接是 get/set 這樣的方法,也就是說不同 Region 的所有 Key 都揉在一起形成一個巨大的哈希表。

那麼會不會是這個原因導致新系統在運行一段時間後變慢呢?理論上是不應該的,因爲 Redis 的處理速度不會因爲 key 數量的增加而變慢。但我們還是決定一試。

修改 J2Cache 的配置,將 redis.storage 配置值改爲 hash ,清理數據,再次更新系統後,到目前運行已經一整天了,一切非常正常。

自此訪問速度問題的處理告一段落。

但是仍有幾個疑點沒有搞清楚:

  1. 爲什麼 Tomcat 的請求堵塞那麼就沒得到處理,卻沒有異常信息,難道是隊列還沒滿?
  2. 爲什麼 Redis 在 key 的數量達到一定程度後響應速度變慢呢?我們用的是 Redis 3.0.5 版本
  3. 早在 J2Cache 1.x 的時候,當時 Redis 版本還比較低,好像是 2.x ,那時候我測試使用 hash 性能比使用 generic 的低很多,爲什麼現在反過來了? 

全文完 !

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章