分布式内存数据库新架构,极速OLTP应用新利器

2018-12-19 10:45:00 浏览数 (1)

内容来源:2018 年 11 月 10 日,Oracle 数据库首席产品经理杨琳在“2018 SOUG年度数据库技术峰会”进行《Oracle TimesTen Scaleout -分布式内存数据库新架构,极速OLTP应用新利器》的演讲分享。IT 大咖说(微信id:itdakashuo)作为独家视频合作方,经主办方和讲者审阅授权发布。

阅读字数:8030 | 21分钟阅读

摘要

TimesTen Scaleout,它实际上是一款关系型数据库,不过是在运行的期间,把数据全量加载到内存当中来进行实现。

Oracle TimesTen

先简单的说一下TimesTen Scaleout的历史,它实际上是一款关系型数据库,不过是在运行的期间,把数据全量加载到内存当中来进行实现。TimesTen从1998年开始在全球作为首款内存关系型数据库上市,到现在已经过去了20年,这些年里我们一直在做的一件事,纵向的提高它的性能。

因为大家都知道,从过去20年的发展历程来看,硬件的资源本身容量越来越大,性能越来越好,价格越来越便宜。因此TimesTen为了应对硬件变更的需求,需要不断提高纵向性能,随之从单机的方式实现到过渡的分布式。

但是现在又有很多的新挑战来临了,现在很多的客户不单单是要纵向的扩容,而且希望横向的扩展。为了应对这个需求,我们提供了分布式的全新解决方案。我们强调同一款产品,两种部署方式,也就是说你从网上下载的软件,实际上就是一个安装包。安装包解压缩之后,可以按照自己的需要以基于主备备的传统的方式来实现纵向的扩容能力,也可以使用我们提供全新的分布式的方式来管理数据。

这两者之间应对的方向是有区别的,单机的处理能力也非常好,而且对应用来说非常简单。在主备这样的传统的部署方式下我们能提供一个超低延时的能力。但是如果要做分片、分布,提高高并发的读写的能力,可能还需要引入网络。这种情况下,可以考虑在稍微牺牲网络延迟,以提高吞吐量。

TimesTen Classic内核技术

刚才强调了TimesTen是一款关系型数据库,这里可能需要重申一下什么叫关系型数据库。在分布情况下,我们强调的是原子性一致性,隔离性和持久性。支持标准的SQL,同时整个数据库运行在内存当中,可以通过应用进程嵌入式的访问数据库,来实现微秒级响应的能力。

我们知道常规的计算下,要求一个IO的响应时间是1/1000秒,相当于是微秒级。磁盘的响应式微秒级,那内存的响应其实是纳秒级的。所以说在内存计算里面,我们能做到微秒级的数据库级别的响应,伴随单位时间内的超高的吞吐量,这就是TimesTen过去20年一直在做的一个纵向的吞吐的能力。

作为企业级的产品,实际上TimesTen在中国已经有差不多十年的历史,从07、08年开始中国的各大运营商都陆续地采用TimesTen作为计费的系统来控制余额。类似这些服务在后台,实际上使用的很多都是TimesTen的技术。他们在使用时候有持有化的要求,TimesTen同样能够很好的满足。

此外我们对Oracle数据库也有很好的兼容性,包括对Oracle的数据类型,PL/以及丰OCI的接口都有良好的一个集成。同时对Oracle后台的数据交互,也可以做到用缓存方式部署的集成。

架构图:Classic Instance

从18.1开始,我们的安装方式发生了一点的变化,在分布式架构下,解压缩即安装。我们实例有别Oracle的实例,Oracle的实例是在内存中运算的一个程式,但是TimesTen里面它相当于Oracle_home有一系列的可修改的软件包,里面包含一系列可执行文件,同时有一组进程,这一组进程本身只支持一套或多套数据库的。

这是什么意思呢?我们来看一下上图,首先有这样一个硬件环境,然后我们定义一个实例。在这个实例里面,安装解压缩完之后,创建实例启动了一个守护进程,它会拉起一个内存结构,内存结构就是TimesTen内存数据库,这个数据库会被subdaemon进程接管,subdaemon进程本身还会去做持久化的相关的维护,

数据库构建起来之后要开放给应用连接,因此我们会有一个server的进程,类似于oracle的监听程序。监听进程打开之后,可以做很多的应用访问。如果说有高可用需求的话,也可以通过复制的代理程序去跟远端的主备备进程做一个实时的复制。

对于oracle数据库,我们还开放一个缓存的能力,可以缓存oracle数据库里面的热点数据子集到TimesTen。这样就能加速响应时间,并且减少了对oracle的负载压力。

同时作为一个比较成熟的内核引擎,我们还支持的丰富的这监控管理工具,这是产品本身自带的。对于微秒级响应的能力,实际上是建议将应用跟TimesTen部署在同一台机器上,通过一个本地库文件的链接,它会将这个进程直接嵌入到TimesTen里面进行访问。这样的话就完全去除了网络开销,完全没有网络只是内存对内存的一个指针变量的偏移巡视,所以性能会非常好。

应用连接

从应用角度来讲,使用TimesTen可以使用直连的模式进行访问,这个性能是最好的,只不过它需要将应用程序跟TimesTen部署在同一台机器上。如果说有远端的解耦的架构,可以部署在其他的主机上,装TimesTen的客户端,然后通过调用TimesTen的TCP/IP的方式去访问数据库,数据库最后解析也会通过server变成一个直连到TimesTen本地。

应用开发

连接的接口也是非常丰富的,在过去20年的沉淀里面,我们基本支持当前主流的所有的API的接口,基于oracle OCA的支持的能力,得以支持主流的 Python、nodeJs、Ruby、Go等编程语言的接口。

连接方式方面,为了哪些没有使用过TimesTen的客户,能力开放的相对标准。直连的话,可以通过修改JDBC,就像连接oracle一样,将连接串指向TimesTen数据库名就可以了。如果是cs的方式,只需客户端去配置一个连接串,就可以连接了。

内存结构

说到内核技术,先要谈一谈我们的内存数据库的内存结构,内存结构本身在从TimesTen设计的第一天开始,就将数据全量加载到内存作为一个前提进行运算。所以我们在打造的时候,实际上是先有内存结构,再有磁盘的异步镜像。

我们在打造的时候,实际上先构建了一个DbHdr,用于存取数据库的基础信息,然后有一个永久区域,这个永久区域是被持久化的。里面包含的就是用户真正关心的数据,以及系统中相对关键的一些原数据,包括表、索引。

这里面强调一点,在内存里边,我们用的词不是以block的方式存储,而是以page的方式存存储。在TimesTen内存结构里面,页的大小是不固定的,我们定义的是存取一个page,会存取256行的数据,行数是基于行存储的技术,而行存储技术本身取决column。

接下来就是一个临时区域,里面存放了大量的临时的信息,它是不被持久化的。比如说建的一些临时表,临时索引用来做编译,有一些锁的资源排序的资源,包括连接的一些基本信息和我们缓存的一些SQL命令,

最后就是作为关系数据库,需要记录DML的变化,TimesTen在这里有一个log nuffer的区域,通过多轨的方式来进行内存当中的保留。所以说从应用角度来讲的话,访问任何数据在内存里面只是做一个指针的偏移,返回给应用。

TimesTen是基于行的内存数据库技术,这样的内存技术,首先在操作系统层面上需要用huge的方式来锁定住的内容结构,尤其是在linux市场,如果是大于256G的容量,从产品角度本身没有一个设计的上线,共享内存段的大小,可以随着操作系统硬件资源释放出来的。但是需要在操作系统上配置一下,linux上的内存段默认限制是256G的。

此外在超大单机处理能力的情况下,还需要考虑NUMA的这样的多核技术,比如说现在的操作系统,支持多槽位的cpu处理,这些槽位之间的CPU处理,在CPU级别的一级缓存,二级缓存之间,所存取的信息是不被共享内存本身共享的。因此我们建议绑定4槽位cpu,以这样的方式来避免NUMA的影响,绑定实例到对应的cpu的槽位。

持久化——检查点

从企业角度来讲,很多客户都会关心一个问题,掉电了怎么办? 这里就来讲一讲TimesTen是怎么处理这个问题。

举个例子,刚才都提到了TimesTen本身是通过检查点的方式来做数据文件的在磁盘上的存放,实际上这个检查点是内存的一个实时镜像,也就是说内存的快照。

接下来主要讲的是在事务处理的变化过程中的刷出的机制,前面提到到了持久化的区域是DbHdr加永久区域,永久区域里面的数据是按页的方式来存取的,每个页里面存储的是某个对应表里面的256行中的部分行数的信息。这些信息有可能被修改, 在被修改的过程中,会发生什么情况呢?

假设T0时刻数据库已经持久化了一次到ds1文件上,在T1时刻我们发现有一些事物进来了,对page 0进行了修改,page 0里面的信息是ds1里刚才没有的一些脏数据。Page 2对 ds0来说也进行了修改,DS0还没有被做下一次检查点,所以说它在做检查点之前,对于ds0来说page 2里面是脏数据。

这个时刻我们发生了一个检查点的触发操作,默认是十分钟,因为DbHdr有一些连接信息,所以会被持久化。Page 2里面的信息由于是被标记成了脏数据,所以它是按页的脏数据的方式去刷出到ds0的文件里面,这是一个增量刷出的过程,之后它之前被标记的脏数据就会被清除掉。

接下来在T3时刻我们又做了一些修改,脏字节也标记到对应的页上了。之后我们会针对标记的页进行检查点的触发。触发的时候,我们会把对应的脏数据刷出到ds1文件,他们是交替着写,也就意味着在同一个时间点,极限场景下会有一个正在写出的一个ds1,同时还有一个ds0,ds0配合事务日志的方式,就可以实现恢复。

持久化——事物日志

事务日志在内存结构里面是有一个log buffer区域,它是通过多轨的方式进行一个刷出,刷出是在操作系统文件上承载了数日的文件。文件写满之后会增加,增加的大小是可配置的。

事务日志的刷新分为两种,第一种是默认的异步的刷新,任何一个事务过来都会定期的触发刷新。刷出的过程是多条的性能最好的这样方式。但是有些场景,比如说金融客户有些关键的业务需要持久化到磁盘再返回给应用,这种情况下我们也支持同步的刷出。

异步和同步之间是在数据库级别进行了默认配置,你也可以在连接级别、事务级别,进行修改。

持久化——恢复

对于数据的加载我们有检查点文件,配合的事务日志文件在磁盘上,数据库的重新加载需要读取内存镜像,如果是脏的checkpoint文件,可以读到连续的共享终端中,因为它是一个快照,所以说会直接把之前的结构在内存里面做一个镜像的回放。

构建到内存段之后的话,还可以基于日志做事务的前滚和回滚,最后达到一个一致性的点,一直读到事务日志的最后收尾的标记,之后,该回滚的回滚。

在探测里面如果说检查点中没有包含的数据发生了索引的改变,比如checkpoint的检查点做完之后,又做了一些事务的变化,这个变化影响到了某些索引,索引再重新加载的时候,它是没有被持久化的,需要被重建,重建的过程也是可以并行执行的。只有脏页索引才会被重建,不是所有的索引都会被重建。之后仍然会把能力开放给应用进行正常的访问。

并发能力

作为OLTP优化的关系型数据库。我们也是又隔离机制的,通过Read committed的方式默认进行这样的行为的,也就是说读写之间不互斥。

在读写的过程中,如果读操作还没读取到目标前,目标就已经被写入了,这时候写的操作实际上会影射出来一个新的版本,这个版本本身是没有被提交的,所以读取的时候读的是早期的版本,一旦目标被修改提交之后,后续发起的所有的读取操作,读取的数据就是修改过的数据。

同时TimesTen也支持隔离机制,采用线性隔离的方式,这种方式对会对单线程有很大的优化,但是对并发来,它会有很多的共享锁排查锁这样的机制,所以要尽可能在高并发系统上避免隔离机制。

索引

数据库事务处理的应用肯定需要修改数据库,修改之后的数据查找,就涉及到索引。TimesTen支持两种主要类型的查找,第一个查找的方式是基于哈希索引,这种方式对等值查询的性能是最好,但是对范围查询来说不太适合。

缺点在于它需要维护一个哈希链表,这个哈希链表的估值是整个表全量值的一个超集,不能小于总行数。

另一种方式是我们默认的索引创建,它能够很好的做范围查找,等值查找的性能也不会特别差。更主要的是在高并发的维护情况下,这种新的索引模式对读完全没有锁。对写来说,由于B-tree索引是分节点的维护,减少了热点征用,以细粒度闩锁的设计,来减少对整个b-tree锁的对并发的影响。

TimesTen Scaleout

市场上有很多的客户对分布式的强烈需求,同时客户自己本身也在做各种的分片的一个尝试来应对他们海量数据高并发的处理。

这种情况下,TimesTen进行了内核的改造,改造的量并不是特别大,我们引入了管理实例的概念。管理实例有点类似于刚才介绍的传统模式,但是它里面存放的东西只是元数据,实际的数据是放在数据实例里面进行管理的。

这个图里面唯一有一个变化的地方,仅仅是database变成了database Element。Element只是整个分布式集群里面的一个节点,里面存放的只是N分之一的数据,数据是打散的。

为了解决分布式里面的全局一致性的问题,我们引入了一个叫Epoch的机制,它配合着全局事务的概念,能够做到在分布式里面保证原子性。基于这个前提就可以做到分布式里面的强一致性。

基于这样的全新的架构基础上,我们做了很多的封装,在实例级别可以跟内部通道,跟其他的实例进行自动的内部的交互。

同时还引入了管理实例的概念,所以管理实例会直接跟TimesTen实例进行通信,来做实时状态的变更和集群拓扑关系的变更。

这样的能力使得我们可以实现在一个位置在管理实例上,一键安装一键管理。对数据库来说,不管是十个节点,20个节点,30个节点,只用在管理节点上执行一个命令进行配置,剩下的任务是由管理实例自动的去做,不需要登录到具体的数据实例进行配置。

High Availability

在企业级的需求里边,他们对TimesTen早期有一个很强的诉求,就是不用复制的方式帮实现高可用。

虽然TimesTen从引擎来讲的话,原生支持复制,但是我们在分布式架构里面做了很大的改造,让它支持多副本的技术。在Aynchronous的架构下面,首先配置的主机,用Data Space Group来隔离物理位置, 之后在一对机房的两个节点,可以分配成一个副本,这样两个副本就实时同步了。

在这个基础上,我们整个数据库是基于多个副本级的方式来呈现给这个应用的。副本集现在支持的是两副本,后续会支持三副本。

基于之前介绍的内核的原理,我们的实例级别和数据库级别有持久化能力,这些能力开放到分布式里面,就是一个个的节点,叫做Element。Element有自己的持久化,存放的数据是一小部分的数据单元,并且有能力接受应用的访问,这就意味着所有的节点,即使在高可用的方式下,都是全读全写的能力开放给应用。

内部因为我们要做一致性的OLTP的应用访问,所以是基于优化的两阶段提交,来实现事务的强一致性。

TimesTen Scaleout Architecture Overview

TimesTen的软件安装,需要先准备zookeeper。它是作为轻量级的成员管理的角色来管理实例之间的行为。我们建议使用内外网的架构,当然我们也支持单网卡,不过单网卡的吞吐量会混杂内部事务和外部的应用连接。因此还是建议使用双网卡,做两个内网和外网的隔离。之后可以选一个节点作为的管理节点,管理节点本身在生产环境上,可以使用高可用的方式进行储备,这都是自动可以配置的。

之后选择一个拓普关系,创建数据实例的属性,一旦配置好之后就可以在管理实力上进行一键的安装配置把数据库拉起来。

如果需要一键日志收集或者一键的备份,可以配一个资料库。通过管理节点的命令了,来实现整个的自动化的管理。

对于图形化管理,可以使用SQL DEVELOPER工具,通过SSh的方式连到管理实例上进行管理。

性能小贴士

通用篇

首先讲一下普世性的优化贴士,比如在使用内存技术的时候,我们需要尽可能节省你的内存空间,所以建议在TimesTen里面使用原生的字段。对于变长的字段合理使用Inline和out of line。

作为关系数据库的肯定要做到合理使用的索引,通过我们的索引指南来选择是使用哈希哈斯范围索引,同时还需要更新优化器的统计信息,因为TimesTen本身也是基于成本的优化器,对于SQL来说的也需要做好正常的数据量变更之后的更新同信息的收集,让数据库真实的执行计划能够反映数据的实际情况。

最后就是数据库本身的配置,包括基于目标负载和硬件等优化数据库参数,使用HDD存储需要将检查点文件和事务日志文件隔离I/O,避免I/O争用,使用huge pages,无法使用的情况下,则考虑在内存中锁定数据库。

Scaleout篇

在分布式里面首先引入的是网络,我们建议是使用万兆网进行内部的配置。尽可能的是选择大容量的主机要好于低配的小组机,因为你有更少的网络开销,能够充分发挥的纵向的并发的能力。

优化方面,对于表的分布,如果是超大表的解决方案,可能要考虑用哈希的分布打散。

在分布式中,TimesTen还追加了全局索引和本地索引的概念。在分布键选择的时候可以选择某一个主键,或者是任何的组合键,作为哈希分布的分布键。

应用篇

在应用开发角度我们也有一定的优化建议的。比如说使用参数化的SQL,而不是用硬写的方式将绝对的查询值写到SQL的变量中。而且我们建议在每一次发起连接的时候做一个parse,这样生成的执行计划就避免了硬解析和软解析,甚至会复用它的执行计划到其他的连接,由此性能方面会减少很多的不必要的开销。

有时候SQL中还会涉及隐含的数据类型或者字符集的转换,如果能的避免的话,也可以减少不必要的开销。

同时刚才强调了TimesTen里面的内存结构是以256行作为一个page存放。所以从插入批处理的角度来讲,我们可以以256行的倍数进行批处理,这样的话能够充分利用它page的能力,减少行插入的性能损耗。

适用场景

如果低延迟需求,比如要求的响应时间在一毫秒,或者是几毫秒这样非常苛刻的场景下,我们建议使用TimesTen传统的方式进行部署, 这样即使在单机的情况下也能达到每秒千万级的查询能力。我们的主备备方式,可以实现纵向的企业的能力的扩展。在横向能实现读的能力的扩展。

如果有oracle数据库做加速,可以通过read-only或者read-write-caching的方式进行配置,将oracle的热点数据子集加载到TimesTen当中进行运算,数据的同步是由缓存的代理和复制代理来实现。

如果说在亚毫秒级或者是十毫秒以上容忍度的情况下,对高并发有上亿次的TS需求。TimesTen现在极限的场景测试能达到10亿每秒的查询能力。

所以说这种分布式的架构里面响应时间不是他的优势,但是在多节点同步的高并发处理上,他有绝对的读写扩展的能力,这是需要大家综合考量的点。

以上为今天的分享内容,谢谢大家!

IT大咖说 | 关于版权

由“IT大咖说(ID:itdakashuo)”原创的文章,转载时请注明作者、出处及微信公众号。投稿、约稿、转载请加微信:ITDKS10(备注:投稿),茉莉小姐姐会及时与您联系!

0 人点赞