好久都沒有寫點東西了,是時候有點寫東西的必要了。 去年下年底離職了,躺了幾個月,最近又兜兜轉轉換了一家公司繼續當牛馬了,前段時間八股文背了好多,難受呀,不過我也趁著前段時間自己也整理了屬於我自己的八股文,有好幾萬字吧,哈哈哈,以後就不用到處去找八股文了。 說回正題,這個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=10240000;
SET 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......
繼續苟著吧,現在這裡最大的好處就是不怎麼加班,幹完自己的事情後五點半就可以走了,嘿嘿
--------------以上皆原創,給未來的自己留下一點學習的痕跡!--------