文章目录
产生背景
数据量太大,必须采取分布式数据库来保存数据。
为什么自研?有什么作用?能带来什么效果?会有收益吗?
OceanBase的目标是支持数百TB的数据量以及数十万TPS、数百万QPS的访问量。无论是数据量还是访问量,即使采用非常昂贵的小型机甚至是大型机,单台关系数据库系统都无法承受。当前常见的处理方法主要有以下两种:
-
一.种常见的做法是根据业务特点对数据库进行水平拆分。根据某个业务字段,通常取用户编号,哈希后取模,根据取模的结果将数据分布到不同的数据库服务器上,客户端请求通过数据库中间层路由到不同的分区(和前边讲的数据分布一样)。这种方式目前还存在一定的弊端:
-
第一,数据和负载增加后添加机器的操作比较复杂,需要人工介入。
-
第二,有些范围查询需要访问几乎所有的分区,例如,按照用户编号分区,查询收藏了一个商品的所有用户需要访问所有的分区。(也就是哈希分布是不支持顺序查找的)
-
第三,目前广泛使用的关系数据库存储引擎都是针对机械硬盘的特点设计的,不能够完全发挥新硬件(SSD) 的能力。
-
-
另外一种做法是参考分布式表格系统的做法。例如GoogleBigtable系统,将大表划分为几万、几十万甚至几百万个子表,子表之间按照主键有序,如果某台服务器发生故障,它上面服务的数据能够在很短的时间内自动迁移到集群中所有的其它服务器。这种方式解决了可扩展性的问题,少量突发的服务器故障或者增加服务器对使用者基本是透明的,能够轻松应对促销或者热点事件等灾发流量增长。另外,由于子表是按照主键有序分布的,很好地解决了范围查询的问题。
虽然分布式表格系统解决了可扩展性问题,但往往无法支持事务。例如Bigtable只支持单行事务,针对同一个user_id下的多条记录的操作都无法保证原子性。而OceanBase希望能够支持跨行跨表事务,这样使用起来会比较方便。
为什么分布式表格系统往往无法支持事务?为什么OB支持事务使用起来会比较方便?
今晚加
最直接的做法是在 Bigtable 开源实现(如HBase或者Hypertable)的基础上引入两阶段提交协议来支持事务,比如Google 的 Percolator ,但是他对于事务的相应时间太长了(2~5秒),还是满足不了OB的需求。那么就只能根据业务特点进行一些私人订制了!!
通过分析发现,虽然淘宝在线业务的数据量十分庞大,但最近一段时间(例如一天)的修改量往往不多,因此,OceanBase决定采用单台更新服务器来记录最近一段时间的修改增量,而以前的数据保持不变,称为基准数据。基准数据以类似分布式文件系统的方式存储于多台基准数据服务器中,每次查询都需要把基准数据和增量数据融合后返回给客户端。这样,写事务都集中在单台更新服务器上,避免了复杂的分布式事务,高效地实现了跨行跨表事务;另外,更新服务器上的修改增量能够定期分发到多台基准数据服务器中,避免成为瓶颈,实现了良好的扩展性。
那么这里的意思就是:如果 UpdateServer 不定期分发的话,会成为瓶颈??这是为什么呐??
系统架构
整体服务端架构
主要有以下几个部分:
- 客户端:使用方式与 Mysql 完全相同,基于 Mysql 开发的工具也能够直接迁移到 OB
- RootServer:(存放元数据)管理集群中的所有服务器,子表 tablet 数据分布及副本管理。主备之间强同步。(强同步是什么呐?预计下篇讲解)
- UpdateServer:存储增量更新数据,往往和RootServer公用一台物理服务器。可以自行配置同步的模式,不一定要是强同步。
- ChunkServer:存储基准数据,基准数据有多个副本,一般就是两三份。(这里也应该问一个为什么?)
- MergeServer:接受分析优化客户端请求,转发到ChunkServer或者UpdateServer, 合并UpdataServer和ChunkServer的数据返回给客户端,并定期把UpdateServer上的数据同步到ChunkServer上。Mysql可以通过Mysql通信协议直接访问 MergeServer
OceanBase 支持部署多个机房,每个机房中均部署一个包含 RootServer、 MergeServer、ChunkServer 和 UpdateServer 的完整 OceanBase 集群,如图所示。每个集群由各自的 RootServer 负责数据划分、负载均衡,集群服务器管理等操作。集群之间数据同步通过主集群中的主 UpdateServer 往备集群同 步增量更新操作日志实现。客户端配置了多个集群的 RootServer 地址列表,使 用者可以设置每个集群的流量分配比例,客户端根据这个比例将读写操作发往不同的集群。
客户端如何完成与 MergeServer 通信的?(其实就是先 RootSvr->MergeSvr)
OceanBase 集群有多台 MergeServer,这些 MergeServer 的服务器地址存储在 OceanBase服务器端的系统表内。 OceanBase Java/C 客户端首先请求服务器端获取 MergeServer 地址列表,接 着按照一定的策略将读写请求发送给其中一台 MergeServer,并负责对出现故障 的 MergeServer 进行容错处理。(这个咋负责啊???见下面的流程讲解)
OceanBase Java/C 客户端访问 OceanBase 的流程如下:
-
OceanBase 客户端请求 RootServer 获取集群中 MergeServer 的地址列 表。
-
按照一定的策略选择一台 MergeServer 发送读写请求。 说明: 客户端支持的策略主要有两种:随机以及一致性哈希。一致性哈希的主要目的是将相同的 SQL 请求发送到同一台 MergeServer ,方便 MergeServer 对查询结果进行缓存。
-
如果请求 MergeServer 失败,则从 MergeServer 列表中重新选择一台 MergeServer。当请求某台 MergeServer 失败超过一定的次数时,则将这 台 MergeServer 加入黑名单并从 MergeServer 列表中删除。另外,客户 端会定期请求 RootServer 更新 MergeServer 地址列表。
如果 OceanBase 部署多个集群,客户端还需要处理多个集群的流量分配问题。 使用者可以设置多个集群之间的流量分配比例,客户端获取到流量分配比例后, 按照这个比例将请求发送到不同的集群。
OceanBase 程序升级版本时,先将备集群的读取流量调整为“0”。这时所有的读 写请求都只发往主集群,接着升级备集群的程序版本。备集群升级完成后将流量 逐步切换到备集群,并观察一段时间。如果没有出现异常,则将所有的流量切到 备集群,并将备集群切换为主集群提供写服务。原来的主集群变为新的备集群, 升级新的备集群的程序版本后重新分配主备集群的流量比例。
RootServer
RootServer 管理集群中的所有MergeServer、ChunkServer以及UpdateServer。 每个集群内部同一时刻只允许一个 UpdateServer 提供写服务,这个 UpdateServer 为主 UpdateServer。这种方式通过牺牲一定的可用性获取了强一致性。(为什么这么说呐?)RootServer 通过租约(Lease)机制选择唯一的主 UpdateServer,当原 先的主 UpdateServer 发生故障后,RootServer 能够在原先的租约失效后选择一 台新的UpdateServer作为主UpdateServer。另外,RootServer与MergeServer、 ChunkServer 之间保持心跳(heartbeat),从而能够感知到在线和已经下线的 MergeServer、ChunkServer 机器列表。
OceanBase 内部使用主键对表格中的数据进行排序和存储,主键由若干列组成 并且具有唯一性(这不就是mysql的唯一主键)。在 OceanBase 内部,基准数据按照主键排序并且划分为数据量大致相等的数据范围,称为 Tablet。每个 Tablet 的默认大小是 256MB(可配 置)。目前,OceanBase 采用 RootTable 一级索引结构。
如图所示,主键值在[1,100]之间的表格被划分为四个 Tablet:125,2650, 51~80 以及 81~100。RootServer 中的 RootTable 记录了每个 Tablet 所在的 ChunkServer 位置信息,每个 Tablet 包含多个副本(一般为三个副本,可配置), 分布在多台 ChunkServer 中(会不会出现三个副本都在一台机器上,而恰好这台机器宕机了,那怎么处理呐?)。当其中某台 ChunkServer 发生故障时,RootServer 能够检测到,并且触发对这台 ChunkServer 上的 Tablet 增加副本的操作。另外, RootServer 也会定期执行负载均衡,选择某些 Tablet 从负载较高的机器迁移到 负载较低的机器。
RootServer 建议采用一主一备的结构,主备之间数据强同步,并通过 Linux HA 实现高可用性。主备 RootServer 之间共享 VIP,当主 RootServer 发生故障后, VIP 能够自动漂移到备 RootServer 所在的机器,备 RootServer 检测到以后切换 为主 RootServer 提供服务。
Linux-HA项目为高可用性集群系统维护了一组构建块,包括集群消息传递层,用于各种应用程序的大量资源代理,以及管道库和错误报告工具包。
VIP:虚拟 ip,会与 arp 有关。虚拟IP(VIP)原理
MergeServer
MergeServer 的功能主要包括:协议解析、SQL 解析、请求转发、结果合并、 多表操作等。
OceanBase 客户端与 MergeServer 之间的协议为 MySQL 协议。MergeServer 首先解析 MySQL 协议,从中提取出用户发送的 SQL 语句,接着进行词法分析 和语法分析,生成 SQL 语句的逻辑查询计划和物理查询计划,最后根据物理查 询计划调用 OceanBase 内部的各种操作符。
MergeServer 缓存了 Tablet 分布信息,根据请求涉及的 Tablet 将请求转发给该 Tablet 所在的 ChunkServer。如果是写操作,还会转发给 UpdateServer。某些请求需要跨多个 Tablet,此时 MergeServer 会将请求拆分后发送给多台 ChunkServer,并合并这些 ChunkServer 返回的结果。如果请求涉及到多个表 格,MergeServer 需要首先从 ChunkServer 获取每个表格的数据,接着再执行 多表关联或者嵌套查询等操作。 这不就相当于一个中间件的角色吗?
MergeServer 支持并发请求多台 ChunkServer,即将多个请求发给多台 ChunkServer,再一次性等待所有请求的应答。另外,在 SQL 执行过程中,如果某个 Tablet 所在的 ChunkServer 出现故障,MergeServer 会将请求转发给该 Tablet 的其他副本所在的 ChunkServer。
MergeServer 本身是没有状态的,因此,MergeServer 宕机不会对使用者产生影 响,客户端会自动将发生故障的 MergeServer 屏蔽掉。 (客户端先RootServer查询的一步就会查不到他了,那么再来想一下,他是如何被root检测到的呐,那就是客户 端会定期请求 RootServer 更新 MergeServer 地址列表`)
UpdateServer
UpdateServer 是集群中唯一能够接受写入的模块,每个集群中只有一个主 UpdateServer。UpdateServer 中的更新操作首先写入到内存表,当内存表的数据量超过一定值时,可以生成快照文件并转储到 SSD 中。快照文件的组织方式 与 ChunkServer 中的 SSTable 类似,因此,这些快照文件也称为 SSTable。另 外,由于数据行的某些列被更新,某些列没被更新,SSTable 中存储的数据行是 稀疏的,称为稀疏型 SSTable。
为了保证可靠性,主 UpdateServer 更新内存表之前需要首先写操作日志,并同步到备 UpdateServe
r。当主 UpdateServer 发生故障时,RootServer 上维护的租约将失效,此时,RootServer 将从备 UpdateServer 列表中选择一台最新的备 UpdateServer 切换为主 UpdateServer 继续提供写服务。UpdateServer 宕机重启后需要首先加载转储的快照文件(SSTable 文件),接着回放快照点之后的操作日志。
由于集群中只有一台主 UpdateServer 提供写服务,因此,OceanBase 很容易地实现了跨行跨表事务,而不需要采用传统的两阶段提交协议。(这是为什么呐??相当于单机呗
)当然,这样也带 来了一系列的问题。由于整个集群所有的读写操作都必须经过 UpdateServer,
UpdateServer 的性能至关重要。OceanBase 集群通过定期合并,将 UpdateServer 一段时间之前的增量更新源源不断地分散到 ChunkServer,而 UpdateServer 只需要服务最新一小段时间新增的数据,这些数据往往可以全部存放在内存中。另外,系统实现时也需要对 UpdateServer 的内存操作、网络框架、磁盘操作做大量的优化。(这他妈的代码肯定非常牛逼了,猜的!!!)
ChunkServer
ChunkServer 的功能包括:存储多个 Tablet、提供读取服务和执行定期合并,分裂等。
OceanBase 将大表划分为大小约为 256MB 的 Tablet,每个 Tablet 由一个或者 多个 SSTable 组成(一般为一个),每个 SSTable 由多个块(Block,大小为 4KB ~ 64KB 之间,可配置)组成,数据在 SSTable 中按照主键有序存储。查找 某一行数据时,需要首先定位这一行所属的 Tablet,接着在相应的 SSTable 中 执行二分查找。
SSTable 支持两种缓存模式,Block Cache 以及 Row Cache。 Block Cache 以 Block 为单位缓存最近读取的数据,Row Cache 以行为单位缓 存最近读取的数据。
MergeServer 将每个 Tablet 的读取请求发送到 Tablet 所在的 ChunkServer。 ChunkServer 首先读取 SSTable 中包含的基准数据。MergeServer 接着请求 UpdateServer 获取相应的增量更新数据,并将基准数据与增量更新融合后得到最终结果。
由于每次读取都需要从 UpdateServer 中获取最新的增量更新。为了保证读取性 能,需要限制 UpdateServer 中增量更新的数据量,最好能够全部存放在内存中。 OceanBase 内部会定期触发合并,在这个过程中,ChunkServer 将从 UpdateServer 获取一段时间之前的更新操作
。通常情况下,OceanBase 集群会 在每天的服务低峰期(凌晨 1:00 开始,可配置)执行一次合并操作。这个合并 操作往往也称为每日合并。
最后先附上updateServer的代码:https://github.com/alibaba/oceanbase/tree/master/oceanbase_0.4/src/updateserver