一次優化經歷

2021-08-03 20:01:16 字數 1612 閱讀 9063

問題:excel資料匯入,儲存到資料庫中,為了優化查詢效率和其他一些業務需求,需要將資料的一列屬性切分後儲存到redis中,插入資料庫前要保證其中乙個屬性不重複,另外乙個屬性已經在資料庫中。

為了將問題描述簡單些,我們假設excel中有兩列資料a和b,其中資料a要保證資料庫中不重複,資料b保證資料庫中已經存在,並且要將a切分為字首儲存到redis中。

原本的**匯入兩千條資料要10幾分鐘的時間,比較明顯的可以優化的地方有兩點:

1:原本**中判斷資料a是否重複,用遍歷資料a,去資料庫中查詢是否已經存在

2:原本**中判斷資料b是否已經存在,也是遍歷資料b,去資料庫中查詢是否存在。

我們知道,每次資料庫連線都有耗時,2000條資料,遍歷查詢就需要2000次資料庫連線,我們可以採用in查詢,並且如果in查詢的條件有索引,也是會利用索引的。

為了證明,我建了乙個臨時表,只有乙個字段,查詢一百次,分別用in和迴圈查詢,迴圈查詢100次時間可以見:

而用in查詢可以看到時間

相差很大。

可以看到時間效率相差很大。

我將插入語句優化後,整體匯入時間有了很大提公升,2000條資料需要幾分鐘的時間,在資料庫方面優化已經足夠,那麼問題點出現在**?

首先想到的一次性讀入兩千條資料,會不會導致full gc。因為建立乙個大物件時,會將物件直接分配到老年代,如果老年代連續空間不夠,會導致full gc。所以我們應該避免建立壽命短的大物件。根據jvm引數將此原因排除。

那麼只有儲存redis耗時了,原redis儲存方式是for迴圈插入,2000條資料,a欄位平均20個字元,那麼就需要迴圈呼叫4萬次redis的zset操作。此時redis的pipeline就派上了用場,首先我們知道redis是單執行緒操作,所有的操作必須按順序執行,執行完乙個操作返回一次結果, 因為每一次client鏈結redis server都是一次tcp鏈結,那麼4萬次,就需要8萬次tcp報文傳輸,大部分時間消耗在傳輸耗時中了,那麼pipeline就是客戶端一次傳送多個命令,無需等待前乙個命令返回結果,等到server處理完所有操作,統一返回報文。那麼2萬次操作只需要兩次報文傳輸,不過redis推薦每次操作不超過1萬個(經過實踐,一次傳送10萬指令沒有問題),那麼我將其劃分為幾次,分批操作。

優化後2000條資料只需要十幾秒鐘的時間就可以匯入完成。

以上優化比較簡單,都是一些基礎知識。

還有一些不足的地方,一是一次性讀取整個excel資料有待優化,因為一次性建立大物件,會直接分配到老年代,而老年代的**需要full gc。我們的匯入物件又壽命很短,所以會觸發full gc。

二是redis的pipeline不會保證事務性,即有的操作失敗後不會影響其他操作,不過我們可以獲取返回值後判斷哪些失敗,重發請求即可。

細節:在redis中還儲存了資料a的快取,其實查詢a是否存在有兩種方案選擇

1:單個查詢,可以利用到redis快取(我們會對a屬性在redis做key value快取) 

2:in查詢,不會利用redis快取

那麼根據業務需求,匯入的資料絕大部分都是資料庫中不存在的,只有極個別的錄錯資料才會存在資料庫中,那麼redis命中率極低,會穿透查詢資料庫,所以我採用了in查詢,不利用redis快取。

一次優化記錄

備註 由於隱私 部分使用了偽 偽sql 直接查十點查全部 select from 使用者優惠券表 where 優惠券id in select id from 優惠券表 where 限制 新使用者 and 90天內 總時間40 秒 這裡用exlpain分析 優惠券id是有索引的,但是實際上沒有走索引。...

一次優化記錄

今天收到乙個同事的求助,說有乙個sql跑了乙個多小時沒有結果。我看了看,這個sql是這樣的 隱藏了敏感資訊 select 號碼,列2,列3,max starttime flag from 表1 t1 where flag 0 and 號碼 not in select 號碼 from 表2 t2 gr...

一次優化web專案的經歷記錄(二)

前言 最近很長一段時間沒有更新部落格了,忙於一堆子專案的開發,嚴重拖慢了學習與思考的程序。開水倒滿了需要提早放下杯子,晚了就會燙手,這段時間以來,寫的東西越來越不嚴謹,各種低階錯誤頻出,早該停下總結並鞏固一下了。但出於一些原因一直沒付諸於行,終於,燙到手了 在python裡,這很容易實現,借助裝飾器...