一個mysql的group_concat導致的問題

  好久都沒有寫點東西了,是時候有點寫東西的必要了。

  去年下年底離職了,躺了幾個月,最近又兜兜轉轉換了一家公司繼續當牛馬了,前段時間八股文背了好多,難受呀,不過我也趁着前段時間自己也整理了屬於我自己的八股文,有好幾萬字吧,哈哈哈,以後就不用到處去找八股文了。

  說回正題,這個group_concat的問題是最近在修復一個問題的時候發現的,是以前的人挖的坑,最近都不知道填了多少坑了,特喵的。

一. 問題背景

  一個機構樹的表,就是那種有層級的,類似於下圖這樣的,然後我想查詢某一個公司下所有部門的員工,我們就要把這個機構表遞歸找到一個公司下所有的部門,然後關聯一下用戶表查詢就行了

 

  但是有人爲了追求性能高一點,就把遞歸查詢機構的邏輯使用使用find_in_set()函數和group_concat()函數封裝成了mysq的自定義函數,然後調用的時候在sql級別進行處理了, 

DROP FUNCTION IF EXISTS queryChildrenAreaInfo;
DELIMITER ;;
CREATE FUNCTION queryChildrenAreaInfo(areaId INT)
RETURNS VARCHAR(4000)
BEGIN
DECLARE sTemp VARCHAR(4000);
DECLARE sTempChd VARCHAR(4000);
SET sTemp='$';
SET sTempChd = CAST(areaId AS CHAR);
WHILE sTempChd IS NOT NULL DO
SET sTemp= CONCAT(sTemp,',',sTempChd);
SELECT GROUP_CONCAT(id) INTO sTempChd FROM t_areainfo WHERE FIND_IN_SET(parent_id,sTempChd)>0;
END WHILE;
RETURN sTemp;
END
;;
DELIMITER ;

 

  使用這種方式,在測試環境肯定沒問題,但是到了生產環境機構表數據多了之後肯定就會踩坑,GROUP_CONCAT(id)返回的數據有最大限制的,可以使用SHOW VARIABLES LIKE "group_concat_max_len" 進行查詢,默認是1024個字節,下圖所示。

  也就是如果查詢的數據超過1024個字節後,只會保留前1024個字節的數據,至於修復方法,需要修改mysql配置文件或者使用sql語句臨時修改:SET GLOBAL group_concat_max_len=10240000SET SESSION group_concat_max_len=10240000;

 

  如果沒有修改的話,就可能導致一個問題,一樣的代碼在測試環境跑的很正常,一到生產上就拉胯,你肯定以爲是代碼哪裏和生產不一致,可能比對了很久,然後測試環境自己也測試了n次,但就是生產上數據不完整,此時你就會兩眼無神,懷疑人生....

 

二 解決方

  2.1. 直接修改mysql的配置文件,擴大group_concat_max_len的最大容量,至於擴大到多少,就要靠你自己去根據數據量去衡量了,不過一般的開發也不想去爲了這個一點問題就改生產數據庫配置吧,麻煩....

  2.2 如果是oracle數據庫,自帶了遞歸查詢的關鍵字:start with connect by prior, 有興趣的可以自己研究一下,挺好用的,但是如果項目中是mysql數據庫,那就不適用了

  2.3 使用sql進行遞歸查詢,不過這種sql就是很雞兒難看懂,要是讓你維護這樣的sql你想打人的心都有了,所以我也不是很推薦

-- 根據⼀個⽗節點爲id爲1 查詢所有⼦節點(包含⾃⾝)
SELECT au.id, au.name, au.parent_id
  FROM (SELECT * FROM t_areainfo WHERE parent_id IS NOT NULL) au,
       (SELECT @pid := 1) pd
 WHERE FIND_IN_SET(parent_id, @pid) > 0
   AND @pid := concat(@pid, ',', id)
UNION
SELECT id, name, parent_id
  FROM t_areainfo
 WHERE id = '1'
 ORDER BY id;  

 

  2.4. 其實我們陷入了誤區了,想一想有必要把這麼複雜的邏輯都放到sql語句上處理麼?其實這種越複雜的sql,會給服務器的壓力也是倍增的,而且特別難排查出問題,這點是最致命的,因爲只要能排查出來的問題就都不是問題。

  我的解決方案是: 首先查詢出所有的機構信息,注意,如果機構信息太多,我們可以再細化,比如先查詢一級機構,再查詢二級機構....分批次去查詢我們的數據,再內存級別進行組裝; 然後根據我們查詢的機構信息再調用一次數據庫查詢用戶信息就好了,雖然和數據庫交互可能多了兩三次,但是邏輯變得簡單了,有問題一下子就能排查出來了。

  錯誤示範: 先查詢一級機構下所有的部門,然後遍歷每一個部門分別再去數據庫中查詢下一級部門.....這樣你會被打死的,千萬不要循環中嵌套着查詢數據庫的邏輯

三 還有話說

  繼續瞎逼逼幾句,最近就是搞公司的歷史遺留的項目,技術棧老,問題多,一個幾萬用戶的對內商城項目,扣減庫存的邏輯是查詢數據庫,內存中扣減了之後再將庫存更新到數據庫中......看到代碼我都驚呆了呀。

  由於我剛來沒幾個月,之前聽他們討論有什麼超賣問題,我想着這尼瑪不超賣就出了鬼了, 然後我就提出了這個缺陷, 並使用了數據庫樂觀鎖嘎嘎優化了。這段時間幫着壓測這個商城項目,真的就是一堆破代碼,我還要去給各種優化,性能起碼提升了好多倍都不止,尼瑪數據庫關鍵的索引都有不加的,有的sql執行都需要好幾秒的,加了索引之後30ms......

  繼續苟着吧,現在這裏最大的好處就是不怎麼加班,幹完自己的事情後五點半就可以走了,嘿嘿

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章