1. 程式人生 > >mysql線上分表分庫方案

mysql線上分表分庫方案

  1、 總體方案

                   OSS需要一臺不提供服務從庫輔助完成,通過這臺從庫建立資料快照,快照資料按照新的路由拆分規則分別匯出,匯出完成後建立trigger記錄增量資料,

               將匯出的分片資料分別匯入拆分後對應的新表並以此作為基準資料,匯入完成後對增量資料進行回放,總體流程方案如圖所示。

               

             2、 怎麼新老表資料的一致性

                    實現資料的一致性需要有些輔助工具。

                   (1)心跳錶

                      心跳錶作用是判斷新表建立、基準資料匯入及回放的增量資料是否同步至從庫的依據,心跳錶資料永遠為一行記錄,完成相關操作時,主庫上對心跳錶id欄位加1操作,如果主從讀取心跳錶所獲取的id值相同,那麼就認為主從同步結束。

                    (2)觸發器

                              將資料有從庫的老表匯入到主庫的新表過程,從庫上中斷sql同步執行緒,建立觸發器,所有對原表DML操作的記錄都需要通過觸發器記錄至增量表中。

                    (3)增量表

                       將新表的資料匯入老表的時候記錄基準資料之後,對增量資料回放到新老表來保證新舊錶間資料同步。

                      保證資料的一致性做法如下圖所示:

                       

                    在主庫中建立新表和心跳錶,然後將不提供服務的從庫中老表的資料按照分庫分表的路由規則分發到主庫的新表中;與此同時從庫中會建立一個增量表,用於記錄遷移過程中資料庫的增量

             INSERT、UPDATE、DELETE操作,當老表中的資料全部匯入到新表後,會校驗新老表資料的一致性,資料一致以後,開始將增量表的資料重放到新老表中,當增量資料少於1000條時會鎖住主庫

            中的老表,防止不斷產生新的增量,同時把剩餘的增量資料重放到從庫的老表以及主庫的新表中,從而達到新老表資料的一致。

           3、怎麼不影響線上服務

                基準資料匯出方是一個不提供服務的從庫,主庫的新表也不想外提供服務,最多在匯入資料的時候影響到主庫的IO,如果匯入頻率控制好的話對線上基本無影響。

           4、 怎麼保證業務遷移到新表的過程中不影響線上服務

                很遺憾,OSS做不到這一點,因為資料遷移之前,業務方使用的是老表的資料,當資料遷移到新表以後,為了保證新老表資料的一致性,必須停止服務,然後業務方上線對新表的操作才可以,業務方在全推過程中不能

                對外提供服務,對於像錢包的payserver,從驗證到全推的過程至少1個小時,而停止一個小時的服務是不現實的,而這一點在下面的提供的方案中得以解決。

               總結:  這個方案有他的好處,此方案是有OSC方案衍生而來的,能完保證資料的一致性,雖然在資料遷移過程中不影響線上的服務,但是在資料遷移後需要停止服務完成和業務方的對接,不能保證平滑遷移。

               但是該方案遷移週期比較短,如果對線上服務影響要求不高,並且對遷移時間要求比較高的情況下建議使用該方案。

三、 新的線上分庫分表解決方案(暫時叫做MySQL NEW Online Schema Sharding 簡稱 NOSS)

               1、 總體解決方案

                      NOSS方案需要業務方雙寫,並且同時需要通過單獨的拉庫操作來達到資料的一致性,通過控制拉庫的速度從而控制對線上的影響,總體方案如下圖所示,

              除了選取拉庫所需要的主鍵以及資料的一致性校驗,其他的所有的操作都是在主庫上執行。

           

             2、怎麼保證新表的資料和老表的資料一致

                   (1) 雙寫

                         對於雙寫後新產生的資料,通過新老表雙寫達到新老表資料的一致性。

                   (2)拉庫

                         通過拉庫,將雙寫前的資料從老表中按照路由規則分發到新表中。

                         注意:雙寫在事務中,業務會對記錄加鎖,拉庫也是一樣的需要在事務中並且對將拉去的資料進行加鎖。

                  (3)資料的校驗

                         當拉庫指令碼結束以後,會校驗新老表資料的一致性,首先找一個從庫斷開和主庫的同步,然後將從庫中的新老表資料進行一致性校驗,從而判斷資料的一致性。

             3、怎麼保證不影響線上服務

                   (1)控制拉庫的速度

                               在拉取資料的時候要選取一個合理的拉庫速度,方法是拉庫指令碼剛開始會以一個比較小的速度的拉取資料,然後每隔一段時間增加一些速度;當然指令碼會配置拉取速度的預期上限,

                         當速度達到預期時,速度不再增長,如果在達到預期前產生了對資料庫的影響則停掉拉庫指令碼,記錄下產生問題時的拉取速度,然後選取一個比這個速度小的速度作為拉取速度拉去資料。

                   (2)控制每個事務處理的資料量

                            拉庫時每個事務處理的資料量要做限制,防止事務太大執行時間太長,當雙寫中的記錄和拉庫事務中的記錄衝突時等待時間過長導致事務回滾。

                    通過控制這兩點能達到對資料庫較小的影響,從而不影響線上服務。

              4、怎麼保證業務遷移到新表的過程中不影響線上服務

                   業務方可以通過多次上線達到不影響線上服務的目標,上線流程分為3步

                   (1)第一輪業務方上線

                             此時業務方上線過程對新老表進行雙寫,但是提供給業務方資料的是老表。

                            作用:1. 保證新老表資料的一致性

                                        2.  用老表資料提供給業務方是因為新表的資料還不全

                     (2)第二輪業務方上線

                              在老表中的資料遷移到新表以後,並且資料一致性得到驗證後開始上,此時仍然是雙寫,但是提供給業務方資料的是新表。

                             作用:1.  防止有別的使用老表的業務方沒有及時更新自己的業務

                                          2. 用於老表向新表過渡,並且可以當新表出錯時用老表提供服務。

                       (3)第三輪業務方上線

                               如果第二輪業務方上線後,新表提供的資料穩定,並且其他業務方也不再使用老表,那麼下掉對老表的所有操作。

                              作用:完成業務方獲取資料從老表到新表的過渡

       總結:此方案可以解決之前提出的所有問題,但是也有他的缺點,資料遷移由於有速度限制,所有資料遷移的時間比OSS時間長,業務方分三次上線,那麼整體完成由老表到新的的過渡週期

       比較長,所以,如果保證線上服務要求比較高,並且對遷移時間要求不是很緊急的可以使用該方案。

      下面介紹一下這個方案在coredb收款表拆分上的應用:

四 例項分析

    專案背景

                     百度錢包隨著收款業務的增長,收款表的記錄數目迅速增長,由於歷史原因,收款表是單表,mysql單表資料量過億就會對服務造成很大的影響,專案開始階段已經達到7千萬左右,

並且當時的增長速度一個月大約一千多萬行資料,收款表拆分迫在眉睫。

    專案中遇到的問題

     一、 收款表怎麼拆分

          收款表拆分的過程中有兩種方案

          1. 按照時間維度分庫分表

                  優點: (1)資料可以按照時間歸檔十分方便

                               (2) 資料庫表不會突然猛增,因為按照收款表的後三位分庫分表一下就要分出1000張表, 而通過月份分庫分表分表一年只有12張表,需要80幾年才會達到1000張表

                               (3)如果按照收款表的後三位分庫分表,按照時間維度查詢需要便利1000張表,如果按照時間維度分庫分表,按照時間查詢可以直接使用索引定位到時間範圍內的表,不需要便利所有的表。

                  缺點: (1)資料不均勻,某個月如果做活動,資料量會暴增,並且隨著業務的不斷擴大,單月收款表的資料量可能達到一億。

                                (2)由於某段時間資料量暴增,會造成這個表成為熱表,影響資料庫操作

          2.按照收款單後三位分庫分表 

                   優點: (1)資料會均勻分佈在1000張表裡,不會造成短時間內所有表資料量達到1億

                                 (2)和其他表的分庫分表規則一樣,容易維護

                    缺點: (1)資料表一次I性增長2000張(還會有一個map表)

                                 (2)按照時間條件查詢必須便利所有表

               雖然按照時間分庫分表有他的好處,但是按照這個維度分庫分表最大的致命點就是,資料分佈不均勻,並且可能會出現一個月收款表的資料量達到1億的可能,如果出現這種情況資料庫就需要重新改造,代價太大。                      

     二、 資料怎麼遷移

            資料無疑是由老表向新表遷移,並且遷移需要分兩步才能完成

            1. 按照create_time欄位查詢出主鍵

                      為什麼要選取主鍵,因為第二部遷移需要按照這些主鍵將老表中的資料高效的選取出來,如果不用主鍵選取效率會降低,按照create_time欄位查詢主鍵是因為遷移的資料是一段時間內的資料,時間可以作為參照

                ,並且create_time欄位是一個不變的欄位,防止在遷移過程出現數據修改而照成遷移資料不準確,比如按照某些可變欄位選取如modify_time那麼在limit的時候由於欄位改變照成資料變化而limit資料不準確。

            2. 根據上一步選取的主鍵在老表中查出資料插入到新表中

                    通過第1步選取出來的資料作為輸入,在老表中選取出來這個主鍵對應的記錄,然後replace into插入到新表中,這所有的操作在一個事務中並加鎖完成,後面會說明為什麼這麼做

                   這兩部分別有兩個指令碼來完成,我們這裡叫做拉庫指令碼。

                   為什麼不直接查出資料插入到新表中 

                  (1)不能直接把所有的資料選取出來一下子插入到新表中,因為對資料庫的操作需要頻率控制,如果頻率過快會影響線上服務

                  (2)為了保證資料的一致性,在第2部遷移資料需要使用事務和鎖來保證資料的一致性,所以先選出主鍵通過主鍵才能高效的在從老表中查資料插入到新表中。

    三、怎麼保證資料的一致性

            以拉庫指令碼的輸入時間為分割點,分割點之前資料的一致性由拉庫指令碼來保證;分割點以後的資料一致性由payserver的新老表雙寫來保證;當然這些資料的一致性都會有檢驗機制。

            分割點:

                         分割點是這樣的時間,是payserver全部上線以後的時間點,而不是在payserver上線過程中新老bin檔案存在的某個時間,後面會解釋分割點為什麼選取這個點。

          1. 拉庫指令碼

              老庫指令碼上面已經介紹過了,之所以使用事務就是為了保證在對新表的資料一致性,由於老表中被拉取的資料在被拉取過程中可能被修改,比如某個記錄的狀態被改變,以這個為例子講一下

             怎麼保證這些資料的一致性:

                   假如某條資料在新表中沒有,在老表中存在,那麼會出現兩種情況,一個是payserver雙寫的事務先執行,另一個是拉庫指令碼中的事務先執行。

             (1)payserver雙寫的事務先執行

                       如果payserver的雙寫先執行,那麼在更新老表會成功,而更新新表的時候會失敗,這時候老表中的資料是正確的,但是新表中的資料是錯誤的;這時候執行拉庫指令碼中的事務,老表中正確的資料會被

                replace into到新表中,這樣新表中的資料也是正確的了。

              (2)拉庫指令碼中的事務先執行

                      如果拉庫指令碼中的事務先執行,那麼老庫中的資料會被插入單新表中,由於老表中的資料是正確的,從而保證新表中的資料也是正確的;如果這時候payserver的雙寫事務被執行,那麼新老表中的資料被同時修改,資料依然是正確的。

          2. payserver的雙寫

                在分割點以後的資料通過雙寫來保證資料的一致性,原因是由於雙寫之後老表中有但是新表中沒有的資料由拉庫指令碼保證了資料的一致性;對於原來老表中沒有的資料新表中也沒有,在雙寫的過程中會在同一個事務中對新表和老表進行

           插入和更新操作,保證新表表中的資料和老表中的資料一致。

          3. 資料的一致性校驗

              (1)對於由拉庫指令碼拉去的資料一致性校驗方法是這樣的,當老庫指令碼執行結束以後,將新表中拉庫指令碼產生的資料做校驗;停止一個mysql從庫與主庫的同步,然後將新表中的資料按照分庫分表的原則分別放在1000個檔案中,同理老表中的資料,也按照這個原則放在1000個檔案中,然後對所有的檔案進行sort操作,操作成功以後逐一進行md5值校驗,若果都相同那麼資料一致性沒問題。

                    如果校驗發現md5值不一樣,首先要回顧這個流程是否出了問題,如果問題不大,那麼會將md5值不同的檔案中的不同條目選取出來,由老表中的這條資料代替新表中的這條資料,如果問題比較大,有可能任務失敗。

               (2)對於有payserver雙寫保證資料的一致性校驗是這樣的,在payserver的程式碼中,在同一個事務裡如果新表操作失敗但是老表操作成功會打log作為提示,所以只要程式不列印失敗的log,那麼證明payserver雙寫的資料一致性已經保證,如果出現問題,那就是應用程式有問題了。

   四、怎麼保證不影響線上服務

    (一)  不影響payserver的服務

         為了不影響payserver的正常服務,payserver分三個版本依次上線,原因如下。 

        1. 第一輪payserver上線

          第一輪payserver上線的主要作用是為了保證新老表資料的雙寫。

         (1) 對於老表的操作

           對老表的操作保持不變,業務中用到的資料是從老表中選出來的資料。

         (2) 對新表的操作

          插入:    老表插入的資料在新表中也插入,如果插入失敗會報錯

          更新操作:如果新表中資料存在,那麼直接更新

         如果新表中不存在,不報錯,但是會列印提示資訊

         選取操作:如果新表中有資料,不做任何處理,如果新表中沒有資料,不報錯,但是會列印提示資訊

        回滾方案:回滾到上一個版本

        (3)邏輯修改以及原因

          主要的修改思路增加對新表的操作,包括load,insert以及update,但是新表在select以及update找不到資料的時候根據錯誤碼判斷是是資料不存在,還是資料存在只是操作失敗,並列印相關的log,系統並不報錯,但是insert操作失敗需要報錯。

          這樣做的目的是,當payserver上線時,老表中存在的資料新表中不一定存在,所以select和update這些新表中不存在的資料時,對新表的操作會失敗。

        (4)在三中怎麼保證資料的一致性中分割點的選取在這裡解釋一下

                分割點之所以選取到payserver全部上線結束的時間點原因是當payserver上線的時候,會出現新老bin檔案同時存在的情況,其中新檔案對新表進行插入和更新操作,但是來的bin檔案沒有對新表的操作,這樣就會出現一個問題,當某個插入操作

         落到老的bin檔案上時,在老表中會插入這條資料,但是在新表中卻沒有這條資料;但是剛好更新操作卻落在了新的bin檔案上,這時候對這個在上相過程中產生的老表中存在但是在新表中不存在的記錄就會出現資料的不一致,到那時當新的bin檔案

        上線結束,那麼就不會出現這種資料不一致的現象,也就是上線結束以後payserver的雙寫可以達到資料的一致性。

        2. 第二輪poayserver 上線

            第二輪payserver的上線有兩個作用:

            (1)是業務方使用的收款表資料由老表平滑過渡到新表

                    在這裡對新老表的操作與第一輪操作剛剛好相反,對於插入操作失敗新老表都報錯,但是對老表更新操作失敗但是對新表更新操作成功不會報錯,只是列印log,這樣業務方使用到的資料就是從新表中選出來的資料了。

             (2)保證不影響payserver的服務

                    為什麼不一下子去掉對老表的操作呢? 假如去掉了對老表的操作那麼在上線過程中會出現以下問題:

                    上線過程中還會出現新老payserver同時存在的情況,插入請求落在新的payserver,此時資料會在新表中插入,但是老表中沒有這條資料,但是剛好更新操作落在了老的payserver上,這樣當老的bin檔案去select以及update這條在新表中存在              但是在老表中不存在的資料時,由於這條資料在老表中不存在會出錯FATAL,會影響支付成功率。

        3. 第三輪payserver上線

            第三輪payserver上線主要目的就是用新表完全替代老表,下掉對老表的操作,也就是在payserver的邏輯中去掉對老表的所有操作。

    (二) 不影響業務方的服務 

           不影響業務方的服務主要做到以下兩點

        1. 排查對業務方的影響

            (1) 把分庫分表的專案向業務方說明,他們會根據情況排查自己負責的模組,並將受影響的模組反饋給payserver

            (2)只是業務方的排查還是不夠的,因為有些指令碼的負責人可能沒有通知到,或者有些模組雖然是他們負責,但是由於沒有出現過問題,或者是新人多沒有接觸到也不知道這個事情;所以需要我們負責再次排查一遍,

            比如排查收款表使用的業務時,我在   db-eb-pay-fn00.db01.baidu.com 排查了所有的線上業務,結果真的找到了一些之前業務方沒有反饋 給payserver的業務。

       2. 業務方配合payserver工作

           (1)業務方根據payserver的改動修改自己的業務邏輯

            (2)嚴格控制上線步驟,不要產生由於上線順序造成的業務影響。

  五 上線步驟

   六 為什麼選擇了這個方案

         大家會發現這個方案上線過程很複雜,流程很麻煩,但是為什麼要選擇這個方案,難道沒有更好的方案嗎?

    期初是有四套方案供選擇,但是最終確定了這個方案,這裡簡述一下為什麼沒有選擇其他的方案。

        方案一 :

         這個方案與最終方案流程基本類似,最終方案也是在這個方案上做了優化,這裡不再累述

        方案二:

        方案二採用的是DBA使用的MySQL Online Schema Sharding,之所以沒有選擇這套方案

        (1) 這套方案並不是很成熟,存在bug並且維護這個方案的RD最近辭職,選擇這套方案危險比較高

         (2)這套方案需要停止服務一段時間,而這段時間可能比較長,是payserver不能容忍的,因為支付行業不同於其他產品,涉及到資金的流通,不允許超過五分鐘的停服務時間。

        方案三:

         主要思路是通過binlog重放來達到新老表的資料一致,但是這也需要停止服務去完成新老表的資料一致,而且這個時間也是不能被接受的。

 綜上: 其他方案都需要停止payserver的服務,而且停止服務的時間都大於5分鐘,這樣不可以被接受,所以最終選擇了現在這套方案。