【死磕 Java 基礎】— 我同事一個 select 分頁語句查出來了 3000W 條數據


某天我正在工位上聽着 Vicotry,愉快地敲着 hello world ,這感覺就像我寫的代碼能征服世界。突然運維給我打了一個電話,說我們某台服務器 OOM 了,要我過去看下,這感覺就像 xxx,你懂的。

去運維室、登錄服務器、查看日志、....一頓操作猛如虎,看到一個 List 對象 600MB +(原諒我們服務器 low,運維比較小氣,就給 1C2G 的服務器),檢查當時的 SQL 語句,一看,我的乖乖,將近 4000w + 條數據。我的第一感覺就是,難道又是哪個業務在導出大批量數據?但是我們所有的 Excel 導出數據都做了校驗,數據量大於 5w 條就后台分批次導出(所以有時候你要慶幸服務器 low,因為服務器 low,你就需要進行各種各樣的優化,所有大數據量的操作都需要想辦法優化,所以我們這個應用就有了各種有意思的騷操作,后面有機會分享下)。難道沒有控制住?看了日志並沒有發現大數據量的 Excel 導出,所以可以斷定是分頁的地方沒有分頁。看代碼在一個 if 語句里面找到了坑,如下:

PageHelper.startPage(queryDTO.getPage(), queryDTO.getLimit());

Page<UserDTO> page;
if (isWitchFlag()) {
    page = userMapper.selectUserList(queryDTO);
}

isWitchFlag() :

    private boolean isWitchFlag() {
        String witchFlag = systemConfigMapper.selectSwitchFlag("key");
        return "1".equals(witchFlag);
    }

對 PageHelper 不是很熟悉的人一定不知道這個坑在哪里!在 PageHelper 使用文檔(https://pagehelper.github.io/faq/)中第一句就闡述了:

只有緊跟在 PageHelper.startPage 方法后的第一個 Mybatis 的查詢(Select)方法會被分頁。。請注意關鍵詞緊跟。為什么要緊跟呢?因為 PageHelper 的分頁原理使用了 ThreadLocal,他的分頁參數和線程是綁定在一起的,當我們執行 PageHelper.startPage() 語句時,他會將分頁參數綁定到 ThreadLocal 中:

setLocalPage()

在攔截器 PageInterceptor 中,最后的 finally 會將 Page 分頁信息給 remove 掉:

所以,上面那段代碼的分頁信息被 if 語句中的 select 查詢語句給消耗掉了,下面真正需要分頁的語句當然就不會執行分頁信息啦。怎么解決?兩種方案:

  • 治標不治本:將 PageHelper.startPage() 挪到 if 語句里面,讓真正的查詢語句緊挨着它。這種方案不治本的原因在於,如果又有小伙伴不知道這個坑,有可能又會踩。
  • 治標治本:使用 Function Lamdba 表達式。

使用 Function Lamdba 來將 PageHelper.startPage() 與分頁查詢語句緊挨在一起,規避掉這個坑

首先我們需要定義一個 PageHelperTool,該 PageHelperTool 是封裝了分頁語句:

@Builder
public class PageHelperTool<P,R> {
    private final Function<P, Page<R>> pageFunction;

    public Page<R> getPageInfo(P request) {
        PageHelper.startPage(((PageRequest)request).getPage(),((PageRequest)request).getLimit());
        return pageFunction.apply(request);
    }
}

然后將分頁的地方全部替換為 PageHelperTool 就可以了:

        Page<UserDTO> page;
        if (isWitchFlag()) {
            PageHelperTool<QueryDTO,UserDTO> pageHelperTool = PageHelperTool.<QueryDTO,UserDTO>builder()
                                                            .pageFunction(userMapper::selectUserList)
                                                            .build();
            
            page = pageHelperTool.getPageInfo(queryDTO);
        }

這樣就可以徹底解決因誤用 PageHelper 導致分頁失效的問題了。

最后一句話:注意看文檔啊!!!!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM