1. 程式人生 > >sqlproxy分表中sort的實現

sqlproxy分表中sort的實現

剛剛完成sqlproxy中大表分表後對查詢語句中的order by的實現;寫個部落格記錄下。

排序演算法的選擇:

       在設計之初查看了下mysql中進行order by語句的實現(之前的blog中有),mysql在排序的時候使用的排序演算法主要包括:基數+計數排序,快速排序,filesort。

       因為sqlproxy中的排序其實是對多個已經排序的結果集合的merge的一個過程,所以我們決定採用類似filesort的排序方式:

       1. 從前N結果集合中取第一個(或下一個)行。

       2. 通過一次遍歷找出其中最大(或最小)的,將該行拷貝到輸出緩衝中。

       3. 如果該行是所在結果集合的最後一行,將它與第N個結果集合進行一個swap,然後N=N-1;

       4. 如果N==0, goto 5。

           否則goto 1。

       5. end。

對包結構的調整:

1. 調整包的編號

       根據mysql的通訊協議,所有的包都有個包頭,這個包頭的第4個位元組儲存的是該包的編號。從客戶端發給服務端的請求包的編號是0,然後服務端返回給客戶端的包的編號從1開始,逐個遞增,客戶端在接收服務端返回的查詢結果時會檢查這個編號。

       所以在對多個後端mysqld返回的結果集合中包進行merge排序的時候需要按順序進行重新編號。

2. 調整包中的列

       如果order by語句的排序列不在select的list中的話,結果集合merge就無法進行了,因為沒有用於排序的列的資訊。

       所以這裡我們採用的策略是:

       1. 首先重構這個查詢語句,把缺失的列新增到select關鍵字之後,並將重構後的語句傳送到後端mysqld去執行。

       2. 利用上一節提到的演算法進行排序,不同的是在將資料包拷貝到資料緩衝是需要進行些調整。

           根據mysql通訊協議,結果集主要的構成如下:

           【result set header packet】

           【field packet】*n

           【eof packet】

           【row data packet】*m

           【eof packet】

           n表示列的個數,m表示行的個數。

          在這一步的調整中需要把第一步中新增進去的額外列從結果集中去除。

          首先是result set header packet, 該包中記錄了結果集中列的個數,如果小於255的話,只用一個位元組來記錄,第5個位元組(前4個為包頭)。這個值需要修改,減去額外列的個數。

          然後是對field packet的選擇性拷貝,結果集中的每一列都對應一個field packet,因為第一步中將額外的列插入在select關鍵字之後,假設額外的列的個數別x,那麼只需要跳過前x個field packet就行。

          最後是對row data packet的調整,每一行記錄對應一個row data packet,它的調整包括:

           a. 拷貝的時候需要把行中前x列給跳過去 (行中每個列都是"長度"+“字串”的方式排列)

               行中列的獲取,可以直接使用mysql的api: sql-common/pack.c中的net_field_length。

           b. 調整該row data packet的包頭中記錄的包長度(減去被跳過列的長度)。

備 注:通過測試和檢視mysql的程式碼,我們發現如果把額外欄位新增到select list的末尾,就只需要去調整 result set header packet 和 field packet, 而不需要去動量很大的row data packet。 這麼做應 該會調高效率,不過覺得有點取巧,並且考慮到將來版本可能的不相容性,暫時沒有采用,如果後面效能出現瓶頸的話,可能會採用吧。

分開實現還是統一實現:

       目 前的開發主要實現的是2種sort,一個是order by的列在select list中,一種是order by的列不在select list 中。 這兩種情況有很多的共性,可以實現一個通用的方案同時滿足這2種情況,也可以為他們各自實現一個方案,只是共用一小部分的簡單函式。

       我們最後討論的結果是分開,各自實現一個方案。我們判斷依據主要還是效能,因為效能是sqlproxy存在的最大價值。理由主要包括:

       1. 實現一個通用的方案,那麼這個方案的效能往往是由最複雜、效率最低的情況下的效能決定。正所謂越通用,有越低效。

       2. 實現一個通用的方案,無法避免的要在其中加入很多的條件分支判斷。一般而言if和else的代價是很小,可以忽略的;但需要注意的是sort操作是要作用到結果集合中的每一行,當查詢結果集的行數達到一定數量級的時候,這個代價就不能忽略了。

       3. 分 開實現的話,對於各個情況都可以根據它們的實際情況進行特定的優化。比如對於order by的列已經在select list中這種情況,當merge 到只剩一個結果集有行資料時,我們可以一次性通過遍歷來修改剩餘行資料包的包序號,然後統一做一個memcpy,而不需要像在比較的時候為每一行做一次 memcpy。