簡單說說SQL中Join的使用,說說sqljoin
最近工作中,遇到了一起由於慢SQL引起DB CPU > 90% ,資料庫hang住。。最終導致其他業務查詢統統失敗。
細看下來是由於幾張大表Join關聯查詢引起的,故障本身很常見,不過讓我想到有必要講講如何規避Join的問題。
以下的討論都是基於資料庫能力有限的前提下,否則後續的討論就可以直接忽略了。
首先,我們來談談SQL Join的使用情境
1)如果系統存在
高並發、分布式
商務邏輯簡單
資料的一致性要求不高
允許延遲讀
那麼建議在SQL中少使用Join。減少join的目的是在這類業務情境下,除了直觀地降低了高並髮狀態下的資源消耗外,更大的好處是降低了業務之間的耦合,增加了擴充性。服務就可以拆分成多個微服務和多個資料庫,便於在一部分負擔過重時進行增配;或者直接改為使用緩衝等等。
2)如果系統存在
以低並發、頻繁複雜資料寫入
CPU密集而非IO密集
商務邏輯通過資料庫處理甚至包含大量預存程序
對一致性與完整性要求很高的系統
需要大量的報表和統計
那麼是需要資料庫Join的,可以說是無法避免Join。比如部分金融業務、財務系統、公司專屬應用程式之類,複雜join也是不可避免的,不僅要寫,還要寫好,才能發揮資料庫最大的功用。
對於情況1,我們有以下幾種常見的方案來替代Join:
1. 分多次select取不同表的資料,然後在應用代碼裡做Join;
2. 各自存資料的同時,做一張寬的冗餘表,從寬表裡取查詢資料;
3. 需要Join的資料儲存在緩衝中(如redis),緩衝可以使用主動式(資料修改時更新緩衝)或被動式(緩衝刪除後,讀取時才載入);
4. 從獨立的使用者API介面進行讀取。和方法1類似,在代碼裡做彙總。
對於情況2, 我們的目標是最佳化Join,提升對應的效能,常見的方案如下:
1.用小結果集驅動大的結果,目的是為了儘可能減少Join語句中的NestedLoop的迴圈總次數,比如,當兩個表(表A和表B)Join的時候,如果表A通過WHERE條件過濾後有10條記錄,而表B有20條記錄。如果我們選擇表A作為驅動表,也就是被驅動表的結果集為20,那麼我們通過Join條件對被驅動表(表B)的比較過濾就會有10次。反之,如果我們選擇表B作為驅動表,則需要有20次對錶A的比較過濾。
2.保證Join語句中被驅動表上Join條件欄位已經被索引,保證被驅動表上Join條件欄位已經被索引的目的,正是針對上面第1點的考慮,只有讓被驅動表的Join條件欄位被索引了,才能保證迴圈中每次查詢都能夠消耗較少的資源,這也正是最佳化內層迴圈的實際最佳化方法。
3.增大Join_Buffer_Size的大小,MySQL在完成某些join需求的時候(all row join/all index /scan join)為了減少參與join的“被驅動表”的讀取次數以提高效能,需要使用到join buffer來協助完成join操作。當join buffer 太小,MySQL不會將該buffer存入磁碟檔案而是先將join buffer中的結果與需求join的表進行操作,然後清空join buffer中的資料,繼續將剩餘的結果集寫入buffer中,如此往複,這勢必會造成被驅動表需要被多次讀取,成倍增加IO訪問,降低效率(執行計畫中如果現實using join buffer)。如果join語句不是很少的話,個人建議可以適當增大join_buffer_size到1MB左右,如果記憶體充足可以設定為2MB。
最後一點總結就是,資料不大的時候怎麼做都行,就按資料庫規範設計最好。資料量大的時候,為了效能就只能犧牲一些規範了。任何的規範都是在特定情況下的某種妥協,脫離了這個環境,就不一定成立了。
掃描二維碼或手動搜尋