@Lucien
2016-02-23T10:41:03.000000Z
字数 4923
阅读 700
在此输入正文
京东分布式数据库架构与实践——田琪(京东数据库系统研发负责人)
主要介绍京东统一数据库中间层架构剖析及实践经验,分析如何做到数据透明拆分、自动扩容、无缝在线拆移及数据高可靠、服务高可用等功能及架构。
京东分布式数据库架构特性
• 支持MySQL,MariaDB,MongoDB等数据库
• 服务高可用,主库故障,从库自动切换
• 数据高可靠,定期快照备份,增量备份
• 数据自动拆分,一键无缝迁移扩容
• 针对特殊业务需求,定制优化特殊的数据库版本
MySQLFabric架构
MySQL Fabric特点:
Flashcache
SSD混合存储方案
资源隔离与配额 - 容器、虚拟化技术 • 底层存储方面的挑战与工作
我今天主要讲的是京东的云数据库相关的系统,数据库这方面我们做了很多事情,也是帮助京东去做自动化的一些事情。我们今天的话题主要集中在几个方面,一个是介绍分布式数据库架构和开源方案,有些是我们曾经考虑过的,但是没有用的,有些是我们用了的,都是我们过去两三年的时间所做的事情。
首先介绍一下京东分布式数据库,上面的APP是我们做的业务,下面的是Proxy和数据库实例。当时主要做的是数据拆分,落到下面不同的实例。下面的实例会通过cgroup做资源管控,外围的Master是我们的中心节点,存放中心的路由。比如说做了数据分片,我要知道哪个分片落到哪个实例,其中的关系是什么,可以找到你的数据在哪里,每个路由会放在中心的节点上,然后保证拿到的路由都是一致的。
外围还会做在线的无缝迁移,下面的分片可以来切,都是对线上的业务没有任何影响的。这一端会有监控,不用说了,所有的系统都有监控。还有一些外围的模块对我上面的实例做备份,定期上传到分布式存储的服务。总结起来整体的特性是支持MySQL、MariaDB、MangoDB等数据库。
服务高可用,主库故障,从库自动切换。数据高可靠,定期快照备份,增量备份。数据自动拆分,一键无缝迁移扩容,可以做到自动,但是对运营不是很好。针对特殊业务需求,定制优化特殊的数据库版本。
原生MySQL协议,接入使用标准MySQL客户端。数据根据路由规则分库分表,对业务访问透明。单库容量满,可以快速在线无缝迁移,不影响业务。接下来讲一下数据拆分,下面引入了虚拟的抽象层,会把一张表洒落在下面,其实这张图是把tb1拆成了四份,相当于放到datanode里面,其实会落到我的实例上。按照我这个图示实际上拆成了四份,前两个放一起,后两个放一起,这两个之间的关系,实际上就是路由。当然,也可以不分表。
内部实现是服务器顶端的类似网络服务器,前端和客户端之间是MySQL协议的交互,前端收到正常的解析包,确定一个完整的MySQL的协议,告诉你这个包是什么类型,这是典型的二进制的协议。收到这个协议之后,确认收到协议包之后,根据这个包的类型去做解析,根据路由的规则确定最终分配到哪些实例,拿到后端去做连接通信,把请求发走。很多情况是一个SQL可能改成两个,这个时候需要有中间的内容支持,每个字段可以拆分很多,最终有一个合并的过程。如果说数据分成了十分,相当于把一条鱼变成了十条鱼放到不同的内容当中,这是逻辑上的内容,实际上和IO没有关系。本质上说一个线程就搞定了,在SQL.Parser的时候不太合适,因为这个部分CPU的处理比较大。
主要说一下在线无缝迁移,不影响运营。比如说我库里面的数据分成两片,整个过程是分成线上和线下两个部分。线下最主要是把shard拉出来,这个过程仍然在写入,并且在这个过程中快到这个位置开始的点,所有的增量都从这个点开始。我有一个增量的点,把快照拉过来之后,线上还在源源不断的追,仍然是在线下进行的。在某个时间点追的差不多的时候,通过校验方法会知道,这个时候我就要转换了。收到这个通知之后,第一个阶段会阻塞分片的写入,如果不阻塞的话,另外一个分片可能永远追不上,因为线上的压力会比较大,这个时候就可以追上了。追上来之后,还是要对这两片数据做一下校验,看一下是否一致。
如果没有什么问题,就做第二个阶段,JManager更新一下本地的路由,会切换到第二个位置。我在讲了这个原理之后,想问大家一个问题,原来整个系统对线上影响的时间是多少?原来我们和淘宝的人讨论过,对这个定义不太一样,大家觉得对线上的影响是多少?3秒吗?这么准确?怎么估出来的?其实对线上的影响多长呢?关键的环节是阻塞分片的写入,如果你追完了以后,校验两边是否一致,不然写花了都不知道。
实际阻塞写入之后追上是非常快的,已经追的差不多了。实际上整个系统对线上的影响取决于两个分片,追完之后得校验。校验了以后对线上影响比较小,3秒钟校验完的话,就影响3秒钟。其实从编码开始都要校验一遍,这是要非常细致的。
第二个是节点之间的一致性怎么保证的呢?其实这里面最关键的问题是什么?因为我线上至少很多JProxy,如果中心节点同步到路由的时候,如果说分片不一致,数据肯定会乱掉,这是毫无疑问的。所以,最关键的问题是中心节点和JProxy的路由是一致的。可能大多数同仁的做法不一样,其实单纯在这个场景下也不复杂。
提问:如果在你路由的过程中有一个JProxy隔离了,它认为和别人不一样,但是只是隔离了,而不是挂掉了。
田琪:如果有任何一个不OK,我就不会做这件事情。如果返回是OK了,但是接受了阻塞分片的请求,并且逻辑是先阻塞的,先把活干了。
提问:多久没有返回呢?
田琪:这个需要去网上看一下,必须收到所有请求才会做这个事情。JProxy启动的时候,一定和路由是一致的。迁移计划统一提交至JTransfer模块,将迁移计划入库,定时执行。迁移以库为单位,最大的好处是工具用起来比较方便。迁移中间状态持久化入库,是因为我希望所有的过程,今天我要做一个迁移,提一个迁移表,提完了之后我就不管了,让他来执行,看结果。根据这个结果,每一步都会记录。如果这个路由写进去,即使JManager挂掉了,你也可以继续做下去,这个系统本身是比较简单的。
总结一下迁移步骤,有一些细节是没有说的,最主要的是恩两个版本不一样,或者是编码不一样,迁过去一定是错的。做完了以后要做数据校验,做完了以后要停止JProxy的写入,整个系统对线上影响的时间取决于这一步,切换过程就结束了。
JManager的节点路由中心化管理,路由变更统一入口到JManager。路由信息持久化到数据库,如果说路由搞乱了,数据一定是花掉了,你也不知道从哪里恢复,非常重要。所以说路由信息与JProxy做一个定时校验,其实后来发现运行一段时间以后不需要校验,前面的东西可以保证不出现特殊情况了。
然后介绍一下关于此类方案的局限,两年多就把这个系统设置了,做了两个月,后来上线了。大推的时候不是很久以前,但是在落地以前遇到了局限,一个是缺少全局事务管理。比如说JProxy的方案,比如说写一条,后面一直写一堆数据,这不是全局的事务。有可能这台机器成功了,那台机器失败了,不能保证成功和不成功。
拆分键不可更新,本质上是一个原因,比如说某个ID拆成两个,如果说我在原来路由上把原来ID删掉,在另外一个ID上加载,目标不一样,又是一个分布式节点。迁移的时候,工具用的很简单,一个命令就搞定了。但是分片效率不高,不会集中在一起,是分散和随机的。如果说自己写的,可以让它的效率更快。
一般情况下接入的时候,一个是要不要去查询,语法支持是没有问题的。但是数据量大是不行的,过程非常慢,消耗大量的内存,复杂查询支持不完整。前两天大家都做了和搜索相关的事情,与主流分布式NoSQL对比一下,用MySQL多一点。CONS只有简单的查询接口,不同副本策略导致写入一致性问题。最重要的一条是缺少完善工具运维工具和经验积累。PROS存储引擎简单,最大的好处是天然分布式而生。比如说30多万的代码里面,从引擎的角度考虑事情,可以减少三分之二的代码,MySQL里面只保留索引包块,记录、配置和基本的模式需要加速,其他模块都不需要了,这是只需要存储落地。但是单机的时候是没有用的,如果说数据多一点是不能操作的。比如说最早的时候不支持单机的安全性,如果一个机器挂着的话,会容易丢数据。
用户真实的需求首先有丰富的查询接口,可以随意使用复杂查询,没有容量及性能限制,支持分布式事务,支持丰富的隔离级别。想实现这些东西,我举一个案例,这是自己写的搜索引擎,可以做优化的。这三张表是和外界的关联关系,落地的时候是离散的,这是写的第一条记录。整个查下来是大量的随机,分布在里面。如果说把下面的存储语言的接口变成排序的格式,三张表是交错在一起的,我会按照这里面的数据做排序,我一个数据就把三张表做出来了,性能非常好。
另外一个是时序问题,这张图来自于刘奇,我在单机系统里面去支持隔离非常简单,生成一个ID就可以了。所有的版本通过这个ID,一行记录都有ID。如果我想知道这条语句应该不应该看到这个数据,根据大小的关系可以区分出来哪些数据的开启时间是比较晚的。但是分布式系统我没法得到ID,时间是不准确的。所以说谷歌的解决方引入了Uncertainty,但是有一个问题是某个时间段你是不清楚的。
另外一个是隔离性问题,我们要提供分布式系统的隔离性,不同的隔离性带来的结果不一样。比如说上面是Serializable,下面是Snapshot,一个是黑棋变白棋,一个是白棋变黑棋,另外的结果是都变成了白棋,另外一个是都变成了黑棋,这是不同隔离带来的不同结果。
其实我们做的工作中也参考了其他的方案,Fabric是一个客户端,特点是无中间代理层,架构设计比较典型。但是当时的情况是没法商用,功能不是很完善,基本上只实现了HA服务高可用和master节点。
我们觉得单机的性能不是特别好,要提升单机性能,最基本的办法是购买好一点的硬件,比如说SSD的盘。最开始的方案是结合社区主流的方案,比如说bcache,但是我们最终选择了flashcache,原理是把这一块盘分成若干4K的小块。根据这个应用场景能够估算出它优化的效果,你觉得用了这样一个东西,从原理上来说能提升多少呢?其实提升不了多少,最终还是要落地的。我们测的结果是读性能比raid10提升2到3倍,写性能无任何优势,毛刺现象会比普通磁盘明显。
单机性能提升之后最重要的事情是隔离,因为一台机器怕浪费了,要充分发挥机器硬件特性,互相之间要没有影响,有容器方案和虚拟机方案。对内毫无疑问是容器服务,虚拟机性能和各方面都没有容器优化。它的好处是隔离性特别好,因为需要引入硬件,比如说看它支持不支持BMX,那是英特尔的指令集,要让你在其中切换,这个肯定是对性能有影响的。
容器本质上在操作系统上调用是容器资源隔离技术的核心实现,内核新增nsproxy数据结构使不同进程可以拥有各自不同的命名空间。其实最早封装容器技术的是Warden,docker而比较方便的是对镜像管理的打包,除了这些之外是操作系统接口的封装,没有特别高深的。其实自己写一点程序,你也可以写一个容器系统,很简单的。
现在操作系统提供了六个资源隔离,其实是远远不够的。比如说设备、时间等等都要用。其实docker是通过上层用的工具来限制,不让你做这件事情,弥补缺陷的。但是用容器的时候,应用起来还是有一些差距的。而且容器用的时候最大的问题是IO,因为是因为资源都是限制一个进程,但是操作系统的IO特点不适合高速硬件设备,IO带宽控制只能用CFQ IO调度器。依赖较新内核版本,LXC需要至少3.8以上。
另外在操作系统一层也在做改写,原来是一个队列,所有请求过来从这个队列里进行排队,这是所说的电梯算法。在IO调度器里面,新的驱动设备是不用选这个东西了,并且限制了IO也是有一些影响的。