記一次復用資料庫連線池填坑巢狀和關聯查詢sql語句

2021-08-28 21:44:15 字數 2910 閱讀 9070

本文為了記錄問題排查解決的過程,以及為後續遇到類似問題解決提供方向。

先描述下問題,需要通過關聯查詢table_a和table_b兩表的資料,分頁顯示,其中,還需要查詢每條記錄的參與人數,對應資料在表table_c中。碼**時為了方便想用一條sql解決問題,測試環境由於資料量級的問題,一切ok!上線時候,查詢資料出現超時情況,前段請求超時時間設定為5秒。

1、排查問題

通過檢視日誌發現部分請求的響應時間5-6秒。隨分析問題原因!

本專案中存在許可權問題,不同使用者檢視資料量不同。然後發現普通使用者檢視列表資料無任何問題,管理員賬號檢視資料出現超時問題,管理員賬號的許可權是可以檢視所有資料,由於檢視列表介面沒有複雜邏輯,先排查下sql語句的執行時間!

先附上sql語句

select t.*, s.name, (select count(o.id) from table_c o where o.act_id = t.id)

from table_a t left join table_b s on t.type = s.type_code where 條件

上面的sql語句很簡單,檢視生產上這條sql執行時間,發現查詢20條記錄差不多接近3秒,全量資料10s以上。檢視執行計畫,發現沒有命中索引,都是全表掃瞄,table access full。首先想到的是left join 改寫為inner join,然而依然是全表掃瞄,後來想到查詢結果裡面有s.name的 ,若把s.name去掉,table_a表inner join table_b關聯欄位有索引是可以命中索引 index range scan(關聯查詢的查詢結果字段需要是索引字段,才能命中索引)。但是查詢結果裡面是需要name。因此不可行。

可命中索引的關聯查詢

select t.*, (select count(o.id) from table_c o where o.act_id = t.id)

from table_a t left join table_b s on t.type = s.type_code where 條件

2、分析問題

先檢視三個表的記錄個數,表a幾千條資料,表b幾十條資料,表c有十幾萬條資料。通過執行計畫可知巢狀查詢的表c沒有全表查詢,檢索個數為幾百條。那麼總的查詢記錄數也不多,不可能出現執行時間這麼長的情況!檢視執行計畫發現表a的位元組數達到5232918,依稀猜測可能導致此問題的原因是表a的一條記錄太大。後來想到sql查詢結果是按頁獲取的,每頁的大小不同的資料庫有不同的限制。然後分析表a的結構,表a欄位數為40多欄位,沒有超過50,目前不考慮增量表(當前上線時刻,不可能改動表結構),存在幾個字段儲存的值非常大,有clob欄位。按資料庫每頁可以存1m來算,單條記錄為100k,則可以存10條,如果單條記錄為1k,則可以存1000行。資料儲存的記錄數越多,查詢的結果會更快,沒有存下來的記錄都需要重新查詢一遍。

初步定位問題可能是大表導致關聯查詢時間過長,那麼先想辦法減少查詢的記錄行數;盡可能一次查詢多的資料。由於知道表a行資料過大,因此,先通過where條件查詢出a表的記錄,然後在做關聯查詢。

有條件的查詢然後關聯查詢

select t.*, s.name, (select count(o.id) from table_c o where o.act_id = t.id)

from (select t.* from table_a t where 條件)t

left join table_b s on t.type = s.type_code where 條件

上面的sql確實是減少檢索表a記錄個的數,通過執行計畫可知檢索記錄數減少了,位元組數也下降了。奈何條件不能有效的減少表a的查詢的結果,因此臨時表的結果記錄數依然偏多。

3、解決問題

由於表b記錄數和行記錄都很小,因此表a和b的關聯查詢可繼續使用。表c的記錄數十幾萬條,在巢狀查詢的時候需要查詢表a中的記錄id,然後到表c中查詢匹配的記錄。由於表a資料較大,因此需要減少表之間的關聯查詢。個人理解資料庫先快取表a的記錄,然後去表c查詢匹配的記錄個數,原本的想法是減少資料庫對錶a的中間快取量(此處不能確定巢狀查詢是否如此,後續會去查閱相關資料)。那就先把sql拆分,先通過表a和b關聯查詢獲取結果,觀察結果及執行計畫。

去除巢狀的關聯查詢

select t.*, s.name from (select t.* from table_a t where 條件)t 

left join table_b s on t.type = s.type_code where 條件

上面的sql語句由於關聯查詢結果有表b的非索引字段,因此依然沒有命中索引,關聯查詢的執行計畫的檢索記錄數和位元組數都沒有發生變化。由於是分頁查詢,查詢時間確實非常快了0.2m,在有快取的情況下,查詢更快。因此可以確定耗時長的應該就是在巢狀查詢的過程中,由於表a的行記錄很大,佔的位元組數較大,而表c的記錄數又是非常多。因此sql語句執行時間過長。

現在怎麼把錶c的查詢結果,拼接到最終的查詢結果中。程式運算元據庫最耗時的就是連線,現在的框架中都用了資料庫連線池,本專案中使用了druid連線池,專案啟動時就建立了多個連線池,基本省去了建立連線這個最耗時的過程,因此可以利用連線池的復用來拼接查詢結果。

查詢表c的記錄

select count(o.id) from table_c o where o.act_id = '20'
在邏輯層先通過關聯查詢獲取查詢結果,在對結果依次查詢表c中的結果。本來是運算元據的次數增加了很多,但是,最終處理時間確實減少了幾十倍。因為表c的查詢語句簡單,而且,有效利用的連線池,減少了耗時的操作。

總結在很多網際網路公司基本是嚴令禁止關聯表查詢的,由於本專案中的資料量級較小,為了減少邏輯處理過程,因此依然用了關聯查詢。此問題出現在上線時間節點,因此在不可能改變硬體環境,以及較小改動情況下,擇優解決問題。最大的心得體會是資料庫裡面內容還是很深,了解的太少,這裡記錄問題的解決過程,以便後面此類問題的解決,以及為解決相關問題提供方向。首先是要找到問題,然後利用所了解的知識去解決問題,最後去補充盲點和嘗試更優的方案解決問題。

記一次資料庫踩下的坑

公司部署了一套系統,之前在別的地方部署沒有問題,執行正常,但是在濟南部署時就出現了問題 出現的問題是 錯誤日誌資訊不能插入錯誤資訊的日誌表中,導致前台查不到錯誤資訊資料 1 首先檢視錯誤日誌查詢的sql,確實沒有資料產生,這就排除了是前台不載入的問題 2 之後排查節點狀態記錄表,發現錯誤記錄的標誌產...

記一次資料庫遷移遇到的坑

因為一開始專案中用的是oracle的資料庫,後來因為一些原因需要換成mysql的。由於oracle中所有庫表 欄位名都是大小不敏感的,所以專案中各種大寫小寫都有人用,但是遷移到mysql上面,他是區分的,然後就把mysql也設定成不區分大小寫了,並且字符集也設定成了utf8 general ci。後...

mysql資料庫連線池使用 一 dbcp方式的配置

apache的資料庫連線池 dbcp的常用配置說明,因為專案中用到了需要對其封裝,所以必須先了解怎麼配置以及各個配置欄位的含義,理解的基礎上開發我們自己的資料庫連線池。可以參考官網dbcp官網。select 1 true false 3000 mysql com.mysql.jdbc.driver ...