今天,我们来聊聊“阿里云内存数据库 Tair ”,我先介绍下自己,我的真名叫朱国云,花名宗岱,2008年加入阿里。在阿里主要从事存储数据库相关,包括文件存储、缓存、内存数据库等。一开始参与过阿里云飞天操作系统的研发,后来主要负责Tair的研发。
1.Tair的发展历程
接下来,我们正式进入Tair 相关的交流。整体包括Tair的发展历程、重大节点、技术难点以及相关实践展开。我们先来看看“Tair的历史和发展”,可以看下面这张图:
Tair的历史和发展
- 2009年,Tair1.0在淘宝孵化。
- 2014-2015年,阿里云刚起步不久,淘宝的一些基础服务和阿里云整合在一起,所以在阿里云上也提供了云的缓存服务,包括云Memcache和云Redis。从Tair1.0到Tair2.0,两者之间的核心区别就是一个是纯KV,一个兼容了Redis。
- 2018年,阿里内部有非常多高度连接数据的场景,如数据的存储、查询和计算。这个过程中诞生了TairGraph。
- 2019年后,我们把Tair的这些能力推出到了公共云上。包括,在云Redis里面包含了Tair,这一版即Tair3.0。
- 今年,我们把Tair产品在阿里云上独立了出来,客户能够直接从Tair产品购买和管理。在跟客户交流中,他们有一个会经常会问到的疑问:为什么Tair这个系统能够做13年?从淘宝这个时代就开始诞生的系统与产品,今天还在阿里内部依然大规模运行的其实是比较少了,Tair就是其中的一个。当然这里面经历了非常多的系统迭代和架构的演进。
接下来,我们来看看Tair在阿里内部的应用。
现在在阿里集团内部,绝大部分BU的核心在线业务都使用了Tair,包括最开始淘宝、天猫电商的交易导购广告,到菜鸟的电子面单物流送货轨迹,再到钉钉消息的推送、优酷的视频播放列表等,这些在线业务都是用了Tair。
今天在一些偏 TP 类的在线业务场景,业务主要核心依赖的就是Tair加数据库。Tair在这里面的核心定位就是承载超大的流量和高速的存储,且并不仅仅是一个缓存,有非常多业务直接把它作为内存的存储去用。
从上文可以了解到,Tair在阿里支撑着大部分的项目,且不断进行优化,在这过程中,Tair也遇到过很多的技术挑战,接下来我们看看Tair发展过程中的重要节点和技术挑战。
2.Tair的重要节点和技术挑战
2.1 重要节点
在淘宝电商交易刚起步的时候,和现在的系统架构比,当时的业务架构相对来说是比较简单的。
在Tair诞生之前,我们内部有一个TDBM系统,这是一个独立的、单机缓存服务,容量是很有限的,所支撑的访问量也有限。最关键的是它是一个单点,在高可用上是没有的。所以在 2009 年的时候,开发了Tair 1.0。
Tair 1.0是一个独立的、分布式的缓存服务,是集群模式的,也完整实现了节点的扩缩容,这是区别于TDBM系统的。在Tair 1.0形成基础的技术架构后,基于这条路线上Tair演进了很多年。
随着移动互联网诞生,整个应用场景变得更丰富了。电商变得更繁荣、搜索广告推荐用得越来越多、社交网络的兴起、手游的发展、 LBS的应用,在这些行业的在线场景中,对缓存和高速存储系统的需求变得越来越大,从下图可以看到,Tair 1.0也是随着这些行业不断演进和发展。
数据量的变大,对Tair的内存存储提出了新的挑战,如何低成本的解决面向互联网在线数据的存储和查询。正恰SSD固态硬盘的成熟,Tair基于 SSD存储介质,实现了Tair LDB持久存储引擎,提供了一个高性能大容量的解决方案,而在2018 年之后持久内存的出现,新的存储介质,也给Tair提供了一个新的演进契机。
整体来说,在这个阶段,Tair从1.0的缓存定位逐步演进到了NoSQL的存储系统。接口层面,它也从一个最简单的 KV 接口演进到提供了更复杂更丰富的数据接口。
那么Tair的技术架构是怎样的呢?在当时是比较典型的架构:SDK、管理节点和数据DB节点,集群内部通过类一致性哈希的算法,通过哈希把数据切分到多个分片上去。同时通过主从机制来实现数据DB节点的高可用。
整体系统除了数据流之外,还有一整套的泰斗(Tiddo)来配合完成系统的管理。例如宕机的自动切换,集群扩缩容后的自动迁移平衡数据。除此之外,也可以选择带Proxy的架构,Proxy可以实现更丰富的访问聚合、连接收敛、QueryCache等高级功能。
图上右边其实是单个 DB 进程,它是统一的服务技术框架,它可以支持多个存储引擎在里面。譬如,我们原来定义的 Tai1.0 MDB 那它就是一个 KV 内存的,就我们上面的这个进程框架是一模一样的,下面的存储引擎是可以替换的。
2.2 技术挑战
接下来,我会讲三个我们就在阿里集团这几年面临的这一些技术挑战,就是阿里集团做单元化、热点、性能与成本。
- 技术挑战--单元化
单元化这个项目其实非常宏大,相当于要从整个应用层开始,到中间件,譬如 MQ、TTDL这一层,再到Tair跟数据库这一层。Tair当时的三大块包括MDB、RDB和LDB。对于MDB,我们是作为缓存去用的。所以在做单元化的时候,并不会主动去做数据同步,而是由数据库这一层做数据同步之后到另外单元来做一个反向的失效。对于 RDB和LDB,很多业务是拿来作为数据最终的存储去用的,所以这两大块我们相当于做了自己的数据同步方案。
这里面比较难解的问题是,怎么能够确保源端写入的能够快速地在目的端所消费掉?这个问题会和源端的写入速度目的端的消费速度、两个集群间机房的距离、网络速度都是相关的。第一是要做好容量规划,第二是在进程上面,我们要更多地做批量的、合并的处理,流式地去发送。
另外,我们任何一个系统,特别是在线处理类的,没有办法确保没有问题产生。无论是新发版还是触发了一个很久之前有 bug 的情况,往往是很难定位的,可能在几分钟里面定位不出来或者半小时都定位不出来。那这个时候可以快速地把流量切到另外一个机房去,这样对业务整体基本上没影响。这也是单元化带来了一个非常大的好处。
- 技术挑战--热点
2016年双十一的一个小插曲,我们Tair的一台机器流量特别大,最高大概到了70%+左右,该服务器的 QPS 在当时几十万次每秒,但是value size特别大。当时一看就是一个火热的手机商品,也是那几年双十一最热的商品项之一,它的访问量最大,而流量都到了一台服务器上。当时平稳度过了,如果访问量再大一点,流量再大一点,那可能服务器流量真会跑到一个非常高的水位,对稳定性产生影响。
目前绝大部份存储系统和数据库系统,对于一份数据都是单节点访问,这个问题其实是很难避免的。而单个数据节点访问量大,造成该现象的原因很多,例如有热点的商品反问,还是业务写了个死循环之类,这些都可能造成某一个节点的访问量特别大。
在那年双十一,Tair最新的SDK 其实具备本地缓存的功能,可以通过推送开启。但是有的业务升级到了新版的Tair SDK,有的还没有,所以很难全部控制住,这也是系统复杂之后应用复杂之后常见的现象,很难确保版本统一。同时,SDK侧的本地缓存,占用应用服务器的资源,内存是非常宝贵的,这个缓存可能会对业务产生影响。既然SDK侧的方案并不是最合适的,所以我们考虑通过服务端来解决。
最终讨论的方案是,每一个DB 节点上开一个热点区域,然后实时监测到有热点发生。热点发生之后,会把热点推送到一个集群里面的 N 台机器数,譬如 10 台左右。在有热点发生情况下,我们可以用集群里面 10 台机器来扛,而不是像原来一台机器去扛。
这里面最核心的关键是如何快速精准地发现,然后推送到其他数据节点上。这个时候我们可能会有在可失效的时间里面读到脏数据,对于绝大部分业务来说基本上都是可接受的。无法接受的,我们就不开启这个特性。
- 技术挑战--性能与成本
2017 年产生了一个新问题,就是随着Tair在阿里内部的规模越来越大,对成本降低有了要求,比如成本降低10%、20%,甚至更多。
成本优化,最直接的一个工作就是提升单机单节点的服务能力。下图左边可以看到我们任何的服务进程基本上都会有锁,因为锁的存在,使得整个进程的性能没有办法跑得非常高,所以我们对整个MDB包括今天的Tair都进行了改造。改造之后把每一个操作尽量在单个线程里面做下去,相当于尽量把锁去掉,然后再用上了DPDK加用户态协议栈技术。通过一系列优化之后,在下图可以看到锁只占了1%。在32c的机器上极限可以跑到 500万QPS ,如果是64c的话可以达到上千万,这个吞吐基本上是线性的当然,真正线上运行的时候,并不是运行到这么高负载,而是确保高吞吐、低延时和稳定的一个权衡值。这个工作中的成果之一——Tair HotRing,我们也将之发布在FAST会议上。
以上是阿里集团内部Tair近几年面临的一些重要技术挑战,稳定性、单元化和性能成本等等。
3.云原生内存数据库Tair产品形态
阿里云的内存数据库Tair经过多年演进,已经完整兼容了Memcache&Redis,还有一部分是我们的图引擎,兼容了开源的 Gremlin和Neo4j的 Cypher。我们还有一个支持标准 SQL 的引擎,这个我们用来作为高性能的实时数据处理。所以,我们今天 Tair 内存数据库的定义是一个缓存,加上高性能数据库及数据实时处理的定义。
Tair产品的架构形态,与我们今天在阿里云上售卖的 Redis 其实是一样的,分为标准的主从版、双副本主从版,也有一个集群版。所以最大可以从1个 G 扩展到数个 T ,那么它跟社区托管的 Redis 不一样的是什么呢?我们基于持久内存,基于云盘,推出了持久内存型和容量存储型,这两个其实能够满足客户对访问量与容量不同要求的情况与场景。性能增强型,它的整体性能比开源 Redis 会高两倍及以上,包括我们在里面做了非常多企业级的功能,来面向客户的关键业务场景。
我们今天主推的是两种形态,一种是Tair 性能增强,这个相当于在客户业务的核心关键场景,我们建议客户去选。相比开源 Redis,首先是它的性能更强,能够支持因运营活动或业务变化所带来的流量突高,访问连接数也会更多。开源的Redis其实支持活跃的上万连接数比较困难,而Tair性能增强其实是可以支持数万的。
例如我们其实看到有很多客户用社区版Redis,它就是用了读写分离一主五从。这个架构可以看到整个可扩展性非常低,它从从节点上读,其实读到的一致性也会比较弱。所以我们给客户推荐Tair的集群版,最终价格比社区版贵了 1.2 倍,但是总体容量是社区版的 4 倍,包括总吞吐它也是 4 倍。所以Tair的性能增强版比开源版在很多场景下对客户的整个 TCO 来说相对更有优势一些。
2018年之后我们花了非常多的经历在是持久内存形态上,有两个核心目标,第一是提供一个比社区 Redis成本更低的系统,性能和Redis差不多。吞吐大概是社区版redis的90%+,成本大概是社区版的70%。它比社区版好的是每一个操作都能够做到持久化,落到持久内存上。
下面这一页是我们的持久内存性能对比。左边是 Redis 6.0,然后右边是 Tair 持久内存版。我们可以看到写跟读的性能总吞吐大概都是 90% +。右边这张图,客户端延迟 P95(us),我们把Redis 里面的 AOF 重写机制给去掉了,所以这个毛刺抖动是更低的,读的话确实比全内存版本慢一点,因为本身持久内存的访问延迟会高一点。
接下来我们看看第4部分,这部分其实是讲前面支撑我们 Tair 产品形态的关键能力,就包括跟开源的 Redis 的核心区别是什么、关键技术点是什么?先看看客户用 Redis 的一些痛点。
4.云原生内存数据库Tair关键能力
4.1 Redis的一些痛点
我们可以看一下左边跟右边的痛点为什么会是这样的情况?
其实总结下来就是开源的 Redis 在超多访问链接下,它的性能会下降。但是在容器化的时代下,应用的服务器变得越来越多,每一台应用它的访问连接数也会越来越多,几千上万是很常见的。
另外,在延时&抖动上, Redis 其实是一个单线程的,那在这种情况下,它只要有一个慢查询,譬如 Hgetall 之类的,它往往会带来整体变慢,其他的短查询也得不到很好地处理。
HA 高可用会出现误判,跟前面一样,一个 Hgetall比较大的情况下,处理线程会把CPU 全占住, HA 的判活就有可能得不到处理,所以它的整个数据操作与控制操作都是在一个工作线程内处理的。还有整体的内存统计是没有区分开的,所以用户往往发现配了实例内存,还没有用满的情况下就发生了数据淘汰。
然后,我们从内核的技术上来讲一下 Redis 与 Tair 的一个区别。
4.2 Redis vs Tair
上图左边是开源的 Redis 在6.0之前,它是一个单线程的。在 6.0 之后,它号称是一个多线程的,但是从右图也能够看到,它只是在 IO 处理这块搞成多线程了,但是在内部真正的数据操作这一块,它依然是单线程在做。
为什么它很难做成多线程呢?一是因为在原来的 Redis 内部,所有都是单线程的,大部分操作是没有锁的,所以想改成多线程是非常困难的;二是代码是在10年前写的,并没有进行重构过,在历史代码上去做优化是相对困难的。
对于Tair来说,我们脱离Redis,从头自研,我们把网络接收线程跟工作处理线程独立开了,都可以用 N x M 的方式灵活地去配;这样 Tair 就可以处理数十万的活跃的连接数,因为网络线程足够多,单机的处理引擎可以提升得足够高,甚至可以跑到百万级。
在业内也有一种讨论,到底是搞成分布式好还是搞成单机好?
在我看来,非常多情况下,单机的形态会有不少优势。如今业务越来越复杂,如果把它搞成分布式,往往会有一些跨节点的计算,甚至要求这些计算要有事务。搞成集群后,跨节点的计算和事务会变得越来越复杂,很难去处理。在这种情况下,能够给客户一个大规格、大访问处理量的单机引擎其实是最合适的。
当然,在多线程内部我们也做了一些慢查询请求,把它实时监测出来,并且分离到慢查询请求池里面。这一类的工作,我们尽量确保用户的请求能够在一个比较确定的访问延时里面返回给用户。
接下来我们看看Tair引擎的高可用,前面我们讲了社区版的 Redis ,它的探活跟数据操作其实是在一个工作线程里面,因为数据操作慢了之后,会产生一些误判,所以我们把所有的管控请求放到独立的处理系统里面;就把这些 HA 把这些访问、统计信息等完全隔离,包括用户的数据访问跟系统的高可用统计都是隔离开的,确保质量更好。
接下来我们讲讲Tair的集群架构。
4.3Tair的集群架构
Tair的集群架构,包括搬迁的扩缩容,与开源的社区版是完全不一样的。开源的社区都是用 Gossip ,相当于是P2P 来做信息的同步与探活。另外它的节点变得越来越多的时候,想在集群里面达到信息的一致性也会变得越来越慢。社区版的迁移扩缩容是按 Key 级别的,所以在大 Key 的时候往往会出现一些迁移卡顿等,这个时候也会有一些 HA 的误判。我们如今在这一方面也做了一些改进,相当于是由中心节点对整个集群做 HA 的判活,包括集群管理。整个数据搬迁是按slot去搬迁的,所以整个搬迁速度会比按key快很多。
4.4 特定重要场景的优化
Pubsub相当于是 Tair 与 Redis 里面做消息处理用的。原来的单线程,如果是挂载的客户端多的话,其实推送起来会比较慢。它的单线处理,相当于在Tair 里面把它作为一个多线程处理,所以这是一个并发的处理操作。
4.5 TairStack:丰富的数据模型
TairStack有着丰富的数据模型,这其实是在阿里内部实践中积累出的常用数据结构,目的就是让业务开发更容易。从上图可以看到,我们有些是对外开源了,包括:TairHash、TairString等,这些结构也是可以放到开源的 Redis 里面去用,跟开源的 Redis 是完全兼容的,这些module在公共云上也被非常多客户使用了。
接下来我们看看Tair的企业级能力。
4.6 Tair的企业级能力
Tair的企业级能力这里我主要讲3部分,包括全球多活、安全能力和 Tair引擎可观测性。
- 全球多活
如今的一些客户,特别是中大型的客户,他希望在多个地域做多活。所以我们今天是可以做到三地域多活的同步。它的原理是通过 Binlog 来做 3地的多活。我们更建议应用在做多活的时候,能够在应用层面按 key 分布到多个单元,这样会更容易避免冲突。
- 安全能力
安全能力部分在公有云上也是比较重要的一个地方。我们提供给客户的实例是在VPC内部,整个安全网络上面是有确保的,客户可以通过 SSL来加密访问Tair,这个上面可以进行更高层次的访问通信的加密。
我们有一个功能叫 PITR,可以帮助客户把数据恢复到客户指定的任意时间点,可以到秒级别。还有一个重要的功能就是用户的审计。经常会有一些客户说,我的访问量怎么这么大?这个访问源是从哪里过来的?或者是我的数据被清理掉了,是哪里被删的,通过这个是能够看得到的。所以从整个技术实现上来说,我们其实做了一些高频的快照,大家可以认为是一个全量的快照,再加上增量的 Binlog 来帮助客户恢复到那个时间点去。
- Tair引擎可观测性
在可观测性上我们投入的也比较多,当然还是有一些没有做得特别好,譬如集群级别的聚合工作其实一直在探索。我们能够把有热 Key 的、有访问量比较大 key 的实时地看到,包括在引擎级别,每一个操作的访问延时是能够看得到的。一旦慢了的话,我们可以看到在哪一块操作上慢了。
5.Tair 应用
- 电商方面:
Tair在电商方面,主要是用做缓存与内存存储,一般就用在登陆系统、用户系统、商品系统、购物车、个性化推荐上面。
- 游戏方面:
游戏客户最重要的除了低延迟、弹性伸缩、高可用之外,还有备份回档、无感扩容。我们今天在主动扩容下,它的业务并不会掉线。这个是通过我们整体的集群方案,包括数据的迁移方案来提供给客户的。
- 金融、安全风控方面:
在金融、安全风控方面,Tair里面做了一些计算的算值,可以在某一个时间段里面譬如购买商品的数量,这一类的计算来做防黄牛反作弊。
- 生活服务方面:
LBS 生活服务,主要通过即时的功能来完成。它与 Redis Geohash的核心区别是 Geohash只能做点对点关系的邻近的查询,譬如它只能搜最近10km的人或者是离我最近的店。
6.总结
第一部分讲了Tair从阿里集团经过 13 年,发展到阿里云上推出给客户使用。从单一缓存发展到帮助客户构建多样化实时场景。
第二部分是前几年在阿里集团内部所面临的一些重要技术挑战,包括热点、多活、性能、成本等问题的解决和优化。
第三、第四部分关于通过Tair自研引擎充分利用云基础设施上面不同的存储介质来给客户提供更合适的选择和更高的服务SLA。最后关于Tair的应用解读,助力客户构建在线实时场景。