起因
web運行頁面加載速度突然變慢,從5秒左右變爲2~3分鐘。
查找問題源頭:
1:代碼問題:
因爲業務邏輯複雜,運行兩段sql進行數據查詢,根據第一段sql查出來的數id在第二段sql裏進行in查詢,但由於第一段sql查出的數據量大概有6W+,所以在拼接sql中字符串長度過長導致報錯GC。
2:數據庫問題:
因爲業務邏輯變更,導致sql查詢的其中一張表,從1~2w條數據四五天內增加至150W+,導致單表查詢運行速度超過5s,關聯查詢至少要20s左右。
解決:
1:修改代碼邏輯,通過獲取小數據量的值對大的sql進行關聯查詢,減少java內存消耗。
其中有一段業務邏輯是要獲取一棵樹上根據樹的多個節點,獲取多個節點下的所有子節點,開始的想法是,根據java代碼來實現,實現方式是:
- 獲取某個父節點下面的所有子節點
/* 獲取某個父節點下面的所有子節點
* @param menuList
* @param pid
* @return
*/
static List<String> childMenu = new ArrayList<>();
public static List<String> treeMenuList(List<Map<String, Object>> menuList, String pid) {
for (Map<String, Object> mu : menuList) {
//遍歷出父id等於參數的id,add進子節點集合
if (pid.equals(String.valueOf(mu.get("parentid")))) {
//遞歸遍歷下一級
treeMenuList(menuList, String.valueOf(mu.get("id")));
childMenu.add(String.valueOf(mu.get("id")));
}
}
return childMenu;
}
但是這種方式仍然會使優化過mysql後的加載速度達到7~8秒,由於時間問題沒有去管爲什麼會慢,但猜測是因爲傳入的List集合過大。
思考另外一種方案用mysql函數去處理這段邏輯,在一片文章中找到很好的例子:
SELECT * FROM person WHERE department IN
(SELECT department_id FROM department WHERE department_id = 20006
UNION
(SELECT department_id FROM
(SELECT * FROM department ORDER BY parent_id,department_id) depart_sorted,
(SELECT @pv := 20006) initialisation
WHERE find_in_set(parent_id,@pv)
AND length(@pv := concat(@pv,',',department_id))));
生產庫中測試運行速度秒級,已達標。
2:優化sql,創建索引
由於是數據量突然增加的問題導致的,首先想到的就是創建索引。
但是沒想到,創建索引的時間超出我的想象,分別用了腳本,navicat,倒庫等多中方式創建都不成功,開始想到的是因爲數據量太大,在等待一個小時後發現還沒有成功,知道事情沒有這麼簡單,開始尋找出路。。。。。
是否有某個查詢把表造成死鎖;查詢:
select trx_state, trx_started, trx_mysql_thread_id, trx_query from information_schema.innodb_trx;
查詢後果然發現了一條有關建索引表的sql存在死鎖,果斷kill。
至此頁面加載的速度已經從23分鐘優化至23秒(其中包括一些sql語句的優化未寫出);
才疏學淺,不喜勿噴。