大資料量實時統計排序分頁查詢(併發數較小時)的幾點建議

大資料量實時統計排序分頁查詢的瓶頸不是函式(count,sum等)執行,

不是having, 也不是order by,甚至不是表join, 導致慢的原因就在於“資料量太大本身”

化整為零

就是將表劃分為M份相互獨立的部分,可以是分表,也可以是不分表但冗餘一個取模結果欄位

實際結果是不分表比分表更加靈活,只需稍加配置,就可以動態切分大表,隨意更改M的大小。

將1條慢sql(大於30秒)拆分成為N條查詢速度巨快的sql(單條sql執行時間控制在20毫秒以內)

然後再web應用中以適當的執行緒數去併發查詢這些執行時間快的N條小sql再彙總結果

兩步查詢

第一步查詢中去併發執行這N條小sql, 只取排序欄位和標識欄位,其他欄位一律丟棄

彙總結果後定位出當前頁面要顯示的pageNum條資料,再進行第二步查詢,取出頁面上需要展示的所有欄位

web應用自身計算與資料庫計算的折中

PS:這一點是至關重要的,其他幾點都可以不看,這點是最關鍵的。慢慢解釋一下:

有三種方式統計所有的記錄,

a) 第一種方式是把資料庫中所有記錄(只取排序欄位和標識欄位並且不做任何sum,count having order by等操作)

全部拉到web應用中,在web應用中完成所有的計算

b) 第二種方式是把資料庫中所有記錄做sum count having等操作之後的所有行數拉到web應用中,在web應用中完成剩餘計算

c) 第三種方式是把資料庫中所有記錄做sum count having order by等操作之後把limit後的資料拉到web應用中,

在web應用中對limit後的資料再計算

顯然,第一種方式 資料庫什麼活都不做只取資料 是不可行的。以lg_order_count_seller為例,1500萬行,

如果只算id, seller_id和order_count 這三個bigint型別,至少需要拉8*3*1500 0000 = 360000000=340M,

拉到記憶體中之後儲存需要8*4*15000000= 460M,這還不算List是的2的n次方這個特點和計算排序等的記憶體開銷,

不僅資料庫與web應用機器IO扛不住,就是應用自身恐怕也要OOM了。

第二種方式,所有記錄做sum count having等操作之後,由於是group by seller_id的,總得資料量變為100萬(就是賣家總數),

這樣子一來,共需要拉8*3*100 0000 = 23M,拉到記憶體之後,需要8*4*100 0000 = 30M, 再算上List是的2的n次方這個特點和

計算排序等的記憶體開銷也不會超過100M, IO的時間和記憶體開銷勉強可以考慮接受。

第三種方式,所有記錄做sum count having order by等操作之後把limit後的資料拉到web應用中,因為做了limit,所以,

資料量很小了,無論是IO還是記憶體開銷都已經很小了。可以忽略。

綜合以上三種,第三種方式適用於頁面的前n頁和後n頁,因為這個limit的資料量隨著頁數的增大而增大,

當大到每個切分後的小表的資料量時就轉為第二種方式了。

第二種方式適用於頁面的第[n+1, totaoPageNum-n]頁。

切分成N條小sql後並行執行時排序不穩定性的解決辦法

① 問題描述:

最佳化之前,還是是一條大慢sql查詢時,由於資料庫排序是穩定排序,

所以當兩條記錄排序欄位值相同時他們在頁面上的頁碼位置是固定的。

最佳化之後,當並行執行這N條小sql時,由於無法控制這些小sql的先後執行順序,

導致在web應用中當兩條記錄的排序欄位值相同時在頁面上的頁碼位置是隨機的。

② 解決辦法:

除了拉標識欄位(seller_id)和排序欄位(order_count_sum)之外,再取一個unique(id)的欄位,當兩條記錄的排序欄位值相同時,

再用這個unique的欄位(在賣家監控中這個欄位是id)進行第二次排序。這樣就解決了排序不穩定的問題。

③ 也許,看到這裡會有疑問,為什麼不用seller_id?seller_id也是唯一, 這樣子不是少取id這個欄位,減少IO了?

seller_id雖然也是唯一,可以輔助排序,但是不要忘記資料庫的排序規則是:

如果兩列的值相等,那麼序號在前的排在前面,這裡的序號就是主鍵(自動生成,autoincrement),

如果用seller_id的話還是不能保證排序的穩定性,只能用主鍵id。

優先載入頁面上的主要元素,然後再去非同步載入次要元素,

把資料庫的連線,掃表,計算等資源優先讓給使用者關注的主要元素,次要元素可等主要元素載入完成之後再載入。

反應在賣家監控頁面中,查資料和查頁頁碼的sql語句基本相同,是在競爭同一資源,

所以,需要做一個策略,優先把資源讓給查數,資料查完之後再去查頁碼。

限流

由於多執行緒取資料並沒有從本質上提高資料庫效能,所以必須針對大資料量實時統計排序分頁查詢做限流

我這裡打個比方:食堂有6個視窗,物流團隊吃飯要買6個菜,平均每買1個菜需要1分鐘的時間,

如果派我一個人去一個視窗買的話需要6分鐘的時間

假如派6個人分別去6個視窗買這6個菜,只需要1分鐘的時間

但是,如果除了物流團隊,再來其他5個團隊呢,也就是說6個團隊每個團隊買6個菜共買36個菜,

這樣子有的團隊先買完,有的團隊後買完,但平均時間還是6分鐘。本質上沒有變化。

所以,對於特定的查詢條件,必須進行限流。讓每分鐘至多有6個團隊買菜,這樣子能使得情況變得不至於太糟糕。

從根本上改變現狀

這一點從目前來看只能是展望了,比如mysql資料庫換更為強大的oracle資料庫,

或更換InnoDb引擎為其他,或更換SATA硬碟為SSD ……

從實踐效果來看,最佳化後的效果是很明顯的。

相同的查詢條件,原來一個頁面查詢時間由於超過60秒超時了,根據1-6點建議最佳化之後,查詢時間變為2秒至3。5秒之間。