专栏名称: CSDN
CSDN精彩内容每日推荐。我们关注IT产品研发背后的那些人、技术和故事。
目录
相关文章推荐
新浪科技  ·  【#DeepSeek崩溃次数变少了#?内部人 ... ·  17 小时前  
新浪科技  ·  【#多方恶意申请DeepSeek商标被驳回# ... ·  16 小时前  
爱范儿  ·  劳斯莱斯发布了有史以来最强大的车型 ·  昨天  
DeepTech深科技  ·  “踢掉”OpenAI之后,Figure ... ·  3 天前  
51好读  ›  专栏  ›  CSDN

京东分布式数据库系统演进之路

CSDN  · 公众号  · 科技媒体  · 2017-02-22 10:31

正文

作者: 张成远,京东云资深架构师,《Mariadb原理与实现》作者,开源项目speedy作者,2012年加入京东数据库研发团队,负责京东分布式数据库系统的架构与研发工作,主导了京东分布式数据库系统在公司的落地及大规模推广,擅长高性能服务器开发、分布式缓存/数据库/存储等大规模分布式系统架构。

关于数据库的使用,在京东有几个趋势,早期在京东主要用SqlServer及Oracle也有少量采用MySQL,随着业务发展技术积累及使用成本等因素,很多业务都开始使用MySQL,包括早期使用SqlServer及Oracle的很多核心业务也都渐渐的开始迁移到MySQL,单机的MySQL往往无法支撑这类业务,需要考分布式的解决方案,另外原本使用MySQL的业务随着数据量及访问量的增加也会遇到瓶颈最终也会考虑采用分布式解决的方案,整个京东发展趋势如图1所示。

图1 业务使用数据库演变趋势

分布式的数据库解决方案有很多种,在各个互联网公司使用得也是非常的普遍,本质上就是将数据拆开存储在多个节点上从而缓解单节点的压力,业务层面也可以根据业务特点自行进行拆分,如图2所示,假设有一张user表,以ID为拆分键,假设拆分成两份,最简单的就是奇数ID的数据落到一个存储节点上,偶数ID的数据落到另外一个存储节点上,实际部署示意图如图3所示。

除了业务层面做拆分,也可以考虑采用较为通用的一些解决方案,主要分为两类,一类是客户端解决方案,这种方案是在业务应用中引入特定的客户端包,通过该客户端包完成数据的拆分查询及结果汇总等操作,这种方案对业务有一定侵入性,随着业务应用实例部署的数量比较大,数据库端可能会面临连接数压力比较大的问题,另外版本升级也比较困难,优点是链路较短,从应用实例直接到数据库。

图2 数据拆分示意图

另一类是中间件的解决方案,这种方案是提供兼容数据库传输协议及语法规范的代理,业务在连接中间件的时候可以直接使用传统的JDBC等客户端,从而大大减轻了业务开发层面的负担,弊端是中间件的开发难度会比客户端方案稍微高一点,另外网络传输链路上多走了一段,理论上对性能略有影响,实际使用环境中这些系统都是在机房内网访问,这种网络上的影响完全可以忽略不计。

图3 系统部署示意图

根据上述分析,为了更好的支撑京东大量的大规模数据量的业务,我们开发了一套兼容MySQL协议的分布式数据库的中间件解决方案,我们称之为JProxy,这套方案经过了多次的演变最终完成并支撑了京东全集团的去Oracle/Sqlserver任务。

JProxy第一个版本如图4所示,每个JProxy都会有一个配置文件,我们会在配置文件中配置相应业务的库表拆分信息及路由信息, JProxy接收到SQL以后会对SQL进行解析再根据路由信息决定SQL是否需要重写及该发往哪些节点,等各节点结果返回以后再将结果汇总按照MySQL传输协议返回给应用。

结合上文的例子,当用户查询user这张表时假设SQL语句是select * from user where id = 1 or id = 2,当收到这条SQL以后,JProxy会将SQL拆分为select * from user where id=1 及select * from user where id = 2, 再分别把这两条sql语句发往后端的节点上,最后将两个节点上获取到的两条记录一并返回给应用。

这种方案在业务库表比较少的时候是可行的,随着业务的发展库表的数量可能会不断增加,尤其是针对去Oracle的业务在切换数据库的时候可能是一次切换几张表,下一次再切换另外几张表,这就要求经常修改配置文件。另外JProxy在部署的时候至少需要部署两份甚至多份,如图5所示,此时面临一个问题是如何保证所有的配置文件在不断修改的过程中是完全一致的。在早期运维过程中,我们靠人工修改完一份配置文件,再将相应的配置文件拷贝给其他的JProxy,确保JProxy配置文件内容一致,这个过程心智负担较重且容易出错。

图4 版本一

图5 配置文件

在之后的版本中我们引入了JManager模块,这个模块负责的工作是管理配置文件中的路由元信息,如图6所示。JProxy的路由元信息都是通过JManager来统一获取,我们只需要通过JManager往元数据库里添加修改路由元数据,操作完成以后通知各个JProxy动态加载路由信息就可以保证每个JProxy的路由信息是完全一致的,从而解决维护路由元信息一致性的痛点。

图6 版本二

在提到分布式数据库解决方案时一定会考虑的一个问题是扩容问题,扩容有两种方式,一种我们称之为re-sharding方案,简单的说就是一片拆两片,两片拆为四片,如图7所示,原本只有一个MySQL实例一个shard,之后拆分成shard1和shard2两个分片,之后再添加新的MySQL实例,将shard1拆分成shard11和shard12两个分片,将shard2拆分成shard21和shard22两个分片放到另外新加的MySQL实例上,这种扩容方式是最理想的,但具体实现的时候会略微麻烦一点,我们短期之内选择了另一种偏保守一点在合理预估前提下足以支撑业务发展的扩容模式,我们称之为pre-sharding方案,这种方案是预先拆分在一定时期内足够用的分片数,在前期数据量较少时这些分片可以放在一个或少量的几个MySQL实例上,等后期数据量增大以后可以往集群中加新的MySQL实例,将原本的分片迁移到新添加的MySQL实例上,如图8所示,我们在一开始就拆分成了shard1、shard2、shard3、shard4四个分片,这四个分片最初是在一个MySQL实例上,数据量增大以后我们可以添加新的MySQL实例,将shard3和shard4迁移新的MySQL实例上,整个集群分片数没有发生变化但是容量已经变成了原来的两倍。

图7 re-sharding方案

图8 pre-sharding方案

Pre-sharding方案相当于通过迁移完成达到扩容的目的,分片位置的变动涉及到数据的迁移验证及路由元数据的变更等一系列变动,所以我们引入了JTransfer系统,如图9所示。JTransfer可以做到在线无缝迁移,迁移扩容时只需提交一条迁移计划,指定将某个分片从哪个源实例迁移到哪个目标实例,可以指定在何时开始迁移任务,等到了时间点系统会自动开始做迁移。整个迁移过程中涉及到迁移基础全量数据和迁移过程中业务访问产生的增量数据,一开始会将基础全量数据从源实例中dump出来到目标实例恢复,确认数据正确以后开始追赶增量数据,当增量数据追赶到一定程度系统预估可以快速追赶结束时,我们会做一个短暂的锁定操作,从而确保将最后的增量全部追赶完成,这个锁定时间也是在提交迁移任务时可以指定的一个参数,比如最多只能锁定20s,如果因为此时访问量突然增大等原因最终剩余的增量没能在20s内追赶完成,整个迁移任务将会放弃,确保对线上访问影响达到最小。迁移完成之后会将路由元信息进行修改,同时将路由元信息推送给所有的JProxy,最后再解除锁定,访问将根据路由打到分片所在的新位置。

图9 版本三

系统在生产环境中使用的时候,除了考虑以上的介绍以外还需要考虑很多部署及运维的事情,首先要考虑的就是系统如何活下来,需要考虑系统的自我保护能力,要确保系统的稳定性,要做到性能能够满足业务需求。

在JProxy内部我们采用了基于事件驱动的网络IO模型同时考虑到多核等特点,将整个系统的性能发挥到极致,在压测时JProxy表现出来的性能随着MySQL实例的增加几乎是呈现线性增长的趋势,而且整个过程中JProxy所在机器毫无压力。

保证性能还不够,还需要考虑控制连接数、控制系统内存等,连接数主要是控制连接的数量这个比较好理解,控制内存主要是指控制系统在使用过程中对内存的需求量,比如在做数据抽数时候,sql语句是类似select * from table这种的全量查询,此时后端所有的MySQL数据会通过多条连接并发地往中间件发送数据,从中间件到应用只有一条连接,如果不对内存进行控制就会造成中间件OOM,在具体实现的时候我们通过将数据压在TCP栈中来控制中间件前后端连接的网络流速从而很好的保证了整个系统的内存是在可控范围内。

另外还需要考虑权限,哪些IP可以访问哪些IP不能访问都需要可以精确的控制,具体到某一张表还需要控制增删改查的权限,我们建议业务在写SQL的时候尽量都带有拆分字段保证SQL都可以落在某个分片上从而保证整个访问是足够的简单可控,我们为之提供了精细的权限控制,可以做到表级别的增删改查权限,包括是否要带有拆分字段,最大程度做到对SQL的控制,保证业务在测试阶段写出不满足期望的SQL都能及时发现,大大降低后期线上运行时的风险。

除了基本的稳定性之外,在整个系统全局上还需要考虑到服务高可用方案。JProxy是无状态的,一个业务在同一个机房内部署至少两个JProxy且必须跨机架的,保证在同一个机房里JProxy是高可用的。在另外的机房会部署再部署两个JProxy,做到跨机房的高可用。除了中间件自身的高可用以外还需要保证数据库层面的高可用,全链路的高可用才是真正的高可用。数据库层面在同一个机房里会按照一主一从部署,在备用机房会再部署一个备,如图10所示。JProxy访问MySQL时通过域名访问,如果MySQL的主出异常数据库会进行相应的主从切换操作,JProxy可以访问到切换以后新的主,如果整个机房的数据库异常可以直接将数据的域名切换到备用机房,保证JProxy可以访问到备用机房的数据库。业务访问JProxy时也是通过域名访问,如果一个机房的JProxy都出现了异常,和数据库类似直接将JProxy前端的域名切换到备用机房,从而保证业务始终都能正常访问JProxy。

数据高可靠也是非常关键的点,我们会这对数据库的数据进行定期备份,将备份数据存储到相应的存储系统中,从而保证数据库中的数据即使被删除依然是可以恢复的。

图10 部署示意图

系统在线上运行时候监控报警是极其重要的,监控可以分多个层次,如图11所示,从主机和操作系统的信息到应用系统的信息到特定系统内部特定的信息的监控等,针对操作系统及主机的监控京东有MJDOS系统可以把系统的内存/cpu/磁/网卡/机器负载等各种信息都纳入监控系统,这些操作系统的基础信息对系统异常的诊断非常关键,比如因为网络丢包等引起的服务异常都可以在这个监控系统中及时找到根源。

京东还有统一的监控报警系统UMP,这个监控系统主要是给所有的应用系统服务,所有的应用系统按照一定的规则暴露接口,在UMP系统中注册以后,UMP系统就可以提供一整套监控报警服务,最基本的比如系统的存活监控以及是否有慢查询等。

除了这两个基本的监控系统以外,我们还针对整套中间件系统开发了定制的监控系统JMonitor,之所以开发这套监控系统是因为我们需要采集更多的定制的监控信息,在系统发生异常时能够第一时间定位问题,举个例子当业务发现TP99下降时往往伴随着有慢SQL,应用从发送SQL到收到结果这个过程中经过了JProxy到MySQL又从MySQL经过JProxy再回到应用,这条链路上任何一个环节都可能慢,不管是哪个阶段耗时,我们需要将这种慢SQL的记录精细化,精细到各个阶段都花了多少时间,做到出现慢SQL时能快速准确的找到问题根源快速解决问题。

另外在配合业务去Oracle/SqlServer时,我们不建议使用跨库的事务,但是会出现有一种情况,同一个事务里的SQL都是带有拆分字段的,每条SQL都是单节点的,同一个事务里有多条这种SQL,结果却出现这个事务是跨库的,这种事务我们都会有详细的记录,业务方可以直接通过JMonitor找到这种事务从而更好的进一步改进。除了这个以外,在测试环境时候业务系统一开始写的SQL没有考虑太多的优化可能会出现比较多的慢SQL,这些慢SQL我们都会统一采集在JMonitor系统上进行分析处理,帮助业务方快速迭代调整SQL语句。







请到「今天看啥」查看全文