分布式内存数据库架构简析
OceanBase是阿里集团开发的可扩展https://www.oceanbase.com/分布式内存数据库截至2012年8月,已支持收藏夹、直通车报表、天猫测评等OLTP和OLAP在线服务,在线数据量突破1000亿。
从模块划分来看,OceanBase可以分为四个模块:主服务器RootServer、UpdateServer更新服务器、基线数据服务器ChunkServer和MergeServer合并服务器。OceanBase系统中的数据按照时间线分为基线数据和增量数据。基线数据是只读的,所有修改都会更新到增量数据中。增量数据通过合并操作有规律地合并到基线数据中。
海洋基地背景分析
淘宝收藏夹是淘宝的在线应用之一,淘宝用户将自己感兴趣的宝贝(即商品,此外,用户还可以收藏感兴趣的店铺)保存在其中,以便下次快速访问、比较和购买。用户可以显示和编辑(添加/删除)他们的收藏。淘宝收藏夹数据库包括收藏信息表(逐条收藏信息)和收藏项目表(收藏的宝贝和店铺)等。:
如果用户选择按宝贝价格排序后显示,那么分布式内存数据库需要从喜欢的物品表中读取喜欢的宝贝的价格等最新信息,然后进行排序。如果一个用户的物品集合很大(比如4000件),那么查询对应的物品需要很长时间:如果每件物品的平均查询时间为5ms,那么4000件物品的查询时间可能会达到20s,如果这样,用户体验会很差。
如果喜欢的宝贝的详细信息实时冗余到favorite info表,则不再需要上述查询favorite item表的操作。然而,由于许多流行商品可能被几千到几十万人收集,这些流行商品的价格和其他信息的变化可能导致收集信息表中的大量变化和overflow分布式内存数据库。
海洋基地设计理念
OceanBase的目标是支持数百TB的数据和数十万TPS以及数百万次QPS访问。无论是数据量还是访问量,分布式内存数据库 system即使是非常昂贵的小型机甚至大型机也无法承受。
一种常见的做法是根据业务特征水平分割数据库。通常的做法是哈希一个业务字段(通常取用户号,user_id),然后取模块。根据取模结果,将数据分发到不同的分布式内存数据库服务器,客户端请求通过分布式内存数据库中间层路由到不同的分区。目前,这种方法仍有一些缺点,如下:
- 数据和负载增加后,加机操作复杂,往往需要人工干预;
- 一些范围查询需要访问几乎所有的分区。比如根据user_id分区,所有查询一个商品集合的用户都需要访问所有分区;
- 目前广泛使用的分布式内存数据库 storage引擎是根据机械硬盘的特点设计的,无法充分发挥新硬件(SSD)的能力。
另一种方式是参考分布式表系统的做法,比如Google Bigtable系统,将一个大表分成几万、几十万甚至几百万个子表,子表按照主键排序。如果一台服务器出现故障,它所服务的数据可以在短时间内自动迁移到集群中的所有其他服务器。这种方法解决了可扩展性问题。少数意外的服务器故障或额外的服务器对用户来说基本是透明的,它可以轻松应对促销或热点事件等突发的流量增长。此外,由于子表按照主键有序分布,很好地解决了范围查询的问题。
凡事有利有弊。分布式表系统虽然解决了可扩展性问题,但往往不能支持事务。比如Bigtable只支持单行事务,对同一个user_id下多条记录的操作不能保证原子性。OceanBase希望支持跨行、跨表交易,使用起来会更方便。
最直接的方法是引入两阶段提交协议,支持基于Bigtable开源实现(如HBase或Hypertable)的分布式事务。这一理念体现在谷歌的Percolator系统中。但Percolator系统的事务平均响应时间为2 ~ 5秒,因此只能用于web数据库构建等半在线服务。此外,Bigtable的开源实现还不够成熟,单台服务器能够支持的数据量有限,单个请求的最大响应时间难以保证,机器故障等异常处理机制也存在很多严重问题。一般来说,这种方式的工作量和难度都超出了项目组的能力,需要根据业务特点做一些定制。
通过分析我们发现,虽然网上业务数据量巨大,比如几十亿、几百亿甚至更多的记录,但最近一段时间(比如一天)的变化量往往很小,通常不超过几千万到几亿。因此,OceanBase决定使用单个更新服务器来记录最近的变化,而之前的数据保持不变,之前的数据称为基线数据。基线数据以类似于分布式文件系统的方式存储在几个基线数据服务器中。每个查询都需要融合基线数据和增量数据,并将它们返回给客户端。这样,写事务集中在单个更新服务器上,避免了复杂的分布式事务,高效实现了跨行、跨表事务;此外,更新服务器上的修改增量可以有规律地分布到多个基线数据服务器上,避免了成为瓶颈,实现了良好的可扩展性。
当然,单个更新服务器的处理能力总是有限的。所以更新服务器的硬件配置比较好,比如内存大,网卡好,CPU好;此外,最近的更新操作总是可以存储在内存中,并且已经在软件级别针对这种场景进行了大量的优化。
海洋基地系统架构
OceanBase由以下部分组成:
- 客户端:用户使用OceanBase的方式和MySQL数据库完全一样,支持JDBC、C客户端访问等等。基于MySQL数据库开发的应用和工具可以直接迁移到OceanBase。
- RootServer:管理集群中的所有服务器、子表的数据分发和副本管理。根服务器一般是一主一备,主备之间的数据是强同步的。
- UpdateServer:存储OceanBase系统的增量更新数据。UpdateServer一般是一主一备,主备之间可以配置不同的同步模式。部署时,UpdateServer进程和RootServer进程通常共享物理服务器。
- ChunkServer:存储OceanBase系统的基线数据。基线数据通常存储在两个或三个副本中,这是可以配置的。
- MergeServer:接收并解析用户的SQL请求,经过词法分析、语法分析、查询优化等一系列操作后,转发给对应的ChunkServer或UpdateServer。如果请求的数据分布在多个ChunkServer上,MergeServer还需要合并多个ChunkServer返回的结果。客户端与MergeServer之间采用原生MySQL通信协议,MySQL客户端可以直接访问MergeServer。
OceanBase支持部署多个机房。每个机房都配备了一个完整的OceanBase集群,包括RootServer、MergeServer、ChunkServer和UpdateServer。每个集群负责数据分区、负载平衡、集群服务器管理和其他操作。通过主集群的主更新服务器对备用集群的同步增量更新操作日志,实现集群间的数据同步。客户端配置了多个集群的RootServer地址列表,用户可以设置每个集群的流量分配比例。根据这一比率,客户端将读写操作发送到不同的群集:
OceanBase客户服务端
1)请求RootServer获取集群中MergeServer的地址列表。
2)选择一个MergeServer按照一定的策略发送读写请求。与客户端的通信协议MergeServer兼容原生MySQL协议,只需要调用标准库如MySQL JDBC驱动或MySQL C客户端即可。客户端支持两种主要策略:随机散列和一致散列。哈希的主要目的是将同一个SQL请求发送到同一个MergeServer,方便MergeServer缓存查询结果。
3)如果对MergeServer的请求失败,请从MergeServer列表中重新选择一个MergeServer,然后重试。如果对MergeServer的请求失败超过一定次数,则将MergeServer列入黑名单并从MergeServer列表中删除。此外,客户端将定期请求根服务器更新MergeServer地址列表。
如果OceanBase部署了多个集群,客户端还需要处理多个集群的流量分配。用户可以设置多个集群之间的流量分配比例,客户端得到流量分配比例后,按照这个比例向不同的集群发送请求。
根服务器
RootServer的功能主要包括集群管理、数据分发和副本管理。
RootServer管理群集中的所有MergeServer、ChunkServer和UpdateServer。每个集群中同时只允许有一个UpdateServer提供写服务,这个UpdateServer成为主UpdateServer。这种方法通过牺牲一些可用性来实现强大的一致性。RootServer通过租用机制选择唯一的主更新服务器。当原始主更新服务器失败时,RootServer可以在原始租约到期后选择新的更新服务器作为主更新服务器。此外,RootServer与Mergeserver & Chunkserver保持心跳,因此它可以感知联机和脱机Mergeserver & Chunkserver机器的列表。
OceanBase使用主键对表中的数据进行排序和存储。主键由几列组成,并且是唯一的。在OceanBase中,基线数据按主键排序,并分成数据量大致相同的数据范围,这些数据范围被称为子表。每个子表的默认大小是256MB(可配置)。OceanBase的数据分发模式与Bigtable相同,不同的是OceanBase没有采用RootTable)+元数据表的二级索引结构,而是采用了根表的一级索引结构。
如图所示,主键值在[1,100]之间的表分为1 ~ 25、26 ~ 50、51 ~ 80、81 ~ 100四个子表。RootServer中的根表记录了每个子表所在的ChunkServer的位置信息,每个子表包含分布在多个chunk server中的多个副本(一般为三个副本,可配置)。当其中一个ChunkServer出现故障时,RootServer可以检测到它,并触发向这个ChunkServer上的子表添加副本的操作;另外,RootServer会定期进行负载均衡,选择一些子表从负载较高的机器迁移到负载较低的机器。
RootServer采用一主一备的结构,主备之间数据同步性强,通过Linux HA软件实现高可用性。备用根服务器共享VIP。当主根服务器出现故障时,VIP可以自动漂移到备用根服务器所在的机器上。在备用根服务器检测到它之后,它切换到向主根服务器提供服务。
合并服务器
MergeServer的功能主要有:协议分析、SQL分析、请求转发、结果合并、多表操作等。
OceanBase客户端和MergeServer之间的协议是MySQL协议。MergeServer首先分析MySQL协议,提取用户发送的SQL语句,然后进行词法分析和语法分析,生成SQL语句的逻辑查询计划和物理查询计划,最后根据物理查询计划调用OceanBase内部的各种运算符。
MergeServer缓存子表分布信息,并根据请求中涉及的子表将请求转发给子表所在的ChunkServer。如果是写操作,它也将被转发到更新服务器。有些请求需要跨越多个子表。此时,MergeServer会将请求拆分并发送给多个chunk server,并将这些chunk server返回的结果进行合并。如果请求涉及多个表,MergeServer需要先从ChunkServer获取每个表的数据,然后进行多表关联或嵌套查询等操作。
MergeServer支持多个chunk server的并发请求,即多个请求被发送到多个chunk server,然后所有请求被一次性应答。此外,在SQL执行期间,如果子表所在的ChunkServer出现故障,MergeServer会将请求转发给子表的其他副本所在的ChunkServer。这样,ChunkServer故障不会影响用户的查询。
MergeServer本身没有状态,所以MergeServer的宕机不会影响用户,客户端会自动屏蔽故障的MergeServer。
ChunkServer
ChunkServer的功能包括:存储多个子表,提供阅读服务,执行定期合并和数据分发。
OceanBase将一个大表分成大小约为256MB的子表。每个子表由一个或多个SSTable(一般为一个)组成,每个SSTable由多个块组成(可配置的块,大小从4 KB到64 KB不等)。数据按照主键的顺序存储在表中。要查找一行数据,首先需要定位该行所属的子表,然后在相应的表中执行二分搜索法。SSTable支持两种缓存模式,块缓存和行缓存。块缓存最近以块为单位读取数据,行缓存最近以行为单位读取数据。
MergeServer将每个子表的读取请求发送给子表所在的ChunkServer,ChunkServer。Chunkserver首先读取SSTable中包含的基线数据,然后请求UpdateServer获取相应的增量更新数据,再将基线数据与增量更新融合得到最终结果。
因为每次读取都需要从UpdateServer获取最新的增量更新,所以为了保证读取性能,需要限制UpdateServer中的增量更新数据量,最好将数据全部存储在内存中。OceanBase会周期性的触发合并或者数据分发操作,其中ChunkServer会在前段时间从UpdateServer获取更新操作。一般情况下,OceanBase集群会在服务高峰期(凌晨1:00开始,可配置)每天执行一次合并操作。这种合并操作通常被称为每日合并。
更新服务器
UpdateServer是群集中唯一可以接受写入的模块,并且每个群集中只有一个主更新服务器。UpdateServer中的更新操作首先被写入内存表。当内存表中的数据量超过某个值时,可以生成快照文件并转储到SSD。快照文件的组织方式类似于ChunkServer中的SSTable,因此这些快照文件也称为SSTable。另外,由于数据行有些列更新了,有些列没有更新,所以存储在SSTable中的数据行是稀疏的,称为稀疏SSTable。
为了保证可靠性,主更新服务器需要在更新内存表之前写操作日志,并同步到备用更新服务器。当主更新服务器失败时,根服务器上维护的租约将失效。此时,RootServer将从备用更新服务器列表中选择最新的备用更新服务器,并切换到主更新服务器继续提供写入服务。UpdateServer停机重启后,需要先加载转储的快照文件(SSTable文件),然后回放快照点之后的操作日志。
由于集群中只有一个提供写服务的主UpdateServer,OceanBase可以轻松实现跨银行、跨表事务,而不需要采用传统的两阶段提交协议。当然,这也带来了一系列问题。由于整个集群的所有读写操作都必须经过updateserver,因此UpdateServer的性能非常重要。OceanBase cluster通过两种机制将UpdateServer的增量更新持续分发到ChunkServer:定期合并和数据分发。然而,UpdateServer只需要在短时间内提供新添加的数据,所有这些数据通常都可以存储在内存中。此外,UpdateServer的内存操作、网络架构、磁盘操作在系统实现时也需要进行大量优化。
海洋基地定期整合和数据分发
定期整合和数据分发都是将增量更新从UpdateServer分发到ChunkServer的方法,它们的总体过程是相似的:
1)1)UpdateServer冻结当前活动内存表,生成冻结内存表,打开新的活动内存表,后续更新操作写入新的活动内存表。
2)UpdateServer通知RootServer数据版本发生变化,然后RootServer通过心跳消息通知ChunkServer。
3)每个ChunkServer开始定期的合并或数据分发操作,从UpdateServer获取每个子表对应的增量更新数据。
定期合并和数据分发的区别在于,ChunkServer在数据分发的过程中只在本地缓存UpdateServer中冻结内存表中的增量更新数据,而ChunkServer在定期合并的过程中需要将本地s表中的基线数据和冻结内存表中的增量更新数据合并一次,融合后生成新的基线数据并存储在新的s表中。定期合并对系统的服务能力影响很大,往往安排在日常服务的高峰时段进行(比如凌晨1: 00开始),而数据分发可以不受限制。
- 分享