回覆列表
  • 1 # 會技術的葛大爺

    mysql分庫分表後,跨庫跨表搜尋如何排序?

    資料庫分庫分表可以說是非常常見的一種應對單表資料量過大的手段了。例如:我們的訂單表,通常情況下,我們會將運單表按照1個月、3個月、6個月以上的維度進行劃分,自然也就會按照時間進行訂單表的水平切分。

    這種情況下的分庫分表非常好處理,因為我們能夠強制的按照時間線將訂單儲存到不同的庫中。但是,有可能我們的電商系統使用者量大,訂單量多,一天就有幾十萬單,可能僅僅半個月,我們的訂單量就會上千萬,再加上訂單的商品資料表,如果不分表,訂單表可能就會把系統給拖垮。

    那麼我們就必須面臨將1個月內的資料也要按照一定的規則進行分庫分表。我們可以將訂單表一分為二,分為了OrderDB1,OrderDB2,按照我們按照訂單號來進行區分。訂單號是單數,我們就放到OrderDB1中,訂單號是雙數,我們就放到OrderDB2中。如此一來,訂單表的資料就被平均的分配到了兩個資料庫的表中了,單表的壓力也就降低了。

    而這樣分庫分表以後,我們的訂單表如果需要進行分頁的排序就非常困難了,兩個資料庫中的資料如何進行跨庫的分頁排序查詢呢?

    一般我們有三種方法,分別是:全域性視野方式、允許精度損失方式、二次查詢方式。

    先說全域性視野方式

    通常情況下我們要查詢第三頁的100條訂單資料,我們會寫一個SQL

    select * from T order by time offset 200 limit 100;

    但是分庫以後,這100條資料可能存在很多種方式。

    有可能是平均分佈(極端情況)

    也有可能是全部來自一個庫(極端情況)

    還有可能是散亂分佈的(通常情況)

    由於情況根據我們的Order By條件有很多的可能,所以我們很難知道第三頁的資料到底從哪個庫的哪個位置開始取數。如果我們需要精準的取到資料,那麼就必須重新還原單庫的那種全域性視野。

    如何還原全域性視野呢?

    還是用我們要查詢第三頁的資料來舉例,我們之所以失去了全域性視野,是因為我們無法一次性得到所有的資料結果,那麼還原全域性視野的方式就是讓我們能夠得到所有的資料結果。因此,我們可以將兩個庫中的從第一頁到第三頁的全部資料查詢出來,然後在記憶體中合併後再進行排序,然後就能夠取出正確的第三頁資料了。

    自然,我們的sql也就發生了變化,從

    select * from T order by time offset 200 limit 100;

    改為

    select * from T order by time offset 0 limit 100+200;

    使用全域性視野方式的好處很明顯,就是資料絕對的精準。但是缺點也同樣明顯,當查詢的資料量大時,記憶體的消耗就會變多,而且在頁碼增大的時候,查詢效率會急速的下降。當我們有N個數據庫,我們需要從查詢X到X+Y區間的資料時,那麼我們的記憶體中將會需要組合N*(X+Y)條資料然後排序。

    既然全域性查詢的方式有缺點,那我們就來解決這個缺點,但是肯定會有一些其他方面的犧牲。

    允許精度損失方式

    允許精度損失其實非常的好理解,就是我不去管資料在兩個DB中是如何的分佈的,我只是平均的從兩個庫中排序後取出50條資料,然後組合成為100條進行顯示。

    當然,這種方式的精度就是根據你排序的條件和資料儲存的方式不同而變化的了。假設我們的資料都是按照時間有序的儲存的,我們的排序也是根據時間來進行排序的,那麼我們得到的結果就會比較精準。

    但如果我們的資料是隨機插入多個DB的,我們要按照時間進行排序查詢,或者我們的資料是按照時間順序插入DB的,但是我們需要根據其他條件進行查詢時,資料的精度就會很差。這就看我們對於業務的需要是什麼樣的了。

    不過,使用這種方式查詢,我們就可以不用考慮效能上的問題,查詢的複雜程度很低,只要我們的業務沒有過多的要求,那麼使用這種查詢方式是最為推薦的。

    當然,如果你的業務不允許這樣的情況出現,還需要滿足互動、效率等等各種需求,那麼,就我們還可以使用下面這個方式。

    二次查詢方式

    這可以說是解決分庫查詢的究極武器了,能夠保證資料的精準度、查詢的效率、使用者的互動頁面,犧牲的只是小小的效能開銷和一些程式碼難度的上升。

    方式其實也不難,假設我們要查詢第21頁的資料,每頁5條。這個時候,我們先假設資料是平均分佈的,但是我們在每個庫都查詢全量的5條資料。也就是:

    select * from T order by time offset 100 limit 5;

    這時,我們得到的資料可能是這樣的。

    而兩個DB中,最小的時間是1487500001【minTime】,這個時間記錄下來。兩個DB中各自的最大時間也記錄下來,分別是DB1:1487500041【maxTime1】 和 DB2:1487500061【maxTime2】。

    這時,我們在使用時間去兩個資料庫中再次進行查詢。

    select * from T where time between minTime and maxTime1 order by time; select * from T where time between minTime and maxTime2 order by time;

    由於之前minTime來自於DB1,因此,DB1的資料不會發生變化,但是DB2中的條件被放寬了,因此可能會查詢出更多的資料。結果可能如下:

    而兩個結果集合並以後,相當於就獲得了全域性視野,也就可以很容易的找出這一頁需要的5條資料了。

    當然,我們還可以藉助elasticsearch來完成分庫的排序查詢,由於elasticsearch引入了快取機制,能夠讓查詢更快。

  • 中秋節和大豐收的關聯?
  • DNF玩家小號爆出軍神耳環,處理方法讓寶哥看的心疼,寶哥都不敢這麼做,如何評價?