振飞(高德地图总裁)
炳蔚(高德技术服务平台负责人)
福辰(高德服务端架构师)
高德成立于2002年,是中国领先的移动数字地图、导航及实时交通信息服务提供商,向终端用户提供包括导航、本地生活、叫车等服务的一站式入口。从拥有甲级测绘资质的领先地图厂商,到首家成功转型移动互联网的地理信息企业,再到国民出行平台,以及出门好生活开放服务平台。业务一直在进化,但高德“让出行和生活更美好”的初心未变,本质的核心专注点一直没有改变,就是地图导航,拥有从数据到软件到互联网的完整研发能力和经验,同时在人工智能、大数据、计算机视觉等一系列新技能方面也拥有深厚的积累。
地图是什么?它是真实(物理)世界在网络空间的数字化映射;高德的目标就是“连接真实世界,做好一张活地图”。作为如今各行各业都非常关注的一个概念,“数实融合”代表大家对于实体经济进一步升级发展的期望,代表着能力和效率的现象级提升,以及面向用户和消费者的更优质产品和服务。而要做到这一点,其中的关键在于推动实体经济和数字经济的高度融合,对于高德地图所在的交通出行行业来说,也是如此。我们致力于用“促科技创新、与生态共进”的方针,助力交通产业更好的实现数实融合。
从高德的视角,对于“数实融合”的理解是什么样的?一方面,我们明确了包括人、车、路、店等在内的实体要素,才是交通出行产业中的真正主体,相关领域中耕耘多年的企业和机构,才是真正值得尊重的老师傅,他们在各自领域的专业度和经验不可或缺;另一方面,科技创新平台提供了交通服务和海量用户之间的连接能力、数字化的展示平台,提供了产业要素转化为数据的能力,并且还能为各类新型交通服务提供强大的计算能力。高德的二十余年,始终与大交通产业中其他领域的老师傅携手共进,尊重他们的专业领域,尊重他们的不可或缺,才得以与他们建立起了深厚的合作关系,成为他们服务的科技标配。2022年10月1日,中国国庆黄金周假期的首日,高德实现了创纪录的2.2亿日活跃用户。在2023年3月,受到日益增长的同城通勤和城际出行需求推动,高德的日均活跃用户数量达到了1.5亿的新纪录。
高德一直不断地探索和应用新的技术,以持续提升用户体验、提高效率和降低成本。
首先是北斗高精度定位。作为一家科技企业,高德有幸见证了北斗从起步到世界一流的发展历程。尤其是2020年的北斗三号全球组网成功,客观上帮助我们在产品研发上迅速打开了新局面,车道级导航、智能红绿灯、绿色出行和位置共享报平安等一系列基于北斗高精尖技术的服务得以在手机上落地,并获得了行业内外的好评。如今,高德地图调用北斗卫星日定位量已超过3000亿次,且在定位时北斗的调用率已超越了GPS等其他卫星导航系统。
互联网地图用户高并发访问和随之而来的海量数据存储处理,是我们必须应对的技术难题。其中,云原生和行业无关化架构是高德地图服务端未来的努力方向。云原生是一个新的软件架构模式,它将应用程序和系统环境抽象化,并将它们封装到容器中,以实现快速、可靠和可扩展的部署和管理;云原生是未来软件架构的发展趋势,它的本质是更高维度的抽象、封装和屏蔽,高德地图服务端会聚焦于把云原生相关技术用到日常应用研发,以提高生产力,快速迭代产品,跟业务一起给用户最好的体验。行业无关化架构是针对高德应用的特点提出的,核心是解决研发效率的问题,业务上让更多的行业快速接入高德,技术上尝试元数据驱动+多租户隔离,屏蔽行业变化对底层的影响,做到行业无关化架构,以进一步提高生产力。
随着“高德地图”成为用户出行必备工具之一,其中数据的存储、加密、快速检索和绝对安全就非常重要,是我们工作的重点,目的是让用户在任何时刻、不同的端设备上都能快速的获得自己想要的真实世界的信息,让用户出行更美好。随着业务后续发展很快就会进入万亿时代,无论是存储成本,还是针对数据查询的性能来讲,数据治理对我们来说显得尤其重要,我们要让数据快速发挥出价值,带给用户最真实最实时的数据,还不会过度的浪费成本。
OceanBase是由蚂蚁集团完全自主研发的国产原生分布式数据库,始创于2010年。OceanBase已连续10年稳定支撑双11,创新推出“三地五中心”城市级容灾新标准,在被誉为“数据库世界杯”的TPC-C和TPC-H测试上都刷新过世界纪录。自研一体化架构,兼顾分布式架构的扩展性与集中式架构的性能优势,用一套引擎同时支持OLTP和OLAP的混合负载,具备数据强一致、高扩展、高可用、高性价比、高度兼容Oracle/MySQL、稳定可靠等特征,不断用技术降低企业使用数据库的门槛。
经过长时间的调研和测试对比,我们决定采用性价比最佳的OceanBase来迎接高德万亿(条)数据时代!
正因为真实世界的数据存储量大,高德采用的OceanBase来解决,此篇文章会让大家看到OceanBase在高德的实践体会,我们会从不同的视角去诠释整篇文章。整体如下:
服务端的视角
1)我们为什么选择OceanBase?
2)OceanBase在高德落地过程中分应用的融合方案、痛点和收益?
3)OceanBase在高德应用中未来的规划?
读者的视角
1)高德为什么选择OceanBase,背后选择的原因是什么?
2)高德怎么用OceanBase的,方案是什么,遇到了哪些问题,解决方案是什么?
3)OceanBase在高德应用场景中,表现结果怎么样,稳定性和性能怎么样,降本效果怎么样?
4)结合我们自己的场景哪些可以用OceanBase帮我们解题?
以下用OB代替“OceanBase”,整篇文章也会围绕几点来贯彻核心思想:
1)了解选择OceanBase的原因,了解OB的落地实践
2)了解分布式数据库和OB相关技术内幕
3)作为工具文章,在犹豫是否选择OB的时候会给大家一些思路
阿里云提供的数据存储产品有很多, 简单罗列几个常用的(排名不分先后,没写进来的不代表不好)
PolarDB
Lindorm
OB
ES
MongoDB
...
每种存储都有自己的特色,有关系型数据库,有分布式数据库,有列族数据库等在不同的场景都表现十分出色。然而我们为什么选择了OB呢?
其实从21年开始,我们就一直在做一个事,就是去MongoDB。过去高德有一些业务服务用的MongoDB做存储,由于MongoDB的特性和设计特点,导致它偶尔会出现CPU占用很高,服务Pause无法正常提供服务。 对于上游来说体现就是超时问题了,如果访问量大且重试带来的阶梯效应就是毁灭的,服务基本被打垮。很多时候不是MongoDB本身的问题,它定义是分布式文档存储系统,通过Documents的方式来维护数据,理论上在关系型比较重的场景上确实不太适合。
后来我们服务就相继的迁移到XDB、Lindorm、ES。选择ES是因为成本低和稳定性高;选择Lindorm是因为对于我们的异构场景、Key Value场景和减少请求穿透到XDB的场景非常合适。那么就剩下数据库选型了,虽然OB是NewSQL数据库,但是他保留了关系型数据库的特性,让我们以NewSQL的方式去管理数据,但不是说明任意场景都比较适合,在结构化数据量大的体系中,OB在降本上非常具有优势。
那么这里又有一个疑问?Lindorm和ES不也可以做这种存储吗? 存储和检索都非常高效。 但是从工具的角度Lindorm和ES对我们来说都是加快检索和异构检索使用的,无法真正像关系型数据库那样去使用,往往后面都会存在一个数据库,会严重增加数据沉余和成本。
针对数据库的场景,我们其实都关注两点:OLTP和OLAP。PolarDB是天然的OLTP,分布式MySQL引擎后续架构也支持了OLAP设计。 然而OB本身设计就是NewSQL模式,天然具备OLTP和OLAP两种场景,而且针对大数据有自己的压缩体系,在降本和应用都有天然的优势。
图 1.1 OB的基础属性信息
1)OB是基于Paxos一致性协议实现的数据多副本存储(多版本并行提交),满足多数派即可返回,最终一致性。
2)采用无共享的多副本架构,系统没有单点障碍,保证系统持续可用。即使单副本出现故障,也可以达到多数派可用。
3)OB是可以动态扩容的,扩容后分区表的数据会自动均衡到新节点上,对上层业务透明,节省迁移成本。
4)OB存储具有高度压缩的能力,数据可以压缩到原有数据到三分之一,对于存储量大的业务会极大降低存储成本。
5)OB作为准内存数据库,采用LSM-Tree存储引擎,增量数据操作内存,减少随机写,读写性能超传统关系型数据库,而且支持Hash和B+Tree。
6)比较重要的是OB也支持MySQL的协议,可以直接使用MySQL驱动访问,可以基本完全的将OB当成分布式的MySQL使用。
在布式数据库出来之前,针对大数据量的读写场景广泛采用分库分表方案,也由此诞生了很多分库分表、读写分离中间件。这些方案虽然能带来一定的效果,也会引发一些后遗症,比如:
需要提前规划好分片规则,一旦定好规则就难以移动,扩展困难
分得太细会浪费资源,分得太粗会导致二次拆分
数据迁移困难
说到这里,大家应该还没太多体感,那么我们会从分布式数据库和OB的一些技术角度去诠释上面的决策点(为方便更好的做决策,主要讲一些相应的分布式数据库和OB技术原理,了解的同学可以直接跳过)。
云原生分布式数据库发展历程,经历以下3个阶段。
单机:传统单机数据库、依赖高端硬件、系统难以扩展、成本高。
PG-XC:基于中间件的分布式数据库(TDDL)。待解决全局一致性、跨库事务、复杂的SQL等问题。
NewSQL:高可用、水平扩展、分布式事务,全局一致性、复杂SQL、自动负载均衡、OLAP等。
图 2.1 数据库发展历程 (图片引自OceanBase)
3个阶段衍生出3种架构,但是统一被归整为NewSQL和PG-XC的2大类风格
Sharding on MySQL:一般我们可以在应用侧或者代理层做分片来管理多个MySQL物理库,以解决单机容量和性能不足的问题。现在正在使用的TDDL就是该架构,虽然计算存储资源可扩展,但在扩展和数据迁移时,业务系统层面需要做不少改造和业务数据的灰度,为了保证业务不停机会产生比较大的改造成本和数据迁移的风险。(PG-XC风格)
NewSQL:国内以TiDB和OceanBase为代表的原生支持分布式的关系型数据库,主要也是为了解决MySQL的问题。和Sharding on MySQL不同的是,NewSQL将分片功能作为数据库的一部分来实现,并提供了动态扩缩容的能力,且对上层业务透明,具备透明可扩展性。
Cloud Native DB:云原生数据库国内以PolarDB为代表,具备池化的资源,也可以实现存储资源的动态扩缩容,其一般采用的主备方式来实现高可用,同时其扩容和主备探活等功能需要大量依赖外部系统。(PG-XC风格)
图 2.2 数据库3种架构 (图片引自阿里云[1])
从架构体系又分为2种流派
Shard-Nothing:架构的每个节点有独立的计算和存储功能并且节点之间不共享数据
无状态SQL计算节点,无限水平扩展
远程存储,共享存储节
强SQL支持( 系统自动扩展路由 )
和单机一样的事务支持
跨数据中心故障自动恢复
无限的弹性水平扩展
Shard-Everying:也叫Shard-Storage,即Shared-Disk,架构是在存储层做了统一和共享,但计算节点是独立节点
图 2.3 数据库架构的2种流派 (图片引自论文Survey of Large-Scale Data Management Systems for Big Data Applications)
从上述图可以看出来,Shard-Everying的存储是共享的,前面计算节点多,查询量极大的情况下就会造成共享存储压力过大,导致查询性能非常低下。Shard-Nothing的则不然,有独立的计算和存储节点,数据不共享性能可靠,市面上的NewSQL大多数都是依照Google 的Spanner/F1来进行设计的,这里先简单介绍下Spanner/F1职责:
F1设计的目标
无限弹性水平扩展
跨数据中心故障自愈
事务的ACID一致性
全面的SQL支持,支持索引
Spanner设计的目标
管理跨数据中心复制的数据
重新分片和平衡数据的能力
跨数据中心迁移数据
到这里想必大家都知道什么是NewSQL了, 完美的具备关系型数据库的基因且具备更加强的扩展性和性能,其实NewSQL跟分布式数据库不能完全画等号的。NewSQL还是遵循事务ACID,SQL引擎那一套,保证的是无缝迁移。但是分布式数据库强调的是最终一致性,也是NewSQL要体现的,所以NewSQL是在原生分布式数据库上构建而成的“云原生分布式数据库”,有点绕嘴,用简单的描述来阐述下两种事务模型,大家平时用的最多的也是事务相关。
数据库有3大特征,上文介绍了OLTP 3大特征(SQL解析、事务、存储)中的2种,分别为事务&存储。 接下来会介绍OB的技术内幕。然后也会介绍下OB在OLAP上的表现。
先介绍OLAP 3个特性:列存(解决宽表)、压缩(减少存储空间)、向量化SIMD (Single Instruction Multiple Data) 即单条指令操作多条数据——原理即在CPU寄存器层面实现数据的并行操作
图 2.4 OB存储引擎架构 (图片引自OceanBase)
关键设计:
1)基于LSM理念自研底层存储引擎,未选择RocksDB
2)宏块与微块存储介质,微块是数据的组织单元,宏块则是由微块组成。Compact操作时可以在宏和微块两个级别判断,是否复用
3)多Parttion多副本解耦Compact操作,避免磁盘IO竞争
4)控制User IO/System IO,减少前台请求影响
5)按行存储,按列编码,减少体积
6)三副本Compaction的Checksum防止静默错误
7)支持分布式事务
8)支持B+Tree和Hash索引模式,针对热数据做了很多优化,查询效率很高,本身也通过Bloom Filter Cache来加速查询
特性:
低成本,自研行列混存的压缩算法,压缩率较传统库提高10+倍。
易使用,支持活跃事务落盘来保证大事务的正常执行和回滚。多级转储和合并来平衡性能和空间。
高性能,提供多级缓存保证低延时。OLAP操作提供向量化支持。
高可靠,全局合并时多副本比对和主表与索引表比对校验和来保证用户数据正确性。
多类型缓存, 覆盖数据访问全链路
数据缓存:
Bloom Filter Cache:维护缓存静态数据的Bloom Filter,快速过滤无需访问的数据。当一个宏块上的空查次数超过某个阈值时,就会自动构建BloomFilter Cache。
Row Cache:数据行缓存,在进行单行或多行访问时构建并命中。
Block Index Cache:描述对每个宏块中所有的微块的范围,用于快速定位微块。
Block Cache:微块缓存,在对数据块访问是构建和命中。
Fuse Row Cache:数据在多SSTable中的融合结果。
···
Metadata缓存:
Partition Location Cache:用于缓存Partition的位置信息,帮助对一个查询进行路由。
Schema Cache:缓存数据表的元信息,用于执行计划的生成以及后续的查询。
Clog Cache:缓存Clog数据,用于加速某些情况下Paxos日志的拉取。
总结:
改进底层SSTable的存储结构,通过宏和微块的模式,解决写放大的问题,而且可以随时调整Compact时机,在挪移Parttion的时候,底层数据存储单元非常适合快速移动。
本身OB也会对底层存储进行监控,控制Compact时机,不会导致前端请求收到影响,在Compcat也会针对数据进行对比,防止静默导致数据错乱,而且压缩比高,整体体积很小。
讲解存储和为什么MySQL不能Online DDL等。 主要介绍B+Tree和LSM的存储区别。
图 2.5 数据复制Paxos (图片引自OceanBase)
关键设计
1)Paxos与Raft最本质的区别是在于是否允许日志空洞,Raft必须是连续的,只能串行无法并行,Multi-Paxos允许日志空洞存在,应对复杂的网络环境更鲁棒。(市面上有很多针对Raft的优化,以Beth Index的方式去批量迭代Index等)
2)一次WAL日志
示例:
顺序投票策略对于主库的负面影响比较严重,出于性能提升的原因,数据库的MVCC使得不存在相互关联的事务以并发处理,但是上述的顺序投票策略是的事务#5-#9可能呢被毫不相干的事务#4阻塞,且必须Hold在内存
如果是允许日志空洞,事务#5-#9就可以继续执行,#4后续在落盘
图 2.6 数据复制延展-副本类型 (图片引自OceanBase)
关键设计
1)全能型副本:拥有事务日志,MemTable和SSTable等全部完整的数据和功能。可以随时快速切换为Leader对外提供服务。
2)日志型副本:只包含日志,没有MemTable和SSTable,只参与投票和对外提供日志服务,也可以参加其他副本回复,但是不能对外提供服务。
3)只读型副本:拥有事务日志,MemTable和SSTable等全部完整的数据和功能。他的日志比较特殊,它不作为Paxos承运参与投票,而是作为观察者实时追赶日志,然后在本地回放。对业务取数据一致性要求不高的时候提供只读服务。
图 2.7 分布式事务 (图片引自OceanBase)
关键设计:
分布式事务:2pc + gps线性一致性事务事务流程:
1)prepare:生成prepare version = max(gts,max_readable_ts,log_ts)。
2)pre commit:推高本地Max readable timestamp。
3)commit/abort:日志多数派成功之后,解行锁,应答客户端。
4)clear:释放参与者上下文。
Percolator模型
关键设计:
1)事务第一个分区作为协调者,其他分区作为参与者,事务本地提交,无需等待其他参与者返回。
2)参与者粒度分区,少部分分布式事务。
图 2.8 Percolator事务模型(图片引自OceanBase)
扩展:
1)租户级别的GPS,基于Paxos保障稳定性。
2)实现类似Truetime的机制,在保证正确的情况下,允许多个事务复用SYS,减少压力。
提前解行锁:
优化前:一个事务持锁的时间,包括了4个方方面:
1)数据写入
2)日志序列化
3)同步备份机网络通信
4)日志刷盘的耗时
图 2.9 事务优化过程解析 (图片引自OceanBase)
优化后:
1)日志序列化完成,提交到Buffer Manager之后。
2)就开始触发解行锁操作,不等日志多数派刷盘完成,从而降低事务持锁时间。
3)当事务解锁之后,允许后续事务进来操作同一行,达到多个事务并发更新的效果,提高吞吐。
图 2.10 OB并行引擎&向量化 (图片引自OceanBase)
上文提到了,AP数据库3大特性,向量化,数据压缩,列存。OB的引擎在TP和AP上都有不错的表现,支持了向量化和并行执行引擎,提升了规模化处理逻辑。
OB的存储上是行列混存的,底层SSTable由若干个宏块(Macro Block)构成, 宏块2M固定大小的长度不可更改。在宏块内部数据被组织为多个大小为 16KB 左右的变长数据块,称之为微块(Micro Block),微块中包含若干数据行(Row),微块是数据文件读IO的最小单位。每个数据微块在构建时都会根据用户指定的压缩算法进行压缩,因此宏块上存储的实际是压缩后的数据微块。
图 2.11 OB的存储引擎空间优化 (图片引自OceanBase)
行列混合的存储结构 :
SSTable由多个定长(2MB)的宏块组成,而每个宏块由多个变长的微块组成。微块是读IO的最小单位。OceanBase微块有两种存储方式,平铺模式(Flat)或编码模式(Encoding) :平铺模式即为通常意义上的行存形态,微块内数据以行的顺序连续存储;编码模式在微块里仍旧存储完整的行数据,但存储组织方式则按照列的形式。OceanBase根据数据格式与语义选择多种编码手段,以达到最佳的压缩效果。同时,在执行过程中,数据以列的组织方式载入内存提供给向量化引擎,进一步提升HTAP处理性能。
图 2.12 行列混合模式图形详解 (图片引自OceanBase)
OceanBase数据库提供了多种按列进行压缩的编码格式,根据实际数据定义进行选择,包括列存数据库中常见的字典编码,游程编码 (Run-Length Encoding),整形差值编码 (Delta Encoding),常量编码,字符串前缀编码、Hex编码、列间等值编码、列间子串编码等。
上文我们说了,OLAP场景3大特征 向量化 + 列存储 + 数据压缩是AP场景的大杀器,从上面介绍我们看到了,OB是一个引擎满足TP和AP两个场景。
支持列存和行存
支持向量化
支持并行计算
支持数据压缩,根据不同的类型压缩类型也不同
美中不足的是OB在AP上支持不是特别完美,还在优化中。虽然支持列存储,但从原理上一个大宏块里面包含很多个小微块,宏块层面,数据还是按行扫描,微块层面,一个微块可能包含若干行,这些行是按列存储的。所以OB这个架构还是行存架构,只有微观层面能享受部分列存带来的好处,想要彻底提升分析型性能表现,需要一个彻底的列存引擎。
因为高德业务比较多样化,分别有结构化和非结构化的场景,OB在支持高德业务的同时也衍生迭代了不同功能行产品。
OB 正常模式
OB Key-Val模式
OB NoSQL模式
OB 纯列存模式
OB Serverless模式
OB在高德多个业务场景均有落地,支撑着高德业务数据的稳定性下面将介绍三个不同架构下,典型的业务落地场景:
强一致金融场景的业务落地
海量数据,多点写入场景落地
中心写单元读场景落地
业务背景及诉求
图 3.1 财务结算业务架构
高德359行财务结算服务主要服务于高德信息业务的结算和财务数据,主要诉求是要求数据强一致性,保障数据不丢失,跨地域容灾能力。
业务痛点与技术解题
财务结算服务作为与B端商家合作的钱款计算的一环,对数据一致性有极高的要求,不能出现因数据不一致或最终一致而导致钱款计算错误的情况。
财务结算服务业务逻辑复杂,需要降低迁移改造成本,最好能完整兼容原业务所使用的SQL协议,平滑替换底层数据库,不需要业务过多的改造。
为了提升财务结算系统的数据强一致性,优先选择了三机房的部署架构。
业务架构图如下,图中红框内容即本次数据迁移OB时涉及到的系统改造部分,使用Diamond做XDB和OB的数据源路由控制,通过OMS做数据迁移,并使用MAC进行迁移前后的两份数据的核验。
图 3.2 财务结算OB改造设计
1)财务需要数据的强一致性和跨地域容灾,而OB提供的能力完美支持该需求,其提供的跨地域部署的能力,可使得财务数据同时写入多地域的数据副本中,即使一地域不可用后仍然能保证有完整的数据可用。并且OB的Paxos一致性协议可保证OB多副本数据写入成功后才应答成功,为分布式多副本数据的强一致提供了保证。
2)为了验证和解决SQL兼容问题,未使用OB提供的OceanBase Client去访问OB,而是使用原生的MySQL数据库的SDK驱动,通过MySQL驱动访问OB,在OB上回放财务全量SQL并验证通过。
3)为了降低财务结算系统改造量,将OB的表按不同的功能进行建设,将原有分库分表的设计逻辑应用到OB分区表的分区键设计上,以便上层业务可透明的切换数据源。对于数据量比较稳定的维表数据,比如合同数据等,将其建设为单表,避免全局分布式事务,提高频繁查询的性能。
解题设计核心要点:
1)迁移数据,将数据从XDB(类似MySQL)到OceanBase的数据平滑迁移。
2)协议兼容,验证OB的SQL协议兼容能力,达到业务可平滑迁移应用代码无须修改 。
迁移方案要点:
数据迁移:通过使用OMS进行数据迁移,数据一致性的校验通过使用集团的MAC系统来核对,而实时的数据写入不一致问题通过监控方式报警处理。
图 3.3 财务结算系统数据迁移设计
因财务结算非C端业务,核心关注是数据的强一致型和跨地域容灾型。并在平衡成本后,在OB的同城三机房、两地三中心和三地五中心的部署方案中选择使用同城三机房的部署方案,既可以实现财务结算的数据一致性和跨地域容灾的目标,也能一定程度上节省成本,后续有需要也可以快速升级为三地五中心部署。
在完全切换OB前,XDB和OB都有全量的业务数据,并可通过MAC每天进行全量数据核对,同时对于增量数据不一致情况及时报警处理。同时又因为XDB和OB都保有全量数据,因此一旦切流遇到问题,可通过开关一键回切原XDB数据库。
采用同地三机房部署
OB原生支持能力,多份数据同时写入,无同步延时问题
图 3.4 财务结算部署架构
业务收益
提升存储稳定性,高可用和容灾能力,跨地域级别的容灾能力。
强一致的存储保证,对于结算财务类的数据,保障较高的一致性,而非用最终一致性的妥协换取其他方面的能力。
提升扩展性,提升数据库弹性扩缩容的能力,存储横向扩展对上层系统透明,极大降低数据迁移的风险。
数据压缩,减少数据占用的存储容量,OB使用的LSM-tree结构存储最大可压缩数据至原大小的35%,降低存储费用成本。
平衡升级:迁移OB后仍然可使用MySQL原生驱动,使用MySQL5.7版本的SQL进行业务功能开发,兼容原有存储(XDB)的协议,极大降低业务系统改造成本。
整理压测结果
目前压测数据写流量在4K左右,RT稳定在1ms左右。
图 3.5 财务结算迁移OB效果收益
业务背景
高德地图云同步是高德地图的基础业务服务,主要负责数据的云端存储,多端设备数据同步。
图 3.6 多端设备数据同步云端设计
云同步的业务系统部署架构示意图如下:可以看出云同步业务用户多点就近接入,一次请求系统会多次读写数据库,因此可以看出系统需要,支持海量数据,多点写入,读写性能出色的数据库。
图 3.7 云同步单元化系统架构
业务痛点与技术解题
业务上需要保证数据多端准确写入,及时更新,更换设备后的数据快速读取;数据上随着高德业务的迅速发展云同步存储着大量数据,怎么做到保证稳定性为前提,追求极致性能,同时极大的降低业务成本,也是云同步系统需要技术解题的突破点,从云同步的业务分析得出整体数据库选型要求如下:
异地多活、海量数据存储、低成本
图 3.8 云同步选型详解
可以看出,针对我们的业务场景,OceanBase完全符合了业务上数据库选型要求,从原理上分析(原理详见上节):
从成本上,针对云同步的结构化的海量数据,其"低成本存储的高级压缩技术",数据文本压缩+行列存储数据特性压缩,将成本压缩到极致,且数据量越大优势越大。
从原理上,LSM-tree的底层数据结构,内存处理增、删、改与批量顺序的磁盘写入极大提高了写入性能,足以支持了数万级TPS的数据写入,其B+树的索引支撑了数十万QPS的业务读请求。
从架构上,其多单元同步链路,OMS的秒级数据同步能力,保障了多机房数据同步低延迟支撑了多机房多活的可行性。
从业务上
分布式原生数据库从本质解决了业务研发需要考虑的分库分表问题。
业务特性为多端数据同步,可用ID作为分区键,所有操作在单分区内完成极大提升读写性能。
多语言SDK,支撑各类业务系统,极大简化对接成本;同时支持SQL形式,更换配置无业务入侵。
图 3.9 云同步迁移OB可行性分析
从原理上分析了整体方案的可行性与业务收益,剩下就是业务数据源切换,主要分为DAO层改造、数据迁移、业务放量,达到目标用户无感,无损切换数据源。
DAO层业务改造,使用OceanBase Client实现业务逻辑改造。
简单通用的SDK,Java SDK的流式编程,简单易用
业务无SQL语句配置
调用过程SDK封装业务无感
数据迁移:全量数据迁移,使用集团内部工具DATAX将ID分桶并发拉取,整体数千亿数据2天左右完成全量同步,采用OceanBase异地备份恢复能力,将数据同步到其他单元,天级完成。
数据比对:业务自研千亿级数据对比、更新、同步框架,实现千亿数据天级对比完成,通用框架,实现增量/全量对比/修复。
灰度迁移:数据双写,ID维度 白名单/灰度,支持比例控制,核心支持按ID随时回滚。
图 3.10 云同步迁移OB落地方案
多点写入
三地读写
无网络延迟
同城双主库容灾,异地多活容灾
同城数据库侧容灾切流
三地业务侧容灾切流
三地六向数据同步
三地六向秒级同步
图 3.11 云同步迁移OB部署架构
业务收益
整体降本明显,性能出色。
性能-压测数据:读单单元8wqps,三单元24wqps,写2.8wtps,读in查询,写批量写入,平均RT 均在2~3ms。
图 3.12 云同步迁移OB收益成果
业务背景
高德地图评价业务在辅助用户出行、交易等决策方面有着积极正向的引导,且评论覆盖与POI的CTR、CVR呈正相关;在本地生活大战略背景之下,高德评价业务的建设帮助高德地图从平台采集数据迈入到UGC时代。
图 3.13 评价业务效果展现
从评价业务的形态来看,内容生成有一定门槛,写入TPS不会太高,但是内容会在高德端内各个入口投放,对RT响应有很高的需求,作为一个读多写少的场景,需要有一个支持海量数据存储,可异地容灾,读写性能出色的数据库。
业务痛点与技术解题
评价系统作为整体评价的入口,对于性能有极致的要求,整体RT必须控制在15ms以内。
为了提升读性能,整体采用用户就近接入,三单元读数据,中心写入数据,对于读数据需要异地容灾能力。
需要支持后续数据量的持续增长。
图 3.14 评价系统痛点分析
上述按照评价的业务场景,采用OB主备集群的架构,支撑着整体高德评价系统的大量读写,三单的读性能得到极大提升,主备延迟在秒级,完全满足性能要求。
评价的业务特点决定写入TPS不会太高,中心写能满足当前阶段的系统发展诉求,多点写在容灾能力上更优,但是会带来数据冲突等一系列问题,复杂度更高。综合系统和业务的发展阶段,我们选择了中心写单元读的方案。
我们从成本,架构特点等多个方面横向对比了多个数据库,最终选择OceanBase
业务上,业务不断发展,数据量不断增长,需要解决数据存储的瓶颈,OB优异的横向扩容能力能很好的解决这个问题。
成本上,OceanBase自研的基于行列混存结构/高效数字编码的存储压缩技术,将成本压缩到极致,能有效的降低评价数据的存储成本。
原理上,基于LSM-Tree的底层数据结构,极大提高了写入性能,足以满足评价场景的写入诉求。基于B+树的索引,能满足评价场景大量读的查询诉求。
架构上,基于OceanBase主备库的架构,利用集群原生的复制能力,实现秒级同步,可靠性高。
中心写入
中心写单元读,架构更加简单,不用担心数据覆盖的问题
同城双主容灾,异地多活容灾
同城多机房容灾切流
三地读容灾切流
三地主备数据同步
利用集群间原生同步能力,秒级同步
图 3.15 评价系统部署架构
核心要点:
张北中心主集群可读可写,实现强一致读。
上海和深圳为备集群,通过OB原生的集群复制能力同步,秒级延迟,实现非强一致异构存储的高流量低耗时读业务。
数据一致性保障:
实时:OB主备之间通过集群原生的同步能力,可靠性非常高,基本不需要担心。
离线:提供离线分析的兜底手段,通过MAC平台T+1分析监控数据一致性。
延迟监控:OB主备之间通过集群原生的主备同步延迟监控能力进行监控,集群级别的复制能力性能很高,正常的延迟都在秒级。
上述采用OB主备集群的架构,支撑着整体高德评价系统的大量读写,三单的读性能得到极大提升,主备延迟在秒级,完全满足性能要求。
面对大数据量的C端场景,基本都需要建立分区表对数据分片实现存储的横向扩容,下面我们就讨论分区场景下的索引设计。评价场景下最核心的模型就是评价基础信息,其他如评价标签、评价元素、评价评分等都可以通过评价ID关联出来,因此我们主要讨论如何设计索引实现评价主表(appraise_base)高效查询。
(1)分区键设计:
最开始我们使用评价(appraise_id)作为分区键
partition by key(appraise_id) partitions 512
这么设计的考虑是:通过评价ID进行分区,数据分散比较均匀,同时通过评价ID也是高频查询,这种方案是个比较常规且通用的设计,表面上看起来并没有什么问题。但是按照线上流量实际压测时发现一直达不到预期效果。我们开始静下心来和OB同学一起分析原因:
分析业务流量发现,大多数查询都是评价者(appraiser_id)维度进行查询(业务场景是查询用户在POI下是否首评、POI详情页展示用户最新一条评价),这部分查询都是都是走了全局索引,而全局索引场景下索引和数据不一定在同一个节点上,大多数请求都需要经过一次跨机分布式事务,消耗了较多数据库资源。
对线上查询流量分析:
用户维度单个、批量查询(占比75%)
通过评价ID查询(占比15%)
评价对象维度查询/按时间分页查询(占比10%)
到这里问题原因就比较清晰了,因为分区设计的缺陷导致大量的查询走到一个低效的索引上,整体性能上不去,这里引出了分区设计一个很重要的原则——在main SQL 维度建立分区键!分区索引的性能是最好的。
因此改为评价者(appraiser_id)维度建立分区键
partition by key(appraiser_id) partitions 512
(2)索引设计
分区键确定后其他维度的查询一般都设置为全局索引,分区键维度的多条件查询设置为本地索引,具体为
PRIMARY KEY (`appraise_id`, `appraiser_id`),
KEY `idx_appraiser_id_gmt_create` (`appraiser_id`, `gmt_create`) BLOCK_SIZE 16384 GLOBAL,
KEY `idx_targetid_gmt_create` (`appraise_target_id`, `gmt_create`) BLOCK_SIZE 16384 GLOBAL,
KEY `idex_modified_status` (`gmt_modified`, `status`) BLOCK_SIZE 16384 GLOBAL
partition by key(appraiser_id)
评价者ID维度(appraiser_id)设置分区键
评价ID(appraise_id)设置为主键查询
评价对象维度(appraise_target_id)设置全局索引查询
这里有没有发现idx_appraiser_id_gmt_create的索引设置有点不太合理,索引被设置为全局索引了,这里可以设置为性能更好的本地索引,原因为:appraiser_id已经是分区键,通过appraiser_id路由到的分片上有全量的的数据,通过本地索引完全能定位到数据,不需要走全局索引查询,这个索引最终改为本地索引:
KEY `idx_appraiser_id_gmt_create` (`appraiser_id`, `gmt_create`) BLOCK_SIZE 16384 LOCAL,
(3)分区表下的数据更新
业务上评价数据都是通过评价ID(appraise_id)进行更新,在实际压测中发现CPU 会飙升到70%左右,排查发现通过appraise_id更新时不带分区键,定位分区有额外的成本,因此在更新条件中加入了分区键,这样在相同的压力下CPU 降至20%以下。
评价系统-索引设计总结如下图:在分区表场景下,有一个好的索引设计非常重要。
图 3.16 评价系统索引设计实践总结
业务收益
新的数据库架构完全支撑整体评论体系的读写性能
分布式数据库,不用担心后续的海量数据增长导致重新分库分表
整体压测结果如下:读/写2w,平均响应稳定在1~2ms
图 3.17 评价系统迁移OB收益成果
在第二节,第三节分别讲述了,OB的技术内幕,和高德OB的落地场景,但是详细考虑为什么选择OB,就是关于数据库的选型是怎么思考的?我想说“一切基于业务场景,不同业务适合考虑不同数据库”,如果业务只有一个很小的场景,几十万条数据,几百QPS的查询,且数据基本不增长,那单实例的MySQL就足够满足要求,以云同步业务(3.2节)看,云同步业务特性:单元化、海量数据存储、海量请求,而云同步经历Mongo->Lindorm->OceanBase,为什么进行迁移,同时为什么不使用MySQL,我们对比如下:
PS:关于为什么我们业务上既考虑关系型数据库,又考虑NoSQL数据库,我们业务上本质是结构化数据,适合关系型数据库,但是整体业务查询简单,对于NoSQL或者KV同样可以支撑我们的业务。
表 4.1 数据库选型属性信息
我们最关注的三点:稳定性、业务支撑、成本,三个角度综合考量,OceanBase是我们看到的最优解,确定了数据库,我们就要选择OceanBase的部署架构。
从上面的项目实践可以看出,我们对于异地多活的部署使用了两种架构:多点写入和中心写入多单元读,针对OceanBase也同样的是两种不同的架构:
对于多点写入的部署架构,三个单元的数据库相互独立,通过OceanBase提供的数据同步工具OMS进行三地六向的相互同步,此种部署架构优势如下:
用户就近接入,读写对应单元,无数据延迟
实现完美的异地多活,各单元异地容灾,机房故障,随时切流,用户无损
图 4.1 OB单元化部署架构
对于中心写单元读系统架构,主要采用OceanBase的主从架构,整体优势如下:
图 4.2 OB的中心写单元读架构
用户请求到业务系统无法避免的物理延迟(30~50ms)
同城多机房灾备,无法媲美异地灾备能力
架构简单运维方便
其实中心写,单元读也可以采用OMS,但是没必要:
表 4.2 多单元数据同步对比
关于多点写入的系统(3.2节云同步系统举例),整体会有很多疑问点,例如:
云同步系统是否业务一定要单元化?不单元化会有什么问题?
从成本考虑,已经单元化,三个单元是否可以不存全量数据?
单元化系统容灾切流是否会有问题?
业务背景分析:
(1)从业务要求上,一次请求,云同步系统需要与数据库多次交互,数据库必须是张北、上海、深圳,三中心来降低网络延迟。
(2)从用户访问上,通过APP访问,目前的方式是就近接入,但是用户的连续两次请求很可能跨中心。
(3)三单元数据库同步是一定存在延迟的,只能缩小延迟,但是不可避免(例如网络物理延迟)。
异常情况举例(出现数据缺失与覆盖详见4.2.7.2):
用户连续两次请求更改同一条数据,但是请求跨中心(第一次张北中心,第二次上海中心),会产生什么情况?
(1)用户第一次请求张北更新成功。
(2)用户第二次请求上海更新成功。
(3)如果两次的请求的间隔小于同步延迟,那么数据同步过来后,上海中心的数据最新数据,会被张北中心的数据覆盖,张北中心的数据最新数据,会被上海中心的数据覆盖;会产生数据的不一致性。
单元化解决的问题:
(1) 将用户请求封闭在同一中心单元,解决可能产生的数据不一致性问题。
(2)用户最多产生一次网络延迟,入口层识别用户单元分发时,可能产生转发到其他单元,业务与数据库多次交互均无额外网络延迟。
(1)从成本角度看,单元化系统只存储对应单元数据,不需要数据同步,同时存储成本很低(优点)。
(2)但是三个单元存储非全量数据,业务将无法进行容灾切流,只能依靠数据库同城容灾,做不到异地多活容灾(缺点)。
结论:要看系统对于容灾的稳定性要求,如果要做到异地多活,随时切流容灾,则需要每个单元存储全量数据。
单元化系统解决了日常情况的数据一致性问题,但是如果系统发生容灾切流,依然会产生数据一致性问题,需要进一步解决。
图 4.3 单元化部署同步延时
对于数据缺失很好理解,用户从一个单元切换到另外一个单元,如果用户前一个单元的数据刚写入,但是由于同步链路延迟,用户已经切换到其他单元,但是之前写入的数据还没同步过来,这样,就产生了数据缺失。
这是个极端情况,不过也有可能发生,如下图,如果用户先执行1,后执行2,在执行1和2之间发生了用户单元切换,如果数据延迟大于用户操作会发生什么?会造成数据永久性差异,同时张北OB存在数据错误。
对于张北OB如果数据延迟:则2先进行,1通过同步链路进行,最终数据,id = 1 name = 'a'。
对于深圳OB:则1先进行,2后进行,最终数据,id = 1 name = 'b'。
图 4.4 数据覆盖问题详解
业务侧保证禁写时间,同时要求OMS不能有长延迟。
OMS数据同步时保证数据不被覆盖(目前OMS支持启停后重新追数据,会比较时间戳),所以我们在切流预案中集成了OMS的启停。
OMS延迟越低,风险越小。
图 4.5 避免数据覆盖设计
OMS同步链路的拆分与相互独立。
同步链路可以从数据库维度拆分到表维度,相互链路独立,无影响,同时提升同步性能。
图 4.6 OMS如何降低延时
在我们降所有表独立成三条链路后,测试数据结果,峰值写入百兆/秒,同步延迟在10s~20s之间。
图 4.7 OMS 降低延时效果 (注:因数据敏感,部分数据脱敏)
业务增长迅猛一定选择分区表。
注意目前OceanBase的分区数量最大8192,且分区数在创建时即固定,不可自动分列。
图 4.8 OB表设计选型详解
局部索引:索引和数据相同的分区规则,在同一机器上,可避免部分分布式事务。
全局索引:不管是全局分区索引还是全局不分区索引,其都可能导致索引和数据不在同一机器上,每次写入都是跨机分布式事务。换句话说全局索引会影响表数据写入性能。
有除主键外的全局唯一性的强需求,需要全局性的唯一索引。
查询条件没有分区谓词,无高并发写入。避免全局扫描,可构建全局索引,但一般限制在4条全局索引内。
图 4.9 OB索引间性能对比[2]
引用自:https://www.zhihu.com/question/400141995/answer/2652474150
针对局部索引的读写,一定要在请求时指定分区键,这样在OceanBase实际处理时才会直接确定对应分区,整体性能会有明显差距,以高德评价系统为例,Update场景下命中分区键CPU使用率从75%降低到20%以下。
1)使用全局索引需要避免扫描行数过大,扫描行数过大会带来大量RPC调用,导致RT过高,建议对大查询做分批查询。
2)不建议使用全局索引做更新操作,基于全局索引更新性能比较查,QPS高时系统负载比较高。
一句话解释OBKV版本和OB普通版本,OBKV版本没有SQL解析优化器,很多SQL需要业务手动优化SQL,指定索引等,但KV版本比普通版本要便宜。
表 4.3 OB版本之间的成本对比
PS:目前OBKV版提供了SDK,支持Java/Go/Rust语言。
复制表概念:OceanBase有一种表类型叫做复制表,这种表数据不像分区表一样做分片存储,也不像单表一样只存储于单个Observer上,而是会将全部数据完整的存储在所有Observer上。
主要优势:复制表数据存在于所有Observer, 数据库在制定查询计划时会做针对性优化,尽量保证从本地机器获取数据,避免远程调用。
使用场景:如果某张表数据很少,且业务上这张表需要与其它分区表做大量JOIN查询,可以将这张表设置复制表,提升JOIN性能。有个常见的例子,我们系统中通用会有一些基础数据表,比如城市表、类目表等,这类表如果需要与其他表做JOIN查询,可以考虑设置成复制表,这样可以避免跨分区JOIN。
4.3.5 业务设计选择-Leader副本是否要分散到各个Observer上?
OceanBase一个Zone通常有3个Observer节点,默认情况下系统中所有分区表Lead副本只分布在一台。Observer上,这台Observer抗住这个Zone所有读写请求。我们可以考虑将Leader副本分布到所有Observer上,这样可以提升整个系统的资源使用率。但是这样也会带来其他问题,如果我们系统中存在大量跨分区的读写操作,会增加很多远程调用,导致读写RT升高。
大多数情况下,我们使用系统默认配置就行,只有当遇到数据库性能问题时才考虑打散Leader副本,并进行压力测试验证效果。使用场景总结如下:
分散Leader副本:系统中大部分查询都可以使用局部索引完成,只有少量查询使用全局索引,且系统中写操作使用分布式事务场景也不多。
不分散Leader副本:系统中有一定比例的全局索引或写分布式事务,这种情况下打散后会导致这部分读写操作RT升高。
主键:Oceanbase作为传统关系型数据库替代品,经常使用在OLTP场景,作为业务系统主数据库使用,这种场景下推荐使用自增主键,便于后期维护、数据异构等。Oceanbase虽然自带自增主键,但是只能保证在一个分区内具有单调性,不推荐使用。建议使用外置分布式ID方案,比如“数据库号段模式” 或者 “雪花算法”等。
分区键:
1)Oceanbase分区方式设置很灵活,支持多级分区、Hash和Range函数分区,通常情况下我们使用最多是基于某一个字段Hash分区。
2)分区键选择上需要综合考虑查询方式和数据热点问题,通常情况下我们可以使用查询最多的一个维度作为分区键,保证大部分查询可以有最佳的性能。
3)如果系统中有一组表,彼此关系比较密切,经常一起JOIN使用,这组表可以使用相同的分区方式,且设置相同的分区数量,数据库可以保证这组表同一个分片下的数据分布在同一个Obsever上。比如订单表和订单明细表可以都使用用户ID为分区键。
4)分片数量要适宜,分片太大对查询性能有影响,这个要注意按自己场景设定并测试验证。
针对云同步这样海量的读写,同时针对节假日出行高峰,我们的流量即可能翻倍,经过压测,我们发现了部分瓶颈。
Java Client代码问题,在发起请求处理链接,不合理使用Synchronized。
升级最新包即可解决。
图 4.10 瓶颈Client延时问题
随着压测的增加,平均RT明显上涨,且失败超时明显增加。
原因:Proxy侧性能瓶颈。
图 4.11 读写流量增加业务超时明显(注:因数据敏感,部分数据脱敏)
扩容失败提示无法连接数据库。
业务系统不断重试依然无法连接。
原因是:单Proxy链接数有上限。
OceanBase公有云,正常部署架构为Client -> SLB -> Proxy -> OBServer,如下图 4.4.3 左图,但是针对海量数据请求可能存在瓶颈,单Proxy的链接数,读写性能不足以支撑业务请求时,可以进行架构优化,进行水平拆分;多单元OBServer之间可以采用OMS进行数据同步,针对高TPS的写入,同时写入数据量很大的情况下,OMS同样可以进行拆分,正常情况下,一条同步链路支撑两点数据库之间的全量同步,优化架构可以拆分成表维度互相同步(详见:4.2.7.4节)。
图 4.12 针对海量请求的OceanBase水平拆分
在刚刚过去的出行高峰,云同步业务峰值OceanBase整体RT平稳在2~3ms
图 4.13 海量数据延时优化效果 (注:因数据敏感,部分数据脱敏)
OBKV版本:为了达到最大的性能优化,针对我们没有复杂查询的业务,我们采用OB KV版本,此版本不支持,SQL优化索引自动选择的能力,需要程序指定,示例如下:
我们创建了"item_uid", "item_id"的联合索引,在OB KV如果业务需要走索引,要制定indexName,如代码行6。
TableQuery query = obTableClient.query(table).setScanRangeColumns("item_uid", "item_id");
for (String itemId : itemIdSet) {
query.addScanRange(new Object[]{uidStr, itemId}, new Object[]{uidStr, itemId});
}
QueryRESultSet rESults = query
.indexName(INDEX_UID_ITEM_ID)
.execute();
同时正如上面提到请求需要指定分区键,我们在使用时,一定注意指定分区键,我们这里分区键是uid。
public TableQuery queryWithFilter(int uid, String table, ObTableFilterList filterList) {
return obTableClient
.query(table)
.setScanRangeColumns("uid")
.addScanRange(uid, uid)
.setFilter(filterList);
}
在第四节开头的多点写入部分介绍了三地六向数据同步的架构,OMS会有Store节点,而Store节点的数据是在内存存储的,在我们压测时发现,当写入数据量过大时,会导致内存覆盖,导致同步日志Clog丢失,针对此种解决方案为:内存+日志落盘,保证日志持久化,那如果磁盘满了怎么办?目前磁盘余量会比较大,同时加设磁盘的相关报警,值班人员随时关注。
图 4.14 OMS 数据链路
业务解决在发现问题时第一时间做了业务切流,整体业务无影响,这也同样体现出异地多活的重要性,可以迅速容灾。
直接原因:集群缩容,把SLB切换到了不支持备集群的OBProxy版本上,导致服务不可用。
根本原因:公有云上OB集群的OBProxy默认是和observer同机部署,3月初为了配合支持主备库和POC测试,将OBProxy部署模式切到了独立支持主备库的实例和版本部署,但是元信息记录没有修改(仍然为混部模式)。导致本次缩容过程中,识别到混部模式后,认为需要修改SLB挂载的OBProxy信息,从而挂载到了现在的不支持主备库的版本,业务访问异常。
问题解决方案
OBProxy运维操作自动化优化:避免操作疏漏引入的后续问题,支持创建各版本OBProxy集群、SLB绑定OBProxy等。
释放节点操作新增静默期:有问题可以马上回滚。
问题现象:某一天中午突然收到告警,CPU被打满,整体现象如下图4.5.2 和4.5.3:
图 4.15 OB 异常节点CPU (注:因数据敏感,部分数据脱敏)
图 4.16 OB 正常节点CPU(注:因数据敏感,部分数据脱敏)
问题分析:通过阿里云SQL分析发现,数据库存在部分慢查询以及大量KILL QUERY请求记录
图 4.17 云SQL分析CPU问题效果(注:因数据敏感,部分数据脱敏)
问题原因:
出问题SQL的业务场景:通过工单ID查询工单的回调记录,倒序取100条
select gmt_create,id from table a where a.feedback_id=xxxx order by gmt_create asc,id DESC limit 100;
PRIMARY KEY (`feedback_id`, `id`),
KEY `idx_gmt_create_id` (`gmt_create`, `id`) BLOCK_SIZE 16384 LOCAL
partition by hash(feedback_id)
这个SQL存在两种执行计划:
计划A:使用主键索引,走feedback_id维度的主键索引查询(feedback_id为分区键)。
计划B:使用本地索引idx_gmt_create_id,走feedback_id 维度的本地索引查询。
正常情况下工单的回调记录就几十条(小ID),执行计划选择的是A,但是那天刚好有个工单有某种异常导致下游一直发送回调消息,回调记录达到5w条(大ID),这时候OB引擎认为计划B性能更佳,根据OB执行计划的淘汰机制,一段时间的执行计划切换为计划B。这时候小ID的查询也切换到计划B上,小ID SQL走了idx_gmt_create_id local索引导致性变差超时,引起应用大量发起KILL QUERY,最终导致CPU打满。
小ID走本地索引性能变差的原因:
与OB确认是排序引起的性能问题,具体为正向排序当前3.2.3.3版本做了性能优化,目前版本逆序排序还未做优化,从而出现Desc排序耗时超5秒的情况。大ID执行时扫描的记录数达到Limit时会提前结束扫描全部子分区,而小ID查询时总记录数不足Limit,在未做优化的时候需要扫描全分区,导致性能问题出现。
解决方案:
为解决大小ID查询性能不一致的问题,需要重构idx_gmt_create_id索引,增加feedback_id筛选列,避免全分区扫描。具体索引为KEY `idx_feedback_id_gmt_create_id` (`feedback_id`, `gmt_create`, `id`) BLOCK_SIZE 16384 LOCAL。
OB侧做逆序排序优化,新版本已修复。
高德本着“促科技创新,与生态共进”初心,通过技术创新的手段连接真实世界,做好一张活地图,让出行变得更加美好。在过程中我们会通过技术的手段去高效的迭代产品提升用户体验,也会通过技术的手段去降低成本,涵盖人力成本和资源成本。
其实互联网发展到现在这个阶段,2个关键词“降本”和“提效”也非常重要。我们已经从“单体架构”过度到“分布式架构”到现在的“微服务架构”进入到未来的“云原生架构”,数据也从当初xG衍生到现在的xPB级别。伴随着ChatGPT出现,算法也达到了一个高光的时代,“算法+大数据”的方式,可以让模拟人类大脑的方式去思考,去帮助人类提效,这类新的技术也让机器完成了涅槃,拥有了生命,我们称之为“硅基生命”。
这么一看科技是可以促进创新、促进社会进步的,拥抱新的技术对我们来说显得尤为重要。因为要在这个生态环境下提供更好的服务是我们的初心,我们不但要拥抱新技术,还要反馈社会,促进科技创新,促进生态发展。那么回过头说说我们在云原生上是如何做“降本”和“提效”的。
因OB也是云原生数据库,我们在云原生后续的规划里会继续跟OB合作,在降本和性能(涵盖AP场景)做更大的突破。高德在Serverless上已经取得了100w/QPS+成绩,我们也会继续发挥这个优势,利用云原生+行业无关化架构提升开发效率,比如利用Serverless,能大幅降低研发成本、提升人效并加快迭代速度,让业务更快,一起做好极致的用户体验。
[这里做一个云原生的概念回顾,因为可能读者会产生一个疑问,OceanBase不是分布式数据库吗,什么时候变成云原生数据库了。]
云原生(Cloud Native)其实是一套架构体系,说到体系其实就会衍生出方法论。Cloud的含义是应用和数据都不在机房或者IDC中,而在N多机器组合的云中.Native设计之初就是以云架构为基础,提供资源池化、弹性和分布式服务等核心能力。
非原生到云原生的转化路径为:
1、DevOps迭代与运维自动化,弹性伸缩、动态调度
2、微服务,Service Mesh、声明式API
3、容器化
4、持续交付
OB提供了租户的概念,资源池+弹性+调度都是相互隔离的,数据非常安全。本身OB的组件也都是服务化的基础服务,也可以跑到容器里。做到Native架构所要求的特性,而且OB的资源都在云上。从这些来看OB就是一个云原生数据库了。
上文也简单的介绍了OB的技术内幕,我们直接说结论。DevOps和持续交付这里不提,因为现在每款数据存储工具都会做,而且都会提供极致的用户体验,让运维变得更加简单。这里我们想说的是OB的数据压缩技术,OB对数据压缩和路由上做了很多优化与创新,单说数据压缩,针对不同的列类型提供不同的压缩技术,尽量使得存储量变得小而不失查询效率。
这里是否会产生一个疑问?高德为什么这么在乎数据压缩和存储空间大小。其实可以先思考下这个问题,大家在使用高德地图的时候,体感只是当前使用的功能。为了更好的用户体验,我们做了很多数据和算法上的事来提升用户体验,这也导致高德数据体量非常大,所需要的成本会很高,可能都是指数的级别,从公司的角度来看,优化这块对我们来说收益很大,不但可以节省成本,还可以降低耗电量,保护环境。从另外一个角度来说做极致的优化也是我们的追求。
总结下,未来跟OB合作的事项,以及详细规划。事项如下:
因高德数据体量很大,后续会持续落地OB( 常规结构化和非结构化版本)
探索OB的AP能力,替换ADB方案
探索Serverless版本
图 5.1 高德与OB后续合作展望
高德有许多结构化的数据存储在Lindorm中,Lindorm本质是列存+多副本冗余架构本质上跟OB一样高可用架构。列存的数据库对于大数据计算其实是很友好的,如果一个Sum的计算其实只要读一次列值然后计算就可以(这里是一个列,不涉及IO的计算) 。虽然结构化数据也支持,但是毕竟场景不是很合适因为我们有复杂的查询在里面,这里就比较适合迁移到OB上,从另外一个角度来说OB的多列类型压缩在成本上也有很大优势。
后续我们会持续把结构化数据场景的业务迁移到OB上,比如(存储)高德足迹(结构化存储),目标降本50%以上。
当然迁移后,我们也可以享受OB线性优化的收益,可以压缩数据的节点数量达到最小,当大促或者量大的时候自动进行弹性扩容,而且因OB具备分布式的能力,我们也无需关注负载均衡的问题,完全实现了自动化透明扩容。
图 5.2 OB弹性扩容设计
在高德的复杂场景里,我们处理结构化数据,还有许多非结构化存储场景,这里这的不是简单的KV模式,一个Key对应一个Val。我们其实用的更加复杂,因为我们的非结构化的场景需要动态列-Schemaless能力,也会和固定表结构混用,这样就显得十分复杂。如果再加上一个多版本的数据保留,那么在查询上就会更加复杂。你们以为这就完了吗?我们还会加上一个列级的TTL。这种复杂的组合真的很考验数据库的能力和性能。OB在收到我们的需求后,响应也是非常的及时,快速的开发出NoSQL+多版数据版本来支持我们,目前已经满足了我们大部分场景。列级的TTL也已经开发的差不多了,近月就会提供出一个稳定版本让我们使用。
目前在非结构化数据场景,也一直和OB在一起探索,目标把打车的特征平台(典型的KV场景)迁移到OB上,预计降本目标50%以上。
如果只是单独提出“多版本、动态列、列级TTL”组合能力,看着只是稍微复杂点,其实还好。 如果说我们每个应用的数据量级都是过百T的存储量,几百张表,读峰值在百万级/S,写峰值在十万级/S。 这么看是不是会体感更好点。所以针对这块降本还是很可观的具体降本在30%-40%。
OB目前的引擎能力已经支持了AP场景,但是因为OB的目前的表现还是行,极少能吃到列存在AP上的表现。下半年OB推出纯列存储引擎,在路由和数据模式上(冷热数据)也做了很多优化,性能会提升至少3倍数。我们也会在下半年把现有AP场景切一个试点到OB上,目前预计降本20%。
这里不是对OB的OLAP场景的性能要求苛刻,我们还是本着降本提效的思路,要压榨出OB极致的性能,因为我们在大数据分析场景,对数据分析的实时性要求很高,只要在快速的效果下才能提供极致的用户体验。而且同一个租户集群里,我们可以同时享受到OLTP+OLAP双引擎的优势和收益,降本收益还是很客观的。
图 5.3 OB的OLAP方案
当然我们也会把OB引入到交易的场景里,在交易的复杂场景里,OB也会表现的非常好。
图 5.4 OB交易场景探索
高德本身已经在Serverless上取得了很多成绩,百万级QPS。我们有很多Serverless落地经验,也会跟OB一起尝试落地Serverless版本的OB,再次降本。
资源按需使用,规格动态升级
资源按需使用,底层存储空间扩容
因OB是多副本模式+转存模式+多数派模式,扩容时可用。
图 5.5 OB在Serverless上的探索
说到组装式研发,这并不是一个新词,这种架构模式其实很早就存在了,只是每个公司实现的方式不同。高德针对组装式研发进行了调整,因为对我们来说数据流转就是2大属性,“Request和Response”。但是从业务组合的视角,我们其实只有“Input和Output”。从架构的视角就更加简单明了,也更加灵活了。
我们其实只要针对"输入和输出"进行加工,然后通过流水线的方式把流程编排起来,整个业务流程和数据流转就会非常清晰,我们也会根据公共特性进行封装变成通用组件进行复用,大大提供人效成本和降低后续维护成本。不再担心面对业务孵化和不敢改的局面,真正做到从人到机器再加上业务迭代上的一石三鸟的收益。(因辅助的工具比较多,这里不深入叙述,后续可以单独写一个专题)
组装式不单单可以用到应用上,还可以用到Serverless的FaaS里,其实我们现在好多服务已经进化成Serverless的BaaS模式。整个组装式已经深入到Serverless生态里面。我们也会继续建设Serverless生态,应对初心,促进科技发展,开源高德Serverless FaaS的Runtime脚手架到社区,让大家可以快速复刻高德的Serverless落地方案。我们已经在“交易、出行、广告、车生态”等多个场景里落地了,感兴趣的同学可以去搜索“高德云原生之Serverless体系建设&实践”。
整体抽象如下3点:
组装式研发,乐高组件,动态编排,快速迭代提升人效,降低维护成本,快速迭代业务。
Serverless生态建设,更加丰富的脚手架和工具落地,快速降本。
存储层面支持Serverless,一个函数通杀所有,端云一体,语言无关,降低研发门槛。
图 5.6 Serverless 端云引擎一体化探索设计
[1] 阿里云. PolarDB-X技术架构[EB/OL]. 2023-07-18[2023-07]. https://help.aliyun.com/document_detail/316639.html
[2] PolarDB-X. PolarDB-X、OceanBase、CockroachDB、TiDB二级索引写入性能测评[EB/OL]. 2022[2023-07]. https://www.zhihu.com/org/polardb-x.
[3] Wu L, Yuan L, You J. Survey of large-scale data management systems for big data applications[J]. Journal of computer science and technology, 2015, 30(1): 163-183.
[4] https://glossary.cncf.io/serverless/
[5] https://developer.salesforce.com/docs
推荐阅读
如何在高德地图实现自动巡航效果?
走近高德驾车ETA(预估到达时间)
ARHUD驾车导航技术概览
高德正式发布HQ Live MAP
关注「高德技术」,了解更多