天天看點

線上系統查詢一次要10s,我一怒優化了幾百行的SQL

作者:石杉的架構筆記

目錄

  • 業務場景
  • 如何通過緩存優化查詢接口
  • 基于大資料離線平台進行緩存預熱
  • 本地緩存架構:Ehcache

今天給大家來分享一個知識,那就是平時我們開發系統的時候,如何運用 Ehcache 這款本地緩存架構,把我們的查詢性能大幅度提升優化,甚至讓很多查詢操作性能提升到 100 倍以上,下面就來講講這個話題。

業務場景

首先給大家引入一個場景,就是假設咱們寫的一套 Java 系統要跑一個幾百行的大 SQL 從 MySQL 裡查詢資料,這個查詢是不是會速度非常的慢?

那肯定是了,這種幾百行大 SQL 往往都是那種超級複雜的查詢,可能涉及到了多表的關聯,也有的是那種資料名額的查詢,當然這種資料名額的查詢其實是會常見一些,就是針對各種資料表關聯起來查詢和統計一些名額。

一般來說的話,遇到這種超級大 SQL,往往會導緻查詢 MySQL 性能很差,一般跑個 1s 甚至好幾秒那是很常見的了。

比如下圖:

線上系統查詢一次要10s,我一怒優化了幾百行的SQL

是以 往往對于這種場景來說,如果想要優化一下這個查詢的性能,我們一般會用緩存。

也就是說,這一次用幾百行 SQL 語句查詢出了結果,好不容易用了幾秒鐘特别特别慢,接着其實就把這個結果緩存起來,下次請求過來,直接就用這個緩存裡的資料拿出來傳回就可以了,從緩存裡讀結果以及傳回,最多就是個 1ms 的事兒,根本不用幾秒那麼漫長了。

如何通過緩存優化查詢接口

那麼問題來了,這個緩存的結果是放哪裡?可能很多兄弟說可以放 Redis 裡啊!但是,一定要每次用緩存就立馬上 Redis 嗎?

畢竟 Redis 還得額外部署叢集,一旦引入 Redis,你還得考慮 Redis 是否會有故障,他的一些接入問題,以及跟 Redis 進行網絡通信畢竟也是要耗時的。

是以說,其實咱們優先啊,可以先上本地緩存,也就是說,在業務系統運作的 JVM 的堆記憶體裡,來緩存我們的查詢結果,下次請求來了,就從本地緩存裡取出來直接傳回就可以了。

如下圖:

線上系統查詢一次要10s,我一怒優化了幾百行的SQL

基于大資料離線平台進行緩存預熱

那麼下一個問題又來了,很多查詢他可能當天第一次查的時候,本地緩存裡是沒有的,還是得去 MySQL 裡花費幾秒鐘來查詢,查完了以後才能放入到本地緩存裡去,那這樣豈不是每天都有一些人第一次查詢很慢很慢嗎?

有沒有更好的辦法呢?當然有了,那就是緩存預熱,我們的業務系統可以把每天的查詢請求和參數都記錄下來。

對于一些資料報表的複雜查詢,其實每天的查詢條件都是差不多的,隻不過是當天的日期會有變化而已,另外就是對于一些資料報表的資料,往往是通過大資料平台進行離線計算的。

啥叫做離線計算呢?就是說可能有一個大資料系統每天淩晨的時候會把昨天的資料算一遍,算好的資料結果寫入到 MySQL 裡去,然後每天更新資料就這一次,接着當天就不更新資料了。

如下圖:

線上系統查詢一次要10s,我一怒優化了幾百行的SQL

然後呢,使用者每天都會對我們的系統發起很多次複雜報表查詢語句,但是這個 SQL 多表關聯的一些邏輯,以及附加的一些查詢條件幾乎都是有規律的是差不多的,就是每天選擇的當天日期是不太一樣的。

是以此時我們就可以把這些查詢條件記錄下來,然後每天淩晨的時候,趁着大家都睡覺了,就根據經常查詢的條件和當天日期,提前去查詢資料,查詢結果提前寫入本地緩存。

這樣使用者第一次來通路,就可以直接從本地緩存裡拿到最新的資料了,如下圖:

線上系統查詢一次要10s,我一怒優化了幾百行的SQL

本地緩存架構:Ehcache

接着給大家講講咱們常用的本地緩存架構,Ehcache,這是大名鼎鼎的一個本地緩存架構,基本上 Ehcache 和 Guava 兩款本地緩存架構,用的是最多的,我們以 Ehcache 舉例來講講本地緩存架構是怎麼用的。

首先得在咱們的項目 pom.xml 裡引入對應的依賴,如下所示:

<dependency>
  <groupId>net.sf.ehcache</groupId>
  <artifactId>ehcache</artifactId>
  <version>2.10.2</version>
</dependency>           

接着就可以引入一個 ehcache.xml 這種配置檔案,對我們的緩存架構進行一定的配置了。

如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
    <cache name="report" 
         maxElementsInMemory="1000" 
         eternal="false" 
         timeToLiveSeconds="86400" 
         overflowToDisk="false" 
         disPersistent="false" 
         memoryStoreEvictionPolicy="LRU" />
</ehcache>           

下面給大家解釋一下 ehcache 架構運作起來以後上述那些參數對他的影響,首先 maxElementsInMemory 說的就是他在記憶體裡可以緩存多少條資料。

eternal 意思是說這個緩存是否是永久有效的,如果要是永久有效了那麼 timeToLiveSeconds 也就沒用了。

但是如果不是永久有效的,就可以設定 timeToLiveSeconds 了,比如說可以設定緩存資料生存 24 小時,然後就自動過期,接着就必須要強制從資料庫裡來查詢了。

overflowToDisk 是說如果緩存的資料要是超過了 maxElementsInMemory 的時候,是不是把多餘的資料刷寫到磁盤裡去。

diskPersistent 是說在 JVM 重新開機的時候,要不要把記憶體裡緩存的資料刷寫到磁盤裡去,然後 JVM 重新開機後再把磁盤裡的資料恢複到記憶體裡來,這倆參數,如果要是緩存的資料特别多的話,其實還是可以開啟的。

一方面是記憶體緩存不下了可以刷寫到磁盤去,一方面是記憶體裡的資料重新開機的時候還是持久化一下,然後重新加載到記憶體裡來。

還有一個是 memoryStoreEvictionPolixy 是緩存的回收政策,因為如果要是緩存資料量過多了,導緻記憶體和磁盤都放不下了,這個時候就必須回收掉一部分的資料了,一般都是用 LRU,最近最少使用政策來回收的。

下面是 Ehcache 在代碼裡的使用示例:

public class EhcacheTest {

  public static void main(String[] args) {
    CacheManager cacheManager = CacheManager.create();
    Cache cache = cacheManager.getCache("report");
    cache.put(new Element("key", "value"));
    cache.get("key").getObjectValue();
  }

}           

希望今天給大家分享的本地緩存知識可以幫助到大家以後遇到類似的複雜報表資料查詢場景的時候,可以利用這個知識點去優化自己系統的性能!

------------- END -------------

線上系統查詢一次要10s,我一怒優化了幾百行的SQL

另外推薦儒猿課堂的1元系列課程給您,歡迎加入一起學習~

網際網路Java工程師面試突擊課(1元專享)「連結」

SpringCloudAlibaba零基礎入門到項目實戰(1元專享)「連結」

億級流量下的電商詳情頁系統實戰項目(1元專享)「連結」

Kafka消息中間件核心源碼精講(1元專享)「連結」

12個實戰案例帶你玩轉Java并發程式設計(1元專享)「連結」

Elasticsearch零基礎入門到精通(1元專享)「連結」

基于Java手寫分布式中間件系統實戰(1元專享)「連結」

基于ShardingSphere的分庫分表實戰課(1元專享)「連結」

繼續閱讀