1、 總體方案
OSS需要一台不提供服務從庫輔助完成,通過這台從庫建立資料快照,快照資料按照新的路由拆分規則分别導出,導出完成後建立trigger記錄增量資料,
将導出的分片資料分别導入拆分後對應的新表并以此作為基準資料,導入完成後對增量資料進行回放,總體流程方案如圖所示。

2、 怎麼新老表資料的一緻性
實作資料的一緻性需要有些輔助工具。
(1)心跳表
心跳表作用是判斷新表建立、基準資料導入及回放的增量資料是否同步至從庫的依據,心跳表資料永遠為一行記錄,完成相關操作時,主庫上對心跳表id字段加1操作,如果主從讀取心跳表所擷取的id值相同,那麼就認為主從同步結束。
(2)觸發器
将資料有從庫的老表導入到主庫的新表過程,從庫上中斷sql同步線程,建立觸發器,所有對原表DML操作的記錄都需要通過觸發器記錄至增量表中。
(3)增量表
将新表的資料導入老表的時候記錄基準資料之後,對增量資料回放到新老表來保證新舊表間資料同步。
保證資料的一緻性做法如下圖所示:
在主庫中建立新表和心跳表,然後将不提供服務的從庫中老表的資料按照分庫分表的路由規則分發到主庫的新表中;與此同時從庫中會建立一個增量表,用于記錄遷移過程中資料庫的增量
INSERT、UPDATE、DELETE操作,當老表中的資料全部導入到新表後,會校驗新老表資料的一緻性,資料一緻以後,開始将增量表的資料重放到新老表中,當增量資料少于1000條時會鎖住主庫
中的老表,防止不斷産生新的增量,同時把剩餘的增量資料重放到從庫的老表以及主庫的新表中,進而達到新老表資料的一緻。
3、怎麼不影響線上服務
基準資料導出方是一個不提供服務的從庫,主庫的新表也不想外提供服務,最多在導入資料的時候影響到主庫的IO,如果導入頻率控制好的話對線上基本無影響。
4、 怎麼保證業務遷移到新表的過程中不影響線上服務
很遺憾,OSS做不到這一點,因為資料遷移之前,業務方使用的是老表的資料,當資料遷移到新表以後,為了保證新老表資料的一緻性,必須停止服務,然後業務方上線對新表的操作才可以,業務方在全推過程中不能
對外提供服務,對于像錢包的payserver,從驗證到全推的過程至少1個小時,而停止一個小時的服務是不現實的,而這一點在下面的提供的方案中得以解決。
總結: 這個方案有他的好處,此方案是有OSC方案衍生而來的,能完保證資料的一緻性,雖然在資料遷移過程中不影響線上的服務,但是在資料遷移後需要停止服務完成和業務方的對接,不能保證平滑遷移。
但是該方案遷移周期比較短,如果對線上服務影響要求不高,并且對遷移時間要求比較高的情況下建議使用該方案。
此方案的詳細資訊見附件:MySQL Online Schema Sharding詳細設計.docx
三、 新的線上分庫分表解決方案(暫時叫做MySQL NEW Online Schema Sharding 簡稱 NOSS)
1、 總體解決方案
NOSS方案需要業務方雙寫,并且同時需要通過單獨的拉庫操作來達到資料的一緻性,通過控制拉庫的速度進而控制對線上的影響,總體方案如下圖所示,
除了選取拉庫所需要的主鍵以及資料的一緻性校驗,其他的所有的操作都是在主庫上執行。
mysql線上分表分庫方案 三、 新的線上分庫分表解決方案(暫時叫做MySQL NEW Online Schema Sharding 簡稱 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)嚴格控制上線步驟,不要産生由于上線順序造成的業務影響。
五 上線步驟
附加中有這個方案的上線步驟說明在這裡不再累述 payserver_1.1.63_詳細設計_收款表分庫分表詳細設計.docx
六 為什麼選擇了這個方案
大家會發現這個方案上線過程很複雜,流程很麻煩,但是為什麼要選擇這個方案,難道沒有更好的方案嗎?
期初是有四套方案供選擇,但是最終确定了這個方案,這裡簡述一下為什麼沒有選擇其他的方案。
方案一 :
這個方案與最終方案流程基本類似,最終方案也是在這個方案上做了優化,這裡不再累述
方案二:
方案二采用的是DBA使用的MySQL Online Schema Sharding,之是以沒有選擇這套方案
(1) 這套方案并不是很成熟,存在bug并且維護這個方案的RD最近辭職,選擇這套方案危險比較高
(2)這套方案需要停止服務一段時間,而這段時間可能比較長,是payserver不能容忍的,因為支付行業不同于其他産品,涉及到資金的流通,不允許超過五分鐘的停服務時間。
方案三:
主要思路是通過binlog重放來達到新老表的資料一緻,但是這也需要停止服務去完成新老表的資料一緻,而且這個時間也是不能被接受的。
綜上: 其他方案都需要停止payserver的服務,而且停止服務的時間都大于5分鐘,這樣不可以被接受,是以最終選擇了現在這套方案。