Sitemap" content="www.duozhankeji.com">
全國免費熱線:
分布式MySQL數據庫TDSQL架構分析
作者:admin 点击:19843次 日期:2015-06-03
字號::T | T

      腾讯计费平台部为了解决基于内存的NoSQL解决方案HOLD平台在应对多种業務接入时的不足,结合团队在MySQL领域多年应用和优化经验,最终在MySQL存储引擎基础上,打造一套分布式SQL系統TDSQL。本文是对该系統架构分析。

      腾讯计费平台部托管着公司90%以上的虚拟账户,如QB、Q点、包月服务、游戏的二级账户等,为了保证能顺畅支撑公司各大業務的实时在线交易,并且在各种灾难场景下数据是一致并且可用的,对系統的可用性、一致性切换要求非常高,因此计费团队历来都非常重视高一致性存储系統的建设。 
      到目前爲止,計費高一致性存儲層的解決方案大致經過了3個階段,本文將分享最新的基于MySQL的分布式解決方案。 
      随着業務的发展,基于内存的NoSQL解决方案HOLD平台在高峰期一天支撑3000亿读写,证明了分布式Cache的巨大价值;但随着各种業務的接入,NoSQL方案的不足也逐步显现出来了,如下所示。 
      1.适用的業務场景比较有限,仅提供get/set操作,有不少業務场景希望能通过记录中的其他字段做索引来查询,比如流水类業務。 
      2.不是所有的數據都是熱點,一台64GB內存機器提供的有效內存空間大概在50GB左右,而采用Fusion卡的機型容量一般在1TB以上,對比起來,如果所有數據放入分布式Cache明顯是一種極大的浪費,最合理的當然是熱點在HOLD,冷數據采用基于磁盤的存儲。 
      3.计费平台部多年来在支付领域有了相当多的技术积累,HOLD作为NoSQL系統功能有限,因此建造一套更加强大通用的高一致性存储系統将整个支付领域的实时数据(重点是账户数据、用户订单数据,以及海量的流水数据)统一管理起来非常有价值。 
      基于上面的分析,结合我们在MySQL领域多年的应用和优化经验,最终决定在MySQL存储引擎基础之上,打造一套分布式的SQL系統。 
      1.保持原来的MySQL协议,这样以前访问MySQL系統的C++、Java各类系統都不需要修改,DBA能继续保持原来大部分使用习惯。 
      2.自動的跨IDC容災切換,同時保證數據一致性,對于提交成功的事務保證一筆不丟,達到銀行級對容災的要求。 
      3.灵活的容量伸缩机制,对業務透明,解决MySQL本身扩容不灵活的问题。 
      4.重点支持OLTP类型的在线業務。 
      整體架構
      针对上面的需求,TDSQL最终的结构如图1所示(与当前大部分中心化的分布式系統类似)。 

图1 TDSQL架构
      系統由三个模块组成:Scheduler、Agent、网关,三个模块的交互都是通过ZooKeeper完成,极大简化了各个节点之间的通信机制,相对于第二代HOLD的開發简单了很多。 
      Scheduler作爲集群的管理調度中心,主要功能包括: 
      1.管理set,提供創建、刪除set、set內節點替換等工作; 
      2.所有的DDL操作統一下發和調度; 
      3.監控set內各個節點的存活狀態,當set內主節點故障,發起高一致性主備切換流程; 
      4.監控各個set的CPU、磁盤容量、各個表的資源消耗情況,必要的時候自動發起擴容流程; 
      5.Scheduler自身的容災通過ZooKeqzer的選舉機制完成,保證中心控制節點無單點。 
      Agent模塊負責監控本機MySQL實例的運行情況,主要功能包括: 
      1.用短連接的方式周期性訪問本機的MySQL實例,檢測是否可讀、可寫,若發生異常,會將異常信息上報到ZooKeeper,最終會由上面描述的Scheduler模塊檢測到這個異常情況,從而發起容災切換; 
      2.檢測主備複制的執行情況,會定期上報主備複制的延時和延遲的事務數,若發生了主備切換,自動向新主機重建主備,因此MySQL的主備不需要DBA幹預,對于新增的實例會3.自動采用          xtrabackup通過主機自動重建數據; 
      4.檢測MySQL實例的CPU利用率和各個表的請求量、數據量、CPU利用率,上報到ZooKeeper,ZooKeeper通過全局的資源情況抉擇如何擴容、縮容; 
      5.監控是否有下發到自身的擴容任務,如有則會執行擴容流程(下面會有描述); 
      監控是否要發生容災切換,並按計劃執行主備切換流程。 
      网关基于MySQL Proxy開發,在网络层、连接管理、SQL解析、路由等方面做了大量优化,主要特点和功能如下: 
      1.解析SQL,將識別出的DDL語句直接存到ZooKeeper,讓Keeper來統一調度; 
      2.Watch ZooKeeper的路由信息,拉取最新的路由表保存到本地文件和内存; 
      3.將SQL請求路由到對應的set,支持讀寫分離; 
      4.對接入的IP、用戶名、密碼進行鑒權; 
      5.記錄完整的SQL執行信息,與秒級監控平台對接完成實時的SQL請求的時耗,成功率等指標監控分析; 
      6.对count、distinct、sum、avg、max、min、order by、group by等聚合类SQL一般需要访问后端的多个set,网关会分析结果并做合并再返回,暂不支持跨set join和分布式事务; 
      7.网关无状态,既支持与業務部署到一起,也可以独立部署(可通过TGW或者LVS做容灾)。 
      自動擴容機制 
      目前,針對MySQL的擴容,一般有下面兩種策略。 
      1.垂直扩容。一般通过升级硬件来实现,比如更换更好的CPU,将传统的sas盘换成FusionIO卡这类,然后针对新硬件调整好参数,在硬件结构变化比较大的时候,性能甚至能达到上十倍的提升。但垂直扩容有比较大的局限,就是这种模式随着業務的突增还是比较容易达到瓶颈,特别是面对互联网海量用户的时候,所以在互联网应用场景下,一般仅将垂直扩容当做一个辅助的手段。 
      2.水平扩容。常用的有2种方法,一是不同的库或者表部署到不同的实例,二是一张表需要根据某个字段拆分到不同的字表中(数据分片),这种策略在互联网系統中非常常见,很多系統会将这2种水平扩容的方法结合起来使用; 
      通过上述2种扩容方法的比较,为了应对海量扩展的需求,应该是重点选用水平扩容的方法。但水平扩容的实现一般对業務是有感知的,比如采用什么规则来拆表,拆开的表放到哪些节点,如果某个子表还有瓶颈应该怎么扩容,扩容是否还需要業務配合等等这些事情如果全部交给業務会比较繁琐,因此这些需求应该尽量全部交给TDSQL自身来完成,对業務完全透明。 
      分表邏輯 
      在TDSQL中,每個表(邏輯表)可能會拆分成多個子表(建表的時候通過在建表語句中嵌入注釋的方式提供一個shard字段名,最多會拆分出1W個子表),每個子表在MySQL上都是一個真實的物理表,這裏稱爲一個shard,因此一張表的數據可能會按這樣的方式分布在多個Set中,如圖2所示 

      每个SQL请求到达网关之后,网关会做词法和语法解析,重点会解析出shard字段,如果带了shard字段就可以直接查询路由表并发送到某个具体的set中。计费的OLTP类業務99%的请求都会带上shard字段;如果某笔请求没有shard字段,查询路由之后会将请求发送到所有的shard对应的set中,并对所有返回的结果做一些聚合运算。 
      擴容流程
      上面描述了shard的方式,但是这样的shard结构不是固定不变的,当Scheduler检测到某个set,某个表的CPU、磁盘超过阈值之后就会启动擴容流程。
      这里描述下具体的擴容流程。 
      扩容过程中一般都要尽量避免影响業務,目前来看存在2种比较成熟的策略。 
      策略1先切後搬:先修改路由,將需要遷走的數據的請求直接發送到新set,在新set交易過程中如發現本地的數據不存在,則去原set拉取數據,然後再通過一些離線的策略將要遷移的數據全量再搬遷一次,HOID平台就是采用這樣的策略。 
      策略2先搬後切:讓請求繼續在原set交易,擴容程序首先記錄一個binlog位置點,並將源set中符合遷移條件的數據全部遷移出去,最後再將搬遷過程中新增的binlog追完,最後修改路由規則,將請求發送到新set。 
      綜合來看,策略1最大的優點是假如是因爲壓力大做的遷移,可能很快就能將部分請求發送新set了,實現對原set的壓力分擔;策略2實現上在最後的追路由階段需要更多的精細化控制,實現會稍微複雜點,但策略2有個非常大的好處就是擴容過程中回滾非常方便,如有異常直接幹掉擴容任務即可。 
      对于TDSQL这类数据库業務系統来说,策略1实现会非常麻烦,因为请求到达新set之后可能需要去源set拉取数据,这个需要对MySQL本身进行修改;另外假如一个批量更新的update操作,可能要往新老set都发送一次请求,比较复杂,所以最终选择了策略2。策略2会有更大的通用性,開發模式基本上可以统一到所有类似的系統。 
      下面描述采用策略2具体的擴容流程。假如要将Set1中的t_shard_1的数据迁移一半到Set4中的t_shard_4(1667-3333)。 

图3 策略2的擴容流程
      Scheduler首先在Set4中創建好表t_shard_4。 
      後將擴容任務下發到Set1中的agent模塊,agent檢測到擴容任務之後會采用mysqldump+where條件的方式將t_shard_1中shard號段爲1667-3333的記錄導出來並通過管道用並行的方式插入到Set4(不會在本地存文件,避免引起過多的IO),用mysqldump導出鏡像的時候會有一個binlog位置。 
      从mysqldump记录的binlog位置开始读取binlog并插入到到Set4,追到所有binlog文件末尾的时候(这需要一个循环,每次循环记录从开始追binlog截止到追到文件结尾消耗的时间,必须保证追单次循环要在几秒之内完成,避免遗留的binlog太多导致最后一次追binlog消耗太多的时间,从而影响業務过久),对原来的表t_shard_1重命名t_shard_5,此时针对这个表不会再有新请求,若还有请求过来都会失败,然后再追一次binlog到文件结尾(因为上面的循环保证了追binlog不会太耗时间了,所以此次会快速完成),然后上报状态到ZooKeeper,表明扩容任务完成。 
      Scheduler收到擴容完成的信息之後會修改路由表,最後由網關拉取到新路由完成整體的擴容;從表重命名開始到網關拉取到新路由,這段時間這個原始shard不可用,從我們測試結果來看這個不可用的時間是200毫秒左右;如果某個網關異常,拉取不到新路由,繼續訪問老表t_shard_1會一直失敗,這樣就可以保證數據的一致性。 
      容災機制 
      对于TDSQL来说,我们希望容灾做到自动切换,自动恢复,主备一致性(保证業務提交的事务在切换过程不丢失),跨IDC容灾。 
      【MySQL異步複制】 
      在MySQL发展的早期,就提供了异步复制的技术,只要写的压力不是特别大,在网络条件较好的情况下,发生主备切换基本上能将影响控制到秒级别,因此吸引了很多開發者的关注和使用。但这套方案提供的一致性保证,对于计费或者金融行業是不够的。 
      图4是异步复制的大致流程,很显然主机提交了binlog就会返回给業務成功,没有保证binlog同步到了备机,这样在切换的瞬间很有可能丢失这部分事务。

【MySQL半同步複制】 
      到了MySQL 5.5版本的时候,Google提供了一个半同步半异步的插件,确保必须收到一个备机的应答才让事务在主机中提交;当备机应答超时的情况下,强同步就会自动退化成异步模式(这也是半同步半异步名字的由来)。  

图5 半同步复制
      這套方案相對異步複制,在數據的可靠性方面確實好很多,在主機本身故障的情況下,基本能保證不丟失事務(因爲最後一個事務,至少有一個備機上存在),但一旦退化成異步複制就回到過去了。TDSQL沒直接采用這套方案,是因爲:在主備跨IDC(ping延遲2-3毫秒)時性能非常很低。 
      【Cluster方案】 
      除了上面的方案外,开源社区还有三个Cluster解决方案,分别是Oracle的NDB引擎、Percona XtraDB Cluster和MariaDB Galera Cluster,从公开资料的性能对比上来看,后2者在性能和系統灵活性等方面都强于NDB(同时采用NDB意味着也放弃了InnoDB引擎,NDB主要是基于全内存的,并且需要高速网络环境支持,所以不考虑了);Percona XtraDB Cluster和MariaDB Galera Cluster强同步机制的底层都是采用Galera这套强同步的架构。MariaDB Galera Cluster具有如下非常吸引人的特性: 
      1.MariaDB Galera Cluster 是一套在MySQL InnoDB存储引擎上面实现multi-master及数据实时同步的系統架构,業務层面无需做读写分离工作,数据库读写压力都能按照既定的规则分发到各个节点上去; 
      2.同步复制Synchronous replication:保证节点间数据一致性; 
      3.Active-active multi-master拓扑逻辑:多主的拓扑结构,可以认为没有备机的概念; 
      4.可對集群中任一節點進行數據讀寫:假如一個set有3個節點,則3個節點可以同時讀寫,上次完全不用關心主備切換和讀寫分離; 
      5.自動成員控制,故障節點自動從集群中移除; 
      6.自動節點加入; 
      7.真正並行的複制,基于行級:同一個表可以在集群中任何節點更新,支持不帶where條件,但一次更新的記錄條數有限制; 
      8.每個節點都包含完整的數據副本。 
      目前來看,Galera是一套相當完美的方案。但是,在跨IDC的性能測試中,其性能下降比較大,另外,實現方案也比較複雜,目前對它的代碼理解還不夠透徹,所以暫時沒有在計費領域大範圍推廣使用。但我相信這個方向是對的,有吸引力的,隨著後續Galera越來越完善,我們對它研究得越透徹,也許有一天會采用這套方案。
      【性能測試和分析】 
      上面的三種複制模式對比測試,數據如圖6所示。

图6 三种复制模式的对比
      從圖6的數據可以看出,半同步和Galera模式對性能的損耗還是非常大的,Galera的毛刺尤其嚴重,所以在跨IDC環境下還不是適合計費這樣對延遲要求非常低的場景。 
      为什么性能损耗会这么严重呢?这个看明白MySQL的网络模型就清楚了。外界可查的MySQL最早的公开版本应该是1996年的3.1.1.1版本,这么多年来,网络模型基本上变化不大,与Apache有点类似,有点区别的是MySQL采用的是每个连接一个线程的模型,这套模型最大的好处就是開發特别简单,线程内部都是同步调用,只要不访问外部接口,支撑每秒几百上千的请求量也基本够用,因为大部分情况下IO是瓶颈。不过随着当前硬件的发展,尤其是SSD、FusionIO的出现,IOPS从200+/s进化到几十万甚至百万次/s,IO基本上不再是瓶颈,若再采用这套模型并采用阻塞的方式调用延迟较大的外部接口,则CPU都会阻塞在等网络应答上了,性能自然上不去。 
      不過在MySQL5.6企業版和MariaDB、Percona中都引入了線程池,使得網絡模型靈活了很多,圖7是簡化後的對比模型。 



                     图7 简化的对比模型

      TDSQL采用的強同步方案 
      從上面的分析可知,半同步半異步是比較輕量級的高一致性容災方案,但受限于已有的同步網絡模型,CPU利用不起來。我們如果在線程池基礎之上做一些修改,參考半同步的思路就可以實現一個高性能的強同步方案。 
      目前的做法是采用與Linux內核處理中斷的思路:將上面線程池模型的第三個環節(執行SQL的邏輯)拆成兩個部分: 
      1.上半部分:任務執行到寫binlog爲止,然後將會話保存到session中,接著執行下一輪循環去處理其他請求了,這樣就避免讓線程阻塞等待應答了; 
      2.然后:MySQL自身负责主备同步的dump线程会将binlog立即发送出去,备机的IO线程收到binlog并写入到relay log之后,再通过UDP给主机一个应答; 
      3.在主機上,開一組線程來處理應答,收到應答之後找到對應的會話,執行下半部分的commit,send應答,綁定到epoll等操作。綁定到epoll之後這個連接又可以被其他線程檢測到並執行了。 
      改造後性能提升明顯,如圖8所示。 

      數據高可用性保障機制 
      除上述強同步機制外,TDSQL還做了以下增強,以提升數據的可用性。 
      1.推荐一个set最少配置3个跨IDC的节点,可以按業務的要求对备机开放查询服务。 
      2.支持靈活增加節點,比如覺得3個節點還不夠,可以非常方便地增加節點。TDSQL會自動完成數據的全量和增量複制,此處主要依賴Xtrabackup實現物理複制,性能測試數據表明:一個小時大概可以拷貝500GB數據到新節點。那麽對于Z3(1.1TB盤,一般最多用800GB左右),新加入的節點大概1.5個小時左右就有了全量數據,此功能也可以用在壞盤等情況下替換節點的時候使用,非常方便。 
      3.细心的同学可能会发现上面的强同步还有点小缺陷:比如主机用kill -9杀掉,那么可能写了binlog但没有来得及发送到远端,此时当然也不会返回给業務成功,备机上不存在这笔数据,但主机起来之后会多出来这笔事务。我们的做法是对新增的事务根据row格式的binlog做闪回,当然回退不了的比如drop table之类的,就直接提醒运维手工确认是否清除数据库,然后会由Xtrabakcup机制自动从新的备机全量拉取数据重构。 
      4.節點的監控通過跨IDC部署的ZooKeeper來保證,並且主備切換由一套自動化的嚴格流程來保證。
      接下來的方向 
      1.当将高一致性容灾、高可用性、自动容量伸缩做实后,随着業務的接入,集群的规模会越来越大,TDSQL必将会更加依赖实时的资源调度、隔离框架,因此有必要研究如何将TDSQL与Docker结合起来。 
      2.如前所述,Galera集群是個非常好的發展方向,我們會持續研究並實踐。 
      3.目前大部分MySQL還在使用單個連接單線程模型,線程池也剛起步,以後隨著大家對性能要求越來越高,這塊也許可以繼續突破,比如結合線程池+協程也許是個很好的方向,如果真能引入協程,也許爲MySQL增加調用外部接口的結構會靈活很多。 
      4.TDSQL將數據拆是拆的徹底了,但作爲完整的分布式數據庫、合也需要考慮,比如跨庫少量記錄的join,規模受限的分布式事務等,目前的做法是數據按小時入TDW,在TDW上做OLAP分析。

發表評論

昵稱 * 驗證碼 * 驗證碼
上一篇: Struts2的配置
下一篇: 評點六種Java異常處理的陋習

資質證書

CMMI Ⅲ APPRAISAL ID:30062
ISO9001:08915Q20090ROS
ISO27001:04817I20037R0S
高新技術企業:GR201753000141
網站问题免费诊断

电子商务三位一体發展戰略

技術研發

業務培训

實戰運營

戰略布局