
暂无个人介绍
6月10日,记者采访获悉,知名电竞生态聚合平台——比心陪练日前与阿里云正式达成合作,通过引入阿里云多款自研云数据库产品,已完成了多场景核心业务系统构建,整体IT成本降幅超过40%。比心陪练创立于2014年,是线上电竞陪练领域的开创者和领头羊。经过7年发展,其运营的比心APP上已有超过6000万用户和超过800万电竞陪练师。截至目前,平台累计为技能分享者创造收入超200亿元。随着公司业务的快速发展,尤其自2020年至今,比心APP注册用户和平台数据量呈现爆发式增长,给企业整体IT成本,业务系统稳定性以及业务扩展性带来了极大挑战。经过细致的市场调研,比心陪练技术团队决定与阿里云展开合作,通过引入多款阿里云自研数据库产品,包括云原生关系型数据库PolarDB、云原生内存数据库Tair、云原生多模数据库Lindorm、图数据库GDB等,有效支撑了核心业务数据爆发式增长,以及高并发业务场景,保障了用户的流畅体验。比心陪练数据库技术负责人陈武表示,阿里云自研数据库良好的扩展性和高性能,帮助他们构建了坚实稳定的基础数据架构;基于阿里云自研数据库体系,他们在IT基础上实现了40%的降本,同时在阿里云数据库生态基础上,自研了一套DB智能平台,提升了100%DB治理效率。公开资料显示,阿里云自主研发了PolarDB、Tair、Lindorm、GDB等云数据库产品,并作为中国唯一的科技厂商成功进入Gartner全球数据库领导者象限。过去十多年,阿里云在产品技术领域进展迅猛,获得市场广泛认可,目前已有超过70万个数据库实例迁移到阿里云上,包含政务、零售、金融、电信、制造、物流等多个领域的龙头企业。
新一轮科技革命和产业变革正重塑全球发展格局,以云计算为代表的新一代信息技术相互渗透,成为数字经济智能和创新发展新引擎。在2021阿里云开发者大会上,阿里云数据库宣布推出云原生数据库2.0——一站式在线数据管理平台,以及“阿里云数据库开源计划”,共建云原生数据库生态。2020 年,在 Gartner 发布《2020 年度全球数据库魔力象限报告》,阿里云首次进入全球数据库领导者象限,值得一提的是,这是中国数据库 40 年来首次进入全球顶级数据库队伍。自高举“升级传统商业数据库”旗帜以来,阿里云在云数据库投入众多,陆续推出自研产品:例如在企业级云原生数据库上,拥有云原生关系型事务数据库 PolarDB、分布式版 PolarDB-X;在 OLAP 领域,有云原生数据仓库 AnalyticDB、云原生数据湖分析 Data Lake Analytics;在 NoSQL 领域,拥有云原生多模数据库 Lindorm 和内存数据库 Tair。然而在后疫情时代下,企业对云数据库的诉求发生改变。疫情加速了不少行业的业务在线化和数字化运营,伴随企业数字化转型加速,数据的存储和价值挖掘需求越发强烈,呈现数据量快速增长,数据加速上云,云原生分布式三大趋势。如今阿里云推出一站式在线数据管理平台DMS,这背后有哪些思考?带着这些问题,阿里云资深技术专家、数据库产品事业部生态工具部负责人陈长城和CSDN 创始人&董事长、极客帮创投创始合伙人蒋涛展开独家对话:蒋涛:今天推出的“云原生数据库 2.0”理念和产品,将会给企业带来哪些颠覆性变革?陈长城:一站式在线数据管理平台DMS 与传统的数据集成不同,我们从客户场景视角提出了一站式数据管理服务的理念,从数据生产和集成、数据实时处理(OLTP\NoSQL)、数据分析与发现(OLAP)、数据开发与管理,覆盖数据生产到应用的全生命周期。其中最大的特点是企业能够用数据库的方式进行大数据量的管理,从单机数据库,到云原生分布式数据库如 PolarDB、再到按需集中到数据仓库进行分析、报表开发,以及数据归档和离线数据价值挖掘,实现库仓一体、在离线一体的存储和计算能力。第二个变化是在线数据资产统一管理,低门槛按需建仓,敏捷分析的能力。云原生技术应用让数据库和数据仓库都在一个可控的环境,可以实时无缝打通,实现 DDL、扩缩容等运维无感知,这是传统数据集成做不到的,同时将 ETL 转换内置在链路里大大缩短传统数据集成链路,可将源端库直接作为数仓的 ODS 层进行计算加工,免去数据的物理搬迁问题。真正实现敏捷建仓、按需建仓。蒋涛:对标其他产品,从一站式数据管理工具的角度上,阿里云的领先程度为多少?陈长城:一站式在线数据管理平台为满足企业多样化的业务场景需求,又满足数据集中的需求而生。传统的数据集中需要做数据的物理集中,一站式在线数据管理平台希望做到“按需建仓,敏捷开发”,我们是业界第一个提出这个概念,属于较领先的。蒋涛:对数据库工程师而言,是否需要学新的技术才能用好这些工具?陈长城:原来很多应用开发者在使用数据库,数据工程师或者数据科学家使用数仓,一站式在线数据管理平台通过统一打通数据库和数据仓库,使得更多应用开发者像数据工程师去处理数据,这个平台反而是降低技能门槛。此外,无论是小微企业,还是中大型企业,均可在一站式数据管理平台DMS管理数据和相关开发。由于云原生数据仓库的存储资源是可以无限拓展的,可使得用户数据量膨胀过程中计算和处理分析的方式并没有发生大的改变。蒋涛:这非常关键,当前所有公司都在进行数字化转型,随着数字化规模不断扩张,怎样让数据开发变得更容易,一站式数据管理是非常有价值的。蒋涛:当前开源正在深刻影响 IT 界,如 MongoDB、PingCAP 等开源数据库项目拥有良好的影响,开源将是云数据库的未来吗?陈长城:阿里云数据库本身也是依托开源数据库发展壮大起来,一直是开源社区的积极参与和推动者,一直在将实践过程中遇到的问题,修复的 Bug,原创的技术回馈开源社区,这次将一些更核心的,提升用户价值的技术开放出来,也算投桃报李,相得益彰。阿里云在提供云数据库服务的长期实践过程中,对数据库技术有了更深的理解。在高可用、分布式、存储与计算分离等方向上积累和沉淀了很多技术,阿里云开源的目的还是期望吸引到更多的合作伙伴一起来共建,为行业用户提供更有价值的解决方案。蒋涛:未来云数据库产品如何发展才会更有生命力?陈长城:第一,应用云原生分布式数据库技术,当下业务快速膨胀的数据规模,多样化的数据类型,更复杂的业务特征,和对数据实时洞察的需求,这推动了传统数据库技术向可扩展、高可用、实时弹性等方向发展。通过云原生分布式技术,可以承载大数据量的在线处理和分析,也提供了高可用的生产保障,并对多模数据处理引擎提供了底层支撑,云原生分布式数据库将成为未来企业数据库的首选。第二,可通过一站式在线数据管理平台DMS 对企业数据资产进行统一管理,统一进行安全脱敏和血缘分析,发挥数据价值。对企业数据生产存储、传输加工、计算分析提供全生命周期的服务能力。将企业各种数据库无缝打通,让数据自由流动,构建一体化的解决方案,才能降低企业数据处理和分析门槛,发挥数据价值。
什么是快乐星球?如果你想知道什么是快乐星球的话,请参加数据库夏日618,MySQL带回家只用6块1毛8!除了MySQL,还有Redis只用61块8,原价高达1千2;什么,你在快乐星球成功着陆?看到了么,前方PolarDB,高能¥618!!!原价6千!!!欢迎您来到数据库618快乐星球,点击“此处”,更多惊喜等着您!
金融市场L1/L2的报价和交易数据是量化交易研究非常重要的数据,随着数字业务快速演进,具有时序特征的交易数据激增,对底层数据库和量化分析系统提出了更高的要求。传统的关系数据库支撑这样的数据量级,即便分库分表,查询性能也远远无法达到要求。常用列存NoSQL数据库可以解决这个数据量级的存储,但是这类通用的存储引擎缺乏对时序数据的友好支持,在查询和计算方面都存在严重的不足,且无法支持对量化金融场景实时业务计算、流批一体分析、多源数据融合分析。阿里云原生多模数据库Lindorm联合浙江智臾DolphinDB发布金融高频交易数据量化分析与处理方案,通过云原生方式整合DolphinDB实时高效的数据处理能力和Lindorm多模海量数据融合存储分析能力,集成了功能强大的编程语言和高容量高速度的流数据分析系统,为金融场景海量时序数据的量化分析计算提供一站式解决方案。方案操作简单,可扩展性强,具有良好的容错能力及优异的多用户并发访问能力。➣方案优势能力数据库存储高吞吐低延迟的列式内存引擎。列式混合引擎(基于内存和磁盘)为存储海量数据的数据仓库提供了优越性能。灵活的分区方案:支持值分区、范围分区、列表分区、哈希分区和组合分区。支持单表百万级别的分区数,大大缩减对海量数据的检索响应时间。库内分析:可在数据库中进行复杂的编程和运算,避免数据迁移的耗时。提供多种SQL功能的扩展,包括非同时连接、窗口连接、透视表、复合列等。支持同一个分区数据库内多表快速联结。数据压缩。支持多用户并发访问。每个用户以给定的权限在独立的会话中工作。元数据高可用:多个控制节点使用Raft协议实现强一致性。分区数据高可用:一个数据库可以包含上百万个分区,分区的多副本之间使用改良的二阶段提交协议实现分区副本的强一致性。运维高可用:在线增加服务器节点,在线平衡节点间数据,在线为分区数据表增加字段。数据库的增量备份机制:当分区副本数为N的时候,在N-1个节点宕机的情况下,保证系统仍可以持续写入和读取。使用内嵌的分布式文件系统自动管理分区数据及其副本,为分布式计算提供负载均衡和容错能力。数据库内数据分析编程语言功能强大且表达能力丰富。支持命令式编程、函数式编程、向量编程、SQL编程和RPC(远程函数调用)编程。编程语言的语法与SQL和Python非常相似,易上手易使用。内置1000多个函数,涵盖绝大多数常用的数据处理、数据分析、机器学习等功能,以及文件调用与数据库管理等功能。通过内存引擎、数据本地化、细粒度数据分区和并行计算实现高速的分布式计算。提供即时编译版本,极大加速for-loop, while-loop与if-else等语句的执行速度。支持多种计算模型,包括pipeline、map-reduce和迭代计算。为动态数据分布式计算提供快照隔离。通过在多任务中共享内存的数据副本来提高系统吞吐量。可便捷地分析分布式数据。在单个节点上编写脚本后,无需编译和部署即可在整个集群上执行。流数据无缝集成流数据和数据库表。可以使用SQL查询本地流数据或分布式流数据。内置时间序列、横截面、异常检测以及响应式状态引擎等多种流数据聚合引擎。可使用DolphinDB中的用户自定义函数处理信息。亚毫秒级的信息延迟。使用实时数据更新历史数据仓库只有亚秒级延迟。可以从任意偏移量重现历史信息。提供可配置的选项(如分区、工作线程、队列)用于流量控制和性能调优。生态提供多种编程API,包括C++、Python、Java、C#、Go和Excel等。已有的pandas程序只需做少量改动即可通过pandas API (orca) 在DolphinDB中运行。提供多种插件,包括MySQL、ODBC、HDF5、Parquet等。内置Web服务器,用于集群管理、性能监控和数据访问。提供DolphinDB GUI与VS Code插件等IDE(集成开发环境)用于数据分析。通过内置函数、Web接口或Prometheus实现系统监控。欢迎大家加入钉钉群一起交流讨论~
作者:黄鹏程(马格)一、Tair背景介绍(一)什么是阿里云Tair云原生内存数据库阿里云Tair云原生内存数据库线上名字为阿里云数据库Redis企业版(又称阿里云Tair),从2009年开始正式承载集团业务,是一款历经磨练的企业级产品。它完全兼容Redis的数据结构和通讯协议,包括API接口,并且在内部逐步打磨的过程中,基于Tair研发云上托管云内存数据库。如上图所示,Tair产品分为三个类型,从性能到性价比有不同的产品排布,下面用1.0X表示开源的Redis的性能与价格,从性能和价格上与三个类型的Tair产品进行对比。Tair性能增强型性能约为开源Redis的两倍,支持多种常见数据结构,在天猫/淘宝/高德/优酷中大规模使用,并拥有任意时间点恢复/全球分布式/热点散列等特性。Tair持久内存型基于Intel傲腾持久内存做的数据库,后文会重点阐述其特点。Tair容量存储型直接对标社区Pika等开源存储型Redis产品。然后因为我们会用阿里云的高效的高效云盘,所以说整个的功能和性能都会比Pika强很多。如上图所示,从内存存储方面,Tair三个类型的产品适应不同的场景。Tair性能增强型1)数据读写频繁,要求低延迟,支持大连接;2)成本相对不敏感,以满足业务性能需求为主。Tair持久内存型1)数据读写较频繁,延迟要求可稍微放宽,存储数据量有一定较大容量要求;2)成本控制较之热数据要求更高。Tair容量存储型1)数据读写密集程度低,延迟要求不高,整体存储容量要求高;2)成本是最大考量因素。(二)阿里云Tair面向业务的数据结构让业务创新更容易在业务方面,阿里云Tair与基于传统Redis存在许多不一样的地方,阿里云Tair业务结构如下所示。除此之外,阿里云Tair有以下优点:1)高性能性能增强型,流量上涨淡然处之,性能是开源Redis的2倍。2)持久化持久内存型,数据可靠,方便业务使用,不再担心数据丢失。(RPO=0)3)低成本多种存储介质,选择最优性价比,性价比高于ECS自建。4)丰富数据模型在社区Redis上提供了更多面向应用的数据结构,应用开发更简便。5)企业级能力全球多活,数据闪回,混合多云(热点散列)。(三)阿里云Tair历史上图为Tair的发展历史。从Tair1.0开始,团队就孵化出了云Redis社区版和云Memcache,这是线上社区托管的两个版本。在Tair1.0的时候,就会整个孵化出来,2.0逐步上云,3.0会孵化出来一款叫GDB的产品,是一款线上的图数据库。再往下是Tair(Redis企业版/Tair 3.0),集团和云上是同一个版本,有很多种形态来支持用户在各种业务场景下的需求。二、Tair功能介绍(一)做一个不丢数据的内存数据库—Tair持久内存型我们希望做一个不丢数据的数据库,做法是通过新硬件和新软件来进行。1.新硬件引入英特尔® 傲腾™ 数据中心级持久内存;与DRAM内存相近的性能表现,而且其大容量和非易失性的特性还可帮助系统获得更优的可用性;它相比于DRAM内存的成本和容量优势,也可帮助客户有效地降低总拥有成本。上图为存储介质图,速度延迟从上到下逐步增大,相反,容量逐步减少,成本逐步降低。Storage Class Memory处于中间,性价比较高。2.新软件模式选择新软件的开发模式方案有两种,分别是内存模式(Memory Mode)与应用程序直接访问模式(App Direct Mode)。在内存模式下,应用和OS将其视为易失性内存池,虽对应用透明,但掉电即失,因此不采用。在应用程序直接访问模式下,持久内存和 DRAM充当独立内存资源,通过构建符合Redis协议和数据结构的软件利用两块存储资源构建兼容Redis、具备命令级持久化能力的内存数据库。全新的挑战我们选择重新写兼容Redis的东西,而不是在Redis基础上进行更改,是因为新软件有着全新的挑战:1)替代原有的内存分配器,同时要保证内存分配器元数据的持久化;2)Redis数据结构与索引的持久化要保持一致性;3)持久内存和易失内存如何协同工作,让整个数据库高性能工作,同时还具备强大的持久化能力。3.产品化能力上文结合软硬件的能力阐述了如何去构建持久化内存的形态,下面可以看一下所带来的成果。消除aofrewrite与fsync的无Fork设计,服务更顺滑,P95延时较之内存版Redis更低更稳定;读写性能吞吐为内存版Redis的90%以上。原生命令级持久化能力,操作写入即持久化;缓存主存合一成为可能,成本更低,架构更优雅。4.应用场景应用场景主要分为如下两种。场景一:大数据量、高性能、成本的综合选型考虑机器学习平台、推荐系统等计算数据对性能和容量的要求很高;全内存又使得成本压力巨大;可采用Tair持久内存型以有效降低成本,并能够保持高性能运行;同时用户也无需为降本而综合使用其他数据存储,有效地平衡了系统复杂度与成本。场景二:用作持久化数据存储游戏、直播、数据分析中大数据结果集对外提供查询服务等场景可以使用;可采用Tair持久内存型作为数据最终的存储;对比缓存+主存的两层架构,数据同样可靠;性价比更高,代码更简化,架构更优雅。(二)做一个具备时光机能力的数据库--数据闪回我们希望这个内存数据库具备有时光机能力。时光机能力表示可以将数据恢复到过去指定的任何时间点,也称为数据闪回,它有如下两大特点:七天内任意时间点的数据恢复1)Backup/Restore的终极形态2)支持按秒级的数据恢复(可支持到按条)3)防止删库跑路场景4)Clone & Switching:随时回切根据按照指定Key或者Key Glob Pattern进行原地数据部分恢复1)灵活应对部分数据异常2)其余未指定数据不变化3)游戏数据回档实现(三)做一个全球多地写入的数据库--全球多活1.基本概念我们希望数据库能够多地写入,把多地的内存数据库进行多活操作,用户可以在多地访问与写入,我们帮用户做数据按序同步。2. 三地六向同步全区全服同时在线,解决漫游(roaming)和DNS漂移问题。数据本地访问(locally data access)的流畅性。单元化:独立部署,区域容灾,按需调度。数据的高可靠和性能提升。3. 低时延SLA保障推送模式(Push),Latency= T1 (binlog落盘时延)+ T2 (Replicator发现时延) + T3 (Apply时延)1)整体看,T1 + T2 稳定在10ms,最差100ms;T3基本上时延是网络RTT。2)忙时下不敏感,replicator具备独立的资源,点到点同步通道带宽稳定。适合写入量大,对平均时延要求高的客户。目前全球分布式缓存只能做3地6向。适合跨域多活及单元化业务(阿里内部经验)。Tair全球同步时延测试白皮书:https://help.aliyun.com/document_detail/199010.html4. Session场景Session的容灾至关重要随着业务扩展,session访问压力几何级数增长。对抖动敏感,远距离访问体验差。单元化部署,全球化部署。除去容灾(跨域备份),还要能就近读写(跨域双活,多活)。5. 游戏场景架构特点:玩家可就近接入,本身逻辑就近计算数据读取,基本不存在数据一致性问题,灵活调整分布式节点。可以灵活跨地域部署,数据通过Tair同步延迟根据网络速度决定通常在100ms以下。场景:聊天室,弹幕信息同步;跨服对战;全球道具商城。6.出行场景Tair也在高德地图中深度使用,并解决了出行场景的一些问题。使用前:导航时地理位置信息推送问题DNS漂移是一个边界效应,通常大概影响边界上5%-7%的用户。在导航中跨越边界时,会大量出现交叉访问不同数据区的场景;业务逻辑复杂,可靠性低,用户体验差。使用后:高德交通三地六向同步(Tair only)Redis-enterprise跨域多活提供百万OPS每秒的数据同步。交通在三地都可高速写入/访问;极高性能的优势提供流畅的用户体验。用户可以灵活的在SDK层控制读写,比例等。(四)做一个有计算的内存数据库1. TairCPC高精度计数TairCPC是一种数据的压缩算法(sketches)的内存实现,可以利用很小的空间对采样数据做高性能的计算,适合在实时和流式计算场景下做高性能的风控和安全场景。主要特性1)内存占用低2)增量读写,IO最小化3)毫秒级高性能去重4)超高去重精度5)误差率稳定收敛适用场景1)想以较低的内存实现对海量数据的去重计算;2)容忍一定的四舍五入的误差率;3)适用于实时计算的滚窗和滑窗去重。2. TairTS时序数据处理TairTS是基于Redis Module开发的时序数据结构,提供低时延高并发的内存读写访问,以及快速的过滤聚合查询功能。将存储与计算集于一身,极大地简化用户处理时序数据的流程,结合Tair持久内存型单集群最大规模可达65T。主要特性1)TairTS相对于TSDB类传统时序数据库,可提供更快的写入性能,并提供数10倍的查询性能。2)针对小规模数据场景,TairTS可将批量查询与聚合计算集成到单条命令中,减少网络交互,实现毫秒级响应。3)针对大规模数据场景,可利用索引命令,将大规模数据,分批查询与聚合,实现秒级响应。适用场景1)监控数据的存储与计算2)车联网、工业互联网实时IOT数据处理3)APM秒级监控等三、面向未来最后谈一下未来面对的事情,关注云产品的人可能都知道存储计算分离,我们希望在这基础上能再进行一层分离。将内存也分离出来,做一个大的内存存储池。希望我们的再分离一层,就是把内存也分离出来,因为现在可能是一个大的存储池,为用户提供更加弹性的服务。
一、浅谈云原生(一)云原生,是未来使用云的标准方式我认为云原生是未来使用云的标准方式,云计算资源无处不在、取之不尽、用之不竭,不用关心云资源在哪里、有多少。就像今天我们使用自来水一样,没有⼈会费尽心思考虑水从哪里来。(二)阿里云全面引领云原生分布式数据库发展方向阿里云在云原生数据库领域做了多年的实践、尝试与探索,与开发者一起成长。我们认为接下来云原生数据库必须关注和发展的领域有以下五个:1)云原生分布式将云原生和分布式技术深度融合,将Share Nothing、Share Storage、Share Everything架构深度融合。2)智能化利用AI、机器学习的技术,让数据库系统能够实现自动驾驶的能力,让开发者可以更好地管理和使用数据库的服务,如自动调参、索引推荐、异常检测等。3)安全可信安全可信的能力十分重要,比如说如何确保数据是全链路的、加密的、安全的,在存储、传输、计算过程中都能够提供安全可信的能力。4)在离线一体化减少数据链路,数据从在线处理、到在线分析、到离线的存储,能否提供一体化的体验,让开发者可以更简单、更便捷地访问和处理数据。5)物联网多模面对AIOT、物联网、车联网的蓬勃发展,能否打造一个面向开发者与应用的物联网多模的数据库。以上是我们认为接下来非常重要的五个方向,也正因为在这些方向的耕耘,阿里云在去年获得了Gartner全球数据库领导者的突破。二、阿里云数据库 – 数据管理生命周期相信对于任何一个开发者而言,在数据层面最关心的是数据管理生命周期。下面站在开发者的视角,阐述数据的全链路生命周期到底是什么。第一步是数据的生产和集成,在这一步如何更高效地做数据集成、数据清洗、数据传输、数据备份。当做完这步以后,下一步是数据的实时处理,这里面就是我们非常熟悉的传统关系型数据库、在线交易、OLTP等。紧接着就是数据分析和发现,用户如何做数据脱敏、数据的血缘关系等。以上就是数据管理的生命全周期,我们在上面构建不同的解决方案,和开发者、合作伙伴一起面向应用、行业、客户去打造最终的Killer APP。下面就从生命周期的各个阶段来阐述一下我们为开发者提供了哪些工具,开发者基于这些工具可以做哪些事情。(一)数据生产与集成数据生产与集成是数据的第一生命周期,就像新生儿一样,数据来到这个世界必须要经历采集,然后是存储和处理。如上图所示,在这个过程中阿里云提供了DTS(Data Transmission Service),支持17种以上不同的数据源,可以做实时增量或者全量的同步,让应用非常简洁地实现数据从多元异构的数据源到多元异构的目标端实时的数据同步。DBS可以实现跨云的备份、云上云下数据备份的统一,让数据在多云多端之间无缝流动。DMS(Database Management Service)可以帮助用户做任务编排、数据分析、血缘分析等一系列事情。以上构成了阿里云在数据生产和集成的基础能力。(二)数据实时处理数据生产和集成之后是数据的实时处理。作为开发者,我们最关心的就是确保在任何情况下,在线交易场景的应用永远在线、数据永远不丢失,在这里我们提供了不同的选择。1)云数据库 RDS:提供企业级数据库自治能力首先,阿里云提供自己的云数据库RDS。每个云厂商都有RDS,阿里云的RDS和其他RDS有什么不同呢?发展到云原生数据库2.0,阿里云RDS最大的特点就是提供企业级的数据库自治能力(Autonomous Database Service)。首先,通过Kubernetes构建一个云原生的管控平台,所有的管控能力都进行了微服务化和容器化部署,这样可以屏蔽底层多元异构的资源,为开发者提供一个云原生的开发环境和部署环境。在这个上面,我们利用AI和Machine Learning的技术,构建了自动驾驶数据库平台。为开发者提供了许多能力,比如自动压测,我们可以自动生成压测数据,让它的工作负载与在真实环境几乎一样,这样开发者可以更好地调测在线系统。另外,我们提供了索引推荐、参数调优等一系列自动化自治服务能力。此外,困扰开发者的许多问题,比如在线应用运行速度变得很慢,线程池被打满等,通过DAS(Database Autonomy Service)可以帮助开发者更快更好地发现与解决。2)云原生关系型数据库 PolarDB除了阿里云RDS之外,云原生数据库2.0最核心的能力之一就是云原生关系型数据库PolarDB。为了让开发者能够更好地在PolarDB上开发应用,我们确保PolarDB 100% 兼容 MySQL、100% 兼容 PostgreSQL、高度兼容 Oracle 语法,让开发者实现轻松上云。很多企业和开发者有向全球部署的需求,比如在线教育、游戏,需要我们的应用能够就近服务用户,阿里云推出了全球部署的能力,称为Global Database。这表示PolarDB可以实现跨AZ(Available Zone)的部署,实现RPO等于0,非常低的RTO。通过跨AZ的能力数据实时同步,可以实现用户在开发者的应用上就近访问。为了让开发者更好地体验到阿里云的产品,我们推出了I/O带宽免费、性价比更高的PolarDB实例,价格仅为其他云厂商云原生数据库的30%—40%。除此之外,我们还进行了性能测试。我们用开发者非常熟悉的SysBench,连接事务处理、读写混合测试,测试了CPU 密集型和I/O密集型。如上图所示,我们对比了PolarDB和CPU密集型、I/O密集型两个云原生数据库在SysBench上的标准性能测试,可以看到Polar DB在两种不同工作负载的情况下都展现出非常优异的性能。3)云原生分布式数据库PolarDB-X开发者经常面临海量数据高并发、超高并发的场景。针对这种场景,阿里云推出了PolarDB-X,即PolarDB的分布式版本,也就是将云原生架构存储计算分离,上面再构建一层架构来支持一体化分布式数据库。PolarDB-X支持海量高并发、全局二级索引、HTAP复杂查询、分布式事务、在线弹性扩展。以上图的全局二级索引为例,它支持ACID,这样让开发者可以更关注业务应用的开发,而不需要关注分库、分表这样复杂的逻辑。我们用X-Paxos来支持两个数据副本、一个日志副本,而且我们的三副本可以做到跨AZ部署、支持同城跨机房的RPO等于0。(三)数据分析与发现实时数据库处理之后,当积累了大量的交易数据,如何在数据里面发现信息?这就来到了数据分析和发现阶段。云原生数据仓库AnalyticDB(简称ADB)是云原生架构,计算存储分离,计算资源按需弹性,相对传统方式,成本可以下降3倍。我们在这种云原生的架构上实现了冷热数据分层,1TB可以低至114元/月,一份存储多种计算是未来数据分析领域发展的大趋势,用一个存储多种计算引擎可以让我们适应工作负载,到底是离线的ETL、还是在线的交互式分析,整体成本可以大幅度下降。利用前文的这些技术,可以帮助开发者实现在离线一体化的开发和应用,支持离线ETL以及在线的交互式分析,本质上就是将MTB架构和BSP模型完美地结合在一起。我们也高度兼容生态,并且我们很快会推出Spark兼容版,将这些开源的生态在我们的云原生数仓ADB里面完美结合,实现基于负载的智能化调度和混合应用的支持。(四)数据开发和管理最后是数据开发和管理。我们面向数据库开发者提供一站式的在线数据平台,阿里云的开发者社区支持了几十万数据库开发者利用DMS的能力,访问和管理多元异构的数据库资源。DMS支持开发者们所有耳熟能详的数据库,从阿里云的PolarDB、PolarDB-X、RDS、AnalyticDB等,再到MySQL、Oracle、SQL Server等。接入以后,它提供数据资产、数据库设计、数据库开发、数据集成、数据服务等一站式的能力,帮助开发者实现数据化运维、容灾/多活、T+1/实时/归档、数据集中处理、BI报表、多维分析等能力。三、阿里云数据库开源发布(一)阿里云数据库产品开源路径阿里云作为全球云原生数据库的领导者,将成为第一家宣布核心的云原生数据库技术进行开源的云厂商,邀请开发者一起共建云原生数据库2.0。作为全球数据库领导者,我们将云原生数据库PolarDB for PG Paxos高可用集群版开源,目前在Github上开源公开访问。在9月份,我们会推出基于HLC混合时钟的高扩展分布式版本,在明年会推出Share Nothing的Sharding和插件化版本,在MySQL生态很早就开源了RDS AliSQL,如今做一个重磅升级,RDS会推出RDS GalaxySQL,之后会推出Paxos高可用性版,然后是云原生的分布式版。(二)阿里云数据库开源计划:打造云原生分布式数据库生态我们看一下具体做哪些事情?上图中是我们为MySQL和PG生态两大社区准备开源的组件。数据库正在加速云化,云原生以及分布式技术正在重塑数据库整个技术栈。阿里云在自身互联网业务和云数据库服务有丰富的实践经验,在高可用、分布式、云原生、存计分离有技术积累。这些技术以组件和系统的方式开放出来,与开源社区一起共建云原生分布式数据库生态。所有开源的组件都采用对开发者最友好的协议,遵循Apache Version 2.0协议,欢迎开发者和我们一起共建全球领先、有中国特色的云原生数据库2.0社区。比如通过开源的PolarDB for PG版本,利用X-Paxos协议,帮助开发者快速实现RPO等于0,兼容及高可用数据库。抛弃传统的主备模式,走向三节点的模式,所有开源的组件都是即插即拔即用,让开发者快速享受到PolarDB的能力,并且基于现有的PG、MySQL生态持续发展,欢迎大家加入云原生数据库2.0的开源社区。源码开放地址:https://github.com/alibaba/PolarDB-for-PostgreSQL【相关阅读】阿里云开源PolarDB数据库,与社区共建云原生分布式数据库生态云原生数据库 2.0:一站式全链路数据管理与服务
2021阿里云峰会——全链路数据服务-数据库分论坛《一站式的云原生在线数据平台DMS助力企业提升数据效能》蔡冬者 阿里云数据库高级产品专家阿里云一站式云原生在线数据平台DMS——覆盖数据生产、集成、加工及应用的一站式数据管理平台,助力客户实现“一站式在线数据管理”、“数据永远在线”,助力企业实现数据价值最大化。《异地多活场景解决方案保障企业数据安全,释放数据价值》李娜娜 阿里云数据库高级解决方案架构师阿里云数据库异地多活解决方案,实现业务多活同时保证数据安全,助力客户业务容灾有保障,业务连续性有保障,支撑业务高速发展,助力流量有效隔离,实现企业降本增效。《数据库/数仓升级解决方案加速企业国产化进程》王庶 阿里云数据库解决方案架构师传统商业数据库规模化替换方案,选择PolarDB-O (兼容Oracle语法)+ ADAM + DTS 最佳产品组合,帮助企业客户降低迁移门槛,加快上云进程,支撑企业业务的爆发式增长以及业务的多元化发展。《云原生数据库Lindorm助力车联网破解数据采集存储难题》许力 阿里云数据库高级产品专家中国车联网用户规模不断攀升,海量车辆管理数据将挑战现有数据采集、存储系统能力,阿里云Lindorm,更适合车联网、物联网、工业互联网的数据库!助力车企破解数据采集存储难题,支撑未来网联汽车数字化升级。《云原生数据库应对互联网业务洪峰实践》王远 阿里云数据库高级解决方案架构师云原生数据库具备的统一的云原生架构、极致的弹性、独立扩展的资源、丰富云原生生态工具等,助力企业轻松应对在线教育约课洪峰、在线营销混合负载、游戏全场景等超大规模并发在线事务。
5月28日,2021阿里云合作伙伴大会上,用友网络与阿里云达成全新技术合作,旗下拳头产品NC Cloud与阿里云自研数据库PolarDB完成相互认证,为大型政企客户提供一种高弹性、高可用的国产化ERP方案,加速政企数字化转型。当前,大型企业数字化转型正全面加速,双方核心产品完成互认证之后可以充分满足大型企业IT系统对云原生架构的需求,支撑企业业务敏捷交付,赋能商业创新和管理变革,助力政企客户实现创新发展。目前,双方共同服务的政企客户覆盖零售、金融、政府、工业等领域。2020年5月,阿里云携手用友网络帮助国内大型医药控股集团公司朗致集团将核心ERP系统和核心数据库迁移上云,数据库直接成本下降20%,运维成本下降50%,同时提升了内外部协同效率,促进业务进一步发展。在本次大会上,阿里云正式发布“云数据库生态加速器计划” ——未来一年,阿里云数据库将投入上亿资金与生态伙伴共建云数据库产业生态。在云数据库行业生态合作方面,阿里云设立“云数据库生态基金”,为合作伙伴提供资源,服务与市场支持,助力行业伙伴加速云数据库行业解决方案的集成能力与项目落地。在云数据库人才培养方面,阿里云设立“云数据库生态创研中心",通过专业阶梯培训与认证体系,并聘请大咖导师授课,助力合作伙伴加速培养出更多优秀的云数据库专才。在云数据库产业发展方面,阿里云设立“云数据库生态委员会”,加速发展云数据库产业生态,助力广大政企、新金融、新零售、运营商、电力、交通、教育、医疗等行业客户的核心系统转向以云技术为核心的云数据库系统架构。首批成员有20家,包括用友网络、神州数码、云和恩墨、石基信息、上海驻云、浩鲸、天玑、蓝凌、数澜科技等。据了解,阿里云是中国“首个”进入政企市场的云厂商,如今已服务26家部委、实现全国31个省区市全覆盖;服务了国家电网、南方电网、中石油、中石化、中国邮政、三大运营商等大型企业。阿里云在中国金融云市场排名第一,为中国工商银行、中国建设银行、中国农业银行、中国邮政储蓄银行、中国人寿、中国太保等头部金融机构提供技术服务。
浦东、闵行、宝山、嘉定是上海新能源汽车分布最多的4个区域;上海私用新能源车工作日平均行驶里程40多公里……目前,上海市新能源汽车数据平台日采集信息已经超过6.6亿条,平台数据采集量已超PB,为全市新能源汽车示范推广、政策制定、车辆分析等提供有效决策依据。据介绍,该平台基于阿里云原生多模数据库Lindorm开发而成,拥有高并发写入和实时检索分析能力,可根据信息采集量的增长进行弹性扩展,数据存储成本非常低。 上海市新能源汽车数据平台由上海市新能源汽车公共数据采集与监测研究中心负责建设并运营,是全国首个、上海市唯一的新能源汽车市级监管平台。截至2021年1月31日,上海市新能源汽车已经接入新能源汽车41.8万辆,涉及车企95家,品牌107个,车型777款,数据存储量突破1个PB,数据规模继续处于全球城市前列。 上海市新能源汽车数据平台每天都会对接入的新能源乘用车和商用车的出行里程、充电时间等多维度数据进行采集,而随着不同品牌及型号车辆的接入量快速增长,数据种类和结构呈现多样化,给平台的运营和维护提出了技术挑战,并且推高了成本。 为保障全上海的新能源车平稳地“跑”在这个平台上,上海市新能源汽车公共数据采集与监测研究中心在关键数据库上选用了阿里云原生多模数据库Lindorm,利用后者的高并发写入、弹性扩展、实时检索分析等能力,以及完善的数据通道保障,确保数据能实时同步至离线计算平台,保障了数据采集、加载、分析、计算链路整体的稳定、高效,以及低成本。 上海市新能源汽车数据平台技术负责人王成名表示,阿里云Lindorm数据库的引入使得数据采集入库性能提升3倍以上、成本降低20%以上;对于采集点及采集频率变更带来的流量突增也可以从容应对,很好地支撑了业务快速发展。 据了解, 阿里云原生多模数据库Lindorm支持海量数据的高性能、高吞吐、低成本存储与检索分析,支持云原生存储计算分离技术,具备极致弹性能力,历经了阿里巴巴经济体10多年磨练,支撑了淘宝、天猫、支付宝、菜鸟网络等核心业务。去年9月,Lindorm数据库正式对外提供服务,帮助汽车、金融、工业物联网等领域的客户根据业务规模弹性伸缩,匹配业务的快速增长。
作者:杨哲超《Gartner 2021十大数据和分析趋势》中指出,图技术使一切产生关联,预测到2025年图技术在数据和分析创新中的占比将从2021年的10%上升到80%。该技术将促进整个企业机构的快速决策。从金融行业角度看,在中国人民银行印发《金融科技(FinTech) 发展规划(2019—2021年)》 等政策驱动下,通过构建金融知识图谱基于多维数据源做决策,可以有效带动金融机构降本增效。图数据库GDB是阿里云自主研发的图数据库产品,经历阿里巴巴集团内丰富的应用场景打磨,具备了丰富的最佳实践。图数据库GDB在2020年进入Forrester图数据平台竞争者象限,也是国内图数据库产品首次入选。阿里云图数据库GDB在满足高可靠性、高性能的同时,也兼顾了低成本的特性,产品使用、运维成本仅为国外图数据库产品的40%。我们将自动特征工程、自动机器学习等AI能力下沉到图数据库引擎中,形成阿里云图智能平台,让整个图模型的构建、分析、发布过程自然连贯。阿里云图智能平台在金融行业已经帮助银行、保险等领域客户构建了金融风控、商品推荐、循环担保检测、异常指标监控、违规团伙挖掘等场景,通过穿透行业应用场景,帮助客户基于多维数据做出精准决策。传统的金融风控模型,能够汇集各个数据源的属性特征信息,但是比较难挖掘数据源之间的深度关联关系。要深度并且快速的挖掘海量数据的关联特征,则会面临非常大的技术挑战。图技术的意义在于将信息升维,而机器学习技术的意义在于对数据规律进行总结。通过图表示学习技术,提取金融知识图谱中的拓扑信息特征,并通过图自动特征工程模块,自动构建特征作为风控模型的输入条件参与模型训练。通过自动机器学习模块,帮助金融机构挑选、调试、集成各个机器学习模型,实现更高精度的风控模型。华瑞银行于2020年正式引入阿里云图数据库GDB,通过对数据资产进行深度关联关系分析,进一步提升风险识别能力。通过打造一套企业级图分析平台,实现了对智慧供应链、航旅消费贷款等业务的智能风险管控。通过阿里云图数据库GDB集成的自动机器学习组件,华瑞银行大幅降低了风控模型研发周期,并在截止目前的实践中检测到6个诈骗团伙,有效防控了业务风险。图数据库的应用可以在高度关联的数据中挖掘数据源间的深度关联关系,通过理解和分析图将信息升维,进而帮助企业获取洞察,这将成为企业未来核心的竞争力。我们也会不断完善我们的图数据库产品和服务,探求用户真正的需求,以帮助更多企业和开发者获得洞察力和竞争优势。直播回顾:https://yqh.aliyun.com/live/detail/23882
作者:天穆 一、基础:数据分布(一)扩展性:scale up & scale out分布式系统里常见的扩展性问题有两种:scale up 和 scale out。拿数据存储举例,如果一块盘存不下,换一个更大的盘,从1t到4t到8t到更大的硬盘,但是这种方式很容易触及到系统的容量上限。因为不可能把一块盘做的非常大,所以现在业界最常用的扩展性的方式是通过scale out方式,一块硬盘不够,用更多的盘,当一台机器盘的数量达到上限,用更多的机器组成集群,如果集群不够了,用更多的集群组成联邦。再往前一步可以用一个机房,用更多机房,甚至全球分布,扩展整个系统的容量。(二)基本问题:数据分布策略做scale out的时候必然会面临一个问题,当存储数据的节点或盘变多了以后,必须要解决数据怎么在硬盘和机器上分布问题,也就是数据分布策略。理解数据分布策略,可以从读、写两个方面开始,比如写的时候一个请求或者要写一行数据,要写到哪个机器上、写到哪个盘上;从读的角度来讲,一个请求读取数据,不可能访问整个集群的所有盘或者所有机器,这样读取这行数据太慢了,所以必须有很好的算法或者是分布策略,能够让读、写的请求能够一次到达目标。最多可以有多一条的方式,但是最终期待的是一条就能获取。讨论具体的分布策略之前,要明白设计分布策略的目标,第一是:负载均衡;第二是:线性扩展。负载均衡:是希望在写的时候,能够均匀的写到每一台机器上、每一块盘上,整体是均匀的,不会某些盘或者某些机器成为写热点,也不会因为某些机器写的太多,水位很高,其他机器都空着。从读的角度来讲也一样,希望读能够很均匀的分布在整个集群上,不会导致热点和倾斜。达到负载均衡以后,还要有线性扩展,比如集群扩缩容、盘坏了要下盘、还要加新的盘上来,这个时候希望无论机器怎么扩容,盘怎么增减,整个系统仍然处于负载均衡的状态。只要保证这一点,当系统盘增加了或者机器增加的时候,整个系统仍然能够处于线性扩容的关系,机器多了能够存的数据就多了,能承载的吞吐也变多了,是整个数据分布的目标。总结:负载均衡:写:均匀的写到集群的每一台机器上,每一块盘上;读:均匀的从集群的每台机器上、每块盘上读数据。线性扩展:机器扩缩容,磁盘上下线,系统始终/最终处于负载均衡状态;系统容量、吞吐与系统资源成正比(线性关系)。(三)两种分布策略目前业界有两种比较典型的分布策略,一种是顺序分布,一种是Hash分布。顺序分布:根据用户定义组件,让数据从最小的主键开始,依次往后排,如图例所示:user_id和ts是联合的主键,先按user_id 1、2、3、4、5排序,排完之后,再按ts进行排序。顺序分布是把整个表做拆分,例如:把user_id 等于1的分到一个Region里面,user_idt等于2、3的分到一个Region里面。Hash公布:需要有一个Hash算法,选一个分区键,经过算法得到所在机器的名字。常见的一种算法就是取模“分区 = user_id % 机器数”,可以拿user_id模上这个机器数,得到所在的机器。如图例所示,假设有3台机器,“%3 = 0”在第一台机器上,“%3 = 1”在第二台机器上,以此类推,是一种基于规则的分布。1)顺序分布:目前比较典型的产品有hbase,tidb。顺序分布的缺点:第一,是比较依赖主键的值,如果user_id分布不均匀,因为通常user_id是顺序分配的,比如有1、2、3、4、5、6、7、8、9、10,user_id更大的时候,热度会比较高,user_id小的时候,热度会比较低。会产生一种问题,越往表的尾部越热,头部的可能就会冷一点,会产生数据倾斜以及访问倾斜,需要通过额外设计或人工介入调整。第二,相同前缀的数据也可能会分开,比如上图所示的“user_id = 3” 的数据,可能会被分到两个Region上,当访问等于3的所有数据时,必然会涉及到两次Region。第三,因为有强大的干预能力,需要很复杂的路由表机制。顺序分布优点:第一,一个Region包含哪些数据,通过路由表决定,比如HBase的meta表,tidb里面是PD。Region可以灵活分布,比如让user_id=1的数据,在Region里面拆分,也可以把user_id=1、2、3合并。第二, Region在哪台机器可以人工指定,比如可以让Region 1单独分一台机器,Region 1、3共享另外一台机器,在生产上,尤其是在有数据热点场景下,有人工介入干预能力。2)Hash分布:是基于规则的分布,选取分区键,user_id根据分布算法或者Hash算法,得到所在的机器。比较典型的代表产品有cassandra、dynamodb。跟传统关系数据库里面的分库分表非常类似,因为没有外部依赖,所以比较简单。缺点第一,是在做扩缩容的时候,需要对很多的数据进行搬迁,所以需要一致性hash方案。第二,是分区无法灵活调整,因为是基于规则的,当数据基于分区键算好分区之后,所在的机器就确定了,不能灵活调整。第三,有数据倾斜问题,比如有超大分区,比如user_id=1是个超大的用户,记录非常多,会产生热点的问题,user_id=1的所有的数据强制分布在某一台机器上,数据特别多的话,这台机器很快会达到上限。(四)Hash分布:分区键的选择如图所示,基于直觉的方式是选user_id作为分区键,为什么不能用把TS也放进去?假设把TS放进去,user_id和TS一起算Hash,势必会产生一种情况,就是user_id = 3的数据,可能分布在整个集群的不同位置,做查询的时候where user_id=3,等于3的所有数据会面临查很多分区。而且 user_id=3下面的TS,没法知道有多少,是一个不可预测的值,这时涉及到跨分区的查询,这种查询会退化成全面表扫描,是不能接受的。选择分区键要结合查询的场景,选择合适的分区键,尽量避免或者一定要避免跨分区的查询。比如where user_id>3,这种是没办法直接高效的定位查询,一定要扫全表;但是where user_id in (3, 6, 9, ...)这种,是可以拆分成多个请求逐个查询,因为是可枚举的。二、Cassandra的数据模型(一)Partition Key,Clustering keyCassandra数据模型里ts叫聚类键,user_id叫分区键,分区键和聚类键加一起,构成表的主键,主键要求唯一性。比如下图所示的表里面,user_id和TS放到一起一定要全局唯一,如果400有两个,就是冲突的数据。对于分区键和聚类键,可以有很多个,可以很多个Key作为分区键,也可以有很多Key作为聚类键。除了主键之外,Cassandra里面还有非主键,或者叫属性列或者叫数据列,比如location存具体数据,不参与数据排序。(二)联合主键与前缀匹配key比较多的场景称为联合主键或,联合主键如何排序以及查询?如图例所示的场景,分区键是city,有两个聚类键,一个是last_name,一个是first_name。因为分区间键不参与排序,当我们做Hash分布的时候,分区键在整个表里面随机分布,但是在某一个特定的分区键下面,clustering key是顺序分布的。图例中是按last_name前缀排序, p排在前面,w排在后面,在last_name相同的时候,再排下一个列, potter相同的时候, Harry排在前面,James排在后面,是这种排序规则。因为是这样排的,所以在查的时候,要从左到右依次去查,有以下几种情况:1.where city = 'hangzhou' and last_name = 'Potter',前缀扫描;2. where city = 'hangzhou' and last_name = 'Potter' and first_name = 'James',单行读;这两种可以很高效的完成,因为查询的扫描范围和结果集一样大,有一些场景不能很好的支持,如:3. where city = 'hangzhou' and first_name = 'Harry',跳过了last_name列,直接查first_name,这种查询first_name不能够用于圈定扫描范围,会变成一个filter,直接对每一行数据过滤,查询的扫描范围是city = 'hangzhou'的所有数据,为每一行数据基于first_name = 'Harry'做过滤,假设'hangzhou'是一个很大的Partition Key,数据量很多,这个查询会非常低效。4. where city >= 'hangzhou',当city >= 'hangzhou'的时候,就是一个跨分区键的查询,也不能被支持。5.where city = 'hangzhou' and last_name >='P' and first_name = 'James',first_name进入filter。6.where city = 'hangzhou' and last_name >='P' and first_name = 'Ron';这两个查询从表上看,James排在前面,Ron排在后面,但是事实上last_name是范围查询,first_name字段变成filter来扫,而不是用来缩小查询范围,所以说5和6两个语句的扫描范围一样。(三)逻辑分区:一组具有相同前缀的行一个Partition Key的值代表一个分区,但本质上来讲,并不是物理上的分区,比如一块盘、一个机器,有物理的实体跟其对应,但是分区不会有一个文件或者实体跟其对应,分区是一种逻辑概念。在这里面把分区定义成是一组具有相同前缀的行,前缀是Partition Key,如下图所示,Partition Key等于杭州,杭州这两行数据就是一个分区,等于上海的就是另外一个分区,这种就是叫逻辑分区。在物理上没有一个有力度的实体跟它对应,所以它的数量可以无穷大,这里的city是一个字符串,可以有无穷多的数据组合,city分区键可以无穷无尽的分区。分区键:值域可能非常大(比如long),分区键的每一个值,都代表了一个"分区";"分区"的数量可能会非常大;"分区"的本质:一组具有相同前缀的行,"前缀"即分区键的值;所有的分区都是"逻辑分区";线性扩展。线性扩展:是指分区根据一致性Hash算法划分到某一个机器上,一台机器可以服务很多分区,机器数量增加之后,能够承载的分区数量也会相应的增加,能够获得线性扩展能力。除非产生了一些巨大的分区,这些分区把一些机器占满了,这种情况下线性扩展能力是受限的。三、范式与反范式设计(一)范式化与反范式化范式化:是传统关系型数据库要求的概念,数据库刚出现的时候,盘都比较贵,存储空间都比较贵,数据库的表设计必须要满足降低数据冗余度的原则,需要范式化的设计,减少数据冗余度。另外需要增加数据的一致性校验,比如有很多表,一些表来存买家,一些表存卖家,一些表来存订单,通过主键和外键之间的关系进行关联,通过外建描述数据的完整性,也是范式化设计的一部分。这种通常是用于关系数据库的设计,而且能够很好的解决复杂业务的设计,通过一整套的方法论,业务模型进行抽象。在NoSQL系统里面,强调反范式化的设计,通过增加冗余度换取更好的性能。带来的一个问题就是数据冗余,存储空间开销上升,但是现在存储越来越便宜了,成本并没有上升很多。范式化(Normalization)目的:➢ 降低数据冗余度;➢ 增加数据的完整性(如外键)。• 通常用于关系型数据库的设计反范式化(Denormalization)增加冗余度,用空间换时间;数据在多个地方都有,存在一致性问题。(二)示例下图所示,是一个部门和部门下的雇员之间的表设计,比如有个department表,存 depId和名字,还有一个user的表,来存每一个人和userId,要描述一个部门还有哪些人的时候,需要把这两张表关联起来。记录表的depId和userId之间的关系,当查一个部门有哪些人的时候,要先扫这个部门的人员表,得到这个部门的userId信息,比如查depId=2,得到的userId是1和2,这时转user表拿到1和2两个ID的用户名,同时拿depId=2的depName,才能获取depName是Math,一次查询,需要有三张表,这是范式化设计。反范式化设计,就用一张表来代替。如下图所示,depName和userName直接存在一起,查询一次搞定。缺点是名字重复存在,depName内容也重复存在,数据冗余度增加。另外,当修改名字的时候,要改很多地方。(三)反范式化优缺点优点:多个表的数据统一到一张表里;JOIN不是必须的(大部分NoSQL也不支持join),查询更高效;采用宽表设计,从业务设计来讲业务更简洁,查询更简洁,整个业务模式会更清晰,SQL会更简单,维护性会更好;当业务出现问题的时候,调查问题的效率得到相应的提升。缺点:冗余存储,空间开销增加。但是因为现在存储变便宜了,所以说成本没有增加。数据冗余之后,带来的一致性的问题,比如只有一张表,Math存了两次,但是假设当有很多张表的时候,都有Math字段,会面临在多张表之间处理一致性问题。(四)原则反范式化设计的基本原则是:根据读写模式来设计表,设计主键;使用分区键来规划数据分布:一次查询需要的数据,尽可能在一个分区里;使用聚类键来保证数据在分区内的唯一性,并控制结果集中的数据的排序(ASC/DESC);设计好主键以后,使用非主键列来记录额外信息。这个时候非主键包含了很多业务字段,比如订单存储,希望其包含订单金额、订单ID、买家名字、卖家信息、商品信息等,是一张大宽表,可以通过一次或者是少量的查询,得到需要的所有数据,避免join,提升整个系统的查询性能。反范式化设计:将原本需要通过join得到的数据,都包含进来。四、典型场景分析(一)典型场景一:物流详情场景描述:电商物流订单,每个订单会经历多轮中转最后达到用户手中。每一次中转会产生一个事件,比如已揽收、装车、到达xx中转站、派送中、已签收。需要记录全网所有物流订单的状态变化,为用户提供订变更记录的查询能力。订单数据量极大,可能有上百亿;体量不能影响读写性能。场景抽象:写:记录一个订单的一次状态变更。读:读取一个订单最近N条记录;读取一个订单的全部记录。如下图所示:表中有两列主键,orderId指订单的ID,是分区键;gmtCreated指事件产生的时间,是聚类键;非主键列detail指的是一次事件的信息,比如已揽收或到达的状态,是数据列。1)物流详情:高表设计"高表"设计:行不断增加,一行描述一个订单的一个事件。一个订单的所有数据,由连续的一组行来描述(一个逻辑分区)。查一个订单的所有数据时,事实是查一组具有相同前缀的行,就是查一个分区的数据。优缺点:单个分区键下的key数量可以很多;过多的数据将导致宽分区的产生,应避免;无论数据量多大,单次next()的RT可控:流式ResultSet。高表设计可以避免很大的行产生,因为所有的变化都产生在行里面,不是产生在列里面。可以很好的解决orderId的问题,如果某一个订单数据量特别多的时候,会产生宽分区,需要避免。常规做法是增加维度,拆开分到不同的分区里面。高表设计无论数据量多大,单次读下一行数据的时间不变,有流式ResultSet能力,一次加载一部分数据。2)物流详情:宽表设计(不推荐)宽表设计:用一行来描述一个订单的所有事件,每一列是一个事件,用事件的发生事件作列名;也可将所有事件encode到一个列里。宽表设计,用一行来描述一个订单的所有事件,每一次事件通过一列来描述。如上图所示,把时间作为列名,每一个列记录了一个订单的某一次事件。也可以把后面的列合到一起,变成一个列。优缺点:单行读;读一个订单的所有的数据时,只做单行读,业务会更简单。无法预知列名,列数量,每一行的列都可能不一样,强依赖schema-free能力;只能读所有数据,不容易实现topN读取;超大行风险:个别行的列特别多;会影响性能。所以在物流详情场景下,不推荐宽表设计,建议用高表设计。(二)典型场景二:时序类---监控系统如下图所示,是CPU监控,对整个集群的多台机器做 CPU指标的监控,CPU指标有user、system、idle等不同类型,还有很多主机如host,一台机器的某一个CPU user指标有很多点位,比如这里面192.168.1.1机器在CPU type里面产生了两个点,一个是30,一个是40,这个是时间线。这个表里面列出来的是监控系统里面需要的数据,在这个数据场景下,怎么选择分区键、聚类键以及监控数据的存储,可以有以下几种选择:分区键怎么选:metric;metric + host;metric + host + type;metric + type + host。监控数据怎么存?一行一个点;一行存所有点;一行存有限个点:如1分钟/1小时内产生的点。1)分区键:只使用metric只使用metric作为分区键,意味着分区只有 CPU,如果加入网络、磁盘,每一个metric是一个分区,意味着所有被监控的对象,所有机器的所有数据,都在一个分区下面,很容易触发单分区限制。因为有变量和不变量的问题, CPU指标本身是不变量,即使未来新增指标,通常也是低频事件。但被监控的机器是变量,会不断的增加,可能数量巨大(比如物流订单的数量)单分区限制:所有机器的指标都聚集在一个分区里,被监控的机器可能无限增长,但单机的承受能力不会线性增长。业务侧:识别变量和不变量cpu指标本身是不变量,即使未来新增指标,通常也是低频事件;被监控的机器是变量,会不断的增加,可能数量巨大(比如物流订单的数量)。2)分区键:metric + hostmetric + host策略可以很好的控制了单分区的数据量,不会出现宽分区。因为除了 host以外,没有其他维度大幅度的变化量,如下图所示,type和TS都不会太大变化,TS本质上是作为host下面的一个子集存在,比如在做一个查询的时候,要查某一台机器的某一段时间范围的 CPU指标,肯定希望这台机器的数据都排在一起,用一个查询搞定,所以TS不能够放在分区键里面。这个设计的缺点是,并发读写同一个机器的cpu指标,请求都路由给同一台机器,不利于并发。优缺点总结:很好的控制了单分区的数据量,不会出现宽分区;单机的所有类别的cpu指标都在一个分区里;并发读写同一个机器的cpu指标,请求都路由给同一台机器,不利于并发。3)分区键:metric + host + typemetric + host + type策略,把指标类别也加到分区键里面,可以很好的适配并发查询模式,提高整个集群的吞吐。因为 metric + host + type整体作为分区键,只有三个全相等的时候,才会分在一个分区里面。另外一种方式是host和type交换位置,其对采用一致性hash的cassandra来说,没有区别。但是对于顺序分布来讲,可能会有一点区别,因为改变了key的值域范围,可能导致值变少了,这个时候会产生聚合效应,可能导致一些潜在的问题。总结:同一台机器的不同cpu类别的指标在不同的分区里,很好的支持并发访问;host和type的顺序对采用一致性hash的cassandra来说,没有区别。4)优化:type合并至metric中metric + host + type策略还有另一个优化,type合并至metric中,如下图所示,是时序数据库建模时的特点,type在时序数据库里面叫 tag,标签的意思,可以有很多标签,比如IP是一种标签,所在机房可能也是一个标签,甚至可以有业务的标签, CPU的 type也是一种标签,这种场景下,可以把标签合到 metric中。因为type是可枚举的,只有几种,不会增加也不会减少。合并过去之后,减少了列,存储的列变少以后,可以提高性能,减少开销,仅数据量较大时,体量小的时候看不出来。还有一种场景,储存的是进程ID,这种时候没办法合并,比如监控每一个进程的网络流量,这个时候进程ID没法合到 Metric里面,因为进程ID不可预估,而且不可枚举。5)数据点位的存储:宽表 or 高表数据点位的存储指的是每个时间点metric的值,依然可以有高表和宽表的设计。如下图所示,高表设计,把TS放到Clustering key里面,一行存储一个数据点。高表设计因为一行只有一列,容易扩展多值监控,对于像经纬度,一个点位有两个值,在高表设计里面很容易扩展。宽表设计,一行存储这个某个机器的某个指标的所有点,这种宽表设计还是会存在单行上线的问题,列多了以后会有性能问题。融合设计,一行记录有限个点,比如存1分钟采集的所有的点或者1小时的点,结合高表和宽表的设计,粒度选择合适的时候,得到最优的性能,而且能够配合整个系统内部机制,如cache,bloomfilter等。当然这些例子,在时序场景下面比较简单,能够解决简单业务的时序设计问题,对于业界的实际数据库来讲,但生产使用时有很多考量因素。总结:高表设计:一行存储一个数据点,如上表所示;容易扩展多值监控:如经纬度;宽表设计:一行存储这个某个机器的某个指标的所有点;单行上限;融合设计:一行记录有限个点:如1分钟,1小时内采集到的所有点;粒度选择:由指标的采集频率决定,以控制单行的列数;适当的控制行数,可以配合一些内部优化机制:如cache,bloomfilter等;注意:时序建模的原理很简单,但生产使用时有很多考量因素,各TSDB都有不同的侧重点。应根据业务实际需要选择合适的模型,没有银弹(三)常见误区1)常见误区一:分页查询常见的分页查询误区,从Mexico[MOU23] 过来的用户很容易遇到一个问题拆请求,如下图所示,做一个大表的扫描,user id=3的数据可能非常多,为了避免一次返回太多的数据,需要对请求进行拆分。比如按TS进行分页,先扫500的,再扫下500,再扫下500,一次一次扫。在 MySQL里面这样做是合理的,因为RPC一次返回所有记录。但是在Cassandra里面没必要,因为Cassandra用的是一种流式ResultSet方式,在系统设计层面,已经考虑到了不断往下next的情况,已经做了请求拆分。比如第一次next的时候,会新加载500行的数据,等到这500行数据消化完了,再下一次next,会加载下500行数据,如此往复,直到所有结果集返回。总结:流式ResultSet:为了避免单次RPC返回过多数据导致RT过高,CQL driver会自动对请求进行拆分;第一次next()调用会从服务端load N行数据,之后的N-1次next()只从内存消费数据;下一次next()会再加载N行数据到客户端,如此往复,直到所有结果集返回。参见:https://docs.datastax.com/en/developer/java-driver/3.2/manual/paging/。结论:不要为了拆分大请求而进行分页。2)常见误区二:修改主键场景1:修改主键的schema,在MySQL里面可以,但在Cassandra里面不允许,只能重新建表。场景2:修改主键的值,本身就是错误的说法。考虑java的map的key, key能修改吗?修改key的逻辑就是删除老key,写入新key。从数据库角度来讲,没有修改主建操作,只有删除、添加这两种操作,非主键可以修改。
作者:玄陵 一、Cassandra简单介绍Cassandra的历史Cassandra = Dynamo(distributed architecture ) + Bigtable(data format)。Cassandra最开始是Apache的顶级开源项目,他开源的数据库历史是来自于2篇论文:Dynamo(distributed architecture ),07年Amazon(亚马逊)发表,Cassandra吸取了Dynamo的分布式架构;Bigtable(data format),由Google(谷歌)发表,Cassandra吸取了Bigtable的部分数据组织格式,及表的多种组织格式。由这2篇论文衍生出开源Cassandra。 Cassandra的总体架构分布式层面Apache Cassandra的特性:线性扩展:它有去中心化的架构;高可用:它有多副本,副本读写策略能够保证可用性较高;高性能:主要取决于它底层的LSM Tree Storage;灵活应用:通过CQL语言去访问Cassandra,是wide column的宽表模型。 DHT环(图左): 在环上有5个节点,每个节点把环进行划分,在Cassandra设计里,每一个节点对应负责一部分的数据范围,它能保证所有数据映射到环上最小到最大的范围,每个节点将DHT环的范围进行均衡切分,切分后能保证每个节点负责范围相对均匀,任何一条数据来到这条环上,都可以做到相对均匀的选择它对应的节点。单个节点是由LSM Tree的引擎构成,他有自己的Memtable,有自己的 WAL Commit Log有自己的一个SStable,可以保证它的写、读的性能比较好。环外的Clients,输入一条SQL,选择任意一个Cassandra服务端的节点去进行连接,然后在对应的节点上可以基于你的CQL编译出来一个结果, 选择你的数据需要落在的副本节点上,是它一次在分布式层面的架构写入的流程。Cassandra的初步认识单节点层面Cassandra在单个节点实际组成的模块,在图的左边有ABCDE5个节点,然后去分解B节点对应的组成模块,最上层是由网络层组成的,网络层主要的是实现了用户请求过来的CQL语句,以及对 CQL的Binary协议请求的实现。当CQL请求过来经过我们的网络层,网络层的Server把这个对应的请求转发到下面会有一个CQL的Parser(Cassandra内部的对SQL语言进行解析的模块,这个模块是把对应的SQL类string语言转换成对应的statement,转换成在Cassandra内部可进行请求编译的结果)。还有meta的管理的模块,Cassandra所有的录入表信息以及我们的 scheme信息等管理的模块,底层 Commitlog/sstable/index等管理的模块,最下层是我们的一个文件系统。Cassandra的架构,它底层是直接跟Linux、或者windows平台对应的底层的local file system进行交互的,在看网络层下面的Gossip,是让集群中的ABCDE这5个节点相互的感知到彼此,路由信息都是由Gossip感知维护的,也能够各个节点的探活状态。再下层就是DHT环,在后面个replication的management,是对副本的管理的策略。通过这两幅图,我们可以看到Cassandra在整体的分布式层面,以及在单节点层面,它请求的或者说组织的一个模块情况。通过这两幅图,我们可以大概Cassandra架构有一定了解,它是分布式的数据库、区中心化的数据库,单节点是由lsm区域进行构成的引擎,每个节点它最上层是有一个网络层,然后下面会直接把所有的输入CQL进行编译成结果。 二、Cassandra CQL介绍第二部分详细介绍CQL对应一些语法,通过了解到对应的语法之后,我们可以如何去使用Cassandra,进行简单的读和写、建表等基本操作有一个初步的认识。Cassandra为了方便用户去操作或请求它自己所实现的类似于Query Language,自定义的一个轻的 native的 SQL的语言。可以通过社区datastax或Netflix再或任何其他的以及开源的一个driver去访问我们的Cassandra,通过我们的CQL语言去访问Cassandra,原生支持:java/c++/python/nodejs/php/c#/go/ruby等多语言去访问Cassandra,同时也支持spark 访问。通过HBase KV api VS CQL 写一条数据对比可以看的,缩短了代码行数,直接使用 SQL语言进行一个操作,比较方便。CQL总体介绍把这个分三块Data type、DDL/DML/ACL、INDEX/MV/UDF/IDA:Data type包含:基础数据类型、集合数据类型、UDT(User-defined-Type)。DDL/DML/ACL包含:DDL: CREATE/DROP、DML:SELECT/UPDATE/DELET/INSERT、ACL:ROLE/USER/PASSWORD/…。INDEX/MV/UDF/IDA包含:Secondary index/SaSI Index、Materialized View、UDF/UDA。 CQL几个概念在介绍CQL语言之前,要先去了解几个比较基本的概念,Cassandra自己定义的意义和备注。Partition key是Cassandra特有的,表示分区键,可以确定数据存放的节点。落在哪个节点上面去,数据它属于哪个节点或者哪几个副本都是它决定。cql_type 是由 native_type | collections | udt | tuple |custom_type 类型组成的。数据结构丰富,便于业务直接使用,无需封装,eg:inet,用户使用ip直接使用无需转换。要强调的是Cassandra的Type还定义在别的数据库不常见的一些类型,比方说:Inet就是定义的 IP的数据类型,如果用户要存储某个IP类型,那么它就直接可以用Inet这个类型去存储。所以我们的所有的类型是在建表的时候,通过定义某一个列的名字后,会根据跟上列的对应的类型。Collecion Type是一个集合数据类型,其中map, set,list,是别的数据库里面不常见的,但是是日常用的比较多的类型,Collecion Type可以基于自己的业务场景去操作的。UDT:User Defined Type就是组合了多种类型成包括成一种新的类型,并支持:CREATE /ALTER /DROP语法。CQL DDLDDL的一些使用的方式CREATE KEYSPACE、USE KEYSPACE 、ALTER KEYSPACE、DROP KEYSPACE、 DESCRIBE KEYSPACE;CREATE TABLE 、ALTER TABLE、 DROP TABLE、 DESCRIBE TABLE、 TRUNCATE ,建表,键值空间、修改表、修改键值空间、删除表、删除键值空间、还有清空表。定义KEYSPACE实际上会定义两个东西,第一个Replication,下面会有两个子项目,一个是class,class也分两种,一种是Simple strategy和NetworkTopologyStrategy ,主要定义了的副本的摆放策略,就是在Cassandra执行里面,一行数据会放在多个副本上面,多个副本的摆放策略这里定义它是顺序的还是逆序的,还是说以某种方式摆放的;副本数Replication factor,会定义一行数据写进来,在分布式的节点上面有几个节点会放,副本因子是多少,举例:CREATE KEYSPACE KS1 WITH replicate application,它的class simple strategy它的摆放策略是1;CREATE KEYSPACE KS2, class摆放策略是networkToplogyStrategy,因为使用的NetworkToplogy,这里需要定义一个对应的跟NetworkToplogy相关的叫DC的概念,如图可见定义的 dc1的副本数是1,如果用户有多DC概念,后面可以跟上DC2、DC3、DC4以及在各个DC下面的一个摆放的副本因子。CQL的DDL对Table,在创建一个table的时候,必须要有一些对应的信息:Primary key : 必须定义,由partition key 和 cluster key组成Partition key :必须定义,确定数据的摆放物理位置Cluster key:可以不定义,确定数据在partition 下的摆放情况default_time_to_live: 表级别ttl;如果一个数据希望在所有表下面的数据都有TTL的话 ,就用此定义,单位是秒。Compaction策略:STCS/LCS/TWCS; Compression压缩策略:SNAPPY/LZ4;举例:CREATE对应TABLE,定义了一个 PK的类型是text,CK类型是text,regularcn类型也是text;定义的主键是由PK和CK组成的,这里PK就是partition key,然后CK是 cluster key;对应的参数,如default_time_to_live是 100秒;Compaction策略是 SizeTieredCompactionStrategy,还有一些阈值;Compression用定义的是lz4,如上图下面 tb是没有参数的,如果没有参数它会定义默认的设置一些参数。CQL DMLDML : SELECT / INSERT / UPDATE/ DELETE /BATCH 1、SELECT• 支持获取指定列以及通配符操作;• 支持LIMIT/PARTITION LIMIT/ ORDE BY /GOUP BY ; • 支持native function 处理操作: count、max、min、sum、avg等 • 支持JSON;• 其他多数丰富操作; 举例如图所示SELECT*from这个表,包括count、limit、 some select等数据,大概是一些对应的操作以及select执行 select数据,以Jason的方式输出,这里都有对应的一些例子可以做参考。2、INSERT• 支持常规写数据,primary key 必须指定数据 • 支持写入JSON数据;• 支持cell 以及row TTL; • 保证线性一致性:IF NOT EXIST3、UPDATE• 需要指定primary key,主键列必须要指定• 如果数据存在就更新,不存在则写入 4、DELETE• 支持行、列级别删除举例如图所示 UPDATE对应的数据列或者行数据,使用了一个TTL,UPDATE ks. Tb USING TTL SET age= 20 WHERE name,这个nameprimary key;对应的delete这一行数据,把WHERE name = xl行数据全删掉。5、BATCH• 语法:BEIGIN BATCH开始,以 APPLY BATCH 结束,中间可包含多条INSERT/UPDATE/DELETE;• 支持LOGGED/UNLOGGED BATCH 2种模式。LOGGED BATCH 保证batch数据最终全被写入• 提高写入吞吐;举例如图所示分了两条INSERT一条update以及一条delete,最后以看到select数据是符合需求的。CQL ACL• Role级别操作,支持账户密码,role资源操作;• 各种资源级别的鉴权; • 常见grant 以及revoke操作(role资源和permissions);举例如图所示,首先是以Cassandra用户user去登录,登录时创建了一个ROLE,CREATE ROLE JACK可以 log in,然后password是super user,这是CREATE ROLE的语法里面需要定义的一些操作,第二次以jack用户去 log in的时候,执行了LISE ROLES ,可以看到Cassandra和Jack两个用户对应的permission,执行了一些grant和revoke操作,通可以通过grant、revoke去操作,让 Jack对某个KEYSPACE或select的操作,对 Jack可以决定是否有效。CQL INDEXINDEX : Secondary Index/ SASI / Materialized View1、Secondary Index• Local Index,索引表数据和数据表数据共存;适用于基数适中的数据表列; • 可以指定索引名,若不指定则自动生成; • Counter列不支持二级索引;• 频繁删除以及update的列不推荐使用;• 支持多重索引查询,支持集合数据类型上构建索引; 举例如图所示可以看到例子里面建了对这个表上面两个列对应的进行了构建索引,查询的时候也基于对应的列去查询,以空间换时间的一个方式。2、SASI : Sstable Attached Secondary Index• Local Index的一种,支持较多索引模式:prefix,contains,sparse,支持轻搜索功能;• 对字符串支持的prefix 和 contains功能支持,轻模糊匹配,like ’%key‘, like ‘%key%’• 使用analyzer可以对某个列的文本数据做词干分析;• 丰富的索引构建选项:大小写敏感,索引模式,是否使用anlyzer; • 不支持collection类型;• 实验功能,不推荐生产使用; 例一如图所示,这个例子也对应地构建了一个对应的索引,使用了一个SASI的索引,支持一些对应的操作,可以看到是用like一个模糊匹配匹配出对应的一些数据。例二如图所示,在构建索引的时候指定对应的anlyzer,可以通过anlyzer去做一些数据的分词,但允许的数据量不是特别大,如果真正需要单个类的数据量比较大的情况,还是建议用一些搜索引擎。3、Materialized View• 全局索引表,数据基于数据表构建; • 适用于数据表基数较高的数据列; • 需要指定新的primary key;举例如图所示CREATE Materialized View ,新Materialized View是select所有的数据,把所有的数据都当放到Materialized View里面,类似于全一种新的一个表。
作者:仁威一、车联网市场及趋势浅析(一)什么是车联网未来智能互联化是一个不可逆转的趋势,生活中常用的物品都在逐渐联网化,例如洗衣机、电视、智能家居等设备,通过联网可以用手机控制,甚至于智能穿戴的设备,衣服、眼镜、鞋都有逐渐联网的趋势。手机作为第一个智能终端,它已经从简单的通话工具演变成了可以社交,查看新闻,甚至办公的工具。汽车也是一样,现在汽车正在逐渐联网,成为第二个智能终端。汽车作为智能终端接入网络之后,利用车载设备实现智能化交通、移动金融服务、购物、车家互控等场景逐渐成为现实。这些场景下会产生越来越多的数据,比如,现在可以从手机端直接连接汽车4S店的服务。甚至汽车跟手机的终端打通,已经可以互联互通互动。也可以从汽车端直接控制家电,比如在汽车上有些高端车,可以直接车家互动,在车里面控制家里的空调、洗衣机等。除了这些,未来娱乐内容等方面的数据,以及车上产生的状态轨迹数据都会逐渐向云端存储,而5G的发展无疑加速了进程。(二)市场趋势随着5G网络的建设,传统的移动互联网市场已经饱和,一部分企业开始瞄准5G带来的汽车互联网机遇,探索5G所驱动的汽车革命;同时,汽车企业在过去几年时间提升了认识,逐步开始实践数字化转型,部分企业已经初见成效。之所以5G可以给汽车互联网带来发展机遇,主要原因5G支撑了云计算在汽车上的应用,这使得云语音等服务成为可能。与其他设备的移动互联网应用不同,汽车互联网的应用有一定难度,主要是汽车企业主动性不强,完全外部安装的设备不容易发挥作用,利用5G带来的云平台接入,给汽车企业带来了主动数字化转型的动力,那么汽车互联网的发展会大大加快。汽车联网可以划分4个阶段:第一阶段:2G/3G/4G联网的时候,主要是解决“通”的问题,能看到汽车的状态,比如现在租车或网约车汽车的状态与位置,用2G/3G/4G数据传就足够了。第二阶段:带宽逐渐增加之后,就可以拿到更多车上产生的数据,做大联网的数据。但车联网的大数据分析,能够为保险行业或者其他金融行业提供车主驾驶行为的数据判断。第三阶段:V2X、5G、云端互联,带宽已经越来越高的情况下,可以实现更多车和其他系统的对接,更多更丰富的功能。第四阶段:可以借助于云端无穷无尽的计算能力,去做一些智能化的计算和判断,实现智能驾驶远程连接系统,对接第三方系统,对接车车的互联,车和路测终端,更紧密的互联等更大场景下的智能化应用。中国车联网用户规模不断攀升,5G/V2X技术应用有望实现强劲增长,2022年用户总规模将超过5100万。短期,中国车联网市场规模的增长主要依赖软硬件装载量的提升。中长期,随着车联网生态的丰富,广告、CP/SP等以车联云为核心的车内增值服务将带来更广阔的增长空间。早在2017年,国家就推出了车联网行业的新四化:“网联化”、“智能化”、“共享化”、“电动化”。无论从国家规划还是从科技发展趋势来看,这四化都将成为未来汽车行业的发展方向。未来,所有汽车行业都会从传统的汽车制造企业向出行服务方向进行转型。随着国内汽车保有量和人均收入的快速增长,给客户提供更加可靠、安全、优质的出行服务,将变成车企的主要目标。针对这个目标,阿里云存储产品需要给车企提供可信赖的数据支撑,便于车企对自身进行优化、改进。图源见水印,侵删据前瞻产业研究院发布的《车联网行业市场前瞻与投资占率规划分析报告》数据显示,2020年中国汽车保有量约为2.5~3亿辆,按照15%的渗透率,具备联网能力的车辆将达到4000万辆左右。以每辆1000元的硬件产品价格来估算,单是硬件市场就有400亿元规模。而随着产品功能的丰富,单辆车的硬件产值也会翻倍提高,加之互联网服务,车联网市场的空间可以在5~10年内达到千亿甚至万亿规模。二、车联网海量监控数据怎么存?(一)车联网系统数据存储汽车企业在生产系统中采用云战略实现上云上平台,一方面可以解决过去难以解决的各个系统互联互通问题,另外一方面,可以通过推进产业互联网平台,为各地域的工厂(不一定是同一家公司)之间共享信息提供基础条件。上图左右放的是智能互联系统的云端架构图,从左边这个逻辑架构图可以看到,从下面产品通过网络接入云端,首先连接底层的是产品的数据库,云端在资源池化之后,让用户以更贴近业务的方式去定义业务相关的一些应用和产品。所以说,从第一层把数据对接上来,就要进入云端的数据库集中的存储,有了集中的数据存储层之后,才需要往上去建立应用层应用平台,或者是智能化的规则引擎,数据来驱动平台。根据不同的业务互动场景,去建设相关的应用。右端是数据架构图,表示数据首先从车企端采集以后,不单纯是车上的各个指标数据,还有它的位置信息以及其他更丰富的数据,比如智能后视镜,采集的视频数据、语音的数据和云端服务交互的数据。这些数据是多样化的,无结构的,半结构的或者结构化的数据都会有。所以需要有一个Delegate池化的大数据云端存储平台,能够支撑多模、异构的数据的存储、查询和解析。这些数据不光是车产生的,还有可能第三方系统的,比如车跟支付宝平台对接,微信对接,第三方国家银联的平台,或者是内容提供商的平台对接,服务提供商的平台对接,这些数据源也都可以作为我们数据库全景数据的补充。有了全景数据,未来才能支撑更多更丰富的场景。大致的部署结构如下所示:首先车企端在一般情况下使用OBD、OBU把数据导出来。TBox是车集中的计算平台,就像我们的电脑似的,它把数据从OBD、OBU平台拿到之后上传,通过SIM卡跟云端发生交互。云端拿到数据之后,通过数据解析,一致化之后,再融入第三方数据源,实现不同场景下数据的应用,比如,车载应用:IVI新功能规划、新的交互模式、智能座舱体验;TSP服务:加油充电、道路求援、交通信息、停车服务;正常导向:国家标准(国VI、EV)、第三方数据输入、安全体现等,都可以基于云端的数据,去搭建需求应用系统。(二)车联网典型数据场景–新能源汽车监控BEV新能源汽车监控场景主要是为了满足GB/T32960国标和地标的需求,以便车厂能够获得平台符合性及车辆符合性认证。整个车厂建设过程中,出于日常运营需求,往往在国标基础上建设额外服务,例如:通知、告警、监控、追迹、调度服务、系统报表、数据转发等功能。主要建立能力调度服务:可选服务,可用于车辆运营。报警信息:可选服务,可通知车主、4S店等定制化报警服务。统计报表:可选服务,基于OEM要求进行定制。数据存储:实施接受(10S频率),北京区域要求存储1年,上海地区存储3年。数据查询:各个数据(实时数据/历史数据)的查询显示接口,需求提供报警新展示,单体蓄电池电压数据展示;动力蓄电池包温度数据展示;整车数据展示;极值数据展示。(三)车联网典型数据场景–TelematicsTelematics是指应用无线通信技术的车载计算机,新一代Telematics将以公有云为核心,实现车辆远程控制、远程读取信息和智能赋能。目前有内置和OBD接头两种实现的方式,其本质都是对于汽车各系统ECU的远程监控,已经实现的功能有远程检测,远程控制,呼叫中心等,比较成熟的有bluelink, 安吉星on-star,美国的snap-on,还有delphi出的Verizon Wireless等。(四)车联网典型数据场景–智能后视镜监控为贯彻交警“便捷高效,互联互通,共享共治”的互联+思维理念,充分发挥智能行车记录仪在交警执法中的积极作用,运营商积极配合深圳市交警局打造“车联网生态系统平台”,建立中国智能交通产业联盟上下游企业生态圈,支持车联网生态圈等相关企业发展,实现星级用户车主“一人一车一设备”的愿景,为车主提供多样化的车联网平台管理方案,以提高交警对车辆管理的效率。(五)车联网典型数据场景–车载娱乐系统车载信息娱乐系统(IVI)是智能驾驶舱信息交互的重要载体,基于车身总线系统和互联网服务,形成车载综合信息处理系统,可提供导航定位、车体控制、无线通信、车内娱乐和汽车移动等多种服务内容。产业链中,中控厂商凭借对硬件和软件的整合的产品优势和技术累积,占据了座舱电子产业链的制高点,未来,中控系统将成为人机交互的核心驱动,具备广阔的应用前景。目前,IVI能够实现包括三维导航、实时路况、IPTV、辅助驾驶、故障检测、车身控制、移动办公、无线通讯、基于在线的娱乐功能及TSP服务等一系列应用,车辆电子化,网络化和智能化水平强依赖云端能力。云端和车机端的数据的交互量会大幅的提升,不光是服务能力赋能汽车,同时视频和音频汽车车主驾驶的时候操作的数字路径,都需要从云端去获取,这些数据也需要在云端去集中的存储,就是现在的一个场景和海量数据存储的一个现状。三、云原生多模数据库Lindorm怎么解?(一)什么是云原生多模数据库Lindorm?云原生多模数据库Lindorm适用于任何规模、多种规模的云原生数据库服务,支持海量数据的低成本存储处理和弹性按需付费,兼容HBase、Solr、SQL、OpenTSDB等多种开源标准接口,是互联网IoT、车联网、广告、社交、监控、游戏、风控等场景首选数据库。未来,车联网场景将会是数据量大,数据类型多。不同场景下产生的数据类型多种多样,会有视频、文本、代码链路、用户数字轨迹等方面的数据,以及在车机端点击应用的操作行为。这些都会使得云端数据库建设对应的存储模型,以及查询能力,因此云端多模的存储能力是必选项。同时,需要考虑对现有系统和第三方平台的对接能力,多模的数据库需要有一个开放的标准和协议支持,支撑更大范围的数据生态。Lindorm主要有以下四个特点:1.极致性价比当遇到数据量激增的场景,如果没有很好的数据压缩存储或者冷热数据自动转存的方案,数据成本也会激增。大部分的监控数据是机架式密度数据,有一些运维数据在不出故障的时候就没有什么价值。只有出了故障,在回溯分析定位的时候,它才会发挥价值。像这种数据,如果用传统的关系数据库或者是自建的数据库存储,运维和数据授权的成本非常高昂,是不经济的存储方式。因此,未来需要极致性价比的物联网存储,能够提供PB级甚至EB级存储能力的存储平台,才能有效支撑这种场景。2.云原生弹性未来,我们需要云端提供随时进行池化资源的使用方式,没有业务的时候可以收缩资源占用量,节约成本。业务并发量大的时候,在保证性能的前提下满足成本控制的需求。因此,弹性伸缩数据库的存储,是云原生场景下很关键的能力。3.企业级稳定性支撑如此庞大数量在线运行车辆的场景,稳定性至关重要。例如汽车OTA场景,当发生故障或网络中断、数据丢失的情况,汽车升级可能发生中断,导致了车机端的故障或是智能化控制指令丢失,执行失败等。4.数据生态融合对接更广泛的生态,无疑可以支撑我们更大场景下的这个数据应用,这个也是非常关键的。目标行业:工业物联网、 车联网、APM运维、NPM运维……这些场景都有一个共同的特点,就是产生海量、带持续指标的监控数据,而这些数据的价值密度非常低,它需要一种经济且友好的数据库进行存储。上方为Lindorm数据库体系图。目前Lindorm的核心能力除了能够对接广泛的数据生态等,在数据库内部也有多模数据转存的通道,能够在宽表、时序、全文检索、文件检索引擎之间做数据的协同。上层除了支持SDK等接口之外,还支持其他应用比较广泛的大数据生态接口查询语言。(二)Lindorm产品策略上方为产品发展策略,未来演进方向是云原生多模超融合数据库。首先在金字塔底层的第一阶段,要具备海量多模数据的存储能力,把结构化、半结构化、无结构化的数据以较为经济的方式存储下来,对这些数据做云化的处理,称为数据云。在此基础上,再建设跨数据库引擎的横向融合能力,如宽表、时序、图、关系等模型数据,实现数据库协同查询能力。在Lindorm数据库内部,用户可以通过一致的查询语句,透明查询多种数据库,拿到所需要的信息。无论这个信息是从宽表、时序还是从文本检索引擎来的,均对用户透明化处理,从而更友好地支撑未来数据应用的开发。再往上层的纵向融合指的除了数据库引擎、上层数据交换、ETF流数据处理以及计算引擎,可以在上层搭建数据库内部的运行,例如异常检测、趋势预测、关联性分析、相关性分析等,纵向地将数据库的能力进一步融合,进而达到超融合,实现统一的查询语言、数据视图,数据访问。在车联网的一些具体场景下,比如新能源汽车当前电池包的健康状态监控,或者是公共场景下带业务特征的计算,都可以做成数据库内计算的算子,或是以函数直接通过标准SQL语句去调用,最后连存到算一体化执行,最终实现顶层推理的效果。推理的效果指的是我们只需要告诉数据库我们的需求,则可得到相应的信息。建立统一的数据视图之后,可以用SQL语句定义,比如导致生产次品率上升的主要原因,汽车故障发生告警的主要原因等,这些能力都是现有的数据库和查询所不能实现的。如果要提供以上能力,需要一系列的数据工具,如下所示。上图为逻辑架构图,图中的最下方是能对接的第三方数据源,提供无缝的对接和支持。数据进来之后到宽表时序等文件引擎,在这里面根据业务场景不同,可以划分不同场景进行存储,有配套的数据转储和数据采集工具。再上方就是计算引擎,还有数据检索的引擎。如Phoenix SQL引擎,Lindorm监控服务,以及时序数据分析等服务。再上层是人机界面,为了方便基于数据库开发数据应用,最上层是应用系统。核心优势卖点1)高性价比存储2)多模超融合检索创新技术能力1)时序数据压缩2)工业场景数据建模3)时序统计及非确定性推理检索贴近客户场景1)LindormStudioIDE、工具、SDK2)文档、方案、案例库3)专业、体系化IT&OT融合服务能力打通数据生态1)开放生态:开源:CQL、PhoneixSQL、 ES、MachineBeat…2)商业:OSIsoftPI、Splunk融合方案生态OSIsoft、Intel、工业大脑、IoT、东软、 飞象互联…上方为以前和当前存储方案对比,原来的部署方案需要应用开发人员和运营人员具备较高的动手能力,后续数据量增加的话,每个系统要单独做集群,需要耗费大量的人力和专家进行支撑。当前的Lindorm存储方案是一个数据库帮助客户解决日常问题,用一个接口查询所有多模型的数据库。(三)Lindorm车联网数据存储解决方案在部署形态上,未来的车联网与物联网是便载、地域分布广的场景,单独在云端搭建数据库无法满足性能实时性的需求,所以未来数据库形态逻辑和物理部署可能是一对多,称为云边端一体化的数据库部署方案。边缘端不管是车联网路测端的设备或者车机上的TBox,都可以去嵌入轻量级持续数据采集的数据库或者是多模数据库。在工业场景下,已经实现了在边缘计算节点上部署边缘数据库,它可以无缝对接混合云、私有云数据中心或者是公有云平台数据库,做实时或按策略批量的数据同步。用户在实际部署和使用过程中,可以把分布部署的数据库作为逻辑一体的数据库来管理和实现。比如边缘端部署,它可以采集存储一段时间周期的数据,然后支撑实时面向边缘端所连接的设备或者车辆数据,实时查询与监控的场景。一旦需要全景数据或者是需要回溯分析出报表,统计数据的时候,可以借助云端强大的计算能力与存储能力,做全量数据的回溯,做复杂计算以及根源分析,甚至机器学习场景的模型训练,完美实现逻辑一体,实际物理分离。既能支撑实时场景,又能支撑批量分析场景的数据存储解决方案。物联网行业数据主要以终端设备、传感器实时产生的状态数据、用户操作数字足迹和控制执行结果数据为主;利用监控状态数据可以及时发现设备、异常操作、潜在风险、客户数字体验,从而指导运营运维;物联网实时监控数据经过实时流数据处理平台,或时序数据库聚合对接实时监控大屏,支持设备实时监管、风险态势感知等应用场景;批量存储在近场端、数据云端的全量物联网数据可用来做故障回溯分析、主动探伤检测、异常定位及预测等。BEV新能源汽车监控场景主要是为了满足GB/T32960国标和地标的需求,以便车厂能够获得平台符合性及车辆符合性认证。在整车厂建设过程中,出于日常运营需求,往往会在国标基础上建设额外服务,例如:通知、告警、监控、追迹、调度服务、统计报表、数据转发等功能。针对这个场景,我们提供了基于阿里云Lindorm数据库的整体解决方案。首先,这个方案里除了Lindorm,还用到了其他阿里云数据库产品,比如多维数据分析数据库ADB,第三方开源的Spark streaming,还有IoT网关MQTT等。这个流程从车机端对接云端一般有两套方案,一套方案是车厂对接车联网的服务提供商,他们有自己私有云平台来对接车机端的提供,然后再由他们的平台来对接到阿里云的Lindorm数据库。另外也可以直接按标准协议去对接车机端现有的TBox,直接用 REST接口来同步数据,或者MQTT协议直接发送到IoTGateway。这些数据到了云端之后,它可以分成两个流,一个实时写入Lindorm,快速上报国家平台,周期10秒,在周期内完成上报国家平台以及存入数据库这些动作。另外一个为了实现实时报警与数据分析,数据流也同步推送一份到Spark streaming,由它做实时流数据分析生成事件,然后再存入Lindorm数据库。如果有第三方数据的对接,比如车厂以及其他第三方系统做数据的归集备份,或者是第三方的平台的应用也需要这块数据,就需要推到Kafka里边,然后再由Kafka消息队列发到车场的数据中心。车场数据中心也可以部署一套Lindorm数据库和云端做实时数据同步,当出现网络链路不稳定,或者车厂做数据分析的时候,在车场本地的数据中心也可以完成数据分析的工作。国家数据平台最主要是北理工的平台,有标准的接口协议直接在云端开发相应的数据推送服务节点对接国家平台,地方平台有地方标准和相应接口,可以云端直接转发。这里用到的数据库主要有,Lindorm数据库做全量数据的存储,Lindorm数据库中需要结合其他数据,比如说CRM的数据,车辆信息数据,基于密码查询相关的信息等复杂高维的数据查询,可以同步到ADB,再用ADB同步到数据分析平台QuickBI,来做数据的统计和可视化。另外一个就是开源的Grafana,如果是需要监控全景或针对特定车辆的信息,可以通过Grafana来实时查看最近时间窗口的状态。IoTGateway主要负责和车机端对接,或者和车联网的专有云平台对接。(四)车联网场景下Lindorm商业价值在哪?快:1)高通量车联网数据并发写入快2)时序数据聚合、划窗、统计计算快3)海量监控数据检索查询快4)数据全生命周期管理流程搭建快省:1)海量车联网数据存储成本省2)占用计算、存储资源省3)数据库搭建、应用对接开发工作量省4)数据全生命周期运维管理成本省在车联网场景下Lindorm商业价值可以总结为两点,一个是快,另外一个就是省。快就是,说在高通量的车联网数据并发写入的场景下,可以在云端进行云原生的模式,随机汲取的模式,去实现车企端、在线车辆产生的实时海量高通量的数据,可以快速地在云端数据库写入以及数据快速向第三方平台转发。时序数据聚合、划窗、统计时,在有原生的自研时序数据库情况下,很快地在数据库内做大量数据的聚合、划窗、统计分析。海量监控数据检索查询快,因为它是时序数据库引擎来处理的,可以支持海量多维度数据的聚合查询的计算检索。数据全生命周期的管理流程搭建很快,因为很多能力都是数据库内提供的,就不需要再去人工搭建,或者是维护自建的开源数据库来定制开发能力,或者数据对接的接口,所以说搭建过程也很快。阿里云之上开通Lindorm数据库,几分钟时间就可以完成。省就是,对海量数据存储成本会有大幅的下降,除了自研的冷数据、热数据备份的策略,在存储层也做了自己的优化。在时序数据库数据压缩方面,在现有的压缩算法上做了一些改进,达到了10倍数据压缩效果。占用计算存储资源通过云原生的方式,随机汲取,在没有太多的在线车辆的情况下,收缩它的计算能力,按实际的数据的访问使用量来收费,这样就节约很多数据超配的成本。数据库搭建应用对接开发工作量也会省下很多的人工的成本,一方面是数据库本身支持很多的第三方大数据生态接口的对接,另外云端搭建就不需要关心数据库安装和备份策略等配置的问题。数据库全生命周期运维管理的成本,也会节省很多,灾备集群的方案,数据库的整个转储等这些都是数据库内自动解决的。(五)Lindorm关键技术优势1. 实时无损,高压缩比存储> 10:110:1的深度优化,基于ZSTD算法的压缩,可以把原始的采集的时序数据,比如10GB数据存入到数据库内去做无损的压缩,做到10GB数据存到数据库内,1GB数据的存储容量就足够了,压缩效率相比现在业绩通用的SNAPPY压缩提升50%以上的压缩效率。2. 面向低价值密度监控数据的冷热分离低成本存储冷热分离的低成本的存储方案,我们是在Lindorm数据库内做的一体化整个分离,数据也是自动做分层,冷热的分离,就不需要人工介入。冷数据存储成本一般比现在的热数据要降低80%以上,热数据为了满足性能的要求,一般用SD等高端的存储来存冷数据,性价比较高的存储来存这些历史数据。这样的话既兼顾了查询的性能,同时又节约了成本。3. 云边端融合存储,数据自动实时、批量同步特点:1)边缘版轻量级快速集成部署2)2HA高可用架构3)具备与云端版本一样的功能4)数据自动同步至阿里云TSDB实例集群云边端一体化的存储的方案,边缘端的数据库、嵌入的数据库,根据策略去做实时的云端数据同步以及批量的数据的导入与导出。4. 多引擎超融合数据快速迁移同步在数据库内部是集成了一个ETL工具,做多模数据之间的数据导入导出,甚至对接第三方的HBase或者Cassandra、OpenTSDB等数据的迁移,可以把第三方的数据源数据通过LTS直接迁移到Lindorm数据库,就不用再依赖于第三方工具实现。5. 多模数据融合检索打通IT&OT数据交互多模数据融合检索打通IT&OT数据交互,实现跨多引擎引擎查询,提供全维检索能力。统一API,简单易用1)系统自动维护索引,应用开发不感知索引表2)索引支持非冗余、冗余部分列、全冗余3)查询时基于编译优化(RBO)全自动路由到搜索引擎,并智能判断是否需要回查宽表/时序引擎数据自动同步到搜索引擎1)异步增量索引,基于LTS提供可扩展的同步通道,数据同步可视化2)实时同步索引,引擎间数据强一致针对IT数据和OT数据的交互融合,这一点可以实现一致的跨引擎查询,全文全维度的数据检索能力,通过上层一致数据,试图来帮助评比下层的数据存储、数据引擎的异构性,进一步简化使用成本和维护成本。
作者:柏锐在关系型数据库的查询中join是一个十分常见的操作,通过将几个表关联起来,用户可以在遵守数据库设计范式的前提下高效获得信息。在分析类查询中,大表之间(或大表与小表)的 Join 通常使用 Hash Join 实现,这通常也是查询的性能瓶颈之一,因此如何优化join的查询性能也是计算引擎的重点。Runtime Filter介绍基本原理Runtime Filter是[4]中提到的在数据库中广泛使用的一种优化技术,其基本原理是通过在join的probe端提前过滤掉那些不会命中join的输入数据来大幅减少join中的数据传输和计算,从而减少整体的执行时间。例如对于下面这条语句的原始执行计划如下,其中sales是一个事实表, items是一个纬度表:SELECT * FROM sales JOIN items ON sales.item_id = items.id WHERE items.price > 100如上图左半部分所示,在进行join运算的时候不仅需要把全量的sales数据传输到join算子里去,而且每一行sales数据都需要进行join运算(包括算哈希值、比较运算等)。这里如果items.price > 100的选择率比较高,比如达到50%,那么sales表中的大部分数据是肯定不会被join上,如果提前进行过滤掉,可以减少数据的传输和计算的开销。上图的右半部分则是加入了runtime filter之后的执行计划,从图中可以看到在进行join的build端拉取数据的过程中新增了一个RuntimeFilterBuilder的一个算子,这个算子的作用就是在运行的过程中收集build端的信息形成runtime filter,并且发送到probe端的scan节点中去,让probe端的节点可以在scan就减少输入的数据,从而实现性能的提升。Runtime Filter对Join Reorder的影响在当前的大多数系统中runtime filter所需要的算子都是在优化器的CBO阶段之后插入进物理执行计划的,使用的是一种基于规则的优化方法。然而在[3]中指出如果将runtime filter对执行计划所带来的影响在CBO阶段纳入考虑,则能更进一步地优化执行计划。如下图是一个例子:在这个例子中图(a)是一个原始的查询,需要对k,mk和t三个表进行join。图(b)是在不考虑runtime filter的情况下进行CBO得到的物理执行计划。图(c)是在(b)的基础上通过基于规则的方式将runtime filter加入到物理执行计划中去。图(d)则是将runtime filter放在CBO阶段中得到的物理执行计划,我们可以看到图(d)得到的最优的物理执行计划的最终cost小于图(c)得到的计划。然而如果直接将runtime filter加入到CBO中去,则会引起优化器的搜索空间的指数级增长。这是由于现有的优化器的CBO阶段大多基于动态规划的算法,如果将runtime filter放入CBO中,则子计划的最优解依赖于查询计划中父节点下推的filter的组合和runtime filter应用到的表的方式,这种组合将会引起搜索空间的爆炸。[3]证明了对于星型查询和雪花查询(即通过主键和外键将纬度表和事实表关联起来进行join的查询),某些join顺序在加入runtime filter之后是等价的,从而保证了优化器在CBO阶段搜索空间的线性增长。PolarDB-X中的Runtime FilterPolarDB-X作为一个HTAP数据库,在满足高性能的oltp场景的同时,也能实现对海量数据的高性能的分析场景。为满足客户大数据分析的需求,我们也在自研的MPP引擎中实现了Runtime Filter。其基本原理与上述基本相同,但是我们针对分布式数据库的场景也做了一些专门的优化。Runtime Filter类型的选择在PolarDB-X中我们选择使用bloom filter来过滤我们的数据。bloom filter有着诸多的优点:类型无关:这一特性降低了我们处理多种类型的实现复杂度空间复杂度低:能够提高传输效率和内存开销时间复杂度低:这一时间复杂度既包括生成bloom filter的开销,也指检查是否存在的时间开销,较低的时间复杂度保证了不会引入过多的开销当然在其他的系统中也会包含一些其他种类的过滤器,比如在Spark SQL中如果碰到过滤的是分区列且build端的数据较小,则会选择使用全量的输入数据进行动态分区的剪裁;而如果查询的数据格式是parquet或者orc这样的带索引的格式,则会生成min/max这样简单的过滤器来过滤。但这些过滤器大都针对特定场景,不够通用。Runtime Filter生成的代价估算Runtime Filter的生成、传输和检查会引入额外的开销,如果不加节制地滥用,不但不会提升性能,反而会导致性能的下降。由于代价估算和实现的复杂性,大多数开源系统中都只支持在broadcast join中实现Runtime Filter,比如Trino(原Presto)中就是这样的。这个做法的好处是实现简单,现有系统的改动较小,但同时也会失去很多优化的机会。在PolarDB-X中我们将Runtime Filter的生成规则与优化器的统计信息有效地结合,通过多个纬度的数据来决定是否需要生成Runtime Filter:probe端的数据量的大小。如果probe端的数据量过小,即便被过滤很多的数据,其性能提升也无法弥补bloom filter的额外开销,此时我们会放弃生成bloom filter。bloom filter的大小。bloom filter的大小由输入的数量和fpp(错误率)决定,并和输入的数量成正比。当bloom filter太大,不仅会增大网络传输的数据,也会增大内存占用,因此我们将bloom filter的大小限制在一定范围内。过滤比例。当生成的bloom filter的过滤比例太小时,将其下推到join的probe端不仅不会起到任何的效果,而且精确的过滤比例的计算是一个比较复杂的过程,这里我们使用一个近似的公式来估算过滤性:。只有当过滤比大于一定阀值时我们才会生成runtime filter。Runtime Filter的执行PolarDB-X中的MPP引擎是一个为交互式分析而生的分布式的计算引擎,与Spark、Flink等不同的地方在于采用push的执行模型。这个模型的好处在于中间数据不用落盘,极大地减小了计算过程中等待的延迟,但也增加了Runtime Filter这一特性开发的复杂度。与大部分的开源计算引擎不同,PolarDB-X中的Runtime Filter不仅仅支持broadcast join,也同样支持其他各种分布式 join算法。我们仍然使用上面的一个SQL语句举例子:SELECT * FROM sales JOIN items ON sales.item_id = items.id WHERE items.price > 100在开启了runtime filter之后的物理执行逻辑如下所示:如图所示,build端会将生成的bloom filter发送到coordinator,coordinator在等待各个partition的bloom filter都发送完成之后进行一次merge操作,将合并好的bloom filter发送到FilterExec算子中去,从而实现过滤效果。通过coordinator合并之后的bloom filter的大小与单个的partition的bloom filter的大小一样大,但为每个probe端只传输一次,极大地减少了数据的传输。同时FilterExec在等待bloom filter的过程中并不会阻塞住,而是通过异步的方式接收bloom filter,从而尽量减少 bloom filter生成给延迟带来的影响。为了进一步减少数据的传输,我们通过实现udf的方式将bloom filter下推到DN层,在DN端进行数据的过滤,从而大幅减少网络的开销。如下图所示,PolarDB-X会将bloom filter进一步下推至DN,减少了从DN拉取的数据量,从而减少了网络传输和数据解析的开销。效果评估我们对比了Runtime Filter在 TPCH 100G的数据集上的效果,其结果如下所示:我们可以看到对于耗时较长的大查询,如Q9和Q21我们都取得了2~3倍的性能提升,而对于其他中型的查询也有1倍的性能提升,总体的性能提升在20%左右。参考文献Bloom filterDynamic Filtering in TrinoBitvector-aware Query Optimization for Decision Support Queries, SIGMOD 2020Query Evaluation Techinques for Large Databases【相关阅读】快速掌握 PolarDB-X 拆分规则变更能力!子查询漫谈探索 | PolarDB-X:实现高效灵活的分区管理分布式数据库如何实现 Join?
作者:阿里云数据库OLAP产品部 仁劼Clickhouse是俄罗斯搜索巨头Yandex开发的完全列式存储计算的分析型数据库。ClickHouse在这两年的OLAP领域中一直非常热门,国内互联网大厂都有大规模使用。Elasticsearch是一个近实时的分布式搜索分析引擎,它的底层存储完全构建在Lucene之上。简单来说是通过扩展Lucene的单机搜索能力,使其具有分布式的搜索和分析能力。Elasticsearch通常会和其它两个开源组件Logstash(日志采集)和Kibana(仪表盘)一起提供端到端的日志/搜索分析的功能,常常被简称为ELK。今天很多用户在实际的业务场景中,常常面对ClickHouse和Elasticsearch技术选型的难题。用户对ClickHouse和Elasticsearch的内核知识了解不足,往往只能通过性能测试的手段来进行选型。本文的主旨在于通过彻底剖析ClickHouse和Elasticsearch的内核架构,从原理上讲明白两者的优劣之处,同时会附上一份覆盖多场景的测试报告给读者作为参考。分布式架构Elasticsearch和ClickHouse都是支持分布式多机的数据产品,这里作者首先要比对的就是两者的分布式架构差异,分布式结构设计对产品的易用性和可扩展性具有非常重要的影响。在分布式架构上,核心要解决的几个问题包括:节点发现、Meta同步、副本数据同步。Elasticsearch作为一个老牌的开源产品,在这块上做的相对比较成熟。原生的节点发现、Meta同步协议,给用户非常好的易用性体验。Elasticsearch的Meta同步协议需要解决的问题其实和开源的Raft协议非常相似,只不过在Elasticsearch诞生的时候还没有Raft出现,所以就只能自己动手搞一个了。经过这么多年的打磨,Elasticsearch的Meta同步协议也是相当成熟了。依托于此,Elasticsearch具有非常易用的多角色划分,auto schema inference等功能。值得一提的是Elasticsearch的多副本数据同步,并没有复用Meta同步协议,而是采用传统的主备同步机制,由主节点负责同步到备节点,这种方式会更加简单高效。ClickHouse的分布式架构能力相对会简单一些,这也是因为ClickHouse还是一个比较年轻的开源产品,还处在分布式易用性不断迭代上升的阶段。ClickHouse引入了外置的ZooKeeper集群,来进行分布式DDL任务(节点Meta变更)、主备同步任务等操作的下发。多副本之间的数据同步(data shipping)任务下发也是依赖于ZooKeeper集群,但最终多副本之间的数据传输还是通过Http协议来进行点对点的数据拷贝,同时多副本都可写,数据同步是完全多向的。至于节点发现,ClickHouse目前都没有这方面的能力,都是需要通过手动配置集群节点地址来解决。ClickHouse目前这种脚手架式的分布式架构,导致它具有极强的灵活部署能力和运维介入能力,对用户的易用性略差,用户门槛相对较高,但是在能力上限方面,ClickHouse的分布式部署扩展性并没有短板,集群规模上限对比Elasticsearch没有差异。ClickHouse架构扁平,没有前端节点和后端节点之分,可部署任意规模集群。同时ClickHouse在多副本功能上有更细粒度的控制能力,可以做到表级别副本数配置,同一物理集群可划分多个逻辑集群,每个逻辑机器可任意配置分片数和副本数。存储架构写入链路设计写入吞吐能力是大数据场景下的一项核心指标,用户对大数据产品的要求不光是要存的下,还要写得快。这里首先介绍Elasticsearch的实时写入链路设计:在Elasticsearch的每一个Shard中,写入流程分为两部分,先写入Lucene,再写入TransLog。写入请求到达Shard后,先写Lucene内存索引,此时数据还在内存里面,接着去写TransLog,写完TransLog后,刷新TransLog数据到磁盘上,写磁盘成功后,请求返回给用户。这里有几个关键点,一是把写Lucene放在了最前面,主要是防止用户的写入请求包含“非法”的数据。二是写Lucene索引后,并不是可被搜索的,需要通过refresh把内存的对象转成完整的Segment后,然后再次reopen后才能被搜索,这个refresh时间间隔是用户可设定的。可以看出Lucene索引并没有写入实时可见的能力,所以Elasticsearch是一个近实时(Near Real Time)的系统。最后是每隔一段比较长的时间,比如30分钟后,Lucene会把内存中生成的新Segment刷新到磁盘上,刷新后索引文件已经持久化了,历史的TransLog就没用了,才会清空掉旧的TransLog。△Elasticsearch单Shard写入链路△ClickHouse单Shard写入链路对比Elasticsearch的写入链路,ClickHouse的写入方式更加“简单直接”、极致,上面已经讲过Elasticsearch是一个近实时系统,内存存储引擎中新写入的数据需要定时flush才可见。而ClickHouse则是干脆彻底放弃了内存存储引擎这一功能,所有的数据写入时直接落盘,同时也就省略了传统的写redo日志阶段。在极高写入吞吐要求的场景下,Elasticsearch和ClickHouse都需要为了提升吞吐而放弃部分写入实时可见性。只不过ClickHouse主推的做法是把数据延迟攒批写入交给客户端来实现。另外在多副本同步上,Elasticsearch要求的是实时同步,也就是写入请求必须写穿多个副本才会返回,而ClickHouse是依赖于ZooKeeper做异步的磁盘文件同步(data shipping)。在实战中ClickHouse的写入吞吐能力可以远远超过同规格的Elasticsearch。Segment vs DataPartElasticsearch和ClickHouse的存储设计外表上看起来非常相似,但能力却又截然不同。Elasticsearch的磁盘文件由一个个Segment组成,Segment实际上是一份最小单位的Lucene索引,关于Segment内部的存储格式这里不展开讨论。而Segment又会在后台异步合并,这里合并主要解决两个问题:1)让二级索引更加有序;2)完成主键数据变更。二级索引是一种“全局”有序的索引,全部数据构建到一个索引里面比构建到多个索引里对查询的加速更明显。Elasticsearch是支持主键删除更新的,这都是依托于Lucene索引的删除功能来实现的,更新操作会被转换成删除操作加写入操作。当Lucene索引的Segment里存在多条删除记录时,系统就需要通过Segment合并来剔除这些记录。在多个Segment进行合并的时候,Lucene索引中的存储数据表现出的是append-only的合并,这种方式下二级索引的合并就不需要进行“重排序”。对比Elasticsearch中的Segment,ClickHouse存储中的最小单位是DataPart,一次批量写入的数据会落盘成一个DataPart。DataPart内部的数据存储是完全有序的状态(按照表定义的order by排序),这种有序存储就是一种默认聚簇索引可以用来加速数据扫描。ClickHouse也会对DataPart进行异步合并,其合并也是用来解决两个问题:1)让数据存储更加有序;2)完成主键数据变更。DataPart在合并存储数据时表现出的是merge-sorted的方式,合并后产生的DataPart仍然处于完全有序状态。依赖于DataPart存储完全有序的设定,ClickHouse实现主键数据更新的方式和Elasticsearch截然不同。Elasticsearch在变更主键时,采用的是“先查原纪录-生成新记录-删除原纪录-写入新纪录”的方式,这种方式完全限制住了主键更新的效率,主键更新写入和append-only写入的效率差异非常大。而ClickHouse的主键更新是完全异步进行的,主键相同的多条记录在异步合并的时候会产生最新的记录结果。这种异步批量的主键更新方式比Elasticsearch更加高效。最后总结一下Segment和DataPart内部文件存储的能力差异,Segment完全就是Lucene索引的存储格式,Lucene索引在倒排文件上的存储毋庸置疑是做到极致的,Lucene索引同时也提供了行存、列存等不同格式的原数据存储。Elasticsearch默认都会把原数据存两份,一份在行存里,一份在列存里。Elasticsearch会根据查询的pattern,选择扫描的合适的存储文件。原生ClickHouse的DataPart中并没有任何二级索引文件,数据完全按列存储,ClickHouse实现的列存在压缩率、扫描吞吐上都做到了极致。相对而言Elasticsearch中的存储比较中庸,并且成本至少翻倍。再谈Schemaless讲到Elasticsearch的特性,大家都会提到Schemaless这个词,Elasticsearch可以自动推断写入数据的json-shema,根据写入数据的json-schema调整存储表的Meta结构,这可以帮助用户节省很多建表、加列的麻烦。但是在作者看来,Elasticsearch的这种能力其实叫auto schema inference更为恰当,这都得益于Elasticsearch的分布式Meta同步能力。而Elasticsearch的存储其实是需要schema的,甚至是强绑定schema的,因为它是以二级索引为核心的存储,没有类型的字段又如何能构建索引呢?真正的Schemaless应该是可以灵活高效变更字段类型,同时保证查询性能不会大幅下降的能力。今天用户想变更Elasticsearch index中的某个字段类型,那只有一种方法:就是把整份数据数据reindex。相对比,ClickHouse的存储反而不是强绑定schema的,因为ClickHouse的分析能力是以存储扫描为核心的,它是可以在数据扫描进行动态类型转换,也可以在DataPart合并的时候慢慢异步调整字段的类型,在查询的时候字段类型变更引起的代价也就是运行时增加cast算子的开销,用户不会感受到急剧的性能下降。作者认为Schemeless绝对不是Elasticsearch的护城河能力,相对反而是它的弱项。至于auto schema inference,这是对小规模用户非常友好的能力,但它永远不可能能帮用户创建出性能最佳的Schema,在大数据量场景下大家还是需要根据具体的查询需求来创建Schema,所有的便利最后都是有成本代价的。查询架构计算引擎作者在这里把ClickHouse和Elasticsearch摆在一起讲计算引擎其实有些荒谬的味道,因为Elasticsearch实现的只是一个通用化搜索引擎。而搜索引擎能处理的查询复杂度是确定的、有上限的,所有的搜索查询经过确定的若干个阶段就可以得出结果,但是计算引擎则不然。Elasticsearch虽然也有SQL支持的插件,但是这种插件的实现逻辑就是把简单的SQL查询翻译到确定的搜索模式上面。对于搜索引擎原来就不支持的数据分析行为,Elasticsearch-SQL也无济于事。另外Elasticsearch-SQL当前的翻译能力看起来并不是非常完备和智能,为了获得最高的搜索性能用户还是需要尝试Elasticsearch原生的查询API。对于习惯使用SQL的用户而言,Elasticsearch的查询API是完全陌生的一套体系,复杂查询非常难写。Elasticsearch的搜索引擎支持三种不同模式的搜索方式:query_and_fetch,query_then_fetch,dfs_query_then_fetch。第一种模式很简单,每个分布式节点独立搜索然后把得到的结果返回给客户端,第二种模式是每个分布式存储节点先搜索到各自TopN的记录Id和对应的score,汇聚到查询请求节点后做重排得到最终的TopN结果,最后再请求存储节点去拉取明细数据。这里设计成两轮请求的目的就是尽量减少拉取明细的数量,也就是磁盘扫描的次数。最后一种方式是为了均衡各个存储节点打分的标准,先统计全局的TF(Term Frequency)和DF(Document Frequency),再进行query_then_fetch。Elasticsearch的搜索引擎完全不具备数据库计算引擎的流式处理能力,它是完全回合制的request-response数据处理。当用户需要返回的数据量很大时,就很容易出现查询失败,或者触发GC。一般来说Elasticsearch的搜索引擎能力上限就是两阶段的查询,像多表关联这种查询是完全超出其能力上限的。ClickHouse的计算引擎特点则是极致的向量化,完全用c++模板手写的向量化函数和aggregator算子使得它在聚合查询上的处理性能达到了极致。配合上存储极致的并行扫描能力,轻松就可以把机器资源跑满。ClickHouse的计算引擎能力在分析查询支持上可以完全覆盖住Elasticsearch的搜索引擎,有完备SQL能力的计算引擎可以让用户在处理数据分析时更加灵活、自由。数据扫描ClickHouse是完全列式的存储计算引擎,而且是以有序存储为核心,在查询扫描数据的过程中,首先会根据存储的有序性、列存块统计信息、分区键等信息推断出需要扫描的列存块,然后进行并行的数据扫描,像表达式计算、聚合算子都是在正规的计算引擎中处理。从计算引擎到数据扫描,数据流转都是以列存块为单位,高度向量化的。而Elasticsearch的数据扫描如上一节所述,主要发生在query和fetch阶段。其中query阶段主要是扫描Lucene的索引文件获取查询命中的DocId,也包括扫描列存文件进行聚合计算。而fetch阶段主要是点查Lucene索引中的行存文件读取明细结果。表达式计算和聚合计算在两个阶段都有可能发生,其计算逻辑都是以行为单位进行运算。总的来说Elasticsearch的数据扫描和计算都没有向量化的能力,而且是以二级索引结果为基础,当二级索引返回的命中行数特别大时(涉及大量数据的分析查询),其搜索引擎就会暴露出数据处理能力不足的短板。再谈高并发很多用户谈到ClickHouse,都会有一个错误的映像,ClickHouse查询跑得快,但是并发不行。但这背后的原因其实是ClickHouse的并行太牛逼了,这是ClickHouse的一大强项,一个查询就可以把磁盘吞吐都打满,查询并行完全不依赖于shard,可以任意调整。不可否认处理并发请求的吞吐能力是衡量一个数据系统效率的最终指标,ClickHouse的架构上并没有什么天然的并发缺陷,只不过它是个耿直boy,查询需要扫描的数据量和计算复杂度摆在那,ClickHouse只是每次都老老实实计算而已,机器的硬件能力就决定了它的并发上限。ClickHouse的并发能力事实上是不错的,认为它并发不行是个误区。只是默认情况下ClickHouse的目标是保证单个query的latency足够低;部分场景下用户可以通过设置合适的系统参数来提升并发能力,比如max_threads等。反过来,在这里介绍一下为什么有些场景下Elasticsearch的并发能力会很好。首先从Cache设计层面来看,Elasticsearch的Cache包括Query Cache, Request Cache,Data Cache,Index Cache,从查询结果到索引扫描结果层层的Cache加速,就是因为Elasticsearch认为它的场景下存在热点数据,可能被反复查询。反观ClickHouse,只有一个面向IO的UnCompressedBlockCache和系统的PageCache,为什么呢?因为ClickHouse立足于分析查询场景,分析场景下的数据和查询都是多变的,查询结果等Cache都不容易命中,所以ClickHouse的做法是始终围绕磁盘数据,具备良好的IO Cache能力。其次回到数据扫描粒度,Elasticsearch具备全列的二级索引能力,这些索引一般都是预热好提前加载到内存中的,即使在多变的查询条件下索引查询得到结果的代价也很低,拿到索引结果就可以按行读取数据进行计算。而原生ClickHouse并没有二级索引的能力,在多变的查询条件下只能大批量地去扫描数据过滤出结果(阿里云ClickHouse已经具备二级索引能力,解决了这一问题,性能水平和Elasticsearch相当,后续性能测评部分会进行详细介绍)。但是Elasticsearch具备二级索引,并发能力就一定会好么?也不尽然,当二级索引搜索得到的结果集很大时,查询还是会伴随大量的IO扫描,高并发就无从谈起,除非Elasticsearch的Data Cache足够大,把所有原数据都加载到内存里来。总结来说,Elasticsearch只有在完全搜索场景下面(where过滤后的记录数较少),并且内存足够的运行环境下,才能展现出并发上的优势。而在分析场景下(where过滤后的记录数较多),ClickHouse凭借极致的列存和向量化计算会有更加出色的并发表现。两者的侧重不同而已,同时ClickHouse并发处理能力立足于磁盘吞吐,而Elasticsearch的并发处理能力立足于内存Cache。ClickHouse更加适合低成本、大数据量的分析场景,它能够充分利用磁盘的带宽能力。性能测试在本章中,作者选取了用户业务中多个具有代表性的数据场景,以此对Elasticsearch和ClickHouse做了一个全方面多角度的性能测试报告。具体的测试集群环境如下:ClickhouseElasticsearch节点数CPU:8coreMemory:32GB存储:ESSD PL1 1500GBCPU:8coreMemory:32GB存储:ESSD PL1 1500GB4日志分析场景作者在日志分析场景中选取了两个具有代表性的查询场景进行对比测试,结果如下所示。从结果分析来看ClickHouse和Elasicsearch在两个场景中的性能差距随着where条件过滤后的记录数增大而扩大,在数据量更大的trace_log场景中,两者的分析查询性能差距一目了然。Elasticsearch和ClickHouse完整版建表语句和查询下载:日志分析场景access_log(数据量197921836)ClickHouse中的建表语句如下:CREATE TABLE access_log_local on cluster default ( `sql` String, `schema` String, `type` String, `access_ip` String, `conn_id` UInt32, `process_id` String, `logic_ins_id` UInt32, `accept_time` UInt64, `_date` DateTime, `total_time` UInt32, `succeed` String, `inst_name` String ) ENGINE = MergeTree() PARTITION BY toYYYYMMDD(_date) ORDER BY (logic_ins_id, accept_time); CREATE TABLE access_log on cluster default as access_log_local engine = Distributed(default, default, access_log_local, rand());ClickHouse中的查询语句如下:--Q1 select _date, accept_time, access_ip, type, total_time, concat(toString(total_time),'ms') as total_time_ms, sql,schema,succeed,process_id,inst_name from access_log where _date >= '2020-12-27 00:38:31' and _date <= '2020-12-28 00:38:31' and logic_ins_id = 502680264 and accept_time <= 1609087111000 and accept_time >= 1609000711000 and positionCaseInsensitive(sql, 'select') > 0 order by accept_time desc limit 50,50; --Q2 select case when total_time <=100 then 1 when total_time > 100 and total_time <= 500 then 2 when total_time > 500 and total_time <= 1000 then 3 when total_time > 1000 and total_time <= 3000 then 4 when total_time > 3000 and total_time <= 10000 then 5 when total_time > 10000 and total_time <= 30000 then 6 else 7 end as reorder, case when total_time <=100 then '0~100ms' when total_time > 100 and total_time <= 500 then '100ms~500ms' when total_time > 500 and total_time <= 1000 then '500ms~1s' when total_time > 1000 and total_time <= 3000 then '1s~3s' when total_time > 3000 and total_time <= 10000 then '3s~10s' when total_time > 10000 and total_time <= 30000 then '10s~30s' else '30s以上' end as label, case when total_time <= 100 then '0~100' when total_time > 100 and total_time <= 500 then '100~500' when total_time > 500 and total_time <= 1000 then '500~1000' when total_time > 1000 and total_time <= 3000 then '1000~3000' when total_time > 3000 and total_time <= 10000 then '3000~10000' when total_time > 10000 and total_time <= 30000 then '10000~30000' else '30000~10000000000' end as vlabel, count() as value from access_log where logic_ins_id = 502867976 and _date >= '2020-12-27 00:38:31' and _date <= '2020-12-28 00:38:31' and accept_time <= 1609087111000 and accept_time >= 1609000711000 group by label,vlabel,reorder order by reorder; --Q3 select toStartOfMinute(_date) as time, count() as value from access_log where logic_ins_id = 500152868 and accept_time <= 1609087111000 and accept_time >= 1609000711000 group by time order by time; --Q4 select count(*) as c from ( select _date, accept_time, access_ip, type, total_time, concat(toString(total_time),'ms') as total_time_ms, sql, schema, succeed, process_id, inst_name from access_log where logic_ins_id = 501422856 and _date >= '2020-12-27 00:38:31' and _date <= '2020-12-28 00:38:31' and accept_time <= 1609087111000 and accept_time >= 1609000711000 );性能对比如下:trace_log(数据量569816761)ClickHouse中的建表语句如下:CREATE TABLE trace_local on cluster default ( `serviceName` LowCardinality(String), `host` LowCardinality(String), `ip` String, `spanName` String, `spanId` String, `pid` LowCardinality(String), `parentSpanId` String, `ppid` String, `duration` Int64, `rpcType` Int32, `startTime` Int64, `traceId` String, `tags.k` Array(String), `tags.v` Array(String), `events` String, KEY trace_idx traceId TYPE range ) ENGINE = MergeTree() PARTITION BY intDiv(startTime, toInt64(7200000000)) PRIMARY KEY (serviceName, host, ip, pid, spanName) ORDER BY (serviceName, host, ip, pid, spanName, tags.k); CREATE TABLE trace on cluster default as trace_local engine = Distributed(default, default, trace_local, rand());ClickHouse中的查询语句如下:--Q1 select * from trace prewhere traceId ='ccc6084420b76183' where startTime > 1597968000300000 and startTime < 1598054399099000 settings max_threads = 1; --Q2 select count(*) count, spanName as name from trace where serviceName ='conan-dean-user-period' and startTime > 1597968000300000 and startTime < 1598054399099000 group by spanName order by count desc limit 1000; --Q3 select host as name, count(*) count from trace where serviceName ='conan-dean-user-period' and startTime > 1597968000300000 and startTime < 1598054399099000 group by host; --Q4 select count(*) count, tags.k as name from trace array join tags.k where serviceName ='conan-dean-user-period' and startTime > 1597968000300000 and startTime < 1598054399099000 group by tags.k; --Q5 select count(*) spancount, sum(duration) as sumDuration, intDiv(startTime, 1440000000) as timeSel from trace where serviceName ='conan-dean-user-period' and startTime > 1597968000300000 and startTime < 1598054399099000 group by timeSel; --Q6 select count(*) spanCount, countIf(duration <=1000000), countIf(duration > 1000000), countIf(duration > 3000000) from trace where serviceName ='conan-dean-user-period' and startTime > 1597968000300000 and startTime < 1598054399099000; --Q7 select host, startTime,traceId,spanName,duration,tags.k,tags.v from trace where serviceName ='conan-dean-user-period' and startTime > 1597968000300000 and startTime < 1598054399099000 limit 1000000;性能对比如下:官方Ontime测试集Ontime测试集是ClickHouse官网上推荐的一个分析型查询benchmark,为了更加公证公开地对比ClickHouse和Elasticsearch在分析查询上的性能差异。作者也引入了这个数据集进行测试比对,结果如下所示,ClickHouse在纯分析型查询场景下具有巨大性能优势。Elasticsearch和ClickHouse完整版建表语句和查询下载:聚合分析场景用户画像场景(数据量262933269)用户画像场景也是用户比较难选择使用Elasticsearch还是ClickHouse的一个典型场景,该场景的具体特点是超大宽表,大批量更新写入,查询返回的数据量大,筛选条件复杂多变。用户在使用Elasticsearch时遇到的难点问题主要有两个:数据写不进去,导入慢;数据拉不出来,返回大规模明细数据非常慢。针对这个场景,作者根据真实用户的业务场景,mock了一张接近150列的大宽表进行相关的查询测试,具体的查询如下所示,每条查询返回的结果集在10万到100万行级别。Elasticsearch和ClickHouse完整版建表语句和查询下载:用户画像场景ClickHouse中的查询语句如下:--Q1 select user_id from person_tag where mock3d_like > 8 and mock3d_consume_content_cnt > 8 and mock_10_day_product_avg_amt < 1 settings append_squashing_after_filter = 1; --Q2 select user_id from person_tag where mock_7_day_receive_cnt > 8 and like_fitness = 1 and mock14d_share_cnt > 8 settings append_squashing_after_filter = 1; --Q3 select user_id from person_tag where home_perfer_mock_score > 8 and mock7d_access_homepage_cnt > 8 settings append_squashing_after_filter = 1; --Q4 select user_id from person_tag where is_send_register_coupon > 8 and mock1d_like > 8 settings append_squashing_after_filter = 1; --Q5 select user_id from person_tag where like_sports = 1 and like_3c = 1 and sex = 1 and like_dance = 1 and mock1d_share_cnt > 6 settings append_squashing_after_filter = 1; --Q6 select user_id from person_tag where mock14d_access_homepage_cnt > 8 and like_anime = 1 settings append_squashing_after_filter = 1; --Q7 select user_id,offline_ver,is_visitor,mock1d_comment_like,reg_days,mock14d_share_cnt,mock_30_order_avg_delivery_time_cnt,mock7d_comment_cnt,performance_rate,mock3d_valid_user_follow_cnt,mock30d_consume_content_cnt,like_cnt,like_photo,ls90_day_access_days,mock3d_release_trend_cnt,mock14d_access_homepage_range,qutdoor_perfer_mock_score,mock3d_access_homepage_cnt,mock_15_order_avg_delivery_time_cnt,mock7d_release_trend_cnt,like_food,mock30d_follow_topic_cnt,mock7d_is_access_topic,like_music,mock3d_interactive_cnt,mock14d_valid_user_follow_cnt,reg_platform,mock_7_day_lottery_participate_cnt,pre_churn_users,etl_time,like_anime,mock14d_access_homepage_cnt,mock14d_consume_content_cnt,like_travel,like_watches,mock14d_comment_like,ls30_day_access_days,mock14d_release_trend_cnt,ftooeawr_perfer_mock_score,mock7d_valid_user_follow_cnt,beauty_perfer_mock_score from person_tag where mock3d_like > 8 and mock3d_consume_content_cnt > 8 and mock_10_day_product_avg_amt < 1 settings append_squashing_after_filter = 1;查询性能结果对比如下,可以看出Elasticsearch在扫描导出大量结果数据的场景下,性能非常大,返回的结果集越大越慢,其中Q5是查询命中结果集很小的对比case。 二级索引点查场景(数据量1000000000)在分析查询业务场景中,用户难免会有几个明细点查case,例如根据日志traceId查询明细信息。开源ClickHouse因为没有二级索引能力,在遇到这种情况时,查询性能对比Elasticsearch完全落后。阿里云ClickHouse自研了二级索引能力,补齐了这方面的短板,作者在这里专门加了一个二级索引点查的场景来进行性能对比测试。Elasticsearch和ClickHouse完整版建表语句和查询下载:二级索引点查场景ClickHouse中的建表语句如下:CREATE TABLE point_search_test_local on cluster default ( `PRI_KEY` String, `SED_KEY` String, `INT_0` UInt32, `INT_1` UInt32, `INT_2` UInt32, `INT_3` UInt32, `INT_4` UInt32, `LONG_0` UInt64, `LONG_1` UInt64, `LONG_2` UInt64, `LONG_3` UInt64, `LONG_4` UInt64, `STR_0` String, `STR_1` String, `STR_2` String, `STR_3` String, `STR_4` String, `FIXSTR_0` FixedString(16), `FIXSTR_1` FixedString(16), `FIXSTR_2` FixedString(16), `FIXSTR_3` FixedString(16), `FIXSTR_4` FixedString(16), KEY SED_KEY_IDX SED_KEY Type range ) ENGINE = MergeTree ORDER BY PRI_KEY SETTINGS index_granularity_bytes = 4096, secondary_key_segment_min_rows = 1000000000, min_rows_for_wide_part = 2000000000; CREATE TABLE point_search_test on cluster default as point_search_test_local engine = Distributed(default, default, point_search_test_local, rand());ClickHouse中的查询模板语句如下:select * from point_search_test where SED_KEY = 'XXX' settings max_threads = 1;最终的查询性能对比如下,阿里云ClickHouse具备二级索引能力后,其点查能力完全不弱于Elasticsearch,存储原生支持的二级索引能力,具有极致性能。(阿里云ClickHouse二级索引文档)数据导入性能对比前面列举的所有数据集数据,作者都使用了ESSD本地文件导入的方式测试对比了Elasticsearch和ClickHouse的导入性能。ClickHouse可以直接使用ClickHouse-Client读取各种格式的本地文件进行导入,而Elasticsearch则是通过配置Logstash任务。具体耗时结果如下:结语Elasticsearch最擅长的主要是完全搜索场景(where过滤后的记录数较少),在内存富裕运行环境下可以展现出非常出色的并发查询能力。但是在大规模数据的分析场景下(where过滤后的记录数较多),ClickHouse凭借极致的列存和向量化计算会有更加出色的并发表现,并且查询支持完备度也更好。ClickHouse的并发处理能力立足于磁盘吞吐,而Elasticsearch的并发处理能力立足于内存Cache,这使得两者的成本区间有很大差异,ClickHouse更加适合低成本、大数据量的分析场景,它能够充分利用磁盘的带宽能力。数据导入和存储成本上,ClickHouse更加具有绝对的优势。
国内领先电商服务商——班牛通过阿里云数据库RDS从0到1搭建电商服务平台,目前已赋能1500余家世界500强及知名品牌,实现了数智化多场景落地。2017年,班牛创立之初就依托阿里云承载其整体业务,并选用阿里云数据库RDS PostgreSQL作为基建,支撑起了一万多商户的数据隔离,助力班牛PaaS化诸多场景落地。杭州正马软件科技有限公司(简称“正马软件”)创立于2017年,是国内领先的电商全周期客户服务与营销自动化服务提供商,其独立自主研发的核心电商PaaS产品——班牛,帮助商家实现15个平台服务业务一站式管理,已经服务欧莱雅、安踏、Babycare、阿迪达斯、美的、良品铺子、迪卡侬、戴森、李佳琦、KEEP等1500+品牌。随着正马软件业务的不断增长,原先的系统给技术团队带来了不少烦恼。首先,原有的数据库产品作为底层存储实现积木式工单场景,基于固定表的设计实现,存在诸多问题;其次,业务上有查询和分析的强烈需求,后期都将存在实施和业务落地困难的问题。在两大痛点面前,正马软件选择阿里云数据库RDS PostgreSQL,结合PostgreSQL的多租户功能、内置全文检索、并行计算、以及冷热数据分离存储、复杂SQL高性能优化等特点,巧妙地设计出了对应应用的PaaS化场景,为全行业商家提供客户提供专业的数智化解决方案,实现一体化、标准化、自动化、场景化、数据化管理。只需一个班牛平台,即可打通15个主流电商平台,还可无缝对接企业内外部业务系统,打破数据孤岛,提升数据流通性与复用性。通过如宜家式拼装能力的拖拉拽式班牛智能工单,使安踏等客户的服务人员快速上手,标准化智能工单组件满足了安踏个性化服务场景的搭建需求,学习零成本,服务人员使用即进入业务规范,大幅度降低人员培训成本,同时退款管理处理效率提升30%,退换货自动化比例达70%。此外,班牛还首创批量转账解决方案,帮助安踏通过智能工单直接驱动支付宝付款,一键转账实时到账,降低财务手工操作的支付风险,让售后款项支付更便捷,支付宝小额退款效率提升95%以上。班牛创始人熊大(花名)表示:“公司成立之初就和淘系开放平台保持着良好的合作关系,从使用阿里云ECS,成为阿里云数据库RDS第一个用户开始,到RDS PostgreSQL版本上线并合作使用,一路走来阿里云给了我们很多助力。未来,班牛将与阿里云一路携手,为企业客户提供更完善、更优质的服务管理解决方案,从而带动整体业务的提升。”目前,阿里云拥有国内最丰富的云数据库产品家族,其中,阿里云RDS是国内规模最大、最成熟、体系最完整的云数据库,已有超过40万个数据库迁移到阿里云上。在Gartner公布的2020年度全球数据库魔力象限中,阿里云首次挺进全球数据库第一阵营——领导者象限。
作者:姑胥背景PolarDB-X作为一款云原生分布式关系型数据库[6],支持通过拆分规则将一张逻辑表的数据分布在多个数据节点中。同时,也支持变更拆分规则,将数据进行重新分布[9]。数据重新分布(Repartition)的能力是分布式数据库的核心能力之一。当业务规模扩大时,它可以将数据分布到更多的节点,以达到水平扩展(ScaleOut)的目的。当业务规则剧烈变化时,它也可以将数据以新的拆分规则分布,从而提高查询的性能,更好地适应新的业务规则。下图展示如何将一张单表,通过一条简单的DDL语句online地变更为一个分库分表。数据如何重新分布?其实早在分布式数据库中间件大行其道的年代,开发人员就尝试过各种方式来解决数据Repartition的问题。但是无一例外,这些方案都非常复杂和危险,开发人员通常只能在半夜流量低峰期做Repartition,前前后后需要准备好详细的设计、论证、回滚方案。并且基于分布式的特点,在新老数据切换的那一段时间,几乎总是得在数据一致性和可用性之间做一个痛苦的选择。所以很多方案会有一个停写阶段或数据比对阶段,或两者皆有。PolarDB-X充分论证了分布式数据库Repartition的各项技术细节后,能够做到Repartition过程中数据强一致、高可用、对业务透明、一条简单DDL语句搞定。数据Repartition的关键问题本文讨论的主要是表级别的数据Repartition。比如:将一张拆分表的数据按照新的规则重新分布到不同的数据节点。但是重新分布的过程需要时间,这段时间内又会有新的增量数据进入系统。当所有的数据都重新分布完成后,要将新老数据进行切换,最后停止双写并删掉所有的老数据。仔细研究上述过程的细节后,可以将其概括为这3个关键子问题,后续我们将按照这3个子问题评估各个实现方案:如何进行存量、增量的数据同步?比如:应用层双写、数据同步中间件、数据库触发器等。如何进行流量切换?主要考虑读写流量在新老数据中的迁移。如何控制Repartition的整体流程?主要考虑如何组合各个步骤,比如什么时候开启/停止增量数据同步,什么时候进行流量切换。如果故障了怎么恢复或回滚等。传统的Repartition方案一个传统的Repartition流程大概是这样的:应用集群初始状态的读写流量都在老的数据表上。增加新表并开启双写。双写方式可能是应用层双写,或基于binlog数据同步,或数据库触发器实现双写等。但这个步骤,如果没有事务保证的话就很容易造成数据不一致。启动存量数据同步。可能会启动一个数据比对服务,如果有数据不一致的话,也需要人工介入处理。当存量数据同步完成,增量数据追平后,将读流量切到新表,保持双写一段时间。撤走老表的写流量,完成Repartition但是如果我们仔细推敲一下,发现很多步骤根本就没办法保障数据一致性:上述第2步,开启双写时,分布式系统无法保证所有节点都一起开启双写。所以一定有一段时间只有部分节点开启了双写。那就会出现Orphan Data Anomaly的问题(后文将说明)[2][8],造成新老表的数据不一致。传统的Repartition方案要解决这个问题的话,只能停写。同理,上述第6步撤走老表的写流量时,也会出现数据不一致问题。上述第2步,增量数据的双写一致性难以保障。除了数据一致性问题外,依然存在很多繁琐但依然十分重要的问题:整个流程基本由人工串联,多个环节需要人工介入。所以前期需要学习和理解成本,过程中也需要参与成本。传统方案如果要避免数据不一致问题,在关键节点需要停写,这会对业务造成干扰。整个流程十分脆弱地粘合在了一起,容错性非常差。PolarDB-X的Repartition方案PolarDB-X 是一款存储与计算分离的分布式数据库,所以在架构中会分为计算节点(CN)和存储节点(DN),以及一个元数据库(GMS)。CN负责SQL的parse、optimize、execute;DN负责数据存储;GMS负责存储元数据。为了性能考虑,每个CN节点都会缓存一份元数据。如何进行存量、增量数据同步增量数据双写在分布式的增量数据双写场景中,双写的2端经常会分布在不同的DN节点中,所以自然地单机事务无法使用。而前文已经论证过,XA事务、binlog同步、触发器都无法保证双写的2端数据强一致。PolarDB-X使用了内置的基于TSO的分布式事务[7](👈感兴趣的同学可以点击直达文章)实现增量数据同步,从而保证了Repartition过程中,任意时刻读流量切换的数据的强一致性。值得注意的是,如果一行数据的拆分列的值被修改了,那这行数据可能会路由到另一个数据节点[1]。所以这时候执行的其实是:原数据节点的delete操作+新数据节点的insert操作。在Repartition过程中,因为前后的拆分规则不同,所以一行数据的update操作,可能会演变成4个数据节点参与的分布式事务(如果有全局二级索引会更多)。PolarDB-X会处理好所有这些工作,用户无需任何感知,可将PolarDB-X视为一个单机数据库操作。存量数据同步在存量数据同步时,PolarDB-X会分段进行同步。每一段同步过程中,PolarDB-X会在TSO事务中,先尝试获取源端数据的S锁,然后再写入目标端。如果目标段已经有相同数据,则表明增量双写阶段已经将这些数据同步过了,忽略即可。分布式事务与单机事务一样,会产生死锁。当存量同步而给原表某段数据加S锁时,如果业务Update流量较大,可能导致分布式死锁的发生,PolarDB-X有一个分布式死锁检测模块可以解决这个问题。死锁解除后,存量同步模块会重试。如何进行流量切换Online Schema Change首先我们来看一下前文提到的Orphan Data Anomaly问题[2]是怎么发生的:当开启增量数据双写时,PolarDB-X的CN节点内存中的元数据不是同时刷新的,而是有一个先后顺序,所以一定存在一段时间,某些CN节点开启了增量双写,而另一些没有。于是会出现这样的情况:计算节点CN0已开启双写,并在老表和新表分别写入了3条数据,如下图计算节点CN1未开启双写,执行了delete语句,删除了id为3的一条数据,但是未删除新表的数据新表和老表出现了数据不一致这一问题在Google的Online, Asynchronous Schema Change in F1这篇论文中有过详细的论证。PolarDB-X引入了Online Schema Change(👈感兴趣的同学可以点击直达文章)以解决此类的问题,在此不再赘述。对于Repartition来说,引入下图中的这些状态,以此保证任意2个相邻状态都是兼容的,不会发生数据一致性问题。我们可以具体来看看其中几个最关键的状态:target_delete_only和target_write_only:如上所述,当我们有多个CN节点时,直接开启增量双写会导致Orphan Data Anomaly问题。所以在开启双写之前,我们先让所有的CN节点都达到target_delete_only状态,然后再达到target_write_only状态(就是双写状态)。CN节点在target_delete_only下,只会执行delete语句(如果是update,则转换成delete执行)。代入上图中的例子:CN1会先达到target_delete_only状态,所以即便未开启双写,也能够删除新表中id为3的数据,从而保证了数据的一致性。source_delete_only和source_absent:前文也论述过,如果直接停掉老表的双写,会造成数据不一致。所以我们在source_absent(也就是停止了双写的状态)之前引入了一个source_delete_only状态。它也保证了老表在下线过程中不会产生Orphan Data Anomaly问题。如何控制整体流程并保证稳定性PolarDB-X以DDL的形式为用户提供拆分规则变更(也就是Repartition)的能力,DDL也需要保障ACID特性,但数据重新分布可能需要花费较长时间,系统因断电等原因而故障在所难免。DDL引擎所以PolarDB-X也实现了一套稳定的DDL执行框架,它会将DDL分成很多个步骤,每个步骤都是幂等的。这样就可以保证DDL任务可以随时中断,然后恢复继续运行或者回滚。通过DDL将所有的步骤串联,也将所有的人工操作排除在外,开发人员再也不需要做Repartition的方案设计,再也不需要半夜三更手动执行数据库的高危操作了。分布式MDL死锁检测MySQL在5.7版本引入Online DDL能力后,使得DDL能够更好地与读写事务并行,相比于之前的版本有了很大的改善。Online DDL的基本原理是只在关键的时刻获取MDL锁,而不是在整个DDL阶段都持有MDL锁。PolarDB-X在执行Repartition时也会分多个阶段获取多次MDL锁,从而允许事务达到更高的并发度。但MDL锁有个危险的特性是:它是一个公平锁,并且可能造成MDL死锁。多次获取MDL锁提高了性能的同时,实际上增加了MDL死锁产生的概率,而MDL死锁一旦发生,会导致后续所有的读写事务都被阻塞,MySQL的MDL默认超时时间1年,危害远大于普通的数据死锁。因此PolarDB-X也提供了一个分布式MDL死锁检测模块,用于在关键时刻解除分布式MDL死锁。总结灵活的拆分规则变更能力是分布式数据库的重要特性。PolarDB-X中有3种表类型:单表、广播表、分库分表。使用拆分规则变更能力,用户可以将数据表进行任意的表类型转换,从而更好地适应业务发展。PolarDB-X在提供拆分规则变更能力的同时,保证了数据的强一致、高可用、对业务透明、并且使用起来非常方便。本文简单阐述了PolarDB-X实现拆分规则变更过程中使用到的各项技术点,如读者可见,我们之所以将拆分规则变更能力集成到数据库内核中,是因为很多数据一致性问题非此不可解决。这也是分布式数据库区别于分布式数据库中间件的重要特性之一。拆分规则变更能力只是PolarDB-X诸多特性中的一个。想要了解更多内容,欢迎关注公众号内的其他文章。参考文献Asymmetric-Partition Replication for Highly Scalable Distributed Transaction Processing in PracticeOnline, Asynchronous Schema Change in F1What’s Really New with NewSQLhttps://dev.mysql.com/doc/refman/5.6/en/innodb-online-ddl.htmlhttps://dev.mysql.com/doc/refman/5.6/en/metadata-locking.htmlhttps://zhuanlan.zhihu.com/p/289870241https://zhuanlan.zhihu.com/p/329978215https://zhuanlan.zhihu.com/p/341685541https://zhuanlan.zhihu.com/p/346026906【相关阅读】子查询漫谈探索 | PolarDB-X:实现高效灵活的分区管理分布式数据库如何实现 Join?PolarDB-X 让“Online DDL”更Online
撰稿:PolarDB新型存储引擎团队历史数据归档的问题大部分业务数据的读写特征,都是最新产生的数据会更频繁地被读取或者更新,而更久之前的数据(如1年之前的聊天记录,或者订单信息)则很少会被访问, 而随着业务运行时间的增加,数据库系统中会沉淀大量很少甚至不会被访问到的数据,这部分数据和最新产生的数据混合在一起会产生一系列问题:历史数据和最新的数据存储在一个数据数据库系统中,导致磁盘空间不足。大量数据共享数据库内存缓存空间,磁盘IOPS等,导致性能问题。数据量太大导致数据备份时间过长甚至失败,而且备份出来的数据存放也是一个问题。针对此问题,一种做法是对历史数据做归档,将长期不使用的数据迁移至以文件形式存储的廉价存储设备上,比如阿里云OSS或者阿里云数据库DBS服务。然而实际业务系统中,历史数据并不完全是静态的,针对几个月甚至几年前的“旧”数据依旧存在实时的、低频的查询甚至更新需求,在阿里巴巴内部,类似淘宝/天猫的历史订单查询,企业级办公软件钉钉几年前的聊天信息查询,菜鸟海量物流的历史物流订单详情等。为了解决历史数据的读取和更新问题,可以使用一个单独的数据库系统作为归档数据的存储目的地,称之为历史库。业务对单独的历史库系统一般具有如下的诉求:具有非常巨大的容量,业务可以放心持续将线上数据保存到历史库中,而不用担心容量问题。支持和在线数据库系统一样的访问接口,如都是MySQL协议等,业务可以和在线业务相同的接口访问历史库。必须具有低廉的成本,如使用压缩减少数据所占磁盘空间,廉价存储介质等,确保可以使用较大的代价保存海量的数据。具备一定的读写能力,满足低频的读写需求。作为世界上使用最广泛的开源数据库系统,MySQL生态中一直缺乏一个好用的历史数据归档存储方案,既满足大容量低成本同时又具备一定的读写能力。虽然业界曾经推出过一些高压缩引擎如TokuDB,MyRocks等,但是受限于单物理机磁盘容量限制,存储的数据量有限。PolarDB历史库的推出即为满足这一需求。PolarDB历史库产品阿里云数据库团队将公司内部广泛使用的高压缩引擎X-Engine引擎与PolarDB相结合,使得PolarDB同时支持InnoDB引擎和X-Engine引擎,其中InnoDB引擎负责在线业务的高性能混合读写,X-Engine引擎负责归档数据的低频读写。在PolarDB双引擎的架构上,我们推出了一款主要基于X-Engine引擎存储的数据库产品:PolarDB历史库。历史库单实例的存储空间上限为200TB,结合X-Engine引擎3~5倍的压缩能力,可提供近600TB~1PB的原始数据存储能力,能满足绝大部分客户的历史数据归档对存储容量的需求。使用PolarDB 历史库(X-Engine)具有如下几个优势:超大的容量,200TB的存储空间加上X-Engine数据压缩能力,可提供超500TB以上的原始数据存储容量,同时容量按需付费,不用预先为未来的数据增长预备存储空间。PolarDB历史库与官方MySQL的协议一致,相比于将历史数据备份到HBase等NoSQL产品,业务应用程序不用修改代码即可同时访问在线库和历史库。借助PolarDB底层共享存储提供的快速备份能力,再大的实例也可以实现对数据的快速备份,备份数据上传到OSS等廉价存储设备,确保数据永不丢失。由于PolarDB 历史库提供了超大存储容量,它可以同时作为多个业务历史数据的汇聚地,以方便对所有历史数据进行集中存储和管理,用户可以在如下几个场景中使用历史库:将PolarDB 历史库作为线下自建数据库实例的冷数据存储地,线下自建数据库服务包括且不限于MySQL/Postgre/Sql Server等关系数据库。将PolarDB历史库作为阿里云RDS MySQL或者PolarDB MySQL数据库服务的归档存储地,将较少访问到的历史数据迁移到PolarDB X-Engine中存储,释放在线实例的空间以降低成本并提升性能。直接将PolarDB 历史库作为大容量关系数据库使用,以满足一些写入数据量巨大,但读频次较低的业务的需求(如系统监控日志等)。在线库和历史库之间的数据迁移,可以使用阿里云DTS或者DMS进行,其中DTS可以持续将在线库的内容同步到历史库,而DMS则可以周期性的将在线数据批量导入到历史库。PolarDB历史库技术架构PolarDB历史库功能的推出依赖阿里巴巴数据团队之前在数据库和存储等方向上的创新和突破:阿里巴巴自研的基于LSM-tree架构的存储引擎X-Engine提供了强大的数据压缩能力,满足了归档数据库对低存储成本的要求。PolarDB借助于共享分布式存储服务,实现了存储容量在线平滑扩容,同时计算节点和存储节点之间采用高速网络互联,并通过RDMA协议进行数据传输,使I/O性能不再成为瓶颈。集成到PolarDB的X-Engine引擎同样获得了这些技术优势。下面我们分别讲解X-Engine引擎的基础特点,以及如何将X-Engine与PolarDB相结合以提供一个有竞争力的历史库技术方案。X-Engine存储引擎PolarDB历史库通过引入X-Engine获得存储空间节省的优势,X-Engine引擎可以用如下几个关键点对其进行描述:X-Engine使用了LSM-Tree的分层架构,最近写入的热点数据和历史写入冷数据分开索引,同时创新性的使用事务流水线技术,把事务处理的几个阶段并行起来,极大提升了写入吞吐。分层存储底层的数据是大部分时候为静态只读,在数据页中所有记录采用前缀编码,同时每个数据页中的数据都是紧凑排列不会留空洞,最后底层数据都会默认进行压缩,因此相比原始数据可获得数倍的空间压缩。X-Engine对传统LSM-tree性能影响比较大的Compaction过程做了大量优化,如拆分数据存储粒度,利用数据更新热点较为集中的特征,尽可能在合并过程中复用数据。精细化控制LSM的形状,减少I/O和计算代价,有效缓解了合并过程中的空间增大。X-Engine本身的实现非常复杂,远非几句话可描述,本篇不对其展开详细讲述。X-Engine在阿里巴巴集团内部就作为一个自研引擎集成到AliSQL之中,也集成到公有云RDS MySQL当中,作为归档引擎售卖,而现在我们将其集成到了PolarDB当中。融合InnoDB/X-Engine引擎PolarDB的最初版本是基于InnoDB引擎设计的,其技术架构可以参见文章PolareDB产品架构,在InnoDB引擎上实现物理复制,并在此基础上支持一写多读已经非常具有技术挑战。X-Engine是一个完整独立的事务引擎,具有独立的REDO日志,磁盘数据管理,缓存管理,事务并发控制等模块,将X-Engine移植进PolarDB并实现双引擎的一写多读更具挑战。我们通过大量的工程创新将PolarDB带入双引擎时代:合并X-Engine的事务WAL日志流和InnoDB的REDO日志流,实现了一套日志流和传输通道同时服务于InnoDB引擎和X-Engine引擎,管控逻辑以及与共享存储的交互逻辑无需做任何改变,同时未来新增其他引擎时也可以复用发这套架构。将X-Engine的IO模块对接到PolarDB InnoDB所使用的用户态文件系统PFS上,如此实现InnoDB与X-Engine共享同一个分布式块设备. 同时依靠底层分布式存储实现了快速备份。在X-Engine中实现了基于WAL日志的物理复制功能,并且一步到位的引入并行WAL回放机制,实现了RW节点与RO节点之间毫秒级别的复制延迟。在此基础之上,我们实现了在RO上提供支持事务一致性读的能力。除了涉及到X-Engine支持一写多读需要支持的功能改造之外,PolarDB X-Engine还有很多项工程改进,如针对历史库场景大表DDL的问题,除了部分支持instant DDL的schema变更操作,X-Engine也支持并行DDL功能,对那些需要copy表的DDL操作进行加速。在PolarDB双引擎架构下,我们实现了在一套代码下支持两个事务引擎的一写多读,保证了PolarDB产品架构的简洁和一致用户体验。PolarDB X-Engine普惠版PolarDB集群版基于共享存储实现了一写多读,集群中有一个主节点(可读可写)和至少一个只读节点,但是在历史库场景下,用户一般需要巨大的存储容量,但由于读写量较小,RW节点的计算资源都无法利用完,更无须RO节点提供的读扩展能力。在RW和RO规格相同时,相当于浪费了一半的计算资源。借助X-Engine引擎带来的数据压缩能力,可以降低客户的存储成本,而在历史库当中我们使用单RW节点来提供服务,省去了RO节点的计算资源成本。当然去除了RO节点,在灾难场景,如RW节点异常Crash时,需要更长的崩溃恢复时间。但是依靠底层分布式存储提供的高可用能力,我们依然提供了99.95%的可用性。在历史库这样一个低频读写的场景(很多时候数据为异步批量导入到历史库),用稍低一点的可用性换取成本节省,对很多用户是可以接受的。而对于那些对可用性要求比较高的客户,我们也即将在PolarDB集群版本中提供X-Engine引擎,在降低存储成本的同时,提供与标准版一样的可用性指标。历史库单节点架构下,日常不提供RO节点, 在需要对节点进行运维操作,如进行节点升级需要重启时,通过部署临时的RO节点并升级为RW节点的方式,可以降低升级操作对客户读写的影响。单节点时节点替换流程如上图所示,影响业务的时间为替换过程中HA将流量从原RW切换到新的RW的瞬间。PolarDB X-Engine的性能在PolarDB内核多年的技术积累以及PolarStore提供的极致性能基础上,PolarDB X-Engine在提供极低存储成本的同时,也保证了足够的性能满足业务的诉求。下面我们展示PolarDB 历史库的性能数据。PolarDB X-Engine各规格的纯写性能PolarDB X-Engine各规格的只读性能PolarDB X-Engine混合读写性能
作者:方物需要的背景知识为确保理解顺畅,在阅读之前请提前了解以下概念或原理:关系型数据库中的关系代数模型数据库表达式计算中的三值逻辑与数据库交互的声明式语言 SQLSubquery 与 SemiJoin窗口函数、lossless join理解子查询定义它的定义是如此简单,衍生出来的场景却又如此复杂;它既是一个表达式,也是一个查询树;它灵活到可以成为一个常量,也可以随时表达集合间的关系;SQL 中写入子查询如此自然,真正跑起来时却……根据 SQL 标准,将一个查询块嵌套进一个表达式中,我们就得到了一个子查询。关系型数据库中的查询块,通常由关系代数算子组成的树状计划来表达。而每个算子中的表达式与外层的查询树的关系是暧昧不清的。查询树中的表达式藏着另一棵查询树,本质在于通过表达式来描述查询树间的关系。比如说自定义函数、比较运算符、逻辑运算符。由于表达式类型繁多且过于复杂,很难直接通过传统的关系代数抽象模拟子查询与主查询间的关系。这让子查询成为关系型数据库中实现的难点之一。既然子查询的实现对数据库来说如此复杂,执行的效率也未必高,为何还要实现这种 SQL 语法呢?除了 SQL 标准以外还有其它原因吗?... WHERE AGE > ALL(SELECT AGE ...) ... WHERE SALARY <= ANY(SELECT SALARY ...)将各个查询树理解为一个数据集,通过 JOIN 来描述数据间的交并集关系在部分场景下是非常晦涩的。但观察以上 SQL, AGE >ALL 和 SLARY<ANY 的子查询描述非常接近自然语言,能大大简化用户构建复杂查询的过程。因此从某种意义上来讲,可以将子查询理解为一种历史悠长的 SQL 语法糖。一言以蔽之,子查询就是一个把复杂留给数据库,把简单送给用户的经典案例。分类想要了解子查询,必须先对其进行分类。从语义上可以将其划分为:标量子查询( Scalar Subquery )输出一行值。普通算子输出的二维表(行与列),类似于向量;与之对应称只输出一行一列的子查询为标量。SELECT c_custkey FROM CUSTOMER WHERE 1000000 < ( SELECT SUM(o_totalprice) FROM ORDERS WHERE o_custkey = c_custkey )集合比较( Quantified Comparision ) ALL(全部满足) ANY(只要满足一个)SELECT c_name FROM CUSTOMER WHERE c_nationkey <> ALL (SELECT s_nationkey FROM SUPPLIER)存在性测试( Existential Test )EXISTS(存在) NOT EXISTS(不存在)SELECT c_custkey FROM CUSTOMER WHERE c_nationkey = 86 AND EXISTS( SELECT * FROM ORDERS WHERE o_custkey = c_custkey )语义上的划分更易理解,但数据库会基于逻辑运算的角度抽象出另一种划分方式:Scalar:俗称标量子查询,对外输出一行Semi:(EXISTS, IN, ANY) 可拆解为析取的逻辑关系,例如 AGE IN (SELECT AGE) 子查询可表达为 a.age=b.age[0] OR a.age=b.age[1] OR ... OR a.age=b.age[n]Anti-Semi:(NOT EXISTS, NOT IN, ALL) 可拆解为合取的逻辑关系,例如 AGE >ALL (SELECT AGE) 子查询可表达为 a.age>b.age[0] AND a.age>b.age[1] AND ... AND a.age>b.age[n]将子查询进行逻辑拆解后,并不代表它就是一个普通的表达式。关系型数据库的表达式计算过程中,函数会尽量以行数据作为输入输出。对集合进行处理的函数通常会抽象为一个新的算子,例如聚合函数会单独用 Agg 算子处理。同样的道理子查询也应该抽象为一个单独的算子。算子与表达式切割时的边界划分问题也遵循相同的逻辑。边界数据库引擎一般不会将复杂的数据集运算混淆到表达式计算中,从实现的复杂度考虑,也会尽量明确集合运算与行运算的边界。Scalar:由于它的输出仅有一行,子查询的边界可以划分到查询块上。SEMI/ANTI:查询块可以输出多行, 只有配合子查询的 ANY,ALL 表达式才能将输出限制为一行 boolean 值。因此边界必须包含子查询表达式前面的入参。最后基于子查询本身是否有关联项,还可以分为关联子查询及非关联子查询。凡是非关联子查询本质上都可以视做为一个常量,而关联子查询处理时必须要考虑子查询内外层的数据关系。对这层数据关系的处理便是子查询优化技术的重点。优化技术SQL 中带有子查询会导致查询效率急剧下降吗?SQL 作为一种声明式的语言,仅仅描述了它需要什么样的数据,具体怎么操作则还要看数据库自己的发挥。查询优化技术发展到如今(2021),子查询必然会导致性能下降的说法已经过于武断了。关联子查询的本意为外表每一行数据与子查询集合数据的运算。当外表数据行过多时,这一嵌套的过程将不可避免导致性能低下。因此子查询优化很重要的一步就是去关联化(unnesting),如今的去关联技术已经日趋成熟,HyPer 2015 年就宣称自己能够《Unnesting Arbitrary Queries》。去关联化(Unnesting)上一节提到,由于处理的是集合数据,子查询应该从表达式中剥离出来,以算子的方式展示在执行计划中。这个指代子查询的算子一般称之为 Apply。Apply 这个名字来源于 LISP:指一类特殊的函数,入参是一个参数列表的集合,返回值是对应的一个结果列表集合。从关系代数数据库语义上看与关联子查询的嵌套执行过程类似。最早由微软 SQL Server 论文提出。将子查询转为 Apply 算子后,关联项还残留在子查询的查询树内,所以没有办法直接将 Apply 以 Join 的方式处理。因此,子查询优化通常最重要的一步是去关联化。SELECT c_custkey FROM CUSTOMER WHERE 1000000 < ( SELECT SUM(o_totalprice) FROM ORDERS WHERE o_custkey = c_custkey )我们以上面 SQL 为例,直观地感受一下,为什么说关联子查询的去关联化是十分必要的。下面是未经去关联化的原始查询计划(Relation Tree)。实际执行时,查询计划执行器(Executor)在执行到 Apply 时,针对每一行数据,都要去执行一遍 Apply 右侧的查询树。这样通过嵌套的方式处理关联子查询,处理耗时会随数据量增长呈直线上升状态,如果是多层子查询嵌套,耗时呈指数级上升也不奇怪。为了避免长耗时带来的糟糕体验,必须要将子查询去关联化。基于规则去关联化(Unnesting base on rule)上世纪八九十年代,SQL 标准拓展了子查询的存在范围。掀起了一股研究如何去关联化的热潮,当时研究的主流之一便是基于规则的去关联化。在 01 年发布的《Orthogonal Optimization of Subqueries and Aggregation》论文是基于规则去关联化的集大成作,其中总结了 9 条转换规则:下图中的两次转换分别对应规则 3 与 规则 1。示例中的转换过程是有前提条件的,Join 与 Filter 间的关系契合 AND 逻辑运算符是关键。子查询作为表达式,在 Filter 中起到过滤的作用执行计划中从下到上起到过滤作用的节点(例如 Filter 和 Join),上下叠加的逻辑关系是 AND子查询转为 SemiJoin 后,与上层 Filter 节点叠加,不会破坏与 Filter 中抽离后的表达式间的 AND 关系,但会破坏 OR 关系(比如下面展示的复杂例子)基于规则的转换无法处理所有模式的子查询,比如以下几个例子:// 复杂示例1 disjunction 中的子查询 SELECT * FROM supplier a WHERE s_address IN (select s_name from supplier where a.s_phone=s_phone) OR s_name='1' // 复杂示例2 子查询包含聚合与非等值关联项 SELECT * FROM T1 WHERE AGE > (SELECT AVG(AGE) FROM T2 WHERE T1.NAME!=NAME) // 复杂示例3 多层嵌套子查询 ... (SELECT * FROM T1 WHERE ID IN (SELECT ID FROM T2 WHERE T3.NAME=NAME)) ...Magic Set 去关联化(Unnesting base on magic)Magic Set 是一种非常古老的数据处理技术,最早应用在演绎数据库(Deductive Database)中。如今在关系型数据库中子查询去关联化上也发挥着很重要的作用。96 年 DB2 《Cost-Based Optimization for Magic: Algebra and Implementation》将 Magic Set 作为一个关系代数算子引入到 CBO 中。15 年 HyPer 基于此发展出了自己的 side-ways information passing optimization 技术,用于处理所有类型子查询的 Unnesting。HyPer 官方网站的执行计划展示中,将类似的算子命名为 Magic。考虑这样的一条 SQLSELECT s.name, e.course FROM students s, exams e WHERE s.id=e.sid AND (s.major = ’CS’ or s.major = ’Games Eng’) AND e.grade >= (SELECT avg(e2.grade)+1 --one grade worse FROM exams e2 --than the average grade WHERE s.id=e2.sid OR --of exams taken by (e2.curriculum=s.major AND --him/her or taken s.year>e2.date)) --by elder peers这个 SQL 的 Unnesting 主要难在有非等值关联项 s.year>e2.date,这将导致无法在关联项所在的 filter 层避免 Join 计算。△图例来自 HyPer 的论文《Unnesting Arbitrary Queries》在 HyPer 的论文中,尝试将关联项涉及列的数据 copy 一份,引入到子查询内部。用 Join 计算来替换关联项,从而实现了去关联化。从实现上看,是以一部分空间和额外的 Join 计算为代价,实现了子查询的去关联化。Semi Join 算子衍生数据库通常会利 Semi Join 算子簇表达去关联化后的子查询。这里会遇到另一个问题,Apply 与 Semi Join 的关系代数定义无法完全等价。semi join :“半连接”,意味着仅输出一张表的列,另一张表的列不向上层输出// 复杂示例1 disjunction 中的子查询 SELECT * FROM supplier a WHERE s_address IN (select s_name from supplier where a.s_phone=s_phone) OR s_name='1'考虑以上 SQL,子查询处于 OR 表达式之间,这会导致无法将其简单的转为 SemiJoin,因为 Filter 与 SemiJoin 叠加的过滤关系与 OR 相违。Mark Join :除了输出连接数据以外,还会保留一个 mark 位置,用来标记这一行的连接结果(TRUE/FALSE/NULL)。针对这个场景, HyPer 引入了 Mark Join 替换 SemiJoin 。在 Mark Join 上层的 Filter 中,会形成 markvalue OR sname='1' 的表达式。通过增加一列输出的方式避免与 OR 语义的违背。采用 Mark 机制,让 Join 多输出了一列,不但破坏了 Join 的关系代数含义,执行层也需要做相应的大规模改造。但它除了能解决上面示例中的 OR 子查询以外,对 Project、Scalar 类子查询也可支持,这种做法很有借鉴意义。截至 HyPer 2015 年的论文《Unnesting Arbitrary Queries》发出为止,传统的数据库厂商如 SQL Server 、Oracle 都不支持 Project 中的非 Scalar 子查询,对于复杂子查询的 Unnesting 支持也十分有限,HyPer 作为第一个号称可以 Unnesting 所有子查询的数据库,确实是一个非常激进的实践者。子查询的重写优化技术Oracle 2009 年发表的 《Enhanced subquery optimizations in Oracle》向大家展示了数量繁多的子查询重写优化。这些技术基于 TPC-H 中的子查询做了针对性的优化,估计是 Oracle 的程序员在 TPC-H 打榜时写的。这些重写技术对参数的提取和推导有很高要求。随便列一下其中的内容:子查询间的合并将多个类似子查询合并的技术,如下图的 Q2 转 Q3 ,Q4 转 Q5子查询与主表合并子查询除了与类似的子查询合并以外,还可以与主表合并。例如 Q8 转 Q11窗口函数优化最早 IBM 在 《WinMagic : Subquery Elimination Using Window Aggregation》 一文中提出了基于窗口函数的子查询优化技巧。而 Oracle 也将窗口函数改写作为其子查询重写技术的代表之一。窗口函数优化:查询改写(RBO)阶段触发。在满足一定条件时,将带 agg 的连接关系转化为窗口函数。是为了避免全表扫描而应用的连接(或子查询)重写技术。简单讲就是带 Agg 的子查询被重写成了窗口函数。重写的前提是外部查询块包含所有子查询的表以及过滤条件。例如:Q1: SELECT T1.X FROM T1,T2 WHERE T1.y = T2.y and T2.name='ming' and T2.z relop (SELECT AGG(T2.w) FROM T2 WHERE T2.y = T1.y and T2.name='ming');外部查询块有 T1 和 T2 两张表,包含了子查询中所有的查询表(T2);并且同时也包含了所有的过滤条件(T2.name='ming')。relop 是关系操作符的简写。满足以上条件之后就可以将 Q1 重写为 Q2:Q2: SELECT T1.x FROM T1,( SELECT AGG (T2.w) OVER (PARTITION BY y) AS win_agg, z, y FROM T2 WHERE T2.name='ming' ) V WHERE T1.y=V.y and V.z relop win_agg如果 T1 与 T2 的连接是无损连接(lossless join)的话,Q1 可以转为 Q3:Q3: SELECT V.x FROM ( SELECT T1.x, T2.z, AGG (T2.w) OVER (PARTITION BY T2.y) AS win_agg FROM T1, T2 WHERE T1.y = T2.y ) V WHERE V.z relop win_agg关于 lossless join 这里不做展开讨论,实际上 Q2 和 Q3 之间的区别可以理解为 Agg 与 Join 的 reorder。在将 Q1 重写为 Q2 之后,会在 CBO 中根据 Cost 判断是否需要转化为 Q3 的形式。这里举个具体的例子 Q4(TPC-H Q17):Q4: SELECT sum(l_extendedprice) / 7.0 AS avg_yearly FROM lineitem, part WHERE p_partkey = l_partkey AND p_brand = 'Brand#23' AND p_container = 'MED BOX' AND l_quantity < ( SELECT 0.2 * avg(`l_quantity`) FROM lineitem WHERE l_partkey = p_partkey );在 50G 的场景下,各个 plan 的计算量如下所示:原始执行计划重写为窗口函数后,明显可以看到减少了一次 10^8 级别的扫描。对比重写为窗口函数的两个执行计划,从处理的行数可以看到 Plan2 窗口函数的 Cost 要小很多。但要转成 Plan2 的情况需要一些前提条件,类似 Agg 与 Join reorder 的判断。在 lossless join 的场景下是可以直接转为 Plan2 的,Q4 part 与 lineitem 之间是外键连接,因此是符合这个条件。另外 Plan2 的 Join 利用 Batch Key Access 算法,相当于对 lineitem 表进行 10^4 次索引扫描,Cost 极低。窗口函数优化对 TPC-H Q2 和 Q17 都有显著的 RT 提升效果,在分布式数据库中,实测窗口函数 + BKA 优化对 Q17 有近百倍提升。执行陷阱查询优化并不是子查询技术的全部,从实现角度分析,执行过程中的陷阱更加防不胜防。CountCount 陷阱主要存在于没有 group by 的 Count 子查询中。考虑以下 SQL:SELECT * FROM T1 WHERE T1.NUM = (SELECT COUNT(ID) FROM T2 WHERE T1.AGE=AGE)如果将其转为以下的执行树,当 T2 的某 age 数量为 0 时,SemiJoin 的连接会因为 T1.age=T2.age 表达式结果为 Null,从而无法输出正确的结果。本质上的原因是由于 COUNT 聚合函数的特殊性导致。想要解决必须要 Join 节点输出 Null 行,类似 Left 类型的特性。其它的聚合函数也有些不同的问题,比如说 >ALL 类子查询无法直接转化为 >MAX。因为在空结果集的状态下,>ALL 返回 TRUE,而 >MAX 返回 FALSE。Null-Aware Anti Join观察以下两个子查询,想要输出 User 表中不同名或不同年龄的人:// SQL1 SELECT * FROM USER T1 WHERE AGE NOT IN (SELECT AGE FROM USER T2 WHERE T1.NAME=T2.NAME); // SQL2 SELECT * FROM USER T1 WHERE NOT EXISTS (SELECT 1 FROM USER T2 WHERE T1.NAME=T2.NAME AND T1.AGE=AGE);表面看上去两个子查询是等价的,实际上假如 USER 表中有一行 age 为 NULL 的数据,SQL1 不会输出,SQL2 会将其输出。NOT EXISTS 等价于 Anti Join,Anti Join 的执行器在处理 on 条件时,如果执行结果为 null,则认为没有匹配。问题在于,not in 的子查询的操作数也转成了 on 条件之一,与关联项通过 and 连接。这时就会出现多输出的正确性问题。那么这种情况下,可以选择不将 NOT IN 子查询转为连接,例如 PG 在处理 NOT IN 子查询保留了原来的处理方式。但还有一种选择,Oracle 在 《Enhanced Subquery Optimizations inOracle》一文中提到,他们采用了一种新的算子,Null-Aware Anti Join(NAAJ) 来处理。还是以 SQL1 为例,NAAJ 的处理算法如下:如果 T2 是空集,则返回所有行 如果 T2 的 Age 有任意一行为 Null,则不返回任何行 如果 T1 的 Age 在某行值为 Null,则不返回该行 对于 T1 的每一行,如果 NA 条件执行为 Null 或 TRUE,则不返回该行;否则返回。注意 NAAJ 中的 NA 条件是经过反转的,NOT IN -> NA= >ALL -> NA<=分布式数据库中的子查询分布式数据库的优势在于拥有更多的计算和存储资源可供调用,但由于数据分布在不同的节点,节点间的数据传输 IO 很容易成为性能瓶颈。执行子查询时尤其要考虑如何扬长避短。利用集群的计算性能减少网络 IO 开销去关联化更加重要子查询的嵌套执行会导致网络 IO 随数据量上涨。除了执行缓慢以外,还会导致整体的资源消耗变大,系统容量变低。去关联化除了可以将 O(N^2) 的时间复杂度消除为 O(N) ,可以避免大量的网络 IO 开销。如果数据的分布满足条件,子查询还可以整体下推到存储节点进行计算,充分利用了集群的计算性能以外,也避免了大量 Scan 数据在网络层的传输。所以说在分布式系统中,如何将更多的子查询转为连接执行尤为重要。物化的不同运用物化的思路除了可以用于关联项消除以外,还可以用于削减连接计算中的网络传输数据。如图所示当连接一端的数量量较小时,可以将其全量或部分捞出(Semi 可以部分,Anti 必须全量),作为常量代入到另一侧的执行计划中去处理。Apply 的 PreFilter即便是无法转为连接的子查询,依然可以通过子查询的逻辑特性来削减网络 IO 的数据量。SEMI:表达式通过 OR 连接假设子查询的 condition 为 E,Semi 类型的 Apply 算子的子查询一侧都可以增加以下过滤条件:E OR (E IS NULL) 对于 E1 OR E2 OR E3 ... 来讲 En 为 FALSE 的表达式是可以忽略的ANTI:表达式通过 AND 连接如果是 anti 类型,则可以增加以下过滤条件// E' = NOT E E' OR (E' IS NOT NULL) 对于 E1 AND E2 AND E3 ... 来讲 En 为 TRUE 的表达式是可以忽略的分别举例:SELECT * FRMO R WHERE ID IN (SELECT ID FROM R' ...)SELECT * FRMO R WHERE AGE>ALL(SELECT AGE FROM R' ...)注意 Anti 下推的表达式需要反转通过这样的方式,可以在子节点提前过滤掉不需要的数据。即便在空集的情况下,这个 filter 下推也是成立的。CacheCache 缓存:在无法转化为连接的场景下,关联子查询的嵌套执行会导致极大的性能开销。尤其在分布式场景下还会随数据量增长而导致更多 IO。在多层嵌套的场景下,几千的数据量就会导致分钟级的查询耗时。合理的使用缓存可以极大避免数据量增长带来的 IO 消耗,从而让子查询的原语义执行也可以飞快。CBO 可以根据数据量判断有必要的话,在 执行层 增设一层 Cache,从而将网络 IO 降级为内存。Cache 是永不过时的优化技术,在 Apply 中合理的利用缓存具有化腐朽为神奇的效果。举个最简单的多层 Apply SQL:SELECT * FROM T1 WHERE 100> (SELECT col1 FROM T2 WHERE T1.pk = T2.pk AND 100 > (SELECT col2 FROM T3 WHERE T2.pk = T3.pk))假设 T1,T2,T3 都为 1000 行数据,先看一下 Apply 的执行过程:T1 表作为最外层的主表,只需要扫描一次。扫出的数据有 1000 行,所以 Apply 会往 T2 这边扫扫描 1000 次;同理,每次扫描 T2 都需要扫 1000 次 T3。最终 T2 的扫描次数是 1000 次,T3 的扫描次数是 10^6 次。这意味着在千行的数据量场景下,多层的 Apply 会导致十万乃至百万级的网络 IO 次数。即便 Apply 是预期的慢查询,但这种 Cost 是不可接受的。由此引入 Cache 最主要的目的在于减少网络 IO 次数:如上图所示,虽然到 Cache Node 的 IO 次数没有变化,但 IO 的级别从网络降级到了内存。网络上的 IO 三张表各自仅进行一次。内存的存取速度与网络天壤之别,这里不再放具体 SQL RT 数据支撑。总结在这篇文章中我们简单聊了很多关于子查询的优化技术,如 Magic Set、窗口函数等,相信可以让大家体会到子查询优化的脑洞与精妙。同时实践时的陷阱,不管是对数据库还是对开发者来说,都有很重要的参考意义。最后我们分享了分布式数据库中处理子查询的一些心得,希望能给大家带来一些启发。参考文档Parameterized Queries and Nesting Equivalencies - C Galindo-LegariaOrthogonal Optimization of Subqueries and Aggregation - C Galindo-Legaria, M JoshiUnnesting Arbitrary Queries - T Neumann, A KemperThe Complete Story of Joins (inHyPer) - T Neumann, V Leis, A KemperEnhanced Subquery Optimizations in Oracle - S BellamkondaWinMagic : Subquery Elimination Using Window Aggregation - CZHPW MaUnnesting SQL Queries in the Presence of Disjunction - P. Parízek【相关阅读】探索 | PolarDB-X:实现高效灵活的分区管理分布式数据库如何实现 Join?PolarDB-X 让“Online DDL”更OnlinePolarDB-X SQL限流,为您的核心业务保驾护航
作者:阿里云数据库OLAP产品部 云曦预计算和缓存是计算机领域提高性能以及降低成本的最常见的手段之一。对于那些经常重复的请求,如果可以通过缓存回答,比重新计算结果或从速度较慢的数据存储中读取要快得多,消耗更少的系统资源。在数据库领域中,物化视图是预计算和缓存的自然体现。本文主要介绍什么是物化视图,以及如何实现基于物化视图的查询改写。在第一部分,我们会简单介绍物化视图,并介绍基于物化视图的查询改写的用途。在第二部分,我们将介绍查询优化器使用物化视图进行查询改写的匹配和改写过程。最后,我们将介绍查询改写的几种实现方式,及其优缺点。背景介绍物化视图物化视图是将查询结果预先计算并存储的一张特殊的表。"物化"(Materialized) 这个词是相对于普通视图而言。普通视图较普通的表提供了易用性和灵活性,但无法加快数据访问的速度。物化视图像是视图的缓存,它不是在运行时构建和计算数据集,而是在创建的时候预先计算、存储和优化数据访问,并自动刷新来保证数据的实时性。对于数据仓库,物化视图最重要的功能就是查询加速。数据仓库中存在大量在大型表上执行复杂的查询,这些查询会消耗大量资源和时间。物化视图可以通过预计算的结果回答查询,消除昂贵的联接和聚合所带来的开销,大幅度改善查询处理时间,降低系统负载。对于可以预见并反复使用相同子查询结果的查询,物化视图特别有用。为了实现物化视图的潜力,需要解决三个问题:物化视图选择:选择哪些查询和表构建物化视图;物化视图维护:减少物化视图更新成本和时间;物化视图运用:如何使用物化视图加速查询。本文主要从查询优化器的角度,介绍使用物化视图加速查询背后的技术实现。基于物化视图的查询改写直接查询物化视图可以大幅度改善查询处理时间,但是需要用户修改查询语句。使用物化视图加速查询的一个重要问题是,如何采用一种系统化和自动化的方法,自动使用物化视图回答查询。通过这种透明改写,物化视图可以像索引一样添加或删除,而不会影响已有 SQL。查询改写使得物化视图具有广泛的用途:物化视图可以透明地改写查询,无需改造业务就能使用物化加速查询;方便地应用缓存公共结果集,以及预计算等跨查询优化手段;对于数据仓库,数据集成场景,物化视图可以物化外表结果,屏蔽多个数据源的差异,实现本地副本或读写分离;查询改写结合自动构建物化视图,实现数据库自治加速。查询改写的问题定义为了实现更大范围的改写,查询改写通常被集成在优化器规则中。这有几个方面的好处。首先查询改写可以利用优化器其他规则。依靠优化器其他规则将查询转换成标准和统一的形式,简化匹配流程,增加改写范围。其中比较重要的规则是列消除,谓词下推,解关联子查询等。解关联子查询规则允许物化视图对包含关联子查询的查询进行改写加速。其次,优化器可以递归每一个子树能否被某个视图进行改写。每个相关视图都会对每个子树产生多次改写,一个查询语句不同部分可能被不同的视图改写。最终所有的改写都进入基于成本的选择器中,与原始查询一起选出最优的查询计划。查询改写算法只需要考虑给定的查询表达式和视图,判断这个查询表达式能否从视图中计算出来,然后从视图上构造一个等价的补偿表达式,与原查询表达式等价。查询改写的范围应该尽可能大,查询改写的目标是使用少量物化视图改写大量查询。最终由优化器选择出一个最优的查询计划。查询改写检查优化器通过多种方式来改写查询。最简单的一种情况是物化视图的查询与查询完全匹配,符合这种查询重写类型的查询数量很少。为了进行更通用的匹配,优化器会尝试使用各种规则构造一个等价表达式改写查询。查询改写检查包含两个步骤,改写匹配检查和构建等价表达式。一个查询能被视图回答需要满足下面两个条件:物化视图 Join 关系在查询中存在物化视图有足够的数据来回答查询部分条件下即使不满足条件,视图也可能会被用于改写。例如视图包含一部分查询所需要的数据,可以使用物化视图回答部分查询,剩下的数据从原始数据中计算。这部分改写检查会放在高级改写规则中进行介绍。具体来说改写检查会依次进行 Join 检查和 Ouput 检查,如果查询或视图中含有 Grouping By 和 Aggregation,还会进行额外的检查,如果需要,会尝试对视图进一步聚合。Join 检查当查询和视图的表的 Join 关系相同时,视图才可能包含查询需要的所有的行和列。一种简单的方式是只考虑没有子查询的 Inner Join,这时只用比较查询表和数量是否完全一致即可。或者通过一系列规则检查查询和视图的关系代数树包含的 Join 关系是等价的。更通用的一种方法是构建 Join Graph。Join Grpah 是一个以关系为结点,联接为边的图。Inner Join 的条件表示为无向边,Outer Join 的条件表示为有向边。Join Graph 由查询关系代数树构建而来,通过比较 Join Graph 就可以检查物化视图和查询包含相同的 Join 关系。例如对于下面的查询select c_custkey, c_name, l_orderkey, l_partkey, l_quantity from ( select l_orderkey,l_partkey, l_quantity from lineitem where l_orderkey < 1000 and l_shipdate = l_commitdate ) subquery join orders on subquery.l_orderkey = o_orderkey left join customer on o_custkey = c_custkey对于物化视图create materialized view mview1 enable query rewrite as select * from lineitem join customer On subquery.l_orderkey = o_orderkey left outer join orders On o_custkey = c_custkey查询和物化视图具有相同的 Join 关系,查询可以被改写select c_custkey, c_name, l_orderkey,l_partkey, l_quantity from mview1 where l_orderkey < 1000 and l_shipdate = l_commitdateOutput 检查Ouput 检查保证物化视图有足够的数据回答查询,这包括 3 个步骤:验证查询所有输出需要的列能够从视图中计算出来优化器需要检查视图包含查询所有需要的行数,也就是视图的谓词的范围大于查询的范围补偿谓词能否从视图中计算得来等价关系查询改写能够通过查询中的等价关系扩展改写的范围。因为等值条件具有传递性,等价关系可以从等值条件或者代数关系推导而来。例如可以从 A=date_format(now(), '%Y%m%D') 和 B=date_format(now(), '%Y%m%D'),因为函数是确定性的,可以得到 A=B,如果我们还有 inner join 的条件 B=C,可以进一步得到 A=B=C。可能会有某些其他的条件可以推导出等价关系,例如可以从 A=2,B=3,C=5 推导出 C = A + B。相比等值关系,寻找这种关系会使搜索过程更加复杂,而且我们无法实现所有的可能的相等关系,因此很少考虑这样的表达式。等价关系推导只搜索等值条件,即使可能错过一些改写机会,这样的浅层搜索可以保证速度。表达式检查为了确保查询输出的列和补偿谓词需要的列都能从视图中计算出来,我们需要一种方法确定来自查询的表达式是否和视图中的表达式相等,或者能从中计算出来。表达式检查无法纯粹从语法上实现,两个表达式或者符号的文本相同,并不说明他们关系相等。例如别名的存在就会破坏基于语法的检查。表达式检查需要通过等价关系和表的对应关系推导而来。如果视图和查询中所有的表都是唯一的,那么来自同一个表的列是等价的;如果视图和查询中有表出现多次,即 self join,那么查询到视图的表的映射就存在多种可能,每种可能都需要进行一次改写尝试。有了视图和查询之间列的对应关系和上一节的等价关系,通过代数系统确定来自查询的表达式是否和视图中的表达式相等,或者能否从中计算出来。存在一些启发式的规则,允许一个表达式从另外的表达式中计算出来。比如算数规则从 x + 1 中计算出 x,例从 SUM(x) 和 COUNT(x) 计算出 AVG(x),还存在一些 Function Dependency 规则,例如时间函数,可以通过返回的天的结果计算年等。谓词检查视图改写需要物化视图中存在查询所有需要的行数,即视图的谓词的范围大于或等于查询的范围。使用 Wq 表示查询的谓词,Wv表示视图的谓词,我们需要检查 Wq => Wv,其中 => 表示 Wq 满足 Wv 的含义。优化器提取所有的谓词,并将他们转换为 CNF 的形式,即 W=P1 ^ P2 ^ ... ^ Pn。将谓词按照等值谓词,范围谓词和剩余谓词进一步分为 W= PE ^ PR ^ PU。PE,PR,PU 分别代表等值谓词,范围谓词和剩余谓词。谓词检查变成了 PEq ^ PRq ^ PUq => PEv ^ PRv ^ PUv,由于正交的性质,最终将问题分解成 PEq => PEv,PEq ^ PRq => PRv,PEq ^ PUq => PUv 三类检查。查询的等值谓词用于推导等价关系。查询中任何视图不满足等价关系的谓词都构成补偿谓词,视图中存在查询无法满足的等价关系则无法改写。范围谓词在优化器中可以存储为范围的形式,可以方便的计算差集。对于查询中每一个范围谓词,如果视图存在对应的范围精准匹配,不需要进行补偿,否则视图范围必须大于查询的范围,并通过差集构造一个补偿谓词。查询表达式和试图剩余的谓词共同构成剩余谓词PE,只能进行精准匹配。查询中任何与视图不匹配的谓词都构成补偿谓词。查询与视图不匹配则无法改写。所有的补偿谓词都需要通过表达式检查,确保可以从视图的输出中计算出来。例如查询select l_orderkey, o_custkey, l_partkey,l_quantity*l_extendedprice from lineitem, orders, part where l_orderkey = o_orderkey and l_partkey = p_partkey and l_partkey >= 150 and l_partkey <= 160 and o_custkey = 123 and o_orderdate = l_shipdate and p_name like ‘%abc%’ and l_quantity*l_extendedprice > 100物化视图create materialized view mview2 Enable Query Rewrite as select l_orderkey, o_custkey, l_partkey,l_shipdate, o_orderdate,l_quantity*l_extendedprice as gross_revenue from lineitem, orders, part where l_orderkey = o_orderkey and l_partkey = p_partkey and p_partkey >= 150 and o_custkey >= 50 and o_custkey <= 500 and p_name like ‘%abc%’查询可以被改写为select l_orderkey, o_custkey, l_partkey, gross_revenue from mview2 where l_partkey <= 160 and o_custkey = 123 and o_orderdate = l_shipdate and gross_revenue > 100Grouping 和 Aggregation 检查如果视图和查询带有 GroupBy 或 Aggregation 函数,需要进行额外检查:检查查询请求的数据分组是否与物化视图中存储的数据分组相同,如果不同,优化器会尝试对物化视图进行汇总。如果需要进一步汇总计算,所有需要的列的都可以从视图输出中计算出来。例如查询select c_nationkey, sum(l_quantity*l_extendedprice) from lineitem, orders, customer where l_orderkey = o_orderkey and o_custkey = c_custkey group by c_nationkey物化视图create materialized view mview3 enable Query rewrite as select o_custkey, count_big(*) as cnt, sum(l_quantity*l_extendedprice) as revenue from lineitem, orders where l_orderkey = o_orderkey group by o_custkey查询可以被改写为select c_nationkey, sum(revenue) from customer join mview3 on c_custkey = o_custkey group by c_nationkey如果分组列表不同,只能改写 Group By 在查询最外部的情况,否则无法进行进一步聚合,无法满足第二个改写要求。只有特定的聚合函数支持进一步聚合,常见的聚合函数有 MIN,MAX,SUM,COUNT。例如如下查询select o_custkey, count(*) as cnt, sum(l_quantity*l_extendedprice) as revenue from lineitem, orders where l_orderkey = o_orderkey group by o_custkey物化视图create materialized view mview4 enable Query rewrite as select o_custkey, l_partkey, count(*) as cnt, sum(l_quantity*l_extendedprice) as revenue from lineitem, orders where l_orderkey = o_orderkey group by o_custkey, l_partkey可以被改写为select c_nationkey, sum(cnt) as cnt, sum(revenue) as revenue from mview4 where c_custkey = o_custkey group by o_custkey如果聚合函数在视图中不存在,可以通过一些规则进行计算,例如从 SUM(x) 和 COUNT(x) 计算出 AVG(x),从 SUM(x) + SUM(y) 中计算出 SUM(x + y) ,这些依赖表达式改写检查中启发式的规则。高级改写规则如果物化视图和查询不满足上一节的改写规则,还可以通过其他规则进行转换。Join 补偿如果只考虑 Inner Join,视图和查询的 Join 关系 不一致有两种情况:查询比视图包含更多的联接,或者视图包含更多的联接。如果查询比视图包含更多的表,我们可以简单把缺少的 Join 添加在视图之上,使其满足改写要求。Join 条件会通过谓词改写正确的添加进来,优化器也可以通过后续规则调整计划。Join 补偿使优化器可以更早的进行匹配改写,减少改写的尝试次数。例如如下查询select c_custkey, c_name, l_orderkey,l_partkey, l_quantity From lineitem, customer, orders Where l_orderkey = o_orderkey And o_custkey = c_custkey Where l_orderkey between 1000 and 1500 And l_shipdate = l_commitdate物化视图Create materialized view mview5 Enable query rewrite as Select l_orderkey,l_partkey, l_quantity From lineitem, orders Where l_orderkey = o_orderkey And o_orderkey >= 500可以被改写为Select l_orderkey, l_partkey, l_quantity From mview5 join customer on o_custkey = c_custkey Where l_orderkey between 1000 and 1500 And l_shipdate = l_commitdateJoin 消除如果一个 Join 出现在视图中但是没有出现在查询中,可以尝试使用 Join 消除规则。Join 消除是优化器优化中的一项常见方法,如果 Join 满足以下5个条件,则表 T1 在与表 T2 的联接中时不变的:联接条件是一个简单的等值条件T1.fk 是 T2.pk 的外键T1.fk 满足非 null 约束T2 没有任何的谓词T2 在与 T1 以外的表的联接是不变的这意味着表 T1 在与 T2 的 Join 关系不会影响 T1 的结果,视图中的 T2 可以在 Join Graph 中忽略。不变联接的存在允许在基础物化视图上创建更大的并集或超集,从而允许物化视图包含更大的预计算,也可以改写更多的查询。例如查询Select l_orderkey, l_partkey, l_quantity From lineitem Where l_orderkey between 1000 and 1500 And l_shipdate = l_commitdate物化视图Create materialized view mview6 Enable query rewrite as Select c_custkey, c_name, l_orderkey,l_partkey, l_quantity From lineitem, orders, customer Where l_orderkey = o_orderkey And o_custkey = c_custkey And o_orderkey >= 500可以被改写成select l_orderkey, l_partkey, l_quantity from mview6 where l_orderkey between 1000 and 1500 and l_shipdate = l_commitdateJoin 派生如果存在 Outer Join,视图和查询 Join 关系不一致,可以尝试利用 Join 派生性,从物化视图中的联接重新计算查询中的联接。例如,能从物化视图中的 left Outer Join 的结果里,计算 Inner Join,Anti Join 的结果,Inner Join 又能进一步计算 Semi Join,这样就能使用物化视图过滤某些行来回答不同具有不同 Join 关系的查询。Join 派生可以扩展改写的范围,允许优化器将基于物化视图的改写与解关联的规则规则结合,改写带有 IN,EXISTS 等的查询,也可以避免其他优化规则对 Join 的调整,例如 EliminateOuterJoin 和 PredicatePushDown 可能会将 outer join 优化成 inner join。Union 改写物化视图只包含一部分查询所需的数据,也可以用于查询改写。在很多场景下,物化视图不会也无法存储全部的数据。一个典型的情况是,数据在不断的写入,但是写入只发生在最近一段时间内。在持续刷新的表上构建全量物化视图,这可能导致因为数据插入视图频繁刷新,产生高昂的刷新成本,甚至视图因为持续刷新而完全不可用。更好的做法是构建一个 T+1 条件刷新的物化视图,存储不变的数据,可以降低刷新成本。另一种常见的情况是,数据仓库存储全量的数据,而查询集中在最近几个月的数据,构建全部数据的物化视图成本过于高昂,物化视图只构建最近几个月数据,也能改写绝大多数查询。Union 改写会尝试使用视图回答部分查询,减少查询中实时计算的数据量。Union 改写可以进一步应用 Aggregation 改写,支持使用物化视图部分数据回答聚合查询。例如查询select l_orderkey, l_partkey, l_quantity from lineitem where l_orderkey l_orderkey > 500 and l_orderkey <= 1500 and l_shipdate = l_commitdate物化视图create materialized view mview8 enable query rewrite as select l_orderkey, l_partkey, l_quantity from lineitem where l_orderkey > 1000 and l_shipdate = l_commitdate改写结果select l_orderkey, l_partkey, l_quantity from lineitem where l_orderkey > 500 and l_orderkey <= 1000 and l_shipdate = l_commitdate union select o.shippriority from mview8 where l_orderkey > 1000 and l_orderkey <= 1500查询改写的实现视图改写通常有三种查询改写的实现方式:基于语法的改写基于规则的改写基于结构的改写基于语法的改写文本匹配或者语法匹配是最简单的改写方法,将查询的文本与物化视图的文本或语法树进行比较,完全匹配可以进行改写。这种改写只能匹配完整的查询语句或子语句,细微的变化就会导致查询无法改写,适用的范围很小。基于语法的改写虽然简单,但是效率很高,改写的成本可以忽略不计。基于规则的改写基于规则的改写和其他优化器规则相同,针对不同 Pattern 的查询和视图编写不同的规则,寻找等价的替代关系树。最简单的一条规则就是直接比较子查询和视图的计划,如果相同就能改写。高级的改写规则不需要物化视图等同于被替换的计划,会尝试计算补偿谓词,构建等价查询表达式。例如 Join 改写,比较 Join 查询的子表达式是否和视图 Join 的某个子表达相同或者能否从中计算出来,每一个Join子表达式都存在映射关系,最后检查补偿表达式能否从视图中计算得到。基于规则的改写可以实现大量重写,实现也比较简单,改写匹配速度快,但是也存在局限性。这种改写依赖转换规则来寻找等价关系,因此需要穷举所有可能的转换关系来实现复杂视图的重写。一些复杂的视图不可能穷举所有的等价关系,例如存在很多的 Join 联接或者复杂的 Project 关系,基于规则的改写适用的范围取决于规则的数量。基于结构的改写基于结构的改写与基于规则的改写相反,通过提取查询中的特征,使用一套规则进行匹配改写。优化器将查询表示为 SPJG 标准形式 (Join-Select-Project-GroupBy),提取查询中的 Join,Projects,Filters,Grouping 和 Aggregations 五种表达式,分别与物化视图对应的表达式进行匹配和改写。这个方法是由微软在 2001 年 SIGMOD 论文《Optimizing queries using materialized views: A practical, scalable solution》系统化的提出。这种方法可以改写包含可以改写包含 Join,Filter,Project 的任意查询的方法,运用一系列的步骤匹配并得到补偿表达式。还可以进一步改写含有 Aggreagtion 的查询,在需要时添加 Aggregation 节点返回进一步汇总的结果。基于结构的改写很容易扩展,例如改写 Outer Join 和子查询等,可以完成几乎全部的改写。但是搜索成本较高,尤其是在查询复杂,改写尝试次数很多的情况下。关于我们AnalyticDB 是阿里巴巴自主研发、唯一经过超大规模以及核心业务验证的 PB 级云原生数据仓库。AnalyticDB 在去年10月份推出了物化视图功能,目前的版本支持定时全量刷新和查询改写。AnalyticDB 完整实现了基于文本的匹配和基于结构的匹配,可以改写包含 Join,Filter,Project,Group By 的任意查询,支持 Aggregation Rollup,Union,Subquery,Outer join 等高级改写规则。ADB 会在未来的几个版本内上线基于规则的匹配,提高改写的效率。并在未来扩展更多的改写手段,例如 Grouping sets 改写支持,使物化视图有传统数仓中 Cube 的能力。我们是AnalyticDB for MySQL的优化器团队,负责 OLAP 分布式查询优化器相关研发工作。欢迎投递简历到 jiannan.jjn@alibaba-inc.com,期待与你共同打造世界一流的查询优化器。参考资料Goldstein J, Larson P Å. Optimizing queries using materialized views: a practical, scalable solution[J]. ACM SIGMOD Record, 2001, 30(2): 331-342.Bello R G, Dias K, Downing A, et al. Materialized views in Oracle[C]//VLDB. 1998, 98: 24-27.Zhou J, Larson P A, Goldstein J, et al. Dynamic materialized views[C]//2007 IEEE 23rd International Conference on Data Engineering. IEEE, 2007: 526-535.Jindal A, Qiao S, Patel H, et al. Computation reuse in analytics job service at microsoft[C]//Proceedings of the 2018 International Conference on Management of Data. 2018: 191-203.Calcite: Materialized View. Oracle: Database Data Warehousing Guide: Advanced Query Rewrite for Materialized Views. Redshift: Automatic query rewriting to use materialized views. Snowflake: Creating and Working With Materialized Views.
4月17日,第十五届中国电子信息技术年会上正式颁发2020年中国电子学会科学技术奖,阿里云自研的“云原生分布式关系型数据库PolarDB”项目获得科技进步一等奖。这也是继飞天云操作系统之后,阿里云第二次自研技术获此殊荣。阿里巴巴集团副总裁、阿里云智能数据库事业部总裁李飞飞领奖(右四)阿里巴巴集团副总裁、阿里云智能数据库事业部总裁李飞飞领奖(左一)由多名院士组成的专家组认为,阿里云自主研发的PolarDB数据库技术复杂,研制难度大,在云原生分布式数据库架构、冷热数据分层存储引擎、软硬件一体化的共享存储系统、大规模在线事务数据库的智能运维与优化等方面具有重要创新,在云原生分布式数据库领域整体达到了国际领先水平,并且已经实现规模化市场应用,经济和社会效益十分显著,是我国在数据库领域赶超世界先进水平、构建自主生态的成功实践和重大成果。“越来越多的企业正通过云数据库提升创新效率,降低IT成本。”阿里巴巴集团副总裁、阿里云智能数据库事业部总裁李飞飞透露,PolarDB既解决了传统数据库容量有限、扩缩容时间长等问题,又提供了分钟级扩容、弹性变配、超高并发等能力,性能比MySQL高6倍,成本只有传统商用数据库的1/10。阿里巴巴集团副总裁、阿里云智能数据库事业部总裁李飞飞分享《智能+@阿里云与阿里云数据库》4月18日,阿里巴巴集团副总裁、阿里云智能数据库事业部总裁李飞飞应邀出席了中国电子信息技术年会“智慧+”主论坛高峰对话,作了题为《智能+@阿里云与阿里云数据库》的主题报告,分享了人工智能和机器学习在各个技术方向的升级。李飞飞表示阿里云的业务体系正在全面智能化,核心云服务覆盖从IT架构的规划、迁移、部署、弹性扩缩容,到日常管理的全生命周期的自动化运维。其次,在人工智能和机器学习技术与数据库结合方面,李飞飞认为就像自动驾驶汽车一样,也会有自动驾驶数据库系统。汽车驾驶系统和数据库系统一样都需要根据运行状态实时调整很多参数,self-driven database也会利用机器学习算法对参数进行调优,使数据库运行达到最佳状态。此外,在数据库系统支撑上层的AI应用方面,阿里云研发的新一代空天数据库引擎 Ganos、AI Earth 数知地球,可进行智能化存储与管理大规模空天数据,并实现智能化计算分析与信息深度挖掘。目前已经应用在自然资源、水利、农业、生态环境、气象等领域,助力社会基础设施建设更加智能化。阿里云自研云原生关系型数据库PolarDB是国内首个云原生数据库,其采用存储计算分离、软硬一体化等创新设计,满足大规模业务场景上云需求。日前,PolarDB助力汇付天下云原生改造,将汇付天下数据库整体成本下降75%。一方面为客户省去了大量传统数据库的授权费用;另一方面使用云原生数据库产品,支持极致的弹性能力,资源使用更加节约。同时,用友旗下财务软件公司畅捷通完成“好生意”、“智+”等核心云服务向PolarDB的迁移工作,助力业务系统服务响应时间降低了40%,业务系统并发能力提升了4倍以上,大幅提升业务系统的稳定性和健壮性。游戏厂商江娱互动也通过引入PolarDB,顺畅支撑旗下《口袋奇兵》等游戏产品日均5亿次用户请求,并将整体IT成本降低50%以上。在阿里巴巴集团内部的最佳实践中,PolarDB还全面支撑了2020年天猫双十一,并刷新了数据库处理峰值记录,高达1.4亿TPS。2020年,阿里云数据库作为中国唯一的科技厂商进入Gartner全球数据库领导者象限。过去十多年,阿里云在产品技术领域进展迅猛,获得市场广泛认可,目前已有超过40万个数据库实例迁移到阿里云上,包含政务、零售、金融、电信、制造、物流等多个领域的龙头企业。
阿里云原生多模数据库Lindorm与东软云科技推出联合解决方案,共建面向未来的车联网数字化运营运维云平台。东软云科技阿里车联网云能够提供海量实时监控车辆、驾驶行为、第三方交通天气等数据源数据存储融合,支撑车厂、服务提供商等客户运营大量驾驶人用户数据,快速建立驾驶行为分析模型,面向保险、车厂、商业车队、智慧城市等领域提供数据运营与分析服务。目前,该联合解决方案已在东软睿驰、江铃汽车、长城汽车等客户中得到广泛应用和实践落地。东软集团深耕车载业务近30年,为国内外知名汽车厂商、Tier1客户提供从操作系统开发、核心技术授权到车载应用定制包括车载信息娱乐系统定制化服务及测试服务、仪表定制化开发、HMI定制化开发、车身控制系统软件开发、导航&DB编译服务、车联网、车载测试的整体智能座舱软件解决方案和服务。作为阿里云MSP合作伙伴,东软拥有近300名ACP工程师,卓越的云架构设计、上云迁移、云上实施、云上运维,以及银行级别的安全保障和云优化服务能力。能够提供基于阿里云的车联网云上服务,为车联网解决方案的运营保驾护航。随着智能网联汽车创新场景和相关业务不断丰富,在线车辆采集上传云端的数据量激增,数据种类和结构多样化,目前常用数据存储技术手段局限于利用多种开源数据库(Opentsdb、HBase、Solr等)自建数据库存储系统集群,建设、使用、运维成本随数据量和业务场景快速增加,急需专业、高性价比、免运维的数据库存储方案。阿里云原生多模数据库Lindorm致力于提供“存得起,看得见”的非结构化、半结构化的数据存储与处理解决方案,广泛服务于阿里巴巴集团内部和外部用户,特别是物联网、车联网等智能、互联系统海量数据存储,具备高性价比存储、开放生态兼容、多引擎异构数据融合等能力。Lindorm凭借以下优势为东软车联网业务构建了海量数据云存储一站服务平台。车联网海量监控数据存储成本最低,依靠自研数据压缩存储、冷热分离、存算分离技术将时序监控数据存储成本降到极致;完美对接HBase、OpenTSDB、InfluxDB、PhoneixQL、CassandraQL等大多数主流数据存储生态接口,数据迁移、生态共生方案最完备;提供时序、宽表、索引、文件等多种引擎协作存储能力,为客户屏蔽多类型、异构数据处理复杂性问题;企业级稳定性保障、大于99.99%数据可靠性保障,免运维,专业技术团队售后支持。基于东软云科技阿里车联网数字化运营运维云平台,客户能够运用车联大数据进行智能分析,提供车况分析、用户画像分析及出行分析,从而向车厂研发部、质量部提供业务决策依据,帮助车厂深入洞察用户,提升运营效率,实现精准营销以及业务创新。具体应用场景如下:车厂产品变革及质量监控:通过对车联网大数据的采集分析,从经济性、环境、可靠性、安全性、故障等方面挖掘数据价值,从而向车厂研发部、质量部提供业务决策依据,用于整车厂产品变革。车主驾驶评价及出行助手:根据车主驾驶习惯特征,提升车辆行车效率,最大程度减少车主顾虑,为车主提供驾驶评估和安全指导,提供出行帮助,促进汽车品牌智能化服务转型。汽车后市场服务:整合车联大数据中的人、车数据,根据不同行业客户的特点,提供定制化的个性服务,充分体现“自有数据运营”的价值。精准营销:基于线下位置的数据采集,通过人与地理位置的结合、变化、频次等状态,实现人群标签划分、线下场景捕捉,提供精准的用户画像,帮助车厂深入洞察用户,提升运营效率,实现精准营销以及业务创新。除此之外,东软云科技阿里车联网联合方案还能提供人、车、出行多维度分析,提升车厂决策及运营能力支撑,典型应用场景包括:车况分析:提供车辆性能分析和故障分析,为车厂研发部门、采购部门、质量部门提供车况分析,包括燃油经济性、怠速、故障分析、性能分析等,从而提升整车厂的决策能力,为车厂各生产设计环节带来价值。用户画像:基于购车履历、用车频度、活动区域的分析,帮助车厂洞察购车人群分布,消费偏好等,为车辆销售定位、销售策略提供客观的参考依据,从而实现精准营销以及业务创新。出行分析:整合车辆行驶过程中的时空数据,对车辆行车数据和风险行为进行识别并加以刻画,提供驾驶行为分析、车辆风险画像、路线轨迹洞察,综合反映车辆行驶习惯及风险,为车厂客观了解车辆出行状况提供数据支撑。东软云科技云解决方案和服务事业部 副总经理傅春江表示:“数据是车联网赋能车厂和车联网服务提供商提升客户体验,构建技术竞争力的关键,阿里云原生多模数据库Lindorm具备极具竞争力的数据存储性价比和技术优势,做到了让车联网数据存得起、看得见!”根据IDC预测,2023年全球智慧城市技术投资将达到1894.6亿美元,作为智慧城市核心系统之一的车联网运营运维支撑数据存储也将同步快速增加,东软云科技阿里云联合解决方案围绕车联网系统建设数据存储痛点问题,强强联合共建行业支撑数字基础设施。
作者:晨义01 Hash分区 vs. Range分区用户在使用分布式数据库时,最想要的是既能将计算压力均摊到不同的计算节点(CN),又能将数据尽量散列在不同的存储节点(DN),让系统的存储压力均摊到不同的DN。对于将计算压力均摊到不同的CN节点,业界的方案一般比较统一,通过负载均衡调度,将业务的请求均匀地调度到不同的CN节点;对于如何将数据打散到DN节点,不同的数据库厂商有不同策略,主要是两种流派:按拆分键Hash分区和按拆分键Range分区,DN节点和分片之间的对应关系是由数据库存储调度器来处理的,一般只要数据能均匀打散到不同的分区,那么DN节点之间的数据基本就是均匀的。如下图所示,左边是表A按照列PK做Hash分区的方式创建4个分区,右边是表A按照列PK的值做Range分区的方式也创建4个分区:按照Hash分区的方式,表A的数据会随机的散落在4个分区中,这四个分区的数据之间没有什么的依赖关系,这种方式的优点是:只要分区键的区分度高,数据一定能打散;不管是随机写入/读取还是按PK顺序写入/读取,流量都能均匀地分布到这个4个分区中。Hash分区的缺点是,范围查询非常低效。由于数据随机打散到不同分片列,所以对于范围查询只能通过全部扫描才能找到全部所需的数据,只有等值查询才能做分区裁剪。按照Range分区的方式,根据定义,表A会被切分成4个分区,pk为1~1000范围内的值散落到分区1,pk为1001~2000范围内的值散落到分区2,pk为2001~3000范围内的值散落到分区3,pk为3001~4000范围内的值散落到分区4,由于数据在分区内是连续的,所以Range分区有个很好的特性就是范围查询很高效,例如: select * from A where PK >2 and PK < 500对于这个查询我们只有扫描分区1就可以,其他分区可以裁剪掉。Range分区方式的缺点是:如果各个分区范围的数据不均衡,例如pk为[1,1000]的数据只有10条,而pk为[1001,2000]的数据有1000条,就会发生数据倾斜。所以数据能不能均衡散列跟数据的分布性有关。对于按照拆分列(如例子中的PK列)顺序读取或者写入,那么读或许写的流量永远都在最后一个分区,最后一个分片将成为热点分片。02 默认拆分方式为了让用户能用较小代价从单机数据库到分布式数据库的演进,将原有数据表的schema结构导入到分布式数据系统中,再将数据导入就可以将现有表的数据打散到不同的DN节点,而不需要像我们前面例子中一样,额外添加 partition by hash/range 这样的语句,一般的分布式数据都会按照某种默认策略将数据打散。业界有默认两种策略,一种是默认按主键Hash拆分(如yugabyteDB),一种是默认按主键Range拆分(如TiDB)。这两种拆分方式各有什么优缺点,在PolarDB-X中我们采取什么样的策略?我们一起来探索一下。2.1 主键Hash拆分默认按主键Hash拆分,意味着用户在创建表的时候不需要显式指定拆分方式,会自动将插入数据库每一行的主键通过hash散列后得到一个HashKey,再根据一定的策略将这个HashKey映射到特定的DN节点,从而实现将数据散列到不同的DN节点的目的。常见的HashKey和DN的映射策略有两种方式,按Hash得到的结果取模 (hashKey % n) 和 一致性Hash(将hashKey划分成不同的range,每个range和不同的DN对应)。按Hash结果(hashKey % n)取模这里的n是存储节点的数量,这个方法很简单,就是将拆分键的值按照hash function计算出一个hashKey后,将这个hashKey对存储节点数量n取模得到一个值,这个值就是存储节点的编号。所以数据和DN节点的具体的映射关系如下:DN = F(input) ==> DN = Hash(pk) % n例如系统中有4个DN节点,假如插入的行的pk=1,hash(1)的结果为200,那么这一行最终将落在第0个DN节点(200%4=0)。按hash key取模的方法优点是:用户能够根据hashkey的值和DN的数量可以精准计算出数据落在哪个DN上,可以灵活地通过hint控制从哪个DN读写数据。按hash key取模的方法缺点是,当往集群增加或者减少DN节点的时候,由于DN的数目就是hash取模的n的值,所以只要发生DN节点的变化都需要将原有的数据rehash 重新打散到现有的DN节点,代价是非常大的。同时这种分区方式对于范围查询不友好,因为数据按hashKey散列到不同的DN,只有全表扫描之后才能找到所需数据。一致性Hash一致性Hash是一种特殊的Hash算法,先根据拆分键(主键)的值按照hash function计算一个hashKey,然后再将hashKey定位到对应的分片的分区方式,效果上类似于 Range By (hashFunction(pk)) 。假设计算出来的HashKey的大小全部都是落在[0x0,0xFFFF]区间内,当前系统有4个DN节点,建表可以默认创建4个分区,那么每个分区就可以分配到不同的DN节点,每个分区对应的区间如下图:0x~0x4000(左开右合区间) 0x4000~0x8000 0x8000~0xc000 0xc000~0x10000分区和DN之间的对应关系作为表结构的元数据保存起来,这样我们得到主键的HashKey之后,根据这个HashKey的值的范围和分区的元数据信息做个二分查找,就可以计算出该主键所在的行落在哪个区分,具体的计算公式如下:DN = F(input) ==> DN = BiSearch(Hash(pk))一致性Hash的方法优点是,当添加DN节点时,我们可以将部分分片数据通过分裂或者迁移的方式挪到新的DN,同时更新一下表的元数据,其他的分片数据无需变化;当减少DN节点时,也只需要将待删除的DN节点上的数据迁移到其他节点同时更新一下元数据即可,非常灵活。一致性Hash的方法缺点是对范围查询也不友好。2.2 主键Range拆分主键Range拆分的方式和一致性Hash的本质区别在于,一致性Hash是对拆分键的Hash后得到HashKey,按这个HashKey的取值范围切分成不同的分区,主键Range拆分是按将拆分键的实际值的取值范围拆分不同的分区。对按照主键拆分的表,优点是范围查询非常高效,因为PK相邻的数据分区也是相同或者相邻的;还可以实现快速删除,例如对于是基于时间range分区的表,我们可以很轻松地将某个时间点之前的数据全部删掉,因为只需要将对应的分区删除就可以了,其他分区的数据可以保持不变,这些特性都是按hash分区无法做到的;缺点是在使用自增主键并且连续插入的场景下,最后一个分片一定会成为写入热点。2.3 PolarDB-X的默认拆分方式了解了这两种默认的主键拆分方式后我们来谈谈PolarDB-X是如何取舍的。本质上范围查询和顺序写入是个矛盾点,如果要支持高效的范围查询,那么在按主键递增顺序写入就一定会成为热点,毕竟范围查询之所以高效是因为相邻的主键在存储物理位置也是相邻的,存储位置相邻意味着按主键顺序写入一定会只写最后一个分片。对于OLAP的场景,可能问题不大, 毕竟数据主要的场景是读,但对于OLTP场景,就不一样了,很多业务需要快速生成一个唯一的ID,通过业务系统生成一个UUID的方式是低效的,存储代价也比AUTO_INCREMENT列大。对一个主键做范围查询场景不是很常见,除非这个主键是时间类型,例如某订单表按照创建一个主键为gmt_create的时间类型,为了高效查找某段时间范围内的订单,可能会有范围查询的诉求。基于以上分析,在PolarDB-X中我们是默认按主键Hash拆分,在Hash算法的选择中,我们选用的是一致性Hash的路由策略,因为我们认为在分布式数据库系统,节点的变更、分区的分裂合并是很常见的。前面分析过使用Hash取模的方式对于这种操作代价太大了,一致性Hash能保证我们分区的分裂合并,增删DN节点的代价做到和Range分区一样,能做到按需移动数据,而不需要全部的rehash。特别的,对于主键是时间类型,我们默认是按时间取YYYYDD表达式作用于pk后再按一致性Hash打散,这样做的目的是同一天的数据会落在同一个分区,数据能以天为单位打散,这种方式对于按主键(时间)做范围查询是高效的,前面我们提到过,对于以时间为主键的表,范围查询是个强诉求,同时能更高效将历史数据(例如,一年前的数据)归档。03 table group在PolarDB-X中,为加速SQL的执行效率,优化器会将分区表之间Join操作优化为Partition-Wise Join来做计算下推。但是,当分区表的拓扑发生变更后,例如分区发生分裂或者合并后,原本分区方式完全相同的两张分区表,就有可能出现分区方式不一致,这导致这两张表之间的计算下推会出现失效,进而对业务产生直接影响。对于以下的两个表t1和t2,由于它们的分区类型/拆分键类型/分区的数目等都是一致,我们认为这两个表的分区规则是完全一致的。create table t1 (c1 int auto_increment, c2 varchar(20), c3 int, c4 date, primary key(c1)) PARTITION BY HASH (c1) partition 4 create table t2 (c2 int auto_increment, c2 varchar(20), primary key(c2)) PARTITION BY HASH (c2) partition 4所以在这两个表上,执行sql1:select t1.c1, t2.c1 from t1, t2 on t1.c1 = t2.c2,对于这种按照分区键做equi-join的sql,PolarDB-X会优化为Partition-Wise Join将其下推到存储节点将join的结果直接返回给CN,而无需将数据拉取到CN节点再做join,从而大大降低join的代价(io和计算的代价都大大得减少)。但是如果t1表的p1发生了分裂,分区数目将从4个变成了5个,这时候sql1就不能再下推了,因为t1和t2的分区方式不完整一致了,左右表join所需的数据发生在多个DN节点,必须将数据从DN节点拉取到CN节点才能做join了。为了解决分区表在分裂或合并过程中导致的计算下推失效的问题,我们创造性地引入了表组(Table Group)和分区组(partition group)的概念,允许用户将两张及以上的分区表分区定义一致的表划分到同一个表组内,在同一个表组的所有表的分区规则都是一致的,相同规则的分区属于同一个分区组,在一个分区组的所有分区都在同一个DN节点(join下推的前提),属于同一个表组的分区表的分裂合并迁移都是以分区组为基本单位,要么同时分裂,要么同时合并,要么同时迁移,总是保持同步,即使表组内的分区表的分区出现变更,也不会对表组内原来能下推的join产生影响。特别的,为了减少用户的学习成本,一开始用户并不需要关注表组,我们会默认将每个表都单独放到一个表组里,用户并不用感知它。只有在需要性能调优或者业务中某些表需要稳定地做join下推时,作为一种最佳实践,这时候用户才需要考虑表组。对于表组我们支持如下的管理方式有:表组分区组分裂:一般的,在PolarDB-X中,一个分区表的大小建议维持在500W以内,当一个分区的数据量太大,我们可以对分区进行分裂操作,alter tablegroup split partition p1 to p10, p11表组分区组合并:当一个分区表的某些分区的行数大小远小于500W时,我们可以对分区进行合并操作,alter tablegroup merge partition p1,p2 to p10表组分区组的迁移:前面我们提到在分布式数据库系统中,节点的增加或者减少是很常见的事情,例如某商家为了线上促销,会临时增加一批节点,在促销结束后希望将节点缩容回平时正常的量。PolarDB-X中我们是如何支持这种诉求的?PolarDB-X的CN节点是无状态的,增删过程只需往系统注册,不涉及数据移动。这里主要讨论增删DN节点,当用户通过升配增加DN节点后,这个DN节点一开始是没有任何数据的,我们怎么快速让这个新的DN节点能分摊系统的流量呢?在DN节点准备好后,我们后台的管控系统可以通过PolarDB-X提供的分区迁移命令按需批量将数据从老DN节点迁移到新的DN,具体命令如下:alter tablegroup move partition p1,p2 to DNi将表D加入表组tg1:alter tablegroup tg1 add D将表D加入表组tg1有个前提条件,就是表D的分区方式要和tg1里的表完全一致,同时如果对应分区的数据和tg1对应的分区组不在同一个DN节点,会触发表D的数据迁移。将表B从表组tg中移除:alter tablegroup tg1 remove B04 其他分区方式前面我们对比了一致性Hash和Range的区别,并且我们采用默认按主键拆分的策略,尽管如此我们还是实现了Range分区和List分区以满足客户不同场景的不同诉求4.1 Range分区特别提一下,range分区除了上面提到的范围查询优化的优点外,在PolarDB-X中,我们的存储引擎不光支持Innodb,还有我们自研的X-Engine,X-Engine的LSM-tree的分层结构支持混合存储介质,通过range分区可以按需将业务任务的是“老分区”的数据迁移到X-Engine,对于迁移过来的冷数据,可以保存在比较廉价的HDD硬盘中,对于热数据可以存储在SSD,从而实现冷热数据的分离。4.2 List分区List分区是实现按照离散的值划分分区的一种策略,有了list分区的支持,那么在PolarDB-X中就可以实现Geo Partition的方案,例如对于某个系统,里面有全球各个国家的数据,那么就可以按照欧美-亚太-非洲等区域维度拆分,将不同的分区部署在不同地域的物理机房,将数据放在离用户更近的地域,减少访问延迟。CREATE TABLE users ( country varchar, id int, name varchar, …) PARTITION BY LIST (country) ( PARTITION Asia VALUES IN ('CN', 'JP', …), PARTITION Europe VALUES IN ('GE','FR',..), .... )4.3 组合分区前面提到,在PolarDB-X中我们支持Hash/Range/List分区方式,同时我们也支持这三种分区任意两两组合的二级分,以满足不同业务的不同诉求。下面举几个常见的例子来阐述,如何通过这三种分区的组合解决不同的问题。场景1:用显式的创建list分区表,例如将省份作为拆分键,将不同省份的数据保存在不同的分片,进而可以将不同身份的分片保存在不同的DN,这样做的好处是可以做到按省份数据隔离,然后可以按照区域将不同省份的数据保存在就近的数据中心(如华南/华北数据中心)。但是这种分区有个缺点,就是力度太粗了,每个省份一个分区,很容易就产生一个很大的分区,而且还没发直接分裂,对于这种场景,可以采用list+hash的组合,一级分区用list划分后,分区内再根据主键hash,就可以将数据打散的非常均匀,如:create table AA (pk bigint, provinceName varchar,...) PARTITION BY LIST (provinceName) SUBPARTITION BY HASH (pk) SUBPARTITIONS 2 ( PARTITION p1 VALUES ('Guangdong','Fujian'), PARTITION p2 VALUES ('Beijing','HeBei','Tijin') );一级分区p1/p2是采用list分区形式,可以将p1的子分区固定在region1,p2的子分区固定在region2,如下图所示:场景2:单key热点,当一个key的数据很多时,该key所在的分片会很大,造成该分片可能成为一个热点,PolarDB-X中默认按主键拆分,并不会出现此类热点,因此热点key来自二级索引,因为主表采用按主键Hash拆分,二级索引表的拆分键就会选择和主表不一样的列,对于按非主键列拆分就可能产生热点key。对于热点key,PoalrDB-X首先会将热点key通过分裂的方式,放到一个单独的分片内,随着该分片的负载变大,PolarDB-X会将该分片所在的DN上的其他分片逐步迁移到其他DN上,最终,这个分片将独占一个DN节点。如果该分片独占一个DN节点后,依然无法满足要求,PolarDB-X会对这个分区二级散列成多个分片,进而这个热点key就可以迁移到多台DN上。当分片被打散后,对该key的查询需要聚合来自多个DN的多个分片的数据,在查询上会有一定的性能损失。PolarDB-X对分片的管理比较灵活,对同一个表的不同分片,允许使用不同打散策略。例如对p1分片打散成2个分片,对p2分片打散成3个分片,对p4分片不做打散,避免热点分片对非热点分片的影响。05 小结PolarDB-X提供了默认按主键Hash分区的分区管理策略,同时为了满足不同业务的需求也支持了Range和List分区,这三种分区策略可以灵活组合,支持二级分区。为了计算下推,引入了表组的概念,满足不同业务的需求。Reference [1]Online, Asynchronous Schema Change in F1.[2]https://docs.oracle.com/en/database/oracle/oracle-database/21/vldbg/[3]https://dev.mysql.com/doc/refman/5.7/en/partitioning-management.html【相关阅读】分布式数据库如何实现 Join?PolarDB-X 让“Online DDL”更OnlinePolarDB-X SQL限流,为您的核心业务保驾护航通篇干货!纵观 PolarDB-X 并行计算框架
文章来源:云科技时代 宁川在中国的商业数据库界,有一位Oracle的顶流铁粉,这就是中国地区的首位Oracle ACE总监盖国强。什么是Oracle ACE总监呢?Oracle的ACE项目主要是为了发现和培养精通Oracle数据库产品、洞察技术趋势、具备在行业会议和社区活动中作为领域发言人的技术专家。简单来说,就是最懂Oracle的技术大V。Oracle ACE有三个级别,总监级为最高级别。事实上,盖国强对于Oracle的热爱远不止于此。他不仅出版了《深入浅出Oracle》等多本技术著作,还曾是中国最大的数据库技术社区ITPub的主要发起人之一。当然,他创立的公司——云和恩墨,其主营业务自然也没有远离Oracle。云和恩墨为各类企业提供数据库相关的产品和服务,通过近10年发展,已经服务于上千家企业客户。盖国强曾认为,Oracle会是一直值得押注的数据库厂商。但是现在,他的想法慢慢的发生了改变。他决定带领云和恩墨把更多的关注放在中国数据库厂商身上。这种转向背后,是因为他的判断:数据库产业现在进入了中国时刻。数据库中国时刻的出现,是因为“云改变了一切”——云改变了数据库的技术和商业模式,这是传统数据库的巨大挑战。“如果不能及时做出改变,传统数据库就可能会成为昨日黄花”。在数据库的中国时刻,盖国强这个Oracle顶流铁粉,决定躬身入局。被卷入时代洪流的DBA在细述这个Oracle顶流铁粉在中国时刻的抉择之前,可以先了解一下他的团队是如何被时代洪流所影响的。盖国强和他创立的云和恩墨公司2011年7月,云和恩墨公司正式成立,这是一家以提供数据库生态产品和服务为主的科技企业。云和恩墨以“数据驱动,成就未来”为使命,在数据库、数据云平台、云服务等领域,为企业和个人提供可信赖的产品、解决方案和服务。十年的发展和积累,也让云和恩墨汇聚了一大批在数据库领域经验丰富、专业水平过硬的技术人才。邓秋爽和徐孝亮是云和恩墨的两位资深数据库工程师,他们除了精通Oracle数据库外还通晓MySQL、PostgreSQL等开源数据库。作为后辈Oracle DBA,按说已经“江湖通吃”,但是“例外”还是出现了。2020年的一天,因为具有开源数据库的背景,他们被拉入了一家大型金融央企的数据库平台转型项目。具体任务就是把数据库从Oracle向国产云数据库 ——即阿里云原生数据库PolarDB迁移。PolarDB是阿里云于2017年推出的一款自研云原生数据库,因为云计算模式的快速兴起,这款数据库产品获得了迅猛发展,并助力阿里云入选权威的Gartner全球云数据库魔力象限报告,并在2020年进入领导者象限,与AWS、谷歌等顶级技术厂商同台竞技。在接到这个任务之前,邓、徐二人对PolarDB了解并不多。虽然通过各种渠道听说过国产云数据库的发展趋势、以及阿里云原生数据库PolarDB,但真要在金融业务生产环境中应用PolarDB并将业务系统从Oracle迁移过来,他们其实心里并没有底。不过,好在参与具体项目的还有阿里云数据库工程师。通过深度的交流学习以及实践过程的手把手传帮带,他们很快就上手了,先后帮上述央企的数十个系统实现了从Oracle向PolarDB的迁移。作为资深DBA,邓秋爽和徐孝亮原来的工作是帮助客户把一套套Oracle数据库安装好、用起来、管理好,但是现在,他们却开始帮着客户一套套地拆掉Oracle数据库,加速向国产云数据库迁移。这种变化颇有戏剧性,但放在整个数据库产业变革发展的大时代背景之下,又有着必然性。数据库产业进入“中国时刻”作为数据库大咖的盖国强,很早就预见到了这样的趋势。从全球来看,以Oracle为代表的关系型数据库技术已经进入成熟期,很难再看到翻天覆地的产品变化。但是在中国,因为有独特的市场环境,让中国数据库产业迎来了历史机遇窗口。“任何一项技术和产品,要在生产实践中经受检验和磨练,最后才可能找到新的突破点。”盖国强说,中国有着全世界最庞大的数字基础设施市场和社会运转的信息化体系,这让中国数据库技术有了更好的发挥空间、更大的历史舞台。如今,整个数据库市场正在加速向分布式数据库的路线演进,而分布式数据库也是在中国率先得到了广泛应用。如果回顾历史,阿里巴巴最早开展的双11、秒杀等高并发、高集中度的业务场景实践,对传统商业数据库产生了非常严峻的挑战。在这个场景下,数据库成为制约业务发展的核心瓶颈。为了解决双11高并发的场景,阿里在数据库领域进行了前沿探索,自研出PolarDB这样的云原生分布式数据库。今天,类似的场景已经在国民经济场景中随处可见,比如铁路购票12306、健康码等。正是因为这样的应用场景带来的实践,不断推动中国数据库技术产生新突破。“其实作为Oracle公司来说,我认为就错过一件事,就是在最初没有认识到云的本质”,盖国强在谈到Oracle数据库的发展历史时还是有些遗憾,“今天如果谈阿里云的数据库,为什么取得了这样的成就?根本上是因为阿里云取得的成绩。”“如果把整个数据库技术比作一条河流的话,它一直都在没有间断地向前流淌。而我认为,现在它进入了‘中国时刻’。”盖国强预判,会有越来越多的中国声音、中国技术力量、中国杰出人物、中国优秀客户应用场景走上数据库的历史舞台,展示出奔腾不息的生命力与创造力。Oracle顶流铁粉,躬身入局过去几年,盖国强经常跟阿里云数据库总负责人李飞飞(花名:飞刀)探讨数据库的发展趋势和行业热点,两人很早就达成了一个共识——“数据库是云上的终极决战”。盖国强(左)与李飞飞(右)在数据技术嘉年华现场今天,云的IaaS决战已经结束,AWS、微软云、阿里云和谷歌云是四大超级云服务商。随后,云的竞争从IaaS向PaaS演进,PaaS层最核心的就是数据库。主流云厂商都在发力云数据库,因为只有具备了云数据库能力,才能将云的全栈打通。权威市场研究机构Gartner更是预测,2021年云数据库在整个数据库市场中的占比将首次达到50%;2023年75%的数据库将基于云的技术来构建并跑在云平台之上。盖国强也决定带领云和恩墨公司,加大在中国数据库技术上的投入。2019年开始,云和恩墨向国产云原生数据库重点押注。2020年,针对阿里云PolarDB研发了自有IP的数据库工具产品和生态产品,特别是将云和恩墨在数据库监控、自动化运维等方面的丰富积累向PolarDB迁移。与此同时,云和恩墨在还没有商业合同的情况下,就开始向国产云原生数据库投入研发力量,“投身其中”为建设国产数据库生态做贡献。云和恩墨不仅将自有DBA人才向PolarDB转型,面向数据库人才培养的恩墨学院还主动展开了丰富的PolarDB课程,帮助工程师们熟悉云原生数据库。这样的投资也在逐渐获得回报。过去一年多,由于率先和阿里云达成深度合作关系,云和恩墨成为了多家大型企业客户云数据库的运维管理服务商。商业上的收获之外,更让盖国强欣喜的是,阿里云PolarDB在安全、性能、技术前瞻性等维度上表现非常优秀。“这意味PolarDB打开了大企业和传统企业市场的时代大门,而在云下的企业端,也正是云和恩墨最有积累的地方。”“当认知到国产云原生数据库这个时代带来的机遇时,既不能无动于衷,更不能被动等待,而是要切实加大投入,投身其中。”盖国强说。大势将至,未来已来。盖国强这位曾经的Oracle顶流铁粉,在国产云原生大趋势面前,正全力“躬身入局”。
作者:齐木用关系型数据库一定多多少少会用到 Join 操作。常见的 Join 有 Nested-Loop Join,Hash Join,Sort Merge Join 等等。实际在 OLTP 场景中,最常用的就是基于索引点查的 Index Nested-Loop Join,这样的 Join 往往能在极短的时间内返回,相信这也是大多数开发同学对 Join 的感受。PolarDB-X 不仅语法兼容 MySQL,作为分布式数据库,也力求保持与单机数据库一致的使用体验。在分布式场景下,Join 的两张表可能都是分布式表,因此需要通过多次网络请求获取相应的数据。如何高效地实现这一点呢?MySQL 的 Join 实现我们先看看单机数据库上的 Join 是怎么做的。MySQL 支持的 Join 算法很有限:Nested-Loop Join (NL Join)Batched Key Access Join (BKA Join)Block Nested-Loop Join(版本 < 8.0.20)Hash Join (版本 >= 8.0.18)如果 Join 两侧的任何一张表上 join key 列存在索引,那么 MySQL 通常会使用基于索引的 BKA Join 或 NL Join,我们实际使用中的绝大多数情形都对应这种方式。如果 Join 两侧都没有索引可以用,那么 MySQL 只能退而求其次选择 Block Nested-Loop Join 或 Hash Join(取决于 MySQL 版本)。我们今天主要关注 NL 和 BKA Join。Nested-Loop Join 是最简单的 Join 形式,可以看作一个两层 For 循环。对于外表(也称为驱动表)中的每一行,循环检查内表(也称为被驱动表)的每一行,如果满足 Join 条件则作为 Join 结果输出。如果 Join Key 在内侧表有索引可用,那么内表的循环可以大大简化——只要查索引即可拿到可以 Join 的行,无需遍历整个表。我们也将这种带索引的 NL Join 称为 Index Nested-Loop Join。# Nested-Loop Join for outer_row in outer_table: for inner_row in inner_table: if join_condition is True: output (outer_row, inner_row) # Index Nested-Loop Join for outer_row in outer_table: for inner_row in inner_index.lookup(outer_join_key): if join_condition is True: output (outer_row, inner_row)注:*左右滑动阅览下面的例子中,orders 表通过 customer 表的主键 c_custkey 与之进行 Join,MySQL 会使用 Index NL Join 算法完成 Join。/* Query 1 */ SELECT o_orderkey, o_custkey, c_name FROM orders JOIN customer ON o_custkey = c_custkey WHERE o_orderkey BETWEEN 1001 AND 1005注:*左右滑动阅览BKA Join 可以看作一个性能优化版的 Index Nested-Loop Join。之所以称为 Batched,是因为它的实现使用了存储引擎提供的 MRR(Multi-Range Read) 接口批量进行索引查询,并通过 PK 排序的方法,将随机索引回表转化为顺序回表,一定程度上加速了查索引的磁盘 IO。下面的例子中,Join Key 命中的是二级索引,并且 SELECT 的列包含二级索引中所不包含的列,因此需要进行索引回表得到完整的 Join 结果。/* Query 2 */ SELECT c_name, c_custkey, o_orderkey, o_totalprice FROM customer JOIN orders ON c_cutkey = o_custkey WHERE c_custkey BETWEEN 13 AND 15注:*左右滑动阅览通常 OLTP 查询中 Join 驱动侧的数据量不大,并且 Join 往往都有能匹配的索引。这种情况下,NL Join、BKA Join 的代价与驱动侧的数据量呈线性相关,可以迅速计算出结果。PolarDB-X 的 Lookup JoinPolarDB-X 的架构与 MySQL 有很大的不同,它的架构可以分为 SQL 层和存储层,SQL 层的计算节点需要计算数据所在的分片,然后从多个 DN 节点(数据节点)拉取所需的数据。对于 Join 查询,如果恰好 Join Key 和拆分键一致,那么可以将其下推到 DN 执行。否则,就需要在 CN 节点执行 Join。PolarDB-X 支持多种 Join 算法,包括 Lookup Join、Nested-Loop Join、Hash Join、Sort-Merge Join 等多种执行方式。在 OLTP 查询中最常用的就是类似 MySQL BKA Join 的 Lookup Join,本文主要介绍 Lookup Join,其他 Join 诸如 Hash Join、Nested-Loop Join 等将在以后的文章中介绍。除了 Join 本身的功能需求,PolarDB-X 的 Lookup Join 的设计中还要考虑以下两个性能需求:批量。在分布式数据库中,CN 到 DN 的每一次查询都会经过网络 RPC,其延迟相比 MySQL 的本地调用要大几个数量级,因此批量处理显得更为重要。并发。由于数据可能分布在多个 DN 节点上,如果依次遍历则会引入大量不必要的等待,最好的做法是并发地对所有 DN 进行查询,这样每批数据仅需一次网络 round-trip。Lookup Join 的执行过程如下(非索引回表情形):从驱动侧拉取一批数据。通常情况下数据量不会很多,如果数据较多,那么每个批的大小受到 lookup 端的分片数量以及是否可以进行分片裁剪限制。批大小的选择会直接影响查询性能,如果批特别小会导致 RPC 次数太高,批太大则会导致内存中暂存的数据量膨胀,高并发情况下可能导致 OOM。默认情况下我们尽可能让每个分片平均查询 50 个值、最多不超过 300 个值。计算 batch 内每行数据所在分片,由于 lookup 侧是一个分区表,驱动表的每行数据要 lookup 的数据位于不同的分区中。只有包含数据的分片才需要参与 Join,如果没有任何值被路由到某个分片上,那么这个分片也无需被 Lookup。并发请求所有需要 lookup 的分片,并将查到的数据行以 Join Key 为 Key 构建成哈希表,缓存在内存中。类似于 Hash Join,利用哈希表为驱动侧的每行找到与其 Join 的行,取决于 Join 类型,可能 Join 出 0 行、1 行或多行。/* Query 1 */ SELECT o_orderkey, o_custkey, c_name FROM orders JOIN customer ON o_cutkey = c_custkey WHERE o_orderkey BETWEEN 1001 AND 1005注:*左右滑动阅览这个过程中有一些有趣的细节,例如,当要 lookup 的列不止一列(例如 X = A AND Y = B)时如何处理?这时可以通过 row-expression 组成多列的 IN 条件。如果多列 IN 条件中出现 NULL 如何处理?对于 Anti-Join 如何处理?这些就不在这里展开了,有兴趣的同学可以在评论交流。对于绝大多数 TP 查询,Lookup Join 都可以通过一次 lookup 完成 Join,将延迟降到了最低。全局索引与 Lookup JoinPolarDB-X 还支持全局索引,用户可以为分区表创建全局索引表,加快对索引键的查询。和本地索引一样,如果查询中包含索引未覆盖的列,全局索引也需要进行回表。回表的做法和上一小节的 Lookup Join 过程是完全一致的,不难理解——索引回表可以看作一种特殊的 1:1 的 Join。依赖全局索引的 Join 则更为复杂一些,回忆下 MySQL 的 BKA Join,需要进行两次 lookup:第一次用 Join key 查询全局索引表(用于 Join)第二次用全局索引表中的主键查询主表(用于索引回表)将回表结果以 PK 为 key 构建哈希表,与2中的查询结果 Join,得到完整的 Join 右侧数据将完整的 Join 右侧数据以 Join Key 为 key 构建哈希表,与 1 的数据 Join,得到最终 Join 结果/* Query 2 */ SELECT c_name, c_custkey, o_orderkey, o_totalprice FROM customer JOIN orders ON c_cutkey = o_custkey WHERE c_custkey BETWEEN 13 AND 15注:*左右滑动阅览绝大多数 OLTP 查询数据量都能通过单个 batch 完成,完成查询的总延迟为 3 次 round-trip。不难证明在分布式情况下这是最优的。此外,PolarDB-X 也允许用户手动将更多的列加入全局索引的覆盖列,牺牲部分写入性能换取更好的读取性能,如果所有列均被覆盖则无需进行回表,只需两轮 round-trip 即可。更进一步,PolarDB-X 鼓励用户通过合理设计拆分键尽可能将 Join 下推。参考资料1. Enhancing Productivity with MySQL 5.6 New Features2. MySQL · 特性分析 · 优化器 MRR & BKA3. Block Nested-Loop and Batched Key Access Joins【相关阅读】PolarDB-X 让“Online DDL”更OnlinePolarDB-X SQL限流,为您的核心业务保驾护航通篇干货!纵观 PolarDB-X 并行计算框架HTAP 数据库“必修课”:PolarDB-X Online Schema ChangePolarDB-X 向量化引擎的类型绑定与代码生成每次都需要解释大量指令?使用 PolarDB-X 向量化引擎PolarDB-X 面向 HTAP 的混合执行器PolarDB-X 面向 HTAP 的 CBO 优化器如宝马3系和5系:PolarDB-X 与 DRDS 并驾齐驱PolarDB-X 存储架构之“基于Paxos的最佳生产实践”PolarDB-X 私有协议:提升集群的性能和稳定性技术解读 | PolarDB-X 分布式事务的实现技术解读 | PolarDB-X 强一致分布式事务PolarDB-X 一致性共识协议 (X-Paxos)
作者:和君引言ClickHouse是近年来备受关注的开源列式数据库,主要用于数据分析(OLAP)领域。目前国内各个大厂纷纷跟进大规模使用:今日头条内部用ClickHouse来做用户行为分析,内部一共几千个ClickHouse节点,单集群最大1200节点,总数据量几十PB,日增原始数据300TB左右。腾讯内部用ClickHouse做游戏数据分析,并且为之建立了一整套监控运维体系。携程内部从18年7月份开始接入试用,目前80%的业务都跑在ClickHouse上。每天数据增量十多亿,近百万次查询请求。快手内部也在使用ClickHouse,存储总量大约10PB, 每天新增200TB, 90%查询小于3S。阿里内部专门孵化了相应的云数据库ClickHouse,并且在包括手机淘宝流量分析在内的众多业务被广泛使用。在国外,Yandex内部有数百节点用于做用户点击行为分析,CloudFlare、Spotify等头部公司也在使用。在开源的短短几年时间内,ClickHouse就俘获了诸多大厂的“芳心”,并且在Github上的活跃度超越了众多老牌的经典开源项目,如Presto、Druid、Impala、Geenplum等;其受欢迎程度和社区火热程度可见一斑。而这些现象背后的重要原因之一就是它的极致性能,极大地加速了业务开发速度,本文尝试解读ClickHouse存储层的设计与实现,剖析它的性能奥妙。ClickHouse的组件架构下图是一个典型的ClickHouse集群部署结构图,符合经典的share-nothing架构。整个集群分为多个shard(分片),不同shard之间数据彼此隔离;在一个shard内部,可配置一个或多个replica(副本),互为副本的2个replica之间通过专有复制协议保持最终一致性。ClickHouse根据表引擎将表分为本地表和分布式表,两种表在建表时都需要在所有节点上分别建立。其中本地表只负责当前所在server上的写入、查询请求;而分布式表则会按照特定规则,将写入请求和查询请求进行拆解,分发给所有server,并且最终汇总请求结果。ClickHouse写入链路ClickHouse提供2种写入方法,1)写本地表;2)写分布式表。写本地表方式,需要业务层感知底层所有server的IP,并且自行处理数据的分片操作。由于每个节点都可以分别直接写入,这种方式使得集群的整体写入能力与节点数完全成正比,提供了非常高的吞吐能力和定制灵活性。但是相对而言,也增加了业务层的依赖,引入了更多复杂性,尤其是节点failover容错处理、扩缩容数据re-balance、写入和查询需要分别使用不同表引擎等都要在业务上自行处理。而写分布式表则相对简单,业务层只需要将数据写入单一endpoint及单一一张分布式表即可,不需要感知底层server拓扑结构等实现细节。写分布式表也有很好的性能表现,在不需要极高写入吞吐能力的业务场景中,建议直接写入分布式表降低业务复杂度。以下阐述分布式表的写入实现原理。ClickHouse使用Block作为数据处理的核心抽象,表示在内存中的多个列的数据,其中列的数据在内存中也采用列存格式进行存储。示意图如下:其中header部分包含block相关元信息,而id UInt8、name String、_date Date则是三个不同类型列的数据表示。在Block之上,封装了能够进行流式IO的stream接口,分别是IBlockInputStream、IBlockOutputStream,接口的不同对应实现不同功能。当收到INSERT INTO请求时,ClickHouse会构造一个完整的stream pipeline,每一个stream实现相应的逻辑:InputStreamFromASTInsertQuery #将insert into请求封装为InputStream作为数据源 -> CountingBlockOutputStream #统计写入block count -> SquashingBlockOutputStream #积攒写入block,直到达到特定内存阈值,提升写入吞吐 -> AddingDefaultBlockOutputStream #用default值补全缺失列 -> CheckConstraintsBlockOutputStream #检查各种限制约束是否满足 -> PushingToViewsBlockOutputStream #如有物化视图,则将数据写入到物化视图中 -> DistributedBlockOutputStream #将block写入到分布式表中注:*左右滑动阅览在以上过程中,ClickHouse非常注重细节优化,处处为性能考虑。在SQL解析时,ClickHouse并不会一次性将完整的INSERT INTO table(cols) values(rows)解析完毕,而是先读取insert into table(cols)这些短小的头部信息来构建block结构,values部分的大量数据则采用流式解析,降低内存开销。在多个stream之间传递block时,实现了copy-on-write机制,尽最大可能减少内存拷贝。在内存中采用列存存储结构,为后续在磁盘上直接落盘为列存格式做好准备。SquashingBlockOutputStream将客户端的若干小写,转化为大batch,提升写盘吞吐、降低写入放大、加速数据Compaction。默认情况下,分布式表写入是异步转发的。DistributedBlockOutputStream将Block按照建表DDL中指定的规则(如hash或random)切分为多个分片,每个分片对应本地的一个子目录,将对应数据落盘为子目录下的.bin文件,写入完成后就返回client成功。随后分布式表的后台线程,扫描这些文件夹并将.bin文件推送给相应的分片server。.bin文件的存储格式示意如下:ClickHouse存储格式ClickHouse采用列存格式作为单机存储,并且采用了类LSM tree的结构来进行组织与合并。一张MergeTree本地表,从磁盘文件构成如下图所示。本地表的数据被划分为多个Data PART,每个Data PART对应一个磁盘目录。Data PART在落盘后,就是immutable的,不再变化。ClickHouse后台会调度MergerThread将多个小的Data PART不断合并起来,形成更大的Data PART,从而获得更高的压缩率、更快的查询速度。当每次向本地表中进行一次insert请求时,就会产生一个新的Data PART,也即新增一个目录。如果insert的batch size太小,且insert频率很高,可能会导致目录数过多进而耗尽inode,也会降低后台数据合并的性能,这也是为什么ClickHouse推荐使用大batch进行写入且每秒不超过1次的原因。在Data PART内部存储着各个列的数据,由于采用了列存格式,所以不同列使用完全独立的物理文件。每个列至少有2个文件构成,分别是.bin 和 .mrk文件。其中.bin是数据文件,保存着实际的data;而.mrk是元数据文件,保存着数据的metadata。此外,ClickHouse还支持primary index、skip index等索引机制,所以也可能存在着对应的pk.idx,skip_idx.idx文件。在数据写入过程中,数据被按照index_granularity切分为多个颗粒(granularity),默认值为8192行对应一个颗粒。多个颗粒在内存buffer中积攒到了一定大小(由参数min_compress_block_size控制,默认64KB),会触发数据的压缩、落盘等操作,形成一个block。每个颗粒会对应一个mark,该mark主要存储着2项信息:1)当前block在压缩后的物理文件中的offset,2)当前granularity在解压后block中的offset。所以Block是ClickHouse与磁盘进行IO交互、压缩/解压缩的最小单位,而granularity是ClickHouse在内存中进行数据扫描的最小单位。如果有ORDER BY key或Primary key,则ClickHouse在Block数据落盘前,会将数据按照ORDER BY key进行排序。主键索引pk.idx中存储着每个mark对应的第一行数据,也即在每个颗粒中各个列的最小值。当存在其他类型的稀疏索引时,会额外增加一个<col>_<type>.idx文件,用来记录对应颗粒的统计信息。比如:minmax会记录各个颗粒的最小、最大值;set会记录各个颗粒中的distinct值;bloomfilter会使用近似算法记录对应颗粒中,某个值是否存在;在查找时,如果query包含主键索引条件,则首先在pk.idx中进行二分查找,找到符合条件的颗粒mark,并从mark文件中获取block offset、granularity offset等元数据信息,进而将数据从磁盘读入内存进行查找操作。类似的,如果条件命中skip index,则借助于index中的minmax、set等信心,定位出符合条件的颗粒mark,进而执行IO操作。借助于mark文件,ClickHouse在定位出符合条件的颗粒之后,可以将颗粒平均分派给多个线程进行并行处理,最大化利用磁盘的IO吞吐和CPU的多核处理能力。总结本文主要从整体架构、写入链路、存储格式等几个方面介绍了ClickHouse存储层的设计,ClickHouse巧妙地结合了列式存储、稀疏索引、多核并行扫描等技术,最大化压榨硬件能力,在OLAP场景中性能优势非常明显。
作者:梦实数据库的用户对数据库都有一个很简单的要求:“我能不能随时随地做DDL,不要让我守到半夜再搞了。”为了这个目标,各种各样的数据库采用了不同的方法来优化DDL这件事,MySQL也从5.6开始逐渐支持“Online DDL”。但问题是,为何已经到了MySQL 8.0的时代,你能看到的各种MySQL最佳实践\开发规范里,依然建议把DDL放在业务低峰期来做呢。PolarDB-X对此作了很多优化,期望让用户做DDL的时候能更任性。我们得先看一下,MySQL的DDL,为什么不那么“Online”。MySQL中的MDL(元数据锁)MySQL中有个MDL,它会保证在DDL变更过程中,所有的事务都在使用同一份元数据。例如,我们对一个表做一个加列的操作,ALTER TABLE t1 ADD COLUMN(c1 int),MDL确保正在运行的事务,要么全部能看到c1列,要么全部看不到c1列。实际上,MDL起到了元数据切换过程中,排空已有事务的效果。在MySQL中,DDL/DML/DQL等操作,都会对元数据加锁。MDL会带来一些问题,我们看下面的例子:时间点0:我们在连接1中,对t1表做一个INSERT操作,但是不要提交这个事务时间点1:我们在连接2中,对t1表做一个添加索引的DDL操作,这个DDL操作会阻塞住时间点2:我们在连接3中,对t1表做一个SELECT操作,会发现,这个只读操作也会阻塞住时间点3:提交连接1的事务连接2的DDL操作立即返回结果连接3的SELECT操作立即返回结果这个就很奇怪了,一般我们都以为:只读操作不会被写操作阻塞,为什么SELECT语句都会阻塞?MySQL的创建索引操作(特指例子里这个很常见的DDL)不是ONLINE的吗,为什么会阻塞我的业务?以上现象的原因就在于MDL锁上:连接1的INSERT对t1的元数据加了读锁;连接2的DDL获取t1的排它锁,连接2会被连接1阻塞,同时锁住后续的所有读请求;连接3的SELECT获取t1的读锁,被连接2阻塞住。从上面的现象看,MDL锁带来了如下的问题:如果DDL执行之前有长事务,那会阻塞后续这个表上的所有请求(无论读写),进一步占用MySQL的线程资源,甚至引发雪崩,导致其他表的访问也出现问题;MySQL虽然有ONLINE DDL,这个ONLINE更多体现在回填数据阶段。DDL依旧只能在业务低峰期来使用,没有达到真正ONLINE(完全不阻塞业务请求)的目的。这个事是很坑的,我们需要的是真正的ONLINE DDL能力。元数据的版本这里要先解释一个概念,什么是一个元数据的版本?有一个误区是,一个DDL操作(比如上述的加索引的操作),并不是只有“有索引”“没索引”两个版本,而是分成了多个版本。例如PolarDB-X中的CREATE INDEX操作,整个过程会涉及ABSENT(vn),DELETE_ONLY(vn+1),WRITE_ONLY(vn+2),PUBLISH(vn+3)多次版本切换。对于MySQL来说,也有类似的概念,即在MySQL中,一次DDL操作可能会需要多次获取MDL的操作,这也加剧了MySQL DDL过程中MDL的争抢。双版本元数据为了解决MDL的问题,让DDL能够做到真正的ONLINE,PolarDB-X使用双版本元数据来解决这个问题。所谓多版本元数据指的是,PolarDB-X集群中,允许最多同时存在两个版本的元数据(vn与vn+1),每个事务使用的元数据版本由其事务开始时候的元数据版本为准。当使用低版本元数据的事务全部结束之后,低版本的元数据会被清理掉。我们在PolarDB-X上重复上面的例子,会得到这样的结果,DDL不会再因为长事务阻塞所有的操作:时间点0:我们在连接1中,对t1表做一个INSERT操作,但是不要提交这个事务。同时我们可以看到,此时看到的t1的版本是v1时间点1:我们在连接2中,对t1表做一个添加索引的DDL操作,这个DDL操作会在v1切换到v2过程中被阻塞住,因为它会尝试获取t1:v1的元数据锁时间点2:我们在连接3中,对t1表做一个SELECT操作,这个操作会立即返回,不会阻塞。同时我们看到,这个连接看到的t1的版本是v2我们在连接1中,查看t1的元数据版本依旧是v1。此时我们就能看出,DDL前后的两个事务,使用了不同版本的元数据时间点3:提交连接1的事务连接2的DDL操作立即返回结果3个连接看到的t1的元数据的版本都变成了v5(思考下,为什么是v5?)小结PolarDB-X相对于MySQL,彻底解决了MDL锁的问题,Online DDL的元数据切换做到了非常平滑的切换,不会因为长事务阻塞元数据切换,进一步造成更大范围的阻塞,是一种比MySQL的Online DDL更Online的实现。除了更Online,PolarDB-X还为DDL的性能、开销等都做了很多的优化,欢迎持续关注我们的文章。【相关阅读】PolarDB-X SQL限流,为您的核心业务保驾护航通篇干货!纵观 PolarDB-X 并行计算框架HTAP 数据库“必修课”:PolarDB-X Online Schema ChangePolarDB-X 向量化引擎的类型绑定与代码生成每次都需要解释大量指令?使用 PolarDB-X 向量化引擎PolarDB-X 面向 HTAP 的混合执行器PolarDB-X 面向 HTAP 的 CBO 优化器如宝马3系和5系:PolarDB-X 与 DRDS 并驾齐驱PolarDB-X 存储架构之“基于Paxos的最佳生产实践”PolarDB-X 私有协议:提升集群的性能和稳定性技术解读 | PolarDB-X 分布式事务的实现技术解读 | PolarDB-X 强一致分布式事务PolarDB-X 一致性共识协议 (X-Paxos)
作者:陈招尚(胜通)一、MyBase MySQL 核心特性解析1)MyBase MySQL 的能力矩阵图如下图所示MySQL能力矩阵图:左侧是对中大型企业对数据库的要求:第一,有权限的管理逻辑,针对不同企业客户有不同的特殊要求;第二,资源企业需要可控管理;第三,业务的场景下,数据没有瓶颈;第四,满足本企业定制化特性,例如定制化的业务监控;最后,核心的企业数据库,要具备开放和包容的特性,希望能够在数据库做到可上可下。对于以上需求简单总结,MyBase专属指引MySQL4个方向去发展。第一,RDS MySQL要具备企业级数据库所有的能力,例如权限管理、安全性、监控、自治等。第二,在资源方面,大型企业在做预算的时候要做的简单,真正去使用的时候要做的复杂,让整体的资源效率利用率做得更高,为公司节省成本。第三,要做到灵活的弹性,让数据库最好永远没有瓶颈,最后是一定要做到开放包容。2)具备 RDS MySQL 所有能力企业级RDS MySQL数据库产品优势与价值:1. 可用性业界第一最高保障99.99%可用性,包括:三可用区部署的企业版,双可用区部署的高可用本地盘版独享和独占实例。秒级高可用,最快10秒完成主备切换,透明代理服务极大限度降低切换给应用带来的影响。2. 企业级数据库安全事前、事中、事后全方位数据安全保护矩阵。企业版提供RPO=0数据库解决方案,数据100%零丢失。按时间点秒级恢复,异地恢复,闪回保障数据安全。3. 易扩展最快秒级升级能力满足动态业务需求。只读实例、分析实例随业务需求动态扩展。自动化读写分离功能,业务零改造。4. 高性能AliSQL内核优化,性能比开源高80%。动态自适应线程池,高并发状态下性能保持一致稳定。Query Cache提高至少一倍读性能。经历双十一的内核大并发更新写能力,最高TPS5w。3)AliSQL 特性概览如上图所示,内核特性从8.0 5.7开始进行全面对齐,用可插拔方式实现。开源MySQL升级时可快速跟进,从Feature来看Thread Pool线程池,可看到Oracle的官网是MySQL发行公司,企业版里也有线程池,线程池重要的特性就是对性能上有优化好处,当你的数据库连接非常大的时候,不管是长连接或是短连接,始终保持自适应最优的数据库连接状态。在新的线程池设计, Oracle的官方企业版MySQL实现可以防止的抖动,让性能始终处于高位,同时在链接反应不过来的的情况下,以DB的身份登陆,实现进一步的管理。Fast Query Cache, 在8.0已经下线,因为本身在MySQL里实现机制上还有很大缺陷的情况下,通过克服这样的缺陷,实现明显的结果集缓存效果提升,尤其是大规格,如到32核及以上,MySQL单个对利用多CPU的能力不是非常充分,但是Fast Query Cache可以实现这方面的很大提升。CCL&HotQueue对SQL语句可以在内核里面发布命令,控制它的并发度、过滤等。4)超配与隔离的统一超配与隔离,某种程度上来说是一个互斥的词,但做到了统一:用户间专属隔离,用户内超配共享。如上图所示,在两个用户之间,专属集群在机器级别实现了专属的隔离,云上专属数据库中心,100%资源专属,包含CPU、IO、内存、网络等。因为专属,可以进行部分权限的下放,做到对中心应用和边缘应用的相互隔离,不同业务形态专属不同集群,隔离中心应用和边缘应用的相互影响。内部可以超配共享,主备资源交叉部署,充分利用硬件能力,相比RDS PAAS始终有热备机器未使用,最大可节省相当于PAAS一半投入,充分规划业务,混合交叉部署多方业务于同一个集群。整个PAAS平台,在保证稳定性优先的同时,资源利用率可以提升一倍。用户间专属隔离,用户内可以实现超配共享,实现了统一性关系。5)灵活弹性控制资源专属,可以对资源进行部分弹性控制。如上图所示,是一台主机配置64c512gb级别的机器,开始可以8个8c16gb的实例,但随着业务发展,可以让其中的部分实例,抢到32c16gb的资源,满足不同业务对数据库实例资源的个性化需求,如某些实例CPU给多点,某些实例IO给多点,某些实例网络放宽点,这样的状态优点在于DB可以对实体资源做一些个性化控制。另外,弹性规则自动化实现,避免人为介入的延迟,针对重保实例可设置为独享资源,不参与资源弹性分配逻辑。基于硬件资源的使用率(内存分配率、空间分配率、CPU利用率、IO利用率、网络利用率等),DAS智能化触发实例的无缝调度,实现资源最大化利用(研发中)。6)更加开放的MySQL权限如上图所示,可以将MySQL 的change master to权限释放,主节点接到线下连接到线上高速通道打通直接做备实例,也支持备份文件上传到OSS恢复到专属集群。全面兼容开源,线上、线下、多云体验一致。7)开放OS权限:兼容企业原有系统和原有运维模式部署个性化的企业应用,高效的DBA脚本管理。因为机器是客户专属的,OS普通账号权限要给到客户,如上图所示,常用已经支持的命令如下:100GB云盘:读写MySQL数据空间:只读MySQL日志空间:只读/tmp目录:读写通用账号:安装执行非root软件案例介绍 云数据库专属集群助力小盒科技 快速、低成本上云 ,10万学校,5000万+用户,硬件成本降低60%以上。客户简介小盒科技是中国知名的AI教育公司,旗下有目前国内最大的面向公立学校师生应用的教学工具之一“小盒”系列产品,以及为学生提供个性化、自适应学习辅导服务的AI课程体系“小盒课堂”。截止目前,小盒科技的产品已经走进了全国 31个省市自治区近 400座城市的 10万所学校,有超过 5000万小学师生家长在使用小盒科技提供的工具产品、课程和教学辅导服务。未来,小盒将继续推动大数据、AI、深度学习等技术在教育领域的应用,让每个学生都有自己的AI老师,每个老师都有自己的 AI助教。客户痛点自建数据库硬件规模大,资源利用率低,成本越来越高。维护工作量大,人力成本高。在线教育业务高低峰明显且频繁,线下数据库无法快速动态扩缩容,且对业务有侵入性。随着在线教育的发展壮大,核心业务数据库压力越来越大,需要性能优化和支撑。解决方案小盒科技采用云数据库专属集群的方案承载了核心小学业务和网校业务,在专属集群主机之上按需创建出不同RDS数据库实例规格,通过专属集群的超配能力合理配置资源,按业务场景需求快速构建出读写分离、代理短连接优化等不同数据库架构;同时运维上开放数据库和OS权限,客户可自主可控运维数据库。客户价值在阿里云上采用多台专属集群主机承载数据库业务,相比之前的机器规模,降低了60%以上;利用超配能力使得资源使用率大幅提升,节省硬件和运维成本,同时进一步降低了客户的TCO。1.5个月快速完成核心小学业务和网校业务的全部数据库迁移和平滑上线。便捷的云上数据库服务和线下一样的体验,实现自主可控的数据库运维。专属集群不改变客户DBA现有运维模式的同时,构建在之上的RDS for MySQL提供了自动HA、自动读写分离、快速扩容的能力,使客户更聚焦在业务本身。
云原生技术浪潮席卷各行各业,由此带来的技术演进百花齐放。阿里云RDS MySQL团队同样踏上云原生技术浪潮,进行架构升级,潜心打磨产品,为用户带来更加极致的云数据库体验。阿里云RDS MySQL的云原生之路云服务的价值在于数据库功能服务化,实例管理,监控告警,备份还原,安全访问等数据库基础能力通过标准的服务化形态提供给客户,降低客户的运维管理成本。但是传统的云盘单租户架构下,这些服务化能力通过管控系统编排调度,数据库实例需要跟RDS相关管理服务、底层操作系统共享内存,因此无法充分利用规格所购买的内存量。阿里云RDS MySQL团队基于云原生架构技术,将传统的单租户形态升级为多租户场景,充分发挥资源池化调度优势,实例不再需要跟RDS相关管理服务、底层操作系统共享内存,相同实例规格下,相较于传统架构,云原生架构下的MySQL实例能够更加充分地利用CPU及内存资源,由此带来显著的性能提升。以RDS MySQL基础版 mysql.n1.micro.1规格为例,传统单租户架构下,实例可用的innodb_buffer_pool_size为256MB;阿里云在进行云原生技术架构升级后,相同的MySQL基础版mysql.n1.micro.1规格,实例可用的innodb_buffer_pool_size为768MB;实例能够更加充分利用资源的同时,带来的是性能的显著提升。以MySQL 8.0基础版mysql.n1.micro.1规格,磁盘为ESSD 160GB的实例为例,在相同压测条件下,架构升级后,实例性能提升40%以上。形态TPSQPS规格:mysql.n1.micro.1 磁盘:160GB ESSD传统架构188.143762.72云原生架构264.95(+40.82%)5299.01(+40.82%)除了性能上的提升,云原生架构升级还带来了更短的实例创建耗时、更快速的实例扩容等等在内的诸多优化。阿里云RDS MySQL团队为更好服务客户而不断突破自我,未来会向极致的弹性、更高性价比、业务无中断的云原生数据库服务不断进化,欢迎使用阿里云RDS MySQL!
作者:王涛(改天) 一、双十一数据库回顾阿里双11期间成交额4982亿,高峰订单58.3万笔/秒,数据库高峰TPS1.4亿/秒。核心数据库,比如手机淘宝上看到的交易、购物车、商品、商品详情优惠等,都来自于我们团队数据库,核心链路采用云数据库专属集群(MyBase)模式,基于云原生数据库构建上万个节点RPO=0。全面采用云原生管控形态,交易单元核心链路实现100% PaaS云化,扩容效率提升10倍,与此前相比,扩容时间由30分钟缩短至3分钟。 二、数据库应用场景介绍1)阿里业务特性介绍—RDS高可用版一种是RDS的高可用版, RDS的高可用版是我们平时标准的一种数据库形式,采用是一组多备的模式。大家可以看到上图,一个Master,一个Slave,拖了多个RO节点,这个RO节点就是只读节点,它有个缺点,无法真正做到RPO=0。MySQL基于半同步来做,但是半同步有一个问题,如果当Slave挂掉,半同步会自动降级。MySQL最主要核心还是为了保证主库的高可用,而不是数据一致的高可用,像Oracle等其他商业数据库,当备库被挂掉的情况下,如果你设置合理,备库会摘主库写的操作,但是MySQL不会,所以说无法真正做到RPO=0。2)阿里业务特性介绍—RDS三节点企业版(一)使用RDS三节点企业版的目标:解决RPO=0的问题。RDS三节点企业版理论依据:第一点是基于CAP原则 or BASE理论,CAP原理指一致性(Consistency),可用性(Availability),分区容忍性(Partition)。一般来说,CAP原理只能满足CA或CP,很难保证三个都满足,最后还是做了取舍,满足一致性和可用性为主。第二个是BASE理论,BASE理论表示最终一致性,中间允许有一定的不一致,但是作为数据库OLTP系统来说没法使用BASE理论。所以还是要遵循CAP原则。MySQL不丢数据的实现方式:MySQL半同步,问题是半同步一定要等备库返回才会执行,如果在备库挂的情况下,MySQL不等备库,会造成MySQL半同步降级,主库写了备库没有接收到日志,所以没办法用。MySQL Group Replication 第一点,它的性能不是很好;第二点,“Group Replication”中所谓的Group,至少要三个节点以上,三个节点表示有三份数据,成本开销巨大。RDS自研 RDS自研,是目前使用的方式。实现协议Paxos协议 or Raft协议?目前业界主流的分布式协议主要是两个,一个是Paxos协议,一个是基于Raft协议,我们最终选择使用的是Raft协议。3)阿里业务特性介绍—RDS三节点企业版(二)上图, P代表Prepare,Prepare表示事务准备提交,A代表Accept,Accept表示一定接受。第一点,P3.1准备提交,如果A3.1还没提交,大家没有达成一致的情况下,又发起P4.5提交,这时候可以发起 P4.5动作,但如果是Raft协议,一定要P3.1全部提交,A3.1全部确认完以后,它才能启动P4.5。第二点,A3.1和A4.5,他们同时进行提交,只要达到多数派我们就能提交。Paxos协议和Raft的协议最大的区别就在于Paxos协议可以乱序,只要最终一致就可以;Raft的协议是有序的,如果有一个日志没有进行下去,那么下一个没办法开始,而 Paxos可以。Paxos有三个角色,Propose,发起者;Accept,接收者;Learn,观察者。RDS三节点企业版,我们进行针对MySQL的应用场景,进行重新的角色设计:Propose – LeaderAccept – Follower/LoggerLearn – LearnerLeader是数据库的主节点,主要针对数据库的读和写,主要是写,Follower有全量数据,有资格成为Leader。Logger节点主要是存放数据,保证三节点的投票和协议能够进行下去。Learner有全量数据,但是没有投票权。重新设计角色可以更好地满足Paxos协议,同时性能更好,节省成本。Leader和Follower可以有全量的数据;Logger只有日志,没有数据,但是有投票权,不过它不能成为Leader;Learner有全量数据,但没有投票权。这就是当前使用的RDS三节点企业版。4)阿里业务特性介绍—RDS异地多写由于阿里的业务比较复杂,所以是异地多写的。见上图左半部分,我们有多套环境,环境一对应一组应用,有交易、购物车、商品等,然后写到中心数据库:Store或者Wright。Store主要的作用是从数据库里面脱下日志,Wright主要是将Store上的日志应用到对应单元去,又通过Store,然后Wright反写回去。ThreadId保证了数据标识及事务表做记录,异地多写的过程不会产生循环复制。5)阿里业务特性介绍—业务异地多活异地多活主要解决以下几个问题:具备Region级别逃逸能力用户可以在任意单元进行交易上图表示,用户按照一定的规则分流,可能写在中心,也可能写在单元。单元是自封闭的,然后呈星状复制。异地多活意义:水平扩展能力异地容灾能力6)阿里业务特性介绍—RDS异地只读异地只读意义:Learner全量数据Learner不影响Paxos协议每个Learner都有灾备节点基于数据库原生复制一致性高 三、数据库上云方案选择1)上云方案选择—数据上云方案选择数据上云方式对比上云的方案选择,原来阿里巴巴都是全部在自建IDC,方案选择上一是逻辑迁移,MySQL和DTS都认为是逻辑牵引;二是物理迁移。如何选择通过以下几点对比总结:效率上,MySQL dump效率没有直传恢复效率高,直传恢复可以实现一个t一个小时,如果使用MySQL dump,从导出到导入一个t大约需要2-3个小时。迁移性上,数据对象的平滑迁移,如阿里巴巴的数据库可能比较简单,只有table,但是有的业务数据库还有其他类型数据,就不太适合使用MySQL dump。数据的一致性,备库用数据直传恢复比较好,用户自定义的权限很容易丢,如果使用直传恢复就很少会发生,所以使用直传恢复XtraBackup来做。RDS产品支持物理迁移对比RDS产品支持物理迁移对比,现有两个产品,RDS的版本它是不支持的,但是RDS的MyBase版本是支持的, MyBase数据库主机支持 extra backup物理直传上云。2)上云方案选择—网络特性调研网络特性对比网络特性方面,刚开始使用ALB。现在RDS基本上都使用ALB和NGLB,ALB方案成熟,但有个问题,每一条数据的读和写都会经过ALB,会多一层,达不到极致性能要求;NGLB解决了每一条读和写都会经过的问题,只要首包经过,但是例如在双十一0点数据爆发的时候,性能压力会比较大,这种情况不一定适合使用NGLB;ENI弹性网卡,弹性网卡是标准的云产品,目前的亚马逊等其他云厂商都在使用,程序弹性网卡不支持物理机,ECS的绑卡限制,信息安全组,都是它的一些限制,但是ENI+MyBase可以解决这个问题。总结来说:应用和数据库在同一网络平面上,然后中间没有代理层,没有NGLB或者ALB这一层,效率会比较高,管控和用户在两个IP上面,管控有IP,用户应用又是一个IP,需要管控这边进行实现。思考+结论基于上面有两个思考,就是数据要双向流动又能出,还有性能的考虑,所以使用是弹性网卡+MyBase的方案。3)上云方案选择—网络拓扑图如上图所示是网络拓扑图,最上层是数据库控制面,数据库管控,需要去创建RDS实例等,RDS部署在一个VPC环境,RDS售卖区的VPC是中心的VPC, 每个ECS上可以部署多个数据库,这里换了n多个POd,一个POd就代表一个数据库,对应的是一个弹性网卡, ECS上面飘了一个弹性网卡,连到了用户的VPC,用户之间的VPC是互相打通的,这里用的是 CEN阿里巴巴云企业网,保证用户 VPC和VPC之间相通,上面部分ECS和ECS之间的弹性网卡也就通了,因为数据库访问到对应的用户VPC,用户之间通过云企业网直接打通。用户自建网络和VPC相通是通过拉专线。4)上云方案选择—逻辑金属服务器介绍(一)如上图所示是阿里云的逻辑金属服务器,最底层是阿里云X-Dragon芯片,上面跑着容器第三方虚拟化等;X-Dragon云服务器,上面跑着Doctor、 Container、 Punch等一些容器。5)上云方案选择--逻辑金属服务器介绍(二)逻辑金属服务器分为以下几块:绿色部分的MOC卡主要是一个独立的小型计算机,作用是联通VPC\SLB,EBS云盘。逻辑金属服务器上还有一个独立的CPU和内存,如果是在机器上硬件本地的,想要访问这个ECS的云盘等就是通过下面的MOC卡,然后再录过去。使用逻辑金属服务器是因为几个特性:分钟级交付, 资源弹性兼容 VPC/SLB/RDS 等云盘启动云盘挂载(系统盘和数据盘,使用的都是云盘)兼容虚拟机镜像物理机的性能 + 隔离性虚拟机VNC/控 制台/OpenAPI 体验宕机迁移恢复免人肉自动运维6)上云方案选择—机器选择如上图综合对比,从运维自动化、计算、存储、网络、ecs等多角度,逻辑金属服务器是最优的。7)上云方案选择—存储选择还有一个存储的选择。目前本地盘最大的规格支持6t,而云盘的规格最大支持16t,从数据的扩展角度来说,云盘最好。云盘的快照能力,云盘具有很强的扩张能力,可以支持分钟级的备份,如果本地盘全量备份一次,用XtraBackup,是一个t一个小时,和数据量成正比,而云盘不会这么慢。分钟级的克隆。RDS界面可以支持数据克隆,如果基于云盘快照来做克隆,会更快。跨Region的克隆,云盘是支持,如果数据在是本地盘,想看备份,那本地的数据传到异地,过程就会非常漫长,克隆的恢复时间也是巨大的。云盘支持在线扩容,本地盘不支持,如果本地盘买的是500M实例,要扩到510G可能还有空间,可以很快地进行扩容;如果500g要突然间扩成6个T,机器上没有这么多空间的情况下,那是没办法做在线扩容,这时候要挪数据,在运维窗口中切换等,时间又是一个非常慢的过程。磁盘IOPS可配置性。云盘上可以选PL1到PL3,可以选磁盘的性能等级,但本地盘是没有可选项的。MySQL原子写。通过云盘可以保证原子写,而本地盘MySQL有double right的写,主要保证写的原始性,MySQL一个页的大小是16k, Linux最大的一个页写入就是4k,云盘没有这个问题,只要一次写就可以了,本地盘要写两遍。可靠性方面,云盘的可靠性会比本地盘高,本地盘做的是LVM,但是云盘有三副本。8)上云方案选择 – 充分利用MyBase特性传统的RDS是随机打散的,客户买了一个4c4g的实力或者8c8g的实力,不知道部署在哪里,更多买的是一个MySQL服务,如果我们使用MyBase特性,有几个特点,亲和性等,可以调度MyBase实例,实例上面跑的是购物车或者商品,其他的东西都是可以自己去指定的。如上图所示,Leader和Follower交叉部署,可以保证一个性能最大化,50%是A业务的组,50%是B业务的组,这样的效率是最高的,弹性、隔离、超慢都可以自己实现,每个机器上挂的是分布式存储,可以快速的弹性,从a节点快速弹到b节点。9)上云方案选择 – 异地只读(GDN)异地只读是在阿里云官网买的实例就是各Region独立。在这个项目里面,上云的时候,使用了GDN方案(Global Database Network),如上图所示,是上海三节点,上海的APP通过Mark scale进行了读和写,都在上海本地,如果深圳的APP通过Mark scale进行读写分离,然后把读的流量路由到上海中心,写的流量在深圳本地,这样就可以做到了读写分离的作用,而目前只是在阿里内部使用。 四、未来展望2020双11,MySQL数据使用的是MyBase产品形态,客户可以自己选择亲和性隔离等,经过了上游评估、网络评估、机器评估、数据库本身的形态评估还有异地只读,保证了2020年双11的平滑。整个RDS经过了几个过程:物理机形态,物理机形态就是最早的RDS;存计分离形态,存计分离形态就是的云盘板形态;MyBase形态,多样性方面已在灰度;智能化,通过RDS控制台可以看到大致的页面,已经在做智能化方向,后面智能调参,SQL优化、SQL诊断,性能诊断等都是要做的方向。
作者:玄弟 七锋PolarDB-X 面向 HTAP 的混合执行器 一文详细说明了PolarDB-X执行器设计的初衷,其初衷一直是致力于为PolarDB-X注入并行计算的能力,兼顾TP和AP场景,逐渐为其打造一款具备TB级数据处理能力的数据库。为了做到这一点,借鉴了各种数据库和大数据库产品,包括分析型数据库,实时数仓等,吸取了各方面的优势来打造出一个全新的并行执行引擎。这里将对整个分布式并行执行框架做详细的介绍,希望阅读之后对我们的执行器有一个全面的认识。▶ 整体设计PolarDB-X 是一个 Share Nothing 的数据库,采样了计算和存储分离的架构。其中数据以分片的形式存储于每个DN节点,计算节点叫CN。在计算过程中,DN和CN间、DN和DN、CN和CN间是通过千兆、万兆网络通信。每个CN节点上一般都会有调度组件、资源管理组件、RPC组件等。一个复杂的查询,会被调度到多个CN节点上执行,考虑到数据一般都会根据均匀策略分到各个DN上,所以每个CN节点同时会访问多个DN。当用户提交一条复杂的SQL语句,往往需要访问多个DN节点,这个时候就会启动并行调度策略,整个执行步骤可以简单理解:用户所连接的这个 CN 将承担查询协调者(Query Coordinator)的角色;Query先发送给Query Coordinator,会首先经过优化器生成最新的Plan,然后会拆分到多个子计划(Fragment), 每个Fragment可能会包含多个执行算子。如果有的Framgnt负责扫描DN的话,它里头必定包含Scan算子,从DN拉取数据;Fragment也可以包含Agg或者Join等其他算子;Query Coordinator里头的调度器(Task Scheduler)会按照定义的调度逻辑将各个Framgnts封装成Task,调度到合适的CN上执行,这里头可能会涉及到一些资源上的计算;各个CN收到Task后,会申请执行资源,构造执行的上下文,开始启动Task,并定期会向Query Coordinator汇报状态;各个Task间会通过数据传输通道(DTL)交换数据,当所有的Task都执行完毕后,会将数据结果返回给Query Coordinator,由它负责将结果返回给用户;成功返回给用户后,Query Coordinator和被调度到的各个CN节点的Task会做清理动作,释放计算资源。整个流程大致就这样,有细心的同学会发现我们的框架在DN上有一层叫Split概念。我们的数据是按分片存储在各个DN上的,Split指的是数据分片partition的地址。对于包含扫描算子Scan的 Task,会计算出需要访问哪些 partition,这些 partition 分布在哪些 DN 上,然后封装成splits按比例划分给这些扫描Task。但是实际运行过程中每个扫描Task并不是预分配好splits的,而是预分配部分splits给扫描Task,看哪一个Task扫描的更快就会从Query Coordinator继续获取余下splits,这样可以尽可能避免由于各个扫描Task资源不均衡导致的消费长尾现象。但是如果一个表只被分成了2个分片,是不是意味着扫描任务至多只能是2,这可能起不到明显的并行加速效果。所以我们也支持在分片上继续按照分段做拆分,那么这个时候的Split除了会记录分片的地址,也会记录在分片上分段的位移。按照分段做拆分后,即便数据的分片数量有限,执行过程我们依然可以启动更多的扫描Task,并行去加速扫描。▶ 执行计划执行引擎执行的是由优化器生成的分布式执行计划。执行计划由算子组成。因为PolarDB-X的数据按照分片存储到各个的DN节点上去,执行计划执行也会尽可能的满足数据分布的locality,能下推的计划会被放到DN上执行,不能下推的计划会会被切分成一个个子计划(Fragment),会被放到各个CN节点上执行。所以这里我们需要关心如何将一个从优化器出来的计划,拆分成分布式计划,放到各个CN上执行?为了更好地理解这个过程,我们这里以一条简单SQL: select * from (select useid, count(*) as b from user_data group by userid) as T where T.b > 10 为例,经过优化器生成这样的相对最优计划:针对并行执行计划,为了更高效地执行尽量减少数据传输,可以把执行计划按照计算过程是否需要数据重分布(ReDistribution)分为不同片段(fragment)分布到相应节点执行,并且把一些操作下推来减少扫描输出的数据,上面的计划可能就变成这样的执行计划,由多个子片段构成。不同片段之间通过 NetWork Write/Read 算子进行数据交换。更复杂的比如多表关联(join)查询,会有更多的片段和更复杂的数据交换模式。每个片段的并发度可以不同, 并发度是基于代价推导出来的。多机调度的基本单位是Stage,Stage记录了上下游片段的位置信息,以便上下游之间建立网络通道(DTL)。每个片段调度到计算CN节点后,会被封装成逻辑执行Task,比如fragment-1并发度是2的话,那么会将Task-1.0和Task-1.1 两个Task分别调度到两个CN节点。Task仍然是CN节点计算的逻辑单元,PolarDB-X执行器不仅仅可以支持单机并行能力(Parallel Query),也可以做多机并行(MPP)。所以在CN节点还引入了二层调度逻辑。当然二层调度的好处不仅仅于此,后面我们还会提到。这里会继续在Task内部根据算子间数据交换的特性,继续做切分,切分成不同Pipeline。不同的Pipeline并发度也可以不同,每个Pipeline会根据处理的数据规模大小会计算出不同的并发度,生成具体的执行单元Driver,Driver间会根据二层调度确定上下游的本地通道(Local Channel)。至此你应该可以了解从执行逻辑计划转化为分布式物理执行的整个过程。引入了一些新的名称,这里统一做下梳理:Fragment:指的是逻辑执行计划按照计算过程中数据是否需要重分布,切割成的子计划。Stage:是由Fragment封装而成的调度逻辑单位,Stage除了封装Fragment外,还会记录上下游Stage间的调度位置信息。Task:Stage并不会在CN上直接运行,他们是通过并发度分解成一系列可调度到CN上的Task, Task依然是逻辑执行单元。Pipeline:对CN上的Task根据二层并发度做进一步切分,切分成不同的Pipeline。Driver:一个Pipeline包含多个Driver,Driver是具体的执行单元,是一系列可运行算子的集合。一般来说针对一个复杂查询,一个query包含多个Fragment,每个Fragment和Stage一一对应,每个Stage包含多个Tasks,每个Task会切分成不同的Pipeline,一个Pipeline包含了多个Driver。只有理解上面说的Fragment/Stage/Task/Pipeline/Driver这些概念,你才能更清楚了解我们接下来的设计。▶ 调度策略并行计算在运行之初,需要解决任务调度问题。调度的直白理解,就是将切分好的Task调度到各个CN节点去执行,充分利用各个CN的计算资源。这里头大家很容易有这些疑问:1. 执行过程中各个CN节点的计算资源是不均衡了,那么在多机调度中是如何将各个Task打散到不同CN节点去执行? 2. 和各个DN交互的Task是如何并行的拉数据的?比如某个逻辑表分成了16个物理表,分布在4个DN节点上,有4个Driver去并行拉数据,每个Driver并不是均匀拉取4个物理表,而是根据自身的消费能力来确定拉取的物理表数量;多个Driver下发扫描任务会不会同时恰好落地一个DN节点上,导致某个DN成为瓶颈? 3. 我们完全可以在一个CN节点,同时调度多个Task执行,已经可以做到单机并行,为什么还要二层调度?一层调度(多节点间)为了解决(1) 和 (2) 的问题,我们在CN节点内部引入了调度模块(Task Scheduler),主要负责Task在不同CN节点上的调度,这一层调度我们这里称之为一层调度,在这层调度中,同属于一个Stage的多个Task一定会被调度到不同CN节点上去,确保一个CN节点只能有相同tage的一个Task。调度过程中通过心跳不断维护Task状态机,也维护着集群各个CN节点Load信息,整个调度是基于CN Load做调度的。多机调度流程如下所示:Resource Manager(RM)是CN节点上个一个资源管理模块,RM会借助Task心跳机制实时维护集群各个CN节点的负载,Task Scheduler组件会基于负载选择合适的CN节点下发执行任务,比如CN-1 负载相对集群其他CN节点来说高很多,那么当前查询的Task会分发给其他CN节点,避免调度到CN-1节点去。执行器在执行Task任务时,Task并不是创建好的时候就确定了其消费DN splits的映射关系。各个Task按批次动态拉取splits进行消费, 直白理解就是谁的消费能力越强谁就有可能消费更多的splits。同样为了解决同一个时刻多个任务同时消费同一个DN上的splits问题,我们在调度之初会将splits根据地址信息按照Zig-zag方式,把各个DN上的splits打散到整个splits queue上去,消费的时候可以尽可能分摊各个DN压力,这样计算过程中也会充分利用各个DN的资源。有了一层调度后,我们也可以将同属于一个Stage的多个Task调度到同一个CN,这样其实也可以做到单机并行。如果这样设计的话,我们容易忽略两个问题:一层调度的逻辑比较复杂,需要多次交互,一个CN内部需要同时维护各个Task的状态,代价会比较大,这在TP场景是无法容忍的;一层调度中,并发度越高,生成Task就越多,这些Task间需要建立更多的网络传输通道。二层调度(节点内部)为了解决上述一层调度的不足,为此我们在参考Hyper的论文[1],引入了二层调度,既在CN节点内部单独做单机并行调度,简单来说我们会在Task内部借助CN的本地调度组件(Local Scheduler),对Task做进一步的并行调度,让Task在CN上执行,也可以做到并行运行。下图中,Stage-1和Stage-2是上下游关系,各自并发度都是9,调度到3个CN节点执行。如果只有一层并发度的话,每个CN节点还会调度运行3个Task,那么上下游之间总共会建立81个Channel,CN节点内部Task是相互独立的,这样缺点还是很明显:多个Channel,放大了网络开销,同一份buffer会被发送多次,发送和接收对CPU和Memory都有代价;数据发送的对象是Task,数据本身有倾斜,会导致同节点内Task之间的负载不均衡(hash skew),存在长尾问题。而一层调度和二层调度相结合的话,Stage-1和Stage-2的一层并发度是3,这样每个CN节点只会有1个Task,Task内部并发度3。由于shuffle的对象是Task,所以Stage-1和Stage-2间只会建立9个Channel,大大减少了网络开销,同时Task内部的3个Driver内数据是共享的,Task内部的所有的Driver可以共同消费接受到的数据,并行执行,避免长尾问题。针对于HashJoin,假设Ta为大表,Tb为小表,这两个表做HashJoin,可以让Ta和Tb同时shuffle到同一个节点做计算;也可以让小表Tb广播到Ta所在节点做计算,前者的网络代价是Ta+Tb,而后者的代价是N*Tb(N代表广播的份数)。所以如果只有一层调度的话,N可能比较大,执行过程中我们可能会选择两端做shuffle的执行计划;而一层和二层相结合的调度策略,可以让执行过程中选择BroadcastHashJoin,这样可以避免大表做shuffle,提高执行效率。此外在二层调度策略中,task内部的多线程很容易做到数据共享,有利于更高效的算法。如下图,同样是HashJoin过程中,build端的Task内部多个线程(driver)协同计算:build端收到shuffle的数据后,多线程协同建立一个共享的hash表。这样一个task只有一个build table,probe端收到shuffle数据后,也不用做ReDistribution了,直接读取接受到数据,进行并行的probe。▶ 并行执行聊完调度,接下来应该是关心任务是如何在CN上运行,运行过程中遇到异常我们系统是如何处理的呢?线程模型说到执行,有经验的同学可能会发现我们的调度并没有解决调度死锁问题,比如对于下面这样一个执行计划,两表Join。一般会遇到两种问题:1. 如果先调度f3和f2的话,这个时候假设集群没有调度资源,则f1不能迟迟调度起来。而HashJoin的逻辑就是需要先构建buildTable,这里f1刚好是build table部分。最终会导致执行死锁:f1在等待f3和f2的计算资源释放,而f2和f3又在等待f1构建完buildTable; 2. 如果f1先调度起来了,假设这个时候f2和f3没有调度资源,这个时候f1从DN拉出来的数据,其实是无法发送给f3的,因为f3还没有被调度起来。解决问题1,业界有很多方式,比较常见是在调度之初构建调度依赖关系(Scheduler Depedency):f1->f3-f2。而解决问题2,往往是将f1把DN拉出来的数据先放到内存中,实在放不下就落盘处理。可见处理上述两个问题,执行框架不仅仅需要在多机调度上做复杂的调度依赖关系,同时还需要考虑对落盘的支持。而其实我们在调度的时候,并没有去考虑调度依赖这个事情,我们是一次性把f1/f2/f3全部调度起来了,这个是为何呢?这就要说下我们执行中的逻辑线程模型概念。在大多数计算引擎中,一个查询首先会通过资源调度节点,在各个CN上申请执行线程和内存,申请成功后,这些执行资源会被调度组件占用,用来分配当前查询的Task,不可以再被其他查询所利用,这种是真实的执行资源,和调度资源相互绑定,当CN上可利用的执行资源不够的时候,才会出现调度死锁问题。而在PolarDB-X中,我们并没有在调度的时候申请真实的线程资源,调度只需要考虑各个CN的负载,不需要考虑各个CN到底还剩多少可利用的真实资源。我们的线程模型也并没有和调度资源绑死,每个Driver其实不独占一个真实的线程,换句话说,真实的线程也并没有和调度资源一一对应。虽然说Driver是执行的基本单元,但是在调度上来看,它又是逻辑的线程模型而已。那是不是意味着只要有调度任务,都可以被成功调度到CN上去,答案是肯定的。一次性调度所有的执行单元到CN上去执行,对内存和CPU也是一种开销。比如f2被执行起来后,但是f1并没有执行完毕,那么f2也会不断执行,其数据其实也会被缓存起来,但是也不能无限缓存数据呀?为了解决这个问题,接下来就需要借助我们的时间片执行了。时间片执行我们在每个CN节点内部会有一组执行线程池来运行这些Driver,每个Driver会排队进入线程池参与计算,如果Driver被阻塞就会退出到Blocking队列中,等待被唤醒。比如f2 driver 启动后,从DN拉了数据放到有限空间buffer里头去,这个时候假设f1 driver都没有结束,那么f2 driver 对应的buffer就会满,满了后就会阻塞住,一旦阻塞我们的执行框架就会让f2 driver从执行器退出来,加入到Blocking队列中,简单的说就是将计算资源腾让出来,等待被唤醒。直到f1 driver都执行完毕后, f2 driver会被唤醒,执行框架就会将他移动到Pending队列中,等待被调度到执行线程池中继续运行。这里头还是会浪费点内存,但相对于CPU资源来说,内存资源还是比较充裕的。时间片执行的核心就是需要判断Driver何时会被Block的,总结起来被阻塞的原因一般分为三种情况:根据算子依赖模型来确定,比如图中f1 driver未执行完毕,那么f2 driver其实也会被阻塞(这个是一个可配置的选项);计算资源不足(主要指内存),对应的driver会被挂起,等待资源释放;等待DN响应,物理SQL下发给DN后,Driver会被挂起,等待物理SQL执行完毕。除此之外我们在借鉴Linux 时间片调度机制,会在软件层面上统计Driver的运行时长,超过阈值(500ms),也会被强制退出执行线程,加入到Pending队列,等待下一轮的执行调度。这种软件层面上的时间片调度模型,可以解决复杂查询长时间占用计算资源问题。其实实现起来也挺简单的,就是每计算完一个批数据后,我们会对driver的运行时长进行统计,超过阈值,就退出线程池。下面贴出了Driver处理逻辑的部分伪代码,Driver在执行采用的是经典的Producer-Consumer模型,每消费一个Chunk我们就会累计时间,当超过既定阈值,就会退出来。任务状态机高并发系统,频繁地等待或者任务切换是常见的系统瓶颈。异步处理是一种已经被证明行之有效地避免这些瓶颈,把高并发系统性能推到极致的方法。所以PolarDB-X执行器的整个后端,统一使用全异步的执行框架;同时MPP执行过程涉及到多机的协调,所以这就要求我们在系统内部维护这些异步状态。异步状态的维护特别重要,比如某个查询下的Task执行失败,需要立即通知到整个集群中该查询正在运行的Task任务,以便立即中止,以防出现Suspend Task,造成资源不释放问题。所以在执行器内部,我们从三个维度(Task Stage Query)去维护状态, 这三种State是相互依赖耦合的,比如Query 被Cancel,会立即通知其所有的Stage,各个Stage监听到状态变化,会及时通知给其所有的Task,只有等待Task都被Cancel后,Stage 最后的状态才变更为Cancel,最终Query的状态才被标记为Cancel。在这个过程中我们会引入对状态机异步监听机制,一旦状态发送变更就会异步回调相关处理逻辑。通过维护这些状态,我们也可以及时通过查询或者监控诊断到任务是否异常,异常发生在哪个环节,也便于我们后期排查问题。▶ 资源隔离如果并发请求过多的时候,资源紧张会让请求线程参与排队。但是正在运行的线程,需要耗费比较多的计算资源(CPU和Memory)的时候,会严重影响到其他正常正在运行的Driver。这对我们这种面向HTAP场景的执行器是决定不被允许的。所以在资源隔离这一块,我们会针对不同WorkLoad做计算资源隔离,但这种隔离是抢占式的。CPU在CPU层面上我们是基于CGroup做资源隔离的,根据WorkLoad不同我们把CPU资源分为AP Group和TP Group两组,其中对TP Group的CPU资源不限制;而对AP Group是基于CGroup做硬隔离,其CPU使用水位的最小阈值(cpu.min.cfs_quota)和最大阈值(cpu.max.cfs_quota)来做限制。执行线程分为三组: TP Core Pool 、AP Core Pool、SlowQuery AP Core Pool,其中后两者会被划分到AP Croup一组,做严格的CPU限制。Driver会根据WorkLoad划分到不同的Pool执行。看似很美的实现,这里头依然存在两个问题:1. 基于COST识别的WorkLoad不准怎么办? 2. AP查询比较耗资源,在同一个Group下的多个慢查询相互影响怎么办?出现问题(1)主要的场景是我们把AP类型的查询识别成了TP,结果会导致AP影响到TP,这是不可以接受的。所以我们在执行过程中会监视TP Driver的执行时长,超过一定阈值后仍没有结束的查询,会主动退出时间片,然后将其它调度到AP Core Pool执行。而为了解决问题(2),我们会将AP Core Pool中长时间运行都未结束的Driver,进一步做优雅降级,调度到SlowQuery AP Core Pool执行。其中SlowQuery AP Core Pool会设置执行权重,尽可能降低其执行Driver的频率。MEMORY在内存层面上,会将CN节点堆内内存区域大致可以分为四大块:TP Memory:用于存放TP计算过程中的临时数据AP Memory:用于存放AP计算过程中的临时数据Other:存放数据结构、临时对象和元数据等System Reserverd:系统保留内存TP和AP Memory分别会有最大阈值和最小阈值限制,两者内存使用过程中可以相互抢占,但是基本原则是:TP Memory可以抢占AP Memory,直到查询结束才释放;而AP Memory可以抢占内存TP,但是一旦TP需要内存的时候,AP Memory需要立即释放内存,释放方式可以是自杀或者落盘。▶ 数据传输层(DTL)并行计算是充分利用各个CN资源参与计算,那么DN与DN之间必然会存在数据交互。各个DN上的上下游的Task数据需要传输,比如上游的Task数量N,下游的Task数量是M,那么他们之间的数据传输通道需要用到M*N个通道(Channel),同样的我们将这些通道(Channel)的概念抽象成数据传输层。这个传输层的设计往往也会面临两个问题:1. 通道分为发送端和接受端,当发送端源源不断发送数据,而接受端无法处理的话就会造成内存雪崩; 2. 数据在传输过程中丢失。在业界实现数据传输主要有两种传输方式:Push和Pull。Push就是发送端往接受端推送数据,这里头为了避免接收端处理不过来,需要引入流控逻辑,一般的做法都是在接收端预留了槽位,当槽位被数据占满时会通知发送端暂停发送数据,当有接收端数据被消费空闲槽位出现时通知发送端继续发送,这里头会涉及到发送端和接收端的多次交互,流控机制相对比较复杂。Pull就是发送端将数据先发送到buffer里头去,接收端按需从发送端的的buffer拉数据,而当发送端发送的数据到buffer,接收端假设长时间不来拉数据,最后发送端buffer满了,也会触发上游反压,为了避免频繁反压,往往发送端的buffer不应该设置太小。综合起来我们选择了pull方式来做。采样pull方式也会遇到两个问题:1. 每个receiver一般会和上游多个sender建立连接,那么每次都是通过广播的方式从上游所有的sender拉数据吗? 2. 一次从sender端到底请求多少的数据呢,即averageBytesPerRequest?我们先回答问题(2),我们这里会记录上一次请求的数据量lastAverageBytesPerRequest、当前建连通道个数n以及上一次总共返回的数据量responseBytes,来计算出当前averageBytesPerRequest,具体的公式下面也给出了。至于问题(1),有了当前的averageBytesPerRequest后,结合目前receiver上buffer剩余空间,可以估算出这一次需要向上游几个sender发送请求。在异步通信过程中为了保证传输可靠性,我们采用了类似tcp ack的方式,当receiver端带着token去上游拉数据的时候,则表示当前token之前的数据均已经被receiver端消费完毕,sender可以释放这些数据,然后将下一批数据以及nextToken返回给receiver端。▶ 效果展示前后说了很多干货,下面咱们来点简单实际的东西。这里以TPCH Q13为例来演示下执行器在不同场景下的加速效果,为了方便截图在Q13后面都加了limit。该测试环环境下,CN和DN规格都是2*16C64G。单机单线程下运行,耗时3min31s使用Parallel Query加速,既单机多线程执行,耗时23.3s使用MPP加速,既同时利用两个CN节点的资源计算,耗时11.4s▶ 总结不管是简单查询,还是 Parallel Query和MPP场景下的复杂查询,共用的都是一套执行框架。不同场景下对执行器的要求,更多的是并发度设置和调度策略的差异。相对于业界其他产品来说,PolarDB-X执行器主要特点:在资源模式上使用的是轻量化的资源管理,不像大数据计算引擎,需要额外引入的资源管理的节点,做严格的资源预分配,主要考虑到我们的场景是针对于小集群的在线计算;在调度模型上执行器支持DAG调度,相对于MPP调度可以做到更加灵活的并发控制模型,各个Stage间、Pipeline间的并发可以不一样;区别与其他产品,AP加速引用的是外挂并行计算引擎,PolarDB-X并行执行器是内置的,不同查询间共用一套执行模型,确保TP和AP享有一致的SQL兼容性。PolarDB-X并行计算在线上已经平稳运行了近两年,这两年来我们不仅仅在执行框架上做了很多稳定性工作,在算子层的优化我们也沉淀了不少的技术。但这些还不够,目前比较热的是自适应执行,结合Pipeline模式的自适应执行挑战比较大,我们近期也在研究,欢迎感兴趣的朋友来拍拍砖,一起进步!▶ Reference[1] V. Leis, et al., Morsel-Driven Parallelism: A NUMA-Aware Query Evaluation Framework for the Many-Core Age, in SIGMOD, 2014.[2] Presto: SQL on Everything.[3] A Deep Dive into Query Execution Engine of Spark SQL.[4] Impala: A Modern, Open-Source SQL Engine for Hadoop[5] FusionInsight LibrA: Huawei's Enterprise Cloud Data Analytics Platform. Proc. VLDB Endow. 11(12): 1822-1834 (2018)【相关阅读】HTAP 数据库“必修课”:PolarDB-X Online Schema ChangePolarDB-X 向量化引擎的类型绑定与代码生成每次都需要解释大量指令?使用 PolarDB-X 向量化引擎PolarDB-X 面向 HTAP 的混合执行器PolarDB-X 面向 HTAP 的 CBO 优化器如宝马3系和5系:PolarDB-X 与 DRDS 并驾齐驱PolarDB-X 存储架构之“基于Paxos的最佳生产实践”PolarDB-X 私有协议:提升集群的性能和稳定性技术解读 | PolarDB-X 分布式事务的实现技术解读 | PolarDB-X 强一致分布式事务PolarDB-X 一致性共识协议 (X-Paxos)
以下文章来源于轻金融 ,作者李静瑕当一家支付机构,突然收到比平时高出10倍的流量需求时,该怎么办?这正是2020年“双十一”前夕汇付天下所面临的情形。出乎意料的是,他们仅仅在一小时之内就完成了所有资源的准备和扩容,并且以高可用性和稳定性很好地支撑了这次电商活动。放在一年之前,这是完全不敢想象的。能做到这一切,要源于2017年年中一次会议上的大胆提议。当时,汇付天下董事长周晔提出公司要进行数字化转型,成为一家为商户提供数字化解决方案的科技型企业。要完成这样的转变并不容易,汇付天下需要做的就是先让自己拥有数字化能力。而正是在这样的背景之下,汇付天下决定更换最底层的技术基础设施,逐渐抛弃传统集中式架构,全面转向云原生技术。从2019年末开始,汇付天下用282天的时间,创造了一个行业奇迹,完成了公司历史上最大的云原生技术升级项目“冲上云霄”。2020年10月30日,汇付天下特地举办“冲上云霄”庆功宴。周晔对参与项目的全体人员激动地说 “你们做到了”,宣布汇付天下正式开启了新的数字化旅程。1、282天,科技能力再创新高尽管还会常常想起那惊心动魄的282天,但汇付天下董事长周晔终于可以舒下心来。成立于2006年6月的汇付天下,是一家国内领先的科技型公司,成立之初公司先是抓住了航旅数字化的机会切入了商户收单业务,随后逐渐拓展到商户服务。2019年,公司交易量即高达2.2万亿元。成立至今15年,汇付天下从未拘泥于传统服务,而是不断在进行创新和探索。转折点发生于2017年。当时,支付行业面临“断直连”的严监管,单一的支付运营模式竞争白热化,随着新技术的飞速发展,C端客户对企业的数字化服务需求直线上升,不少支付机构将战略重点转向B端商户的数字化服务。也是在这一年,汇付天下开始酝酿数字化转型,为商家提供更多数字化服务成为其中一个重要的方向。与此同时,董事长周晔清晰地认识到,不论如何战略转型,拥有数字化的技术能力一定是公司的核心竞争力之一。2018年和2019年,汇付天下开始逐步尝试将少部分业务系统进行云化改造,并且看到了不错的收益。但是,当时更多的系统还是跑在传统集中式架构平台之上。2019年底,汇付天下内部对于两种方案进行了激烈讨论:一种是把云环境当做新的虚拟资源,把业务系统从集中式架构平台上原封不动地部署上去;第二种方案是将技术架构进行彻底设计改造,采用云原生技术。图为汇付天下首席架构师梁星元“当时整个行业和公司都在迅速发展,我觉得需要尽快定一个核心技术路线,为公司在市场赢得技术竞争优势。”汇付天下首席架构师梁星元告诉轻金融。2019年底,汇付天下迎来了一个关键时刻。在汇付天下数据技术委员会召开的一次会议上,尽管内部有不同声音,周晔还是拍板,决定将整个技术架构进行云原生重构。“要支撑数字化业务,必须要基于高可用性、高弹性、高敏捷度的技术架构,从长远来看,高速发展的云原生成为更可行的道路。” 梁星元对轻金融说,虽然改造的风险很大,但是收益也大。在云原生时代,所有的计算资源、数据库等基础设施的运维工作,相对以前更加清晰、更加智能化。同时,原来传统时代CPU利用率可能只能到10%,在云原生时代能提高到60%,甚至更多,随着体验和计算资源的效率提升,会给成本带来明显的下降。2020年初,在跟阿里云多次商讨,确定所有改造方案和节奏之后,汇付天下就正式启动了整个“冲上云霄”项目,开始了激动人心的282天。2019年11月16日,上云前夜:启动部署云原生技术的研讨;2020年1月6日,冲锋发令枪:“冲上云霄”项目正式启动;2020年2月13日,完成基础技术与资源准备,启动云原生技术改造;2020年4月30日,初尝胜果。第116天,成功完成首批第19个系统改造;2020年7月23日,迁云攻坚战:第200天,完成第二批18个系统改造;2020年10月14日,圆满收官:第282天,顺利完成47个系统,220个应用的改造。要知道,在汇付天下之前,还没有一家支付公司完成过这样的壮举。这是汇付天下历史上最大的技术升级项目,涉及系统之多,人员之广,前所未有。2、全面拥抱云原生:风险与收益虽然只用了短短的282天就完成云原生改造,但“冲上云霄”项目并非一蹴而就,当时面临着一系列挑战。首先,这是汇付天下有史以来参与人数最多、涉及方面最广的项目,决定了项目复杂程度高;其次,云原生技术栈丰富并且技术难度高,同时业务系统还面临着改造;最后要保证不停机迁移,让用户无感。“当时我们在业内没有可以参考的案例。” 梁星元说,这个时候,选择云计算的合作伙伴就显得很重要。经过压测等多维度技术测试,汇付天下最终决定选择与阿里云合作。用梁星元的话说,一方面,阿里云在整个云计算产品方面成熟度比较高;另一方面,阿里云的运维管理流程、客户保障、用户成功交易机制比较完善、响应及时,服务契合。2019年底,汇付天下云原生攻坚战契机来临,在周晔“平稳迁移、零宕机、可上可下”的总体要求之下,“冲上云霄”项目正式诞生。那么,汇付天下为什么要坚定不移地选择“云原生”架构呢? 云原生并非是一个新的技术领域,是Cloud+Native的一个组合词,从字面理解可以看作是采用云的技术架构设计,应用云的技术栈,并且运行在云特性的基础设施上。云原生包含完整与丰富的技术定义,如微服务,Docker,K8S与DevOps等,而对企业而言,更重要的是企业经营理念与企业文化也需无限契合云计算时代的发展趋势。周晔如此形容汇付天下的“冲上云霄”项目,在“不能影响业务一秒钟”的底线要求下,此次迁云完全是“带电操作”,是真正的“高速路上换轮胎”。在这一背景下,汇付天下可谓是选择了困难模式,那它带来的收益几何呢?事实是,冲上云霄项目收官之后,汇付天下在很多方面实现了跨越式的提升:一是,降本增效明显。 在做整个方案预测的时候,梁星元还曾特地去问过阿里云,底层技术架构采用云原生改造之后,费用会发生怎样的变化。但因为汇付天下是整体架构云原生改造的首例,阿里云也不能给出一个相对准确的数据。“我们当时估计,费用能节省20%就非常好了,但是最终效果超出了预期。” 梁星元说。“2018年到现在,我们流量翻了估计有十多倍,而费用和之前是一样的,这个还是非常让人吃惊的。”梁星元向轻金融透露,目前汇付天下的架构一周能够支撑的交易量较过去提升了数十倍,突破了亿级大关。二是,运维效率大幅提升。在云原生架构上,系统的运维更加清晰,能够自动化、快速地进行资源和弹性的管理。三是,对业务的支持更加敏捷。云原生架构不仅带来了系统运维模式的变化,更为重要的是带来了公司经营模式的转变,业务端能够快速反应客户需求,提供创新服务。具体来看,汇付天下推出轻量化的“支付+SaaS”产品——“Adapay”,客户对接时效从数周大幅缩短至数天,一半以上的客户可以实现自助化运营服务。最后,大批工程师得到历练,实现了技术人才的培养与提升。“这个项目带动了大家,所有技术人员都能由衷呐喊一声‘我们在汇付做技术’。”在庆功宴上,周晔很自豪地说。3、如何让数据创造价值“冲上云霄”项目中,一个占比近4成的重点项目是从传统数据库向阿里云数据库的迁移。过去多年,汇付天下一直都是传统集中式技术架构为主,虽然这样的技术架构为公司早期快速发展提供了有力支撑,但是在支撑汇付天下战略转型的过程中,暴露了缺陷。具体原因有很多,首先是集中式架构中的传统数据库软件授权费用高;其次,无法支撑交易量大幅增加情况下数据库资源的弹性升缩需求;第三,越来越多创新业务产生,对数据库提出了更高的要求,对数据库设计和运维也提出了新的挑战;第四,原有数据库的支持服务无法满足汇付天下的业务诉求。数据库作为核心基础设施,记录着汇付天下客户的每一笔交易,必须要做到安全、准确。在从传统架构向云原生架构切换的时候,汇付天下还提了一个更苛刻的要求:不停机迁移,用户无感。“因为没有特别可以参考的案例,只能自己摸索着前进。” 梁星元坦言,整个迁移的过程中各方面的挑战可以说非常大,“相当于是在高速公路上开着车,在保持安全的情况下把轮胎换掉。” 阿里云数据库专家宁愚和绛云等同学也有同感。在参与项目的282天里,宁愚和绛云等同学曾多次跟汇付天下工程师们在一起支持数据库和系统切换。这种切换通常在凌晨,汇付天下平台上交易量最小的时间段里。现在回想这个项目,他们仍然心有余悸。“汇付天下的系统牵扯着海量资金的结算,只要宕机就会出现问题,出现错账,因此在迁移过程中,大家都非常小心。”所幸,通过对前期的业务梳理和量体裁衣式的数据库选型,涉及的数据库包含RDS for MySQL、 云原生数据库PolarDB、云原生分布式数据库PolarDB-X、云原生数据库仓库ADB等多种数据库产品,并且通过传统数据库兼容评估和改造工具ADAM、数据传输服务DTS等多个阿里云数据库服务,汇付天下顺利完成了数据库的平稳迁移。总体来看,通过云原生改造,汇付天下数据库整体成本下降75%,一方面省去了大量传统数据库的授权费用;另一方面使用云原生数据库产品,支持极致的弹性能力,资源使用更加节约。利用阿里云生态工具,使得数据库的智能化运维能力大幅提升,更加高效,同时基于阿里云数据库数据管理工具DMS构建了一套面向金融企业级的数据安全管控平台。此外,通过运用丰富的云原生技术产品,汇付天下还拥有了全域数据的处理能力,助力营销、风控、服务等方面的提升。如今,数据资产不再是汇付天下“守着”的一块金矿,而成为了创造价值的利器。4、结语为客户提供数字化的解决方案,是汇付天下的使命所在。汇付天下提出的由“SaaS+支付”向“支付+SaaS”转变的商业逻辑,就是为不同类型的商户打造快速、高效且定制化的场景解决方案。这其中,技术是非常核心的底座。“冲上云霄”项目的圆满落地,意味着汇付天下在技术上打了一场成功的创新仗,也成为汇付天下在科技层面的重要里程碑。可以想见的是,云原生这一场仗之后,汇付天下已然开启新的篇章。
老板:我的要求并不多,只是…既要数据高效、又要保证数据安全稳定、还要能降低企业IT成本…不要怕!阿里云数据库帮你解决所有难题!高安全等级,远离拖库等重大风险性能经过阿里巴巴双十一高并发、大数据考验产品形态灵活,自由弹性,业务发展无瓶颈智能运维大幅降低运维成本,助企业资金聚焦核心业务阿里云采购季数据库首次推出买赠活动全场产品买1年免费再送6个月!升级、按量付费转包年包月7.5折!活动时间:3月17日-19日👆👆👆扫描上图二维码或点击 此处 了解详情
作者:陈招尚(胜通)一、自建数据库需要考虑的问题自建数据库需要考虑的问题:第一,搞定数据库高可用问题。第二,提升数据库资源利用效率问题。第三,资源瓶颈。提升利用效率的同时,解决资源瓶颈问题。第四,运维幸福感。运维数据库需要解决运维幸福感问题。第五,为公司节省成本。随着公司业务发展越来越强,对节省成本要求更多。总结:DBA把数据库管理好,是基本岗位要求。“妆成有却无”,我们把妆画好了,但是别人却看不出来。我们数据库产品没有什么问题,并且每年为公司节约成本,对公司管理层来说,是DBA管理工作者的基本功。但是数据库能否正常运行,受很多因素影响,有问题时DBA可能会背锅。因为应用瓶颈,大多用扩机器解决,但有的时候扩机器无法解决数据库问题。还有DBA人数和研发人数比例失调,一位DBA对应的服务有100位~200位研发人员。对支持业务与解决运维问题之间需要平衡,支持业务多一些,运维投入就少一些,DBA常常疲于支持业务与运维间平衡,两者相互制约。自建数据库需要考虑的问题很多,在过去很长一段时间里面,经常面临各种选择问题。比方为了提升业务幸福感,如果晚上挂了启用延迟处理机制等。二、阿里巴巴内部数据库的部署历史阿里巴巴内部数据库的部署历史,10年前MySQL版本还比较低,没有大规模应用于大型系统中。1)第一种部署很简单,如图所示:所有主部署全部在一台机器,所有备部署全部在一台机器。在这种情况下,随着公司的业务高速发展,后端运维岗位投入、IT资本投入都比较大。从DBA团队角度来说,稳定性压倒一切,所以选择这种部署方式。比如部署三个实例,这三个实例同时提供服务,只要这台机器的利用率资源够用就可以满足。那个年代在平时是没有问题的,但是在双11业务压力突然上涨的环境下,会有连接数问题:连接数很多,如果压力不够,对数据库响应时间会很长。比如一台数据库实例,前面服务200台应用服务器,平时连到数据库只有5个连接数,正常情况下实例只有1000个,可提供1000个连接服务。如果在双11高压场景情况下,应用服务器从200台扩冲1000台,为了抗住压力,连接数会设到20~40,数据库系统里面的连接很可能会达到10000或者超过10000。我们当时面临的情况是,应用数据库各个方面都没有异常,但是应用响应时间特别长。MySQL在当时5.1版本,业务压力增大的情况下,连接数是很大的问题。第二个问题是:主机宕机问题。如果有一台主机挂了,主机内的三个实例全部需要切到新的一台机器上去。切换硬件相对的影响很大,三个实例可能包含公司1/3的业务,那么1/3的业务都会受到影响。为了减小这种影响,要求备库能够百分百支撑起这台主机挂掉的情况,所以主库和备库的配置需要保持一致,那么对备库的投入成本与对主库的投入成本也一样多。2)第二种部署因为业务发展太快,硬件达到指数级往上投入,公司要求DBA进行成本优化,如下图所示,实现主备交叉部署,主实例相对用到更多的资源,备实例用于复制备份场景,使用的资源会少一些。这种部署下,我们面临的第一个问题是:部分超卖,堵运气。举例说明,比如主机部署了64核,感觉是部署了96核。DBA实行了部分超卖,这个时候就是堵运气。左边主机如果挂掉,两个主部署会全部切到右边的主机上面,左边实际达不到64核物理上CPU总数,这是第一个面临的问题。第二个问题是读写分离。如果主备交叉部署时,还想要提升资源利用效率,自然会想到会将备部署利用起来,做读写分离,会把一些读请求放到部署上面,这种情况下,左侧的主机有流量,右侧的主机也有流量,利用率更高。当然这样做也有堵运气成分在,在大型促销场景下,需要把主备部署自动切换关掉,相信尖峰时刻机器不会挂掉,另外业务上把很多机器切开,影响面会比较小,所以当时以这种方式应对。3)第三种部署开始构建集群型的方式,兼顾成本与稳定性,如下图所示:第一个提升是将不同的机器充分进行打散,引入现在还在用的内部系统叫移山系统,进行资源调度,根据监控每台机器的资源利用效率,及时触发条件,将整个集群里面的实例进行资源调度。相当于整体实例利用率水平保持在比较均衡的状态。第二个提升是备库预热,如果突然主备切换,备库如果没有承担读会是冷启动,备库需要有一个预热的过程,否则如果流量很大,那么可能会出现备库被烫死的情况,对此我们做了备库预热。第三个提升是读写分离的问题,我们也考虑到读流量。4)第四种部署重要系统和非重要系统交叉部署,如下图所示:在前面三种模式下,核心系统和非核心系统分成两个集群处理,核心系统更会往稳定性部署,而非核心系统会更向成本优化角度部署。这时出现一个情况,就是我知道哪个系统是核心系统,哪个系统是非核心系统,但是如果出现非核心系统突然变成核心系统的情况,在认为的非核心系统里面咨询,非核心系统用到的资源会增强。为应对这种问题,我们增加了重要系统,锁定资源,不被争抢的功能,锁定资源不会增强。非重要系统:大量混合部署,资源随时弹性,移山及时再平衡。比如做大型活动,流量上涨,可以根据资源CPU跟IOPS利用效率把机器上其他资源移走,实现资源利用效率整体平衡。三、经验分享经验1:主备交叉部署,一种伪超配的降成本方案如下图所示,实际上一台机器用到了96核,但是主机上是64核,因为主资源备交叉部署,利用的效率并没有达到满负载。收益:CPU资源利用效率整体上达到70%。连接资源被有效利用。更低成本。注意点:HA机制要灵活,什么时候自动切,什么时候手动切。如尖峰时刻手动断开主备库的连接,以应对高流量咨询问题。主机问题监控,核心资源打散。比如双11的时候,后端资源保障上都会资源打散,大家千万不要让主机满载运行,如果主机满载运行的话,一旦挂掉就会出现雪崩的情况,因为切到备库,备库也会挂掉,这是我们的一个经验。经验2:应用分级,保障粒度差异化应用分级,以不同的分级保障不同粒度差异化,比如分为一般性业务或重要性业务。一般性业务,可以利用交叉部署获得更多资源利用。重要性业务,更偏向稳定性方案,一定要有对外SLA保障。因为很多一般性业务依赖于中小型的业务,做应用分级,可以提升整体利用效率,提高可用性。如下图所示:两种不同等级业务的数据库资源模式两种不同等级业务的数据库资源模式,需要通过三个阶段的隔离实现:MySQL实际上是吃内存性的,比如内存有64g基本上会把64g内存用完,超卖在内存这块是做不到的,所以开始从内存隔离,隔离过后,在一台机器上使用内存大家不会抢单。如果在非重要的集群里面,业务流量突然上涨,非重要变成了重要,就把CPU绑定,实现 CPU的隔离,使其不会被争抢。如果重要性上升到公司的核心业务,这时可能会把它独立出来,进行物理机隔离,进入第三个阶段。经验3:两种抽象的资源部署方案无论是数据库,还是应用部署,资源调度对最底层的分配方式只有两种:均衡分配与紧凑分配。均衡性分配,如下图所示,离散型数据库实例分布,优先在更空的主机上继续分配实例,资源主机的利用率相对比较均衡,整体性能表现也会相对比较稳定。紧凑性分配,如下图所示:堆叠型数据库实例分布,优先在更满的主机上继续分配实例,资源主机的利用率会更高,各主机性能表现不一,成本更优。紧凑型和均衡型交叉应用场景典型应用场景,比如一开始是紧凑性分配,更关注成本。大促的时候,增加了机器,变成均衡分配,实现资源均衡综合调度。大促做完之后,再回归到紧凑性分配,把机器空出来用到需要的地方。这种资源挪腾可以是自动化的,也可以人工手动方式进行挪腾。平时紧凑分配使资源利用率更高,成本更低,大促时使用均衡分配,稳定性更高。如下图所示:经验4:资源弹性,消除业务流量评估焦虑业务流量评估焦虑:评估过程:业务指标–业务QPS/TPS–全链路压测–数据库准备。标准上线流程:提前资源报备,详细部署监控,SQL审核,深夜值班观察表现。三种结果:没抗住是因为没评估好,抗住了是应该的,资源利用率不高是因为没评估好。如上图所示,业务方心目中的每一个数据库都非常重要,业务是他的全部。但是DBA实际操作时会区分出哪些是核心业务,如右图红色部分;哪些业务是非核心业务,那些业务运行一段时间就下线了等。所以从DBA角度看,数据库地位实际上是有差异性的,为应对这种差异性,我们在内部专门做了混合性集群,这种模式下,DBA开始时不需要评估流量大小,可以直接放入集群中。如果业务突然涨上来,可以迅速地弹上去,然后立刻锁定CPU,使其不会再增强,保证一段时间内的重要性地位。如果业务持续增长,再把其转移到专门为其设置的集群里面,这个过程保证了数据库的稳定性和可用性。同时减少了因DBA评估错误出现的问题。四、云数据库专属集群产品介绍云数据库专属集群 部署架构图针对阿里的实践,在云上推出了云数据库专属集群,这种专属集群与PaaS平台区别是什么?PaaS平台买的都是实例,专属集群买的是主机,客户可以自己构建一整套专属集群模式,构建专属的一套技术管理业务,好处还包括能够超配、客户自己管理、可以实现主备交叉部署提升资源利用率、可以实现在不同机器之间资源调度、资源再均衡、资源迅速攀升和收缩等各个方面的能力。以上是专属集群的产品简介,总结:第一它是云上专属的数据中心;第二是自主可控;第三是因为DBA对业务更了解,可以实现成本最优化。云数据库专属集群 能力建设进展PAAS服务有的功能,云专属数据库都有。混合部署在研发中,其他我们都已经实现支持了。原数据库 PasS能力都有,包括高可用、高可靠性能等方面。另外我们还新增了资源调度功能,包括紧凑型、均衡型、通过移山进行资源均衡、资源的弹性扩缩等等。云数据库专属集群是以主机的形式在管理数据资源,不再像PaaS平台是以实例的形式管理。
近日,用友网络与阿里云达成全新合作,用友NC Cloud大型企业数字化平台与阿里云自研数据库PolarDB完成相互认证。未来,双方将携手服务大型企业客户,加速企业数智化转型和商业创新。用友专注企业服务32年,全球领先的企业云服务和软件提供商。用友NC Cloud沉淀了过去20年服务中国大型企业服务经验,聚焦数字化管理、数字化经营、数字化商业,帮助大型企业实现人、财、物、客的全面数字化。当前,企业面对数字化转型的迫切需求,纷纷开始拥抱云原生架构。用友NC Cloud跟阿里云PolarDB数据库完成相互认证之后,可以满足大型企业数字化应用场景中,IT系统对云原生架构的需求,以及海量数据高弹性、高可用、扩展性的需要,支撑企业业务敏捷交付,赋能管理者商业创新和管理变革,助力企业实现数智化转型。用友NC Cloud大型企业数字化平台基于云原生架构,集AI、多端技术、企业级低代码开发等多种能力,具有高可用、高可靠、高性能、高安全等特性,支持大中型以及超大型集团企业迁移上云。基于阿里云PolarDB,NC Cloud不但可以满足大型企业在数字时代海量的数据存储需求,还能大幅降低数据库相关成本。去年5月,阿里云携手用友帮助国内大型医药控股型集团公司朗致集团将核心ERP系统和核心数据库迁移上云,数据库直接成本下降20%,运维成本下降50%,同时提升了内外部协同效率,促进业务进一步发展。2017年,阿里与用友达成全面战略合作以来,双方进行了产业生态的融合,共同推进企业数字化转型和商业创新。目前,用友基于阿里云为企业提供丰富的云服务,加速各行各业大中型企业和小微企业上云。文章来源:用友云服务
作者:墨城引言关系型数据库,随着业务发展,经常需要加表加列来满足新的业务需求,或者增加索引以提升查询性能,这些操作都需要通过数据定义语言 (Data Definition Language, DDL) 来完成。DDL 是结构化查询语言(Structured Query Language, SQL) 的一部分,1974 年 IBM 研究人员设计 SQL 的前身 Sequel 时有两个目标,允许用户通过 Sequel 来操纵和定义数据。当时发表的两篇论文,一篇是广为人知的 "SEQUEL: A Structured English Query Language",介绍如何查询和修改数据,另一篇则专门介绍了 DDL1,2,可以看出,从一开始 DDL 就是 SQL 最重要的组成部分。随后的几十年中,学术界对 DDL 要解决的核心问题“模式演化”(Schema Evolution) ,也就是“不丢失数据内容的前提下,改变数据的定义”,进行了大量研究3,4。如今 Schema Evolution 相关研究的结果已经广泛应用在关系型、对象、文档数据库产品中,其中 Online Schema Change 能力已经成为 OLTP 数据库的标准配置,主流单机 OLTP 数据库都有自己的实现。Online Schema Change关系型数据库中数据以表为单位存储,读写操作依赖表的模式(Schema)和数据(Data)。DDL 通常需要同时修改模式和数据,为了避免并发问题,早期的数据库实现会在 DDL 过程中禁止目标表上的读写操作(简称锁表)。锁表保证了对模式和数据的修改是原子操作。但某些 DDL 操作涉及复制数据(比如索引构建)执行时间可能在几分钟甚至数小时。OLTP 数据库中长时间锁表会对业务产生不可预知的影响,生产环境中不可接受,因此“能够与读写操作并行执行”是 OLTP 用户对 DDL 的核心需求,也是 Online Schema Change 要解决的问题。单机数据库MySQL 从 5.6 版本开始提供 Online Schema Change(Online DDL)5 特性,允许大多数 DDL 语句与 DML 并行执行。DDL 执行分为 Initialization, Execution, Commit Table Definition 三个阶段,通过 MDL(Metadata Lock)6 保护元数据,下面以 CREATE INDEX 为例简单介绍 MySQL Online DDL 的实现。Initialization 阶段主要分析 DDL 语句,确定执行策略,过程中会持有目标表上的 shared MDL;Execution 阶段会获取 exclusive MDL,等待当前表上的事务结束后完成创建 RowLog 等准备工作。之后降级为 shared MDL,允许读写操作并行执行,新事务中涉及索引的变更写入 RowLog,RowLog 切分为多个小块,增量数据始终写入最后一块。同时开始索引构建流程,将全量数据排序后填充到索引中。全量填充完成后按顺序应用RowLog,应用最后一块 RowLog 时禁止目标表上的写入操作;Commit Table Definition 阶段再次获取 exclusive MDL,完成元数据更新后释放 MDL,流程结束,索引对查询可见。MySQL Online DDL 的特点Schema 只有一个版本,通过 MDL 保证同一时刻运行的事务都使用相同的元数据;仅在几个关键时间点加锁,降低对上层应用的影响;Execution 初期 和 Commit 阶段短暂获取 exclusive MDL,禁止目标表上的读写操作Execution 阶段应用最后一块 RowLog 时,禁止目标表上的写操作需要注意的是 MDL 作为一种锁的实现,通过队列来保证公平性,因此额外实现了死锁检测功能解决死锁问题。分布式数据库问题分布式数据库通常是一个集群,出于性能考虑,每个节点需要缓存一份 Schema。如果继续采用单机数据库的 DDL 流程,则需要通过分布式锁来保证加载新版本 Schema 过程中没有读写操作进行,代价极高,并且当集群内节点不能够互相感知时将变为无法完成的任务。讨论解决方案之前,以 CREATE INDEX 为例,看看集群节点使用不同版本 Schema 执行读写操作,带来的具体问题。上图展示的是一个存储计算分离架构的分布式数据库,集群由 CN(计算节点) 和 DN(存储节点) 构成,每个 CN 中缓存一份 Schema。由于 CN0 和 CN1 异步加载 Schema,添加索引过程中可能存在一个时刻,CN0 认为有索引而 CN1 认为没有,此时产生两种异常:索引上有多余数据(Orphan Data Anomaly): CN0 执行了 INSERT,在表和索引上插入数据,随后 CN1 执行 DELETE。由于 CN1 认为没有索引,仅删除了表上的数据;索引上缺少数据(Integrity Anomaly): CN1 执行 INSERT,由于 CN1 认为没有索引,仅在表上插入数据,没有写相关的增量日志,导致索引创建完成后缺少这次 INSERT 的数据。可以看到,如果同一时刻存在两个 Schema 版本的情况无法避免,继续沿用单机数据库一步完成 Schema 版本切换的方案,会导致数据问题。那么如果“一步”切换不可行,“多步”能否解决问题?VLDB 2013 上 Google 工程师给出了一种新的 Schema Change 流程,通过增加两个中间状态来解决这个问题7。解决方案 & 业界实现Google F1 的方案引入了两个中间状态,delete_only 状态的对象上仅执行删除操作,write_only 状态的对象上支持写入,但不允许读取。依然以 CREATE INDEX 为例:解决 Orphan Data Anomaly:CN0 认为索引处于 delete_only 状态,仅在表上插入数据,CN1 认为没有索引,仅在表上删除数据。最终索引和表上都没有 id = 0 的数据;解决 Integrity Anomaly:CN1 认为索引处于 delete_only 状态,仅在表上插入数据,没有写相关的增量日志,但由于还有节点没有更新到 V2 版本,数据回填没有开始。当所有节点都更新 V2 版本后,数据回填操作会在索引中填入这一条数据。以上两个具体场景为例,说明了方案的有效性,论文7中对整体问题和方案做了形式化证明,网上有不少资料,团队同学之前也有过分享8,这里不再展开,只列举结论。Google F1 的方案,包含两个关键点:增加两个中间状态(delete_only,write_only),允许集群中的事务同时使用至多两个最近的元数据版本;增加租约(lease)的概念,保证在一个租约周期内,没有拿到最新版本 Schema 的节点,无法提交事务。第一个关键点,将保证 Schema Change 正确性的条件,从“只能有一个版本”,降低为“最多可以有两个版本”。第二个关键点,给出了 F1 系统“保证最多两个版本”的实现思路。简单来说,Google F1 的方案,成功将问题“在分布式数据库系统上实现 Online Schema Change”转化为“设计一种保证系统中最多有两个 Schema 版本的协议”,并且给出了一种基于租约的协议实现。协议内容可以概括为,以租约周期作为时间单位,协调了三个操作的节奏。Schema 刷新的间隔:每个节点需要在租约过期前,获取一次最新版本 Schema,如果无法获取,则主动退出,由托管服务重新拉起;DDL 的最小时长:每次更新 Schema 版本后,需要等待一个租约周期,保证所有节点都读到最新版本的元数据;事务的最大时长:执行时间超过一个租约周期的事务将被回滚,确保事务仅使用了一个 Schema 版本。原始版本的协议十分简洁,易于描述和验证,但由于将 DDL 执行的最小时长和事务执行的最大时长绑定在一起,使用体验上与单机数据库有区别。对此,业界也给出了多种改进方案,比如:CockroachDB 重新设计了 schema lease9,在两方面做出改进:降低 DDL 执行的最小时长:通过在事务开始时获取一个包含版本信息的租约,事务结束时释放,使得更新 Schema 版本后能够立即确认旧版本是否还在被使用,仅在有长事务或者节点异常(比如网络断开)时才需要等满一个租约周期。由于通过在存储中插入记录来获取租约,会增加事务的执行耗时。只在 DDL 执行过程中限制事务的最大时长:具体做法是,使用 Schema 版本变更开始的时间作为边界,产生一个 [Tv,Tv+2) 的时间窗口。起始时间在窗口内,结束时间在窗口外的事务将被回滚。如果有 DDL 正在执行,则窗口最大为两个租约周期。如果没有 DDL 执行,则不存在 v+2 版本,可以认为是一个无限大的窗口[Tv,+∞)TiDB 的实现中通过 PD 来实时判断是否所有节点都完成了 Schema 版本更新10,部分节点失效的情况下需要等待一个租约周期。当前实现中,Schema 版本更新与事务执行没有交互,执行期间有 Schema 版本变化的事务需要被回滚。5.0 版本中对加减列、加减索引等 DDL 类型,支持在事务提交时修改变更的数据来适应 Schema 变化,避免回滚事务。OceanBase 的每个 ObServer 都同时具备存储和计算的功能,保存有一份元数据拷贝,可以在任务转发过程中判断两端的元数据版本是否一致,整体方案有所不同,总结如下11:元数据分为多个版本一个事务内的所有 SQL 都发到相同 ObServer,避免语句级别的元数据版本回退导致数据不一致的问题事务中的语句需要访问的 ObServer 的元数据版本与 RootService 不同时对语句进行重试从公开资料可以看出,使用存储分离架构的产品对 F1 方案的改进主要集中在缩短 DDL 执行周期,DDL 执行导致部分长事务被回滚的问题并没有解决。PolarDB-X 在使用体验上高度兼容 MySQL,需要设计自己的改进协议。改进方案回顾一下要解决的问题,“保证系统中最多有两个 Schema 版本”,隐含的意思是 Schema 在系统中存在多个拷贝,且每个拷贝的版本可能不同。协议要做的,就是协调每个拷贝的更新操作,给出旧版本从系统中消失的可靠时间点。分布式数据库中有两个地方会缓存 Schema:出于性能考虑,每个节点会缓存一份 Schema,避免每个操作都需要从存储读取 Schema;同一个事务中的语句需要使用相同的 Schema,因此在事务开始时也会缓存一份 Schema。节点上缓存的 Schema,通常由每个节点上的后台线程定时刷新,刷新间隔为一个租约周期。如果节点刷新 Schema 失败,则回滚所有未完成的事务,停止提供服务,直到获取到最新版本的 Schema。这样 Schema 从 V0 变更为 V1 后,只需要等待一个租约周期,就可以保证所有节点要么已经在使用 V1 版本,要么回滚了所有使用 V0 的事务,并停止提供服务。事务中缓存的 Schema 版本无法更新,因此协议需要协调事务和 DDL 执行的顺序,确保使用 V0 版本的事务全部 commit/rollback 之后才继续执行 V1 到 V2 的版本变更。也就是说,对于执行过程中有 Schema 版本变更发生的事务,有两种直观的处理:事务 rollback:版本变更无需等待,DDL 执行时间最短。缺点是,报错的事务数量和业务吞吐量正相关,不符合 Online Schema Change 的初衷;等待事务 commit:避免了事务报错,缺点是如果存在异常节点,无法判断事务结束时间,DDL 执行会被阻塞。Google F1 的实现中,通过限制每个事务的最大执行时长为一个租约周期,简化判断事务结束时间的逻辑。引入租约周期的限制后,按照起止时间的不同,可以将事务分为下面几类:可以看到,F1 会保证执行时间在一个租约周期内的事务 T4 不受影响,但也影响了无 DDL 执行时的事务 T1, T3,同时会导致 DDL 执行时长也至少为一个租约周期。CockroachDB 在此基础上增加了事务结束释放租约的机制,将切换为 V2 版本的时间限制为最后一个 V0 版本租约的过期时间,仅当有 V2 版本写入时才判断租约是否失效。这样缩短了 DDL 的执行时间,并且仅在有 DDL 执行时限制事务的最大执行时长。仔细分析可以发现,租约其实有两用途,确定使用旧版本的事务结束 和 保证异常节点上使用旧版本的事务无法提交。PolarDB-X 在节点内部增加 MDL,通过短暂获取旧版本 Schema 上的 exclusive MDL,排空使用旧版本的事务,将切换为 V2 版本的时间限制为 T5, T6 结束之后。同时,如果 GMS 组件确认节点异,标记旧版本 Schema 失效,等待一个租约周期后可以保证异常节点上使用旧版本的事务无法提交。从用户角度出发,理想 Online Schema Change 方案应该满足下面三个特点,没有 DDL 流程执行时不对事务增加过多额外开销;DDL 流程中,事务不会被强制回滚;DDL 的执行时间尽可能短。MySQL Online DDL 基本符合上述特点,而存储分离架构的产品都有一些取舍,以下从这三个角度出发,对比现有方案和 PolarDB-X 的实现:可以看到,PolarDB-X 方案有如下特点:事务开始阶段在内存中记录 Schema,获取节点内的 MDL,不引入数据读写开销;DDL 执行过程中,正常节点上事务执行不受影响,异常节点上限制事务执行的最大时长为一个租约周期;正常情况下 DDL 毫秒级完成,存在异常节点时退化为分钟级;在节点内部实现了 MDL,通过多版本 Schema 避免排队取锁导致使用新版本的 DML 被阻塞。PolarDB-X Online Schema Change 实现PolarDB-X 是一个分布式 HTAP 数据库,采用存储分离架构,高度兼容 MySQL 生态,支持 Online Schema Change。DDL 执行流程收到用户的 DDL 语句后,首先由接受语句的 CN 节点进行校验,并生成 DDL Job 提交到 Global Meta Service (GMS) 的任务队列中,随后 GMS 通知 CN 节点中的 DDL Worker 领取任务,开始推进 DDL 流程和 Schema 版本变更。上图展示 CREATE GLOBAL INDEX 的执行过程。DDL Worker 发起 Schema 版本变更时,首先通过 GMS 的 Sync 机制尝试通知其他几点加载新版本并排空使用旧版本的事务。如果 GMS 判定有节点异常,则等待一个租约周期后设置旧版本 Schema 为失效状态,继续推进 Schema 版本变更。事务提交时判断节点是否正常刷新了 Schema Cache,以及自己使用的 Schema 是否依然有效,如果不满足则事务回滚。Schema 版本变更流程收到更新元数据版本的通知后,首先检查当前是否已经加载了该版本的 Schema,若尚未加载,加载新版本 Schema,之后尝试获取旧版本上的 MDL,若获取 MDL 成功,代表节点上使用旧版本 Schema 的事务已经结束,该节点上的 Schema 版本变更完成,返回成功。若获取 MDL 失败(超时),返回错误,交给上层重试。同时,事务的第一条查询执行前,获取最新 Schema 版本上的 MDL,目前只有S锁和X锁,通过队列保证公平性,但不需要死锁检测等 MySQL MDL 的组件。MDL 为内存中的锁,不落盘,节点重启时清空。总结Online Schema Change 是 HTAP 数据库的必选功能。单机数据库中,Schema 只有一个版本,通过加锁来避免 DDL 和 DML 并发导致数据问题,通过缩小加锁的范围来实现 Online Schema Change。分布式数据库通过引入中间状态,允许同时存在至多两个 Schema 版本,避免加锁,支持 Online Schema Change 的关键点转化为,设计一种节点交互协议,保证系统中至多同时存在两个 Schema 版本。PolarDB-X 实现的协议,结合 MDL 和租约,不对事务执行引入额外开销;无异常节点的情况下,Schema 版本变更不影响事务执行,无数据回填的 DDL 操作毫秒级完成;存在异常节点时,仅阻止异常节点上的事务提交,无数据回填的 DDL 操作分钟级完成,与业界方案保持一致。参考文献[1] Early History of SQL[2] Using A Structured English Query Language As A Data Definition Facility[3] Schema Evolution in Database Systems: An Annotated Bibliography[4] A Survey of Schema Versioning Issues for Database Systems[5] https://dev.mysql.com/doc/refman/5.6/en/innodb-online-ddl.html[6] https://dev.mysql.com/doc/refman/5.6/en/metadata-locking.html[7] Online, Asynchronous Schema Change in F1[8] https://zhuanlan.zhihu.com/p/84809576[9] https://github.com/cockroachdb/cockroach/blob/master/docs/RFCS/20151009_table_descriptor_lease.md[10] https://github.com/pingcap/tidb/blob/master/docs/design/2018-10-08-online-DDL.md[11] https://developer.aliyun.com/article/663959【相关阅读】PolarDB-X 向量化引擎的类型绑定与代码生成每次都需要解释大量指令?使用 PolarDB-X 向量化引擎PolarDB-X 面向 HTAP 的混合执行器PolarDB-X 面向 HTAP 的 CBO 优化器如宝马3系和5系:PolarDB-X 与 DRDS 并驾齐驱PolarDB-X 存储架构之“基于Paxos的最佳生产实践”PolarDB-X 私有协议:提升集群的性能和稳定性技术解读 | PolarDB-X 分布式事务的实现技术解读 | PolarDB-X 强一致分布式事务PolarDB-X 一致性共识协议 (X-Paxos)
数据库牵动着整个科技产业从早期关系型数据库取得巨大成功到90年代分析型数据库不断涌现再到云原生数据库崛起......今天我们换个姿势聊聊数据库究竟是什么?数据库,不仅是存放数据更要考虑如何管理海量数据打一个比方——想想古代的中药房既要存放成千上万的中药材还要合理归置,方便快速定位不然每逢药师抓药,就会......勤劳智慧的老祖宗想了个办法打造出抽屉式药柜每个抽屉写上药名按药性把抽屉涂成不同颜色寒性蓝色、温性红色,并用笔划排序想新增一种药材,就加一个抽屉药品过期,就把整个抽屉拿走其实,计算机管理数据也是如此最常见的关系型数据库,就像中药柜加药、换药、找药相当于数据库的增删改查# 关系型数据库目前,市场上占比最大的数据库就是关系型数据库广泛用于银行、金融交易所机构并遵循ACID原则Atomicity原子性、Consistency一致性Isolation隔离性、Durability持久性以银行转账为例:原子性要么转账成功,要么转账失败不会有中间状态一致性转账前后,甲和乙账户的总额都是100元甲少了100,乙多了100,甲+乙=100隔离性转账时,甲操作自己的银行账户乙查询自己的银行账户彼此不知晓、不影响持久性转账完成后的更改状况持久保存在数据库之中不会回滚除了关系型数据库企业还会根据业务场景需要混合使用不同类型的数据库# 数据仓库在大型连锁快餐店会用数据仓库整合多种维度数据包括门店POS的销售数据、会员数据历史消费数据、天气数据等进行全面深入分析以便在高峰期时做出更精准的业务决策# 键值数据库在网上购物商城会用键值数据库存储购物车信息它具有良好的伸缩性理论上可以通过横向扩展实现无限扩容# 图数据库比如社交媒体会基于图数据平台通过用户互动情况等数据绘制关联关系图,管理社交关系实现更精准的好友推荐机制# 阿里云MyBase阿里云不仅可以提供上述全部类型的数据库最近还推出了全新的数据库产品形态——云上专属集群MyBase一般云数据库就好比是一个大火锅大家共享一口锅客户需要共享主机资源池同一台主机可能有不同客户的实例做到资源零干扰的挑战非常大相比之下MyBase更像是一人一锅,专属定制MyBase的一大特色就是客户主机完全独占高隔离级别不存在任何主机层面的干扰更加安全、稳定MyBase的另一个好处是自主控制可运维内核有阿里云兜底同时开放了数据库全部权限和可控的OS权限满足客户的自主控制需求就像小火锅一样可以根据个人口味自主选择锅底、食材、调料而且,MyBase内置弹性升级能力长时间资源使用超过80%就会自动弹性升级对用户无感,平滑度过高峰就像小火锅灵活的火候控制食材下多了就调大火,少了就调小火这一特性非常适合在线教育等有明显业务周期的客户开学期间增加计算资源,均匀分布寒暑假自动调整为紧凑策略,节省成本此外,MyBase还将具备资源混合部署能力可以混合部署多个数据库实现数据库与业务系统就近部署满足业务架构实际诉求提升数据库访问效率就好比小火锅食材混搭,万物皆可涮这样一来使用MyBase的客户就可以自由组合业务的主机分布将错峰的业务混搭部署在同一个服务器上比如办公类产品供上班时使用定时数据分析产品则在晚上开启进行大量计算有效提高资源的利用率目前,阿里云拥有国内最丰富的云数据库产品家族,包括自研云原生数据库PolarDB等,已有超过40万个数据库迁移到阿里云上。在Gartner公布的2020年度全球数据库魔力象限中,阿里云首次挺进全球数据库第一阵营——领导者象限。扫描二维码或点击 此处申请MyBase免费试用试用用户新购还能享受惊喜折扣
作者:君启介绍在上篇文章《每次都需要解释大量指令?使用 PolarDB-X 向量化引擎》中,我们介绍了PolarDB-X向量化引擎的原理,以及运行时的结构。本文将对向量引擎的上下文构建进行详细介绍。所谓上下文构建,就是为向量化引擎准备好合适的执行环境,可以概况为以下几个问题:如何确定表达式的输入输出类型,并为SQL中的表达式分配合适的原语?每个原语需要使用不同的向量来进行输入和输出,如何正确地为原语分配向量?每种原语仅为特定类型进行服务,那么我们必然需要为一个表达式配备大量不同的原语,来适应不同的数据类型。如何应对原语数量爆炸这一问题?PolarDB-X引入了表达式绑定、静态类型系统、代码模板等多种技术来解决上述问题。上下文构建表达式绑定在优化器完成优化阶段之后,我们从中得到代表着表达式树形结构的RexNode树,对其进行表达式绑定操作。表达式绑定需要对表达式树进行前序遍历,并完成以下工作:对于遍历到的函数调用节点,提取函数签名信息,通过函数签名进行反射,实例化向量化原语;在遍历过程中进行将遍历到的RexNode节点(包括列、表达式调用、常量等)按遍历顺序分配向量位置下标。下标分配完成后,在运行时依据下标信息统一进行内存分配,供表达式输入、输出使用。下图是表达式 ((a+b)-c)*((a/b)%2) 的表达式树结构,以及通过表达式绑定得到的向量化原语实例,和分配得到的Batch结构:此外,通过在表达式绑定时分配输入、输出向量的位置下标,我们可以灵活的处理各类数据依赖问题,包括以下的数据依赖场景:多个表达式输出到一个向量中,例如case表达式中的各个then、else子表达式。可以给这些子表达式分配相同的输出向量Index;多个表达式将同一个向量作为输入。例如select fun_1(expr), fun_2(expr),expr表达式输出的结果可以作为fun_1和fun_2表达式的输入。可以通过分配相同的输入向量Index来实现。某些条件下,作为输出的中间结果向量可以覆盖掉作为输入的中间结果向量。静态类型系统在基于行的执行器中,类型系统的静态绑定并不是必须的。例如在 MySQL 中,表达式构成一个树状结构,上层的表达式结构通过调用下层提供的不同返回值类型的接口(例如:val_int()、val_decimal()、val_str() 等),递归地计算出最终结果。这种方式实现简单,但是直到运行时才能确定表达式的输入返回类型,进而决定需要调用的计算函数,效率比较低。向量化系统则要求静态类型系统。在解析器和优化器完成执行计划的构建之后,必须保证每个表达式的类型是正确的、不需要运行时确定的。只有这样,才能为它实例化正确的向量化原语、分配正确类型的向量。PolarDB-X将用户传来的SQL解析为AST之后,对于树形的AST需要自顶向下地进行类型推导,确定整个表达式树的类型信息。具体过程包括:操作数类型检查(Operand Type Checker)。子表达式的返回类型,会作为父表达式的操作数类型。每个表达式配备有相应的操作数类型检查规则,通过此规则来检查操作数类型是否合法;隐式类型转换(Type Coercion)。当子表达式的返回类型不能成为合法的父表达式操作数类型时,我们需要调用相应的类型转换规则,尝试进行返回值类型return type到操作数类型operand type的转换。办法是,生成一个合法的IMPLICIT_CAST表达式,将return type强制转换为合法的operand type类型。由于此转换对于用户来说是透明的,所以称为隐式类型转换。返回值类型推导(Return Type Inference)。当表达式具备了合法的操作数之后,可以调用相应的返回值推导规则,通过操作数推出正确的返回值类型。以上三个步骤如下图所示:关于类型系统这部分,在接下来的文章中会进行详细介绍。代码生成技术PolarDB-X采用apache freemarker框架,根据代码模板来批量生成向量化原语的源码,避免了Type-Specific引入的代码量激增的问题。原语 = 代码模板 X 类型配置,原语就是由代码模板,配合以不同的类型,在项目编译时批量生成的。一个简化后的原语代码模板如下所示,其中${} 符号代表在编译时将要被替换成特定类型和表达式。public class ${className} { public ${className}(int outputIndex, VectorizedExpression[] children) { super(DataType.${type.outputDataType}Type, outputIndex, children); } @Override public void eval(EvaluationContext ctx) { super.evalChildren(ctx); VectorBatch batch = ctx.getVectorBatch(); ${type.inputType1}[] array1 = ((${type.inputVectorType1}) leftInputVectorSlot).${type.inputType1}Array(); ${type.inputType2}[] array2 = ((${type.inputVectorType2}) rightInputVectorSlot).${type.inputType2}Array(); ${type.outputType}[] res = ((${type.outputVectorType}) outputVectorSlot).${type.outputType}Array(); for (int i = 0; i < batchSize; i++) { int j = sel[i]; res[j] = (${type.outputType})array1[j] ${operator.op} (${type.outputType})array2[j]; } } }注:*左右滑动阅览参考文献[1] MonetDB/X100: Hyper-Pipelining Query Execution[2] Materialization Strategies in a Column-Oriented DBMS[3] Rethinking SIMD Vectorization for In-Memory Databases[4] Breaking the Memory Wall in MonetDB[5] Relaxed Operator Fusion for In-Memory Databases: Making Compilation, Vectorization, and Prefetching Work Together At Last【相关阅读】每次都需要解释大量指令?使用 PolarDB-X 向量化引擎PolarDB-X 面向 HTAP 的混合执行器PolarDB-X 面向 HTAP 的 CBO 优化器如宝马3系和5系:PolarDB-X 与 DRDS 并驾齐驱PolarDB-X 存储架构之“基于Paxos的最佳生产实践”PolarDB-X 私有协议:提升集群的性能和稳定性技术解读 | PolarDB-X 分布式事务的实现技术解读 | PolarDB-X 强一致分布式事务PolarDB-X 一致性共识协议 (X-Paxos)
作者:德哥阿里云RDS PostgreSQL是一款兼容开源PostgreSQL的全托管云数据库产品,自2015年首次发布以来,根据用户需求不断升级迭代,已支持9.4、10、11、12等多个版本,覆盖了高可用版、基础版、只读实例等多种形态,数据存储覆盖了本地SSD、SSD云盘、 ESSD云盘等多种形态。最高配置可达104核,768G内存,32TB,100万IOPS。为了满足不同行业的数据库诉求,阿里云数据库PostgreSQL推出了冷热分离(oss_fdw)存储外部表,Ganos 地理时空扩展模块,中文分词模块,化学分析模块,图像识别模块等,已经服务了电商、企业ERP、银行、酒店、高科技、物流、互联网、出行、多媒体等各个行业。2021开年,我们带来了最新重磅产品升级发布:RDS PostgreSQL 13重磅发布:兼容社区最新大版本13, Btree索引更加高效、更加节省空间,分区表与聚合查询性能大幅度提升,JSON的使用方面功能更加强大,扩展统计信息增强支持更好的SQL优化逻辑,增加了增量排序和并行索引垃圾回收功能等。一键大版本升级功能发布:为了让低版本的用户可以更加高效率的升级到高版本,享受高版本带来的更多功能和高性能,我们推出了一键大版本升级功能,低版本用户可以近乎平滑的升级到大版本(割接过程短暂只读)。一键基础版升级高可用版发布:较多用户基于早期成本考虑或业务重要程度原因选择了基础版, 在业务重要度提升后, 希望升级到高可用版, 以前只能通过迁移数据实现,耗时久,而且需要改业务连接串,体验不太友好,我们现在支持了一键升级到高可用版本。解决DDL增量订阅问题:在稳定性方面,阿里云RDS PostgreSQL解决了逻辑增量数据同步的难题:DDL无法被捕获,不能同步到下游。因此只要主库执行DDL语句,当这些语句涉及到结构变更或者新增了结构,这些DDL之后产生的增量数据在下游的回放就可能失败。现在我们支持DDL订阅了,结合DTS服务,我们能够支持RDS PostgreSQL的增量数据同步。解决逻辑订阅在流复制HA环境中SLOT位点丢失问题:社区版本存在的一个问题:逻辑增量复制的SLOT位点信息不能同步到流复制从库,这个问题会导致实例发生主从切换后,SLOT信息丢失,这也是很多PG数据库用户头痛的问题。我们在RDS PostgreSQL内核层面支持了SLOT位点的主从同步,主备发生切换后,SLOT位点信息不会丢失,逻辑复制正常。让RDS PostgreSQL更加安全:在安全方面,此次发布我们推出了SQL 防火墙的功能,支持学习模式,自动学习数据库中执行的SQL并记录到白名单中,开启报警后,只有白名单的SQL可以被执行,可以有效防止删库跑路的发生。支持ES索引:在使用灵活性方面,RDS PostgreSQL可以更好地融合其他产品,例如oss_fdw,用户可以把访问较少的数据放在OSS,作为外部表进行访问,特别适合历史数据的存储,可以帮助用户节约成本。此次我们会在RDS PostgreSQL中增加与ElasticSearch的融合,我们为什么要支持这个功能呢?其实ES是很流行的一款搜索引擎,很多用户都在用它,但是用户需要将关系数据库的数据同步到ES需要大费周章,通过逻辑增量复制来实现,或者业务层自己双写来实现,都不能保证一致性、延迟的需求,问题很多。现在,用户可以在RDS PostgreSQL的表中创建ES索引,数据存储在PostgreSQL中,索引在ES集群中,不需要用户维护同步、一致性的问题,正常对RDS PG执行写入和查询就可以了,业务可以透明利用ES搜索引擎的能力,加速RDS PG的数据搜索。我们在产品方面不断追求更快、更稳、更安全的理念,同时探求客户真正的需求,为客户提供更满意的RDS PostgreSQL服务,感谢您的支持!点击 此处 回顾PG发布会
Redis 作为稳居世界排名第一的键值内存数据库,同时也是最受欢迎的开源缓存产品,是应对高并发、大流量、低延迟业务场景的不二选择。2020年7月,伴随着Redis作者Salvatore Sanfilippo辞去项目领导者职务,Redis正式交付给社区,通过核心团队的机制来进行运作。阿里云技术专家赵钊(花名:仲肥)受邀成为全球5位核心成员之一,同时赵钊也是唯一一位来自中国乃至亚太市场的成员。他从Redis 4.0开始,参与到Redis社区的开发,同时也是最新版本Redis 6.0的最大贡献者之一。在阿里云,他还负责阿里云Redis企业版(Tair)的开发工作。此次,由赵钊带队,召集高校、业界及阿里云的知名Redis专家,为大家带来“七天玩转Redis实训营”。不仅会系统性地介绍Redis的整体架构及在多种场景下的最佳实践经验,而且会揭秘阿里云Redis开发规范和运维解法;你还将在讲师的带领下一起构建秒杀抢购与疫情播报应用,完成基于Redis的开发实操。进阶课程包含了企业版Redis(Tair)的场景开发和使用案例,第一次系统性地讲述了Redis在超大规模应用场景下的极致优化。报名时间:2021年3月1日 - 3月14日18:00 开营名额:3000人,报完即止 训练营时间:2021年3月15日 - 3月 21日 价格:原价999元,首期共创价0元 报名要求:希望你有一定的开发基础,对 Redis感兴趣想进一步深入了解 报名链接:扫描文末二维码或点击“阅读原文”马上报名7天训练营你将收获Redis社区核心成员揭秘社区运作方式,Redis系统专家带你了解 Redis 架构及关键技术机制;阿里巴巴讲师带你进行Redis开发运维实操,同时传授 Redis 开发规范及开发运维最佳经验;Redis专家每日钉群授课+答疑,助你解决实操中的种种困惑;每日小助手督促学习,训练营配套学习素材免费下载;完成训练营打卡可获得结营证书,同时通过结营考试可获得Redis周边(限量100份)甚至Cherry机械键盘等大奖(限量1份)。注:具体内容以报名页面信息为准7天训练营课程表训练营结营礼物点击 此处 或扫描二维码马上报名抢占训练营席位哦
作者:无才背景阿里云是业界最受欢迎的云服务提供商之一,享有良好的口碑,尤其是在中国拥有广泛的企业客户基础。在大量客户上云背景下,依然会有很多客户选择通过购买云上基础设施,自己构建数据管理系统,他们是真的不需要数据库服务,还是另有原因。据权威机构的调查,在选择云服务提供商的过程中,通过比较各厂商的技术实力,选定云厂商之后,价格因素就会凸显出来,这种情况在大多数国家都是如此。高性价比的服务,往往会决定客户最终选择服务的形态。问题和挑战在按照资源定价的云服务模式下,客户只考虑了显性成本,往往会忽略其他隐形成本和风险。例如:迁移、系统安全性、合规性和业务稳定性等成本。自建数据库的模式本质和线下数据中心是一样的,并未享受到PaaS化的云服务,也无法最大化提升资产利用率。依然会出现使用率很高的阶段与很少使用或者不使用的阶段交替出现,造成资源浪费。云数据库专属集群MyBase专属集群数据库在阿里集团内部经过多年沉淀,是集团默认上云的方案,2020年正式在阿里云对外提供服务。阿里云数据库专属集群服务,很好地满足了企业级客户数据库上云的诉求,既能享受到云数据库更快、更稳、更高安全的服务,又能如自建数据库一样,管理宿主机对接现有系统,符合企业使用习惯。云数据库专属集群在云上是一个独占资源池,保障客户数据安全,避免和其他租户的资源争抢;同时可对资源进行合理调度,充分利用资源,避免浪费。专属集群服务的优势1、可安装应用的数据库服务在很多场景,数据库和应用紧密结合,通过一体化部署获得更优性能,在部署形态和管理上更加简便,深受游戏行业客户青睐。专属集群通过开放权限,可实现应用和数据库的一体化部署、运行。更多Yum源安装通过阿里Yum源,安装多种常用的软件,例如:Tomcat中间件等。PostgreSQL插件支持在数据库使用过程中,往往有自定义插件的需求。以往的数据库服务,无法覆盖某些特定场景需求。专属集群PostgreSQL支持,例如:Citus分布式数据库插件、MADlib机器学习插件,Timescale时间插件。SQL Server 组件支持SQL Server有很多如:数据报表Reporting Services(SSRS)、数据分析Analysis Services(SSAS)、数据集成Integration Service(SSIS)等服务端组件。在以往的服务中,SSRS等组件是以单独的Windows服务的方式运行的,并未以PaaS服务的形式提供。在专属集群SQL Server中带有SSRS等服务组件,客户可以进行配置直接使用。自有软件安装部署将安装包上传到专属集群主机,在自有目录下进行安装、配置。2、可调节性价比的数据库服务专属集群通过各种方式来提升性价比。备库开放通常情况下,高可用架构下的数据库服务会选择一主一备架构,同时备库并不对外提供服务,甚至只读库也存在备库,这将大大提高数据库的使用成本。专属集群通过将备库开放,通过一主一只读/多只读架构,既满足高可用性要求,又能节省备库成本。CPU超分配在实际使用数据库的过程中,我们发现实例内存占有率较高,但是CPU占用率比较低,一般在20-30%上下,资源并未得到充分利用。通过对客户独占的主机资源进行超分配,提升资源利用率的同时,来降低综合使用成本。自定义从库MySQL实例开放了Change Master权限,可以将专属集群中数据库实例作为其他环境数据库实例的从库,极大提升了数据库管理灵活度。广泛应用于多种场景:客户可以创建多个单机版实例,组成一主多从、从库可访问数据库架构;建立云上灾备环境,实现自建数据库到线上专属集群数据库实时同步;建立云上测试环境,通过全备/增量同步方式,实现数据上云。3、原有系统无缝对接数据库为业务服务,在上云后需要无缝对接原有系统,保障业务快速上云。公共能力企业原有的监控运维系统需要在宿主机上安装agent进行监听、数据采集等工作,专属集群主机支持客户上传agent安装包,并进行部署。轻松获取数据库日志等信息。SQL Server用户① 专属集群SQL Server全面开放sysadmin权限,不存在权限障碍。② 全面支持各种SQL Server组件。③ 常见的ERP系统兼容,与多家企业产品进行了兼容性测试,包括:OA产品Ecology平台等。PostgreSQL用户① 支持AD域,统一进行管理。② 开放Super用户权限,赋予超级管理员权限,对接原有系统无权限障碍。写在最后阿里云数据库在2020年进入Gartner全球数据库领导者象限,成为在基础软件领域首次进入领导者的中国企业,代表中国数据库真正走进世界一流行列。过去十余年间,阿里云数据库在产品技术领域进展迅猛,目前市场份额稳居全球前三,亚太第一,已有超过40万个数据库实例迁移到阿里云上,包含政务、零售、金融、电信、制造、物流等多个领域的龙头企业。未来,阿里云数据库将持续加大创新力度,提供更多丰富的数据库服务,并携手合作伙伴,赋能更多企业数字化转型。点击 此处 申请MyBase免费试用
作者:君启介绍PolarDB-X是阿里巴巴自研的云原生分布式数据库,采用了计算-存储分离的架构,其中计算节点承担着大量的表达式计算任务。这些表达式计算涉及到SQL执行的各个环节,对性能有着重要的影响。为此PolarDB-X引入向量化执行引擎,为表达式计算带来了几十倍的性能提升。传统数据库执行器的缺陷现代数据库系统的执行引擎,大多采用一次计算一行数据(Tuple-at-a-time)的处理方式,并且需要在运行时对数据类型进行解析和判断,来适应复杂的表达式结构。我们称之为“标量(scalar)表达式”。这种方式虽然易于实现、结构清晰,但是当需要处理的数据量增大时,它具有显著的缺陷:为了适应复杂的表达式结构,计算一条表达式往往需要引入大量的指令;对于行式执行来说,处理单条数据需要算子树重新进行指令解释(instruction interpretation),从而带来了大量的指令解释开销。据论文《MonetDB/X100: Hyper-Pipelining Query Execution》统计,在MySQL执行TPC-H测试集的 Query1 时,指令解释就耗费了90%的执行时间。此外,在最初的Volcano结构设计中,算子内部逻辑并没有避免分支预测(branch prediction)。错误的分支预测需要CPU终止当前的流水线,将ELSE语句中的指令重新载入,我们将这一过程称为pipeline flush或pipeline break。频繁的分支预测错误会严重影响数据库的执行性能。向量化执行系统数据库向量化执行系统最早由论文《MonetDB/X100: Hyper-Pipelining Query Execution》提出,它有以下几个要点:采用vector-at-a-time的执行模式,即以向量(vector)为数据组织单位。使用向量化原语(vectorization primitives)来作为向量化算子的基本单位,从而构建整个向量化执行系统。原语中避免产生分支预测。使用code generation(代码生成)技术来解决静态类型带来的code explosion(代码爆炸)问题。向量化引擎为PolarDB-X的表达式计算带来了显著的性能提升。在下图中,横轴为向量大小,纵轴为吞吐量,不同标量表达式和向量化表达式的性能测试对比结果如下:case表达式性能测试对比结果如下:整体流程PolarDB-X中,向量化表达式的执行分为以下几个阶段:用户SQL经解析后,在validator中进行校验,推导和修正表达式的类型信息;这一阶段为向量化运算提供正确的、静态的类型信息;在优化器形成执行计划之后,需要对表达式树进行表达式绑定,实例化对应的向量化原语,同时分配好向量下标,供运行时内存分配;执行阶段,依据Volcano式的结构,自顶向下的触发执行向量化原语,并将向量作为运行时数据结构。运行时结构数据结构在PolarDB-X向量化执行系统中,采用以下的数据结构来存放数据:向量化表达式执行时,所有的数据都会存放在batch这一数据结构中。batch由许多向量(vector)和一个selection数组而组成。其中,向量vector包括一个存储特定类型的数值列表(values)和一个标识null值位置的null数组组成,它们在内存中都是连续存储的。null数组中的bit位以0和1来区分数值列表中的某个位置是否为空值。我们可以用vector(type, index)来标识batch中一个向量。每个向量有其特定的下标位置(index),来表示向量在batch中的顺序;类型信息(type)来指定向量的类型。在进行向量化表达式求值之前,我们需要遍历整个表达式树,根据每个表达式的操作数和返回值来分配好下标位置,最后根据下标位置统一为向量分配内存。延迟物化selection数组的设计体现了延迟物化的思想,参考论文《Materialization Strategies in a Column-Oriented DBMS》。所谓延迟物化,就是尽可能地将物化(matrialization)这一过程后推,减少内存访问带来的开销。在执行表达式计算时,往往会先经过Filter表达式过滤一部分数据,再对过滤后的数据执行求值处理;每次过滤都会影响到batch中所有的向量。以上图中的batch为例,如果我们针对第0个向量设置 vector(int, 0) != 1这一过滤条件,假设vector(int, 0)中有90%的数据满足该过滤条件(选择率selectivity = 0.9),那么我们需要将batch中所有向量90%的数据重新物化到另一块内存中。而如果我们只记录满足该过滤条件的位置,存入selection数组,我们就可以避免这一物化过程。相应的,以后每次向量化求值过程中,都需要参考此selection数组。向量化原语向量化原语是向量化执行系统中的执行单位,它最大程度限制了执行期间的自由度。原语不用关注上下文信息,也不用在运行时进行类型解析和函数调用,只需要关注传入的向量即可。它是类型特定(Type-Specific)的,即一类原语只能处理特定类型。向量化原语的主体是Tight-Loop的代码结构。在一个循环体内部,只需要进行取值和运算即可,没有任何的分支运算和函数调用。一个简单的向量化原语结构如下所示:map_plus_double_col_double_col(int n, double*__restrict__ res, double*__restrict__ vector1, double*__restrict__ vector2, int*__restrict__ selection) { if (selection) { for(int j=0;j<n; j++) { int i = selection[j]; res[i] = vector1[i] + vector2[i]; } } else { for(int i=0;i<n; i++) res[i] = vector1[i] + vector2[i]; } }注:*左右滑动阅览其运算过程利用了selection数组,逐步对向量进行取值、运算和存值,如下图所示:向量化原语带来了以下优点:Type-Specific以及Tight-Loop的结构,大大减少了指令解释的开销;避免分支预测失败和虚函数调用对CPU流水线的干扰,同时也能有利于 loop pipeline 优化【论文引用】从向量中存取数据,有利于触发cache prefetch,减少cache miss带来的开销。我们为各种标量化表达式提供相应的原语实现,从而完成从标量到向量化的转变。例如将加法运算 plus(Object, Object) 针对不同操作数类型生成原语,包括plus(double,double),plus(long, long)等。短路求值在向量化原语的基础上,我们可以进一步对分支运算(也称为控制流运算 Control-Flow)进行短路求值(short-circuit calculation)优化,提升表达式计算的性能。例如,case 表达式由n个when表达式、n-1个then表达式、1个else表达式构成。对于表达式select case when a > 1 then a * 2 when b > 1 then b * 2 else a * b其逻辑语义是:对于满足 a > 1 的向量位置,计算 a * 2;对于满足 a <= 1 and b > 1 的向量位置,计算 b * 2;对于满足 a <= 1 and b <= 1 的向量位置,计算 a * b;把所有位置的数值组合在一起形成新的向量,输出。具有以下树形结构:由于标量化表达式按照volcano结构编排,并提供了统一的next()的接口,case表达式必须执行完所有的子表达式a>1,a*2,b>1,b*2和a*b之后,将全部结果汇总到一起,最后做case语义处理。这种执行方式不能根据when表达式的处理结果及时终止计算过程,而是对全部子表达式无差别执行。引入向量化执行器以后,我们可以设计短路求值来优化此问题,每一个子表达式需要被提供合适的selection数组,从而正确选择列中合适的位置来进行向量运算。设第i个when条件表达式接受的selection元素集合为 ,其输出的selection元素集合为,也就是第i个then条件表达式接受的selection元素集合。那么满足 ,其中是原始的selection数组中的下标集合。我们把求取selection元素集合的步骤称为substract selection,case运算的整个过程如下图所示:总结PolarDB-X向量化引擎利用原语(primitive)来构建表达式,以向量作为运行时数据结构。每种原语仅为特定类型进行服务,从而减少了指令总数;原语中的tight-loop结构不仅对CPU流水线十分友好,也允许CPU进行数据预取,并且避免分支预测。此外,一些优化如延迟物化、短路求值,进一步提升了表达式求值性能。然而,从用户SQL到向量化执行之间,存在着一道巨大的鸿沟。我们需要解决以下几个重要问题:1. 如何确定表达式的输入输出类型,并为SQL中的表达式分配合适的原语?2. 每个原语需要使用不同的向量来进行输入和输出,如何为正确地为原语分配向量?3. 每种原语仅为特定类型进行服务,那么我们必然需要为一个表达式配备大量不同的原语,来适应不同的数据类型。如何应对原语数量爆炸这一问题?在下一篇文章中,我们将为上述问题的解决方案进行详细介绍。【相关阅读】PolarDB-X 面向 HTAP 的混合执行器PolarDB-X 面向 HTAP 的 CBO 优化器如宝马3系和5系:PolarDB-X 与 DRDS 并驾齐驱PolarDB-X 存储架构之“基于Paxos的最佳生产实践”PolarDB-X 私有协议:提升集群的性能和稳定性技术解读 | PolarDB-X 分布式事务的实现技术解读 | PolarDB-X 强一致分布式事务PolarDB-X 一致性共识协议 (X-Paxos)
作者:李鹤01 引言如何对时空数据库中的亿级矢量空间数据进行在线可视化一直是业界难题。因数据体量大,传统方法需要将数据库中数据进行基于缓存切片的服务发布才能可视化,操作流程冗长,且有一大堆需要考虑的问题:如果对矢量数据进行预切片,数据要切多久?切多少级合适?存储瓦片的硬盘空间够用吗?如果使用实时瓦片,实时渲染瓦片的响应时间能保证吗?如果使用矢量瓦片,小比例尺的瓦片可能会有多大体积?传输会不会成为瓶颈?前端渲染能承受多大的数据量?如果是要快速浏览数据库中的大规模在线数据,传统用于“底图服务”的离线切片生产流程几乎无解,不但费时费力,又无法在线联机处理。黑科技来了,本文介绍如何使用RDS PG或PolarDB(兼容PG版或Oracle版)的Ganos时空引擎提供的数据库快显技术,仅用百行代码实现亿级海量几何空间数据的在线快速显示和流畅地图交互,且无需关注切片存储和效率问题。02 技术特性解读Ganos的在线快显处理的核心是将数据库和可视化进行了关联,提供了一种新的可视化索引技术——稀疏矢量金字塔(Sparse Vector Pyramid,SVP)索引。SVP具备两个关键特性:快与省。其中,快指两个阶段的快:金字塔创建快:Ganos利用空间索引对数据在空间上进行密集度划分,根据密集度建立一种稀疏矢量金字塔索引,相比传统切图流程减少了90%的数据计算量。同时,创建金字塔采用了完全并行处理模式,即使1亿条地类图斑数据生成金字塔也仅需耗费约10分钟时间。数据展现快:Ganos采用了视觉可见性剔除算法,根据Z-order排序,过滤掉大量不影响显示效果的数据,从而加快实时显示的效率。Ganos支持直接输出PNG格式的栅格瓦片和MVT格式的矢量瓦片,1亿地类图斑数据实时渲染显示的响应时间都达到秒级。省也具有两个维度:节省磁盘空间:1亿条地类图斑数据生成金字塔索引仅仅占据原表5%大小的额外空间。节省开发时间:仅使用简单的SQL语句,通过调整语句参数即可灵活控制显示效果。03 使用步骤Ganos的快显引擎使用上非常简洁,已高度封装了SQL函数。需要注意的是,第一次使用快显引擎之前,需要显式创建对应的扩展模块,执行的语句如下:CREATE EXTENSION ganos_geometry_pyramid CASCADE;通过执行以上语句,快显引擎的计算组件将会被加载起来。3.1 建立稀疏矢量金字塔假设您已创建了某个矢量大表并导入了数据,接着就可以使用Ganos的st_buildpyramid方法创建矢量金字塔。方法原型如下,更详细的参数描述可以参考官方文档。boolean ST_BuildPyramid(cstring table, cstring geom, cstring fid, cstring config)注:*左右滑动阅览其中table:矢量数据所在的表名。geom:矢量字段名。fid:矢量要素记录的唯一标识,支持Int4/Int8类型。config:json格式的配置参数字符串。在本例中,我们指定矢量金字塔的名称和使用的逻辑瓦片大小(这个瓦片大小并非真实存在的瓦片,仅表示一种空间上的逻辑划分)实际调用如下:ST_BuildPyramid('points', 'geom', 'gid', '{"name":"points_geom","tileSize":512}')注:*左右滑动阅览我们为表points的geom字段创建了一个矢量金字塔,金字塔名我们指定为points_geom,同时设定金字塔的逻辑瓦片大小为512。3. 2 获取栅格瓦片栅格瓦片是图片形式的瓦片(Tile),是使用最广泛的一种地图瓦片形式。Ganos的ST_AsPng方法提供了在数据库端将矢量数据按需动态渲染为栅格瓦片的功能。该功能提供了最基础的栅格符号化能力,更多面向一些不需要复杂符号化的轻量级场景,如数管系统中。方法原型如下,更详细的参数描述可以参考官方文档:bytes ST_AsPng( cstring name, cstring tile, cstring style)其中name:金字塔表名。tile:瓦片索引行列号,Z_X_Y的形式。style:渲染样式。我们可以通过如下参数调节渲染效果:point_size:点大小,单位为像素。line_width:线宽,对线要素和面要素的外边框起作用,单位为像素。line_color:线渲染颜色,对线要素和面要素的外边框起作用。前6位为16进制颜色,后2位为16进制透明度。fill_color:填充颜色,对面要素起作用。background:背景色。一般设置为FFFFFF00,即纯透明。实际调用如下:ST_AsPng('points_geom', '1_2_1','{"point_size": 5,"line_width": 2,"line_color": "#003399FF","fill_color": "#6699CCCC","background": "#FFFFFF00"}')注:*左右滑动阅览我们从矢量金字塔中获取到索引行列号为x=2,y=1,z=1的矢量瓦片,并将该矢量瓦片按照我们配置的样式渲染为栅格瓦片,返回PNG格式的图片。3. 3 获取矢量瓦片矢量瓦片是新兴的地图瓦片技术,具有在前端配置样式的灵活特性,使用WebGL渲染,效果也更加美观,Mapbox等地图框架可以方便的支持这一格式。使用Ganos的ST_Tile方法可以将矢量金字塔中的数据以矢量瓦片的形式提供。方法原型如下,更详细的参数描述可以参考官方文档。bytea ST_Tile(cstring name, cstring key);其中name:金字塔名。在本例中为表名_矢量字段名。key:瓦片索引行列号,Z_X_Y的形式。我们只需要提供标准的TMS行列号和金字塔表的名称即可调用。实际调用如下:ST_Tile('points_geom', '1_2_1');我们从矢量金字塔中获取到索引行列号为x=2,y=1,z=1的矢量瓦片,返回数据为标准的MVT格式。04 实战案例4.1 测试数据我们准备两份矢量数据作为测试样例。buildings表为面数据,数据总计1.25亿条,展现使用栅格瓦片的在线可视化效果。gid|geom | ---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 1|MULTIPOLYGON(((-88.066953 34.916114 0,-88.066704 34.916114 0,-88.066704 34.91602 0,-88.066953 34.91602 0,-88.066953 34.916114 0))) 2|MULTIPOLYGON(((-87.924658 34.994797 0,-87.924791 34.99476 0,-87.924817 34.994824 0,-87.924685 34.994861 0,-87.924658 34.994797 0))) 注:*左右滑动阅览points表为点数据,数据总计10.7万条,展现使用矢量瓦片的在线可视化效果。id|geom | --|------------------------------| 1|POINT (113.5350205 22.1851929)| 2|POINT (113.5334245 22.1829781)|4.2 全栈架构数据库-快显全栈架构包含数据库服务器、python服务端和用户端三个部分,全栈架构如下图所示。4.3 服务端代码为了代码简洁,更侧重于逻辑的描述,我们选择了Python(兼容Python3.6及以上版本)作为后端语言,Web框架使用了基于Python的Flask(使用pip install flask进行安装)框架,数据库连接框架使用了基于Python的Psycopg2(使用pip install psycopg2进行安装)。值得一提的是,我们实现了最基础的功能,当Web服务自身的性能出现瓶颈时,可按不同的平台与框架进行优化,获得更好的响应性能。我们在后端首先建立了矢量金字塔,其后分别实现了两个接口,矢量瓦片接口使用points表中的数据,栅格瓦片接口使用buildings表中的数据,并定义好样式,供前端直接调用。为了方便说明,后端代码同时提供了矢量栅格两个接口,实际使用时可以按需选择。# -*- coding: utf-8 -*- # @File : Vector.py import json from psycopg2 import pool from threading import Semaphore from flask import Flask, jsonify, Response, send_from_directory import binascii # 连接参数 CONNECTION = "dbname=postgres user=postgres password=postgres host=YOUR_HOST port=5432" class ReallyThreadedConnectionPool(pool.ThreadedConnectionPool): """ 面向多线程的连接池,提高地图瓦片类高并发场景的响应。 """ def __init__(self, minconn, maxconn, *args, **kwargs): self._semaphore = Semaphore(maxconn) super().__init__(minconn, maxconn, *args, **kwargs) def getconn(self, *args, **kwargs): self._semaphore.acquire() return super().getconn(*args, **kwargs) def putconn(self, *args, **kwargs): super().putconn(*args, **kwargs) self._semaphore.release() class VectorViewer: def __init__(self, connect, table_name, column_name, fid): self.table_name = table_name self.column_name = column_name # 创建一个连接池 self.connect = ReallyThreadedConnectionPool(5, 10, connect) # 约定金字塔表名 self.pyramid_table = f"{self.table_name}_{self.column_name}" self.fid = fid self.tileSize = 512 # self._build_pyramid() def _build_pyramid(self): """创建金字塔""" config = { "name": self.pyramid_table, "tileSize": self.tileSize } sql = f"select st_BuildPyramid('{self.table_name}','{self.column_name}','{self.fid}','{json.dumps(config)}')" self.poll_query(sql) def poll_query(self, query: str): pg_connection = self.connect.getconn() pg_cursor = pg_connection.cursor() pg_cursor.execute(query) record = pg_cursor.fetchone() pg_connection.commit() pg_cursor.close() self.connect.putconn(pg_connection) if record is not None: return record[0] class PngViewer(VectorViewer): def get_png(self, x, y, z): # 默认参数 config = { "point_size": 5, "line_width": 2, "line_color": "#003399FF", "fill_color": "#6699CCCC", "background": "#FFFFFF00" } # 在使用psycpg2时,将二进制数据以16进制字符串的形式传回效率更高 sql = f"select encode(st_aspng('{self.pyramid_table}','{z}_{x}_{y}','{json.dumps(config)}'),'hex')" result = self.poll_query(sql) # 只有在使用16进制字符串的形式传回时才需要将其转换回来 result = binascii.a2b_hex(result) return result class MvtViewer(VectorViewer): def get_mvt(self, x, y, z): # 在使用psycpg2时,将二进制数据以16进制字符串的形式传回效率更高 sql = f"select encode(st_tile('{self.pyramid_table}','{z}_{x}_{y}'),'hex')" result = self.poll_query(sql) # 只有在使用16进制字符串的形式传回时才需要将其转换回来 result = binascii.a2b_hex(result) return result app = Flask(__name__) @app.route('/vector') def vector_demo(): return send_from_directory("./", "Vector.html") # 定义表名,字段名称等 pngViewer = PngViewer(CONNECTION, 'usbf', 'geom', 'gid') @app.route('/vector/png/<int:z>/<int:x>/<int:y>') def vector_png(z, x, y): png = pngViewer.get_png(x, y, z) return Response( response=png, mimetype="image/png" ) mvtViewer = MvtViewer(CONNECTION, 'points', 'geom', 'gid') @app.route('/vector/mvt/<int:z>/<int:x>/<int:y>') def vector_mvt(z, x, y): mvt=mvtViewer.get_mvt(x, y, z) return Response( response=mvt, mimetype="application/vnd.mapbox-vector-tile" ) if __name__ == "__main__": app.run(port=5000, threaded=True)注:*左右滑动阅览将以上代码保存为Vector.py文件,执行python Vector.py命令即可启动服务。从代码不难推断,无论我们使用何种语言、何种框架,我们只需将矢量或栅格瓦片的SQL语句封装为接口即可实现完全相同的功能。相比发布传统的地图服务,借助Ganos的矢量金字塔功能实现在线可视化是更加轻量好用的选择:针对栅格瓦片,可以在通过改变代码进行样式控制,灵活性大大增强。无需引入第三方的其他组件,也不需要进行针对性优化,就有令人满意的响应性能。可以任意选择使用者熟悉的编程语言与框架,也无需复杂专业的参数配置,对非地理从业者更加的友好。4.4 用户端代码我们选用Mapbox作为前端地图框架,展示后端提供的矢量瓦片层和栅格瓦片层,并为矢量瓦片层配置了渲染参数。为了方便说明,前端代码同时添加了矢量、栅格两个图层,实际使用时可以按需选择。我们在后端代码的同一文件目录下新建名为Vector.html的文件,写入下列代码,在后端服务启动后,就可以通过http://localhost:5000/vector访问了。<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title></title> <link href="https://cdn.bootcdn.net/ajax/libs/mapbox-gl/1.13.0/mapbox-gl.min.css" rel="stylesheet" /> </head> <script src="https://cdn.bootcdn.net/ajax/libs/mapbox-gl/1.13.0/mapbox-gl.min.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.0/axios.min.js"></script> <body> <div id="map" style="height: 100vh" /> <script> const sources = { osm: { type: "raster", tiles: ["https://b.tile.openstreetmap.org/{z}/{x}/{y}.png"], tileSize: 256, }, }; const layers = [ { id: "base_map", type: "raster", source: "osm", layout: { visibility: "visible" }, }, ]; const map = new mapboxgl.Map({ container: "map", style: { version: 8, layers, sources }, }); map.on("load", async () => { map.resize(); // 添加栅格瓦片数据源 map.addSource("png_source", { type: "raster", minzoom: 1, tiles: [`${window.location.href}/png/{z}/{x}/{y}`], tileSize: 512, }); // 添加栅格瓦片图层 map.addLayer({ id: "png_layer", type: "raster", layout: { visibility: "visible" }, source: "png_source", }); // 添加矢量瓦片数据源 map.addSource("mvt_source", { type: "vector", minzoom: 1, tiles: [`${window.location.href}/mvt/{z}/{x}/{y}`], tileSize: 512, }); // 添加矢量瓦片图层,并为矢量瓦片添加样式 map.addLayer({ id: "mvt_layer", paint: { "circle-radius": 4, "circle-color": "#6699CC", "circle-stroke-width": 2, "circle-opacity": 0.8, "circle-stroke-color": "#ffffff", "circle-stroke-opacity": 0.9, }, type: "circle", source: "mvt_source", "source-layer": "points_geom", }); }); </script> </body> </html>注:*左右滑动阅览4.5 矢量瓦片的动态效果可以在前端调节不同效果。调整为新的图层参数后效果如下:{ "circle-radius": 4, "circle-color": "#000000", "circle-stroke-width": 2, "circle-opacity": 0.3, "circle-stroke-color": "#003399", "circle-stroke-opacity": 0.9, }4.6 栅格瓦片的动态效果05 与PGADmin集成PG数据库管理工具PGAdmin原生支持矢量数据的可视化,但因缺乏快显技术,仅能单对象显示或有限结果集显示,无法对大规模矢量数据进行畅快淋漓的全局浏览。我们将Ganos的矢量快显功能与PGAdmin集成,数据入库即可在线浏览全局,快速评估数据概况,大大增强了数据管理的使用体验。06 总结本文从稀疏矢量金字塔的原理与优势入手,介绍了如何利用Ganos实现在线可视化服务的各种功能,并最终通过百行代码实现了一个可以应对亿级数据的地图可视化服务。读者可以进一步在可视化基础上,利用PG/PolarDB Ganos的服务器端快速查询和分析能力进行对象属性查询、空间圈选、空间分析等更复杂功能。这就是Ganos所带来的大规模空间图形显示加速黑科技——稀疏矢量金字塔索引带来的变革。如果您对此有兴趣,更多信息可以参考官方文档。
作者:玄弟早期数据库受限于硬件水平,IO、内存和CPU资源都非常昂贵,比如计算层的数据一多,内存容易爆掉;且只做单核计算,更谈不上用分布式去解决计算加速问题。可见在当时背景下执行器能够做的加速优化微乎其微。但今时不同往日,由于硬件水平的高速发展,分布式技术的日益成熟,执行器在大数据量的加速优化也越来越被重视,而我们的PolarDB-X执行器也就是在这个背景下不断迭代成长起来的。目前PolarDB-X执行器在混合负载场景下,能确保TP和AP工作负载不相互影响,在能保证TP负载低延迟的前提下,AP依然可以保持一个不错的吞吐。在PolarDB-X企业版主实例32核128 GB下,在开启智能读写分离模式并存在只读实例情况下,TPC-C流量将会被路由至主实例,而TPC-H流量将会被路由至只读实例。OLTP负载与OLAP负载能够分别在主实例和只读实例上做到物理资源隔离,同时只读实例可以提供MPP能力,能够充分利用计算资源。 备注:开始通过100并发的纯TPC-C流量,吞吐可以达到23万tpmC;关闭智能读写分离,加入TPC-H流量混跑。TPC-C吞吐下降明显,TPC-H总耗时为840s;开启智能读写分离,TPC-C吞吐恢复到23万tpmC,TPC-H总耗时也可以恢复到最初的274s。发展历程PolarDB-X数据库历经10年的发展,而其执行器起起伏伏也沉底了10年。其最早可以追溯到执行器经典架构:Volcano 模型的计算架构。我们可以先回顾下传统数据库在执行器领域过去几十年的发展,同时也介绍下我们对技术选型的思考。执行器模型:Pull vs. Push早期的执行器多半都采样经典的Volcano 模型[1],如下图所示。执行计划中的每个算子都需要实现next函数,上游算子每一次调用,内部都会调用其输入的next函数, 递归调用,再层层计算返回结果。目前MySql/SQLServer/DB2/Oracle等早期数据库基本都采用这种计算模型。Volcano模型简单灵活,且这种设计不用占用过多的内存。在当时内存是非常昂贵的,火山模型将更多的内存资源用于IO的缓存设计而没有优化CPU的执行效率,这在当时的硬件基础上是很自然的权衡。但是现在CPU的硬件环境与大数据场景下,性能表现却差强人意。究其原因。主要有如下几点:每次 next 都是一次虚函数调用过程是被动拉数据,编译器无法对虚函数进行inline优化,同时也带来分支预测的开销,且很容易预测失败,导致CPU流水线执行混乱。Volcano Style的代码对数据的局部性并不友好,往往造成cache miss。我们知道CPU cache是存储着连续数据空间,每次可以对连续数据进行集中处理,将受益最大。而Volcano模型每次调用只处理一行。鉴于火山模型每次处理一行一行数据,而next调用代价又比较高。所以批量处理模型在业界被提出,在算子间传递数据不再是一条一条记录,而是一批数据,算子每次执行的时候都会在内部攒一批数据,数据大小尽可能和CPU cache对齐,不仅大大提高了cache命中率,而且有效地减少了函数调用次数。除此之外,业界也提出了Push计算模型,如上图所示。可以直观看到push执行模式相对于pull模型来说有效减少了指令的跳转,优化了CPU执行效率。通常做法就是用visitor方式遍历在优化器提供的执行计划,构建出push执行的物理计划。Push模式的算子实现大大提高了算子复杂度,比如merge join在原先pull模型中可以直接在算子内部控制两端输入的数据流。编译执行 vs. 向量化执行当我们提到编译执行的时候到底是在讲什么呢?通常我们是先编写代码,再编译,最后运行。而对于这里提到的编译执行,更多的是运行时期的代码生成生成技术。在执行过程中生成编译生成执行代码,避免过多的虚函数调用和解析执行,因为在执行之初我们是知道关系代数的schema信息。在具备Schema信息的情况下,事先生成好的代码,可以有效减少很多执行分支预测开销。这里直接参考自Impala论文[2]给出来的代码比对。如上图右边的代码非常紧凑,有效消除了字段个数,字段大小,字段类型,对于数据量特别多的处理场景,可以大大减少CPU开销,提高性能,但实际仍有一个问题,如何生成右边的代码?业界常用的代码生成框架有ASM/LLVM IR等。但是每个表达式和算子都需要单独编译,如何减少编译开销?在这个基础上发展出来了Pipeline Compilation技术,就是将多个operator融合在一起编译,减少开销;此外还有Operator Cache技术,将事后编译好的代码cache起来,类似的查询语句可以直接复用编译好的代码,进一步减少编译开销时间[3]。另一种加速思路是向量化执行,简单来说就是通过 batch 的方式均摊开销:假设每次通过 operator tree[4]生成一行结果的开销是C的话,经典模型的计算框架总开销就是C*N,其中N为参与计算的总行数,如果把计算引擎每次生成一行数据的模型改为每次生成一批数据的话,因为每次调用的开销是相对恒定的,所以计算框架的总开销就可以减小到C*N/M,其中M是每批数据的行数,这样每一行的开销就减小为原来的1/M,当 M 比较大时,计算框架的开销就不会成为系统瓶颈了。这样说很多人会误解,这个是不是就是之前提到的批量处理模型呢?看似差不多,实际上要做到向量化执行,需要对算子和表达式做大量的改造,基于SIMD向量化指令操作的思想去重构整个表达式计算。向量化执行可以减少分支预测的开销,充分发挥SIMD指令并行计算的优势;还可以和列式存储有效结合在一起,减少数据额外转换的overhead。我们所知道ClickHouse就是采样了向量化(vectorized query execution)机制。并行:SMP vs. MPP随着多处理器结构硬件的出现,执行器开始往SMP架构发展,既单机并行计算,充分利用多核能力加速计算。在这样的系统中,所有的CPU共享全部资源,如总线,内存和I/O系统等,操作系统或管理数据库的副本只有一个,这种系统有一个最大的特点就是共享所有资源。但是单机并行执行器的扩展能力非常有限,在计算过程中也只能充分使用一台 SMP 服务器的资源,随着要处理的数据越来越多,这种有限扩展的劣势越来越明显。但是 SMP 架构只能利用单个机器的计算能力(scale up),不能扩展到多台机器(scale out)。MPP 就是将计算分布到多个节点的集群中,集群中的内存、CPU等资源理论上可以无限扩展,使得资源不再轻易成为计算的瓶颈。但分式并行计算在充分发挥集群各台机器的CPU等能力的同时,会带来新的问题。调度上如何做到均衡调度,避免更多的网络传输,避免单个节点成为计算瓶颈?分布式计算过程中如何确保资源利用最大化?可见构建一个分布式并行计算比之前的系统复杂的多,要考虑的因素也非常多。HTAP: Single System vs. Separate SystemsHTAP最早的概念是Gartner在2014年的一份报告中使用混合事务分析处理(Hybrid Transactional and Analytical Processing,HTAP)一词描述新型的应用程序框架,以打破OLTP和OLAP之间的隔阂,既可以应用于事务型数据库场景,亦可以应用于分析型数据库场景。实现实时业务决策。这种架构具有显而易见的优势:不但避免了繁琐且昂贵的ETL操作,而且可以更快地对最新数据进行分析。在近期的顶会论文中也不乏出现HTAP架构的身影,推荐一篇SIGMOD的论文《Hybrid Transactional/Analytical Processing: A Survey》,高度总结和归纳了下HTAP架构的技术方案。实现HTAP技术主要两个分类:Single System(一套系统解决 TP/AP)Decoupled Storage,比如SAP HANA、Hyper(行式内存+列式扩展)、SQLServer(列存索引)、MemSQL(行式内存+刷盘时转列存)等;Unified Storage,主要为SQL-on-Hadoop类,比如Impala with Kudu(SQL-On-hadoop类型,支持变更)Separate Systems(两套系统分别应对 TP/AP)Decoupled Storage,比如Lambda架构、以及传统的在线+数仓组合, 技术上使用ETL进行数据同步;Unified Storage,比如基于Hbase构建TP写入一份数据,AP侧是Spark SQL-base使用同一份数据。PolarDB-X 执行器架构PolarDB-X 是由阿里巴巴自主研发的云原生分布式数据库,是一款基于云架构理念,并同时支持在线事务处理与在线分析处理 (Hybrid Transactional and Analytical Processing, HTAP)的融合型分布式数据库产品,因此在PolarDB-X优化器、执行器上针对HTAP混合负载都有对应的设计体现。HTAP架构PolarDB-X主要面向以OLTP事务型为主的分布式数据库,同时支持OLAP的在线分析的混合负载能力,我们期望以Single System的形态,基于Decoupled Storage存储结构支持HTAP形态。PolarDB-X HTAP执行器,主要有几个特征:PolarDB-X 提供一个HTAP的endpoint(可以理解为一个vip),业务所有的TP和AP流量只需要通过这个endpoint访问即可;PolarDB-X 提供RW(读写)、RO(只读)节点的概念,基于Decoupled Compute/Storage的思路提供HTAP混合负载的能力;PolarDB-X 提供面向HTAP的优化器、分布式调度能力,可在Decoupled模式下将TP和AP请求做分离,利用物理隔离满足OLTP的稳定性,另外结合分布式事务多副本强一致的能力,实现OLAP的数据一致性。可参考文档:PolarDB-X 强一致分布式事务原理、PolarDB-X 面向 HTAP 的 CBO 优化器具体HTAP的工作原理和流程:总结一下优势:强隔离、强一致的混合负载,OLTP请求不会因为日志复制而产生延迟引入分布式并行计算,OLAP查询可满足线性扩展能力MPP 并行执行器PolarDB-X在混合执行器设计上,借助于PolarDB-X 面向 HTAP 的 CBO 优化器,可以实现TP和AP的识别和路由,可以在一个实例里同时运行OLTP和OLAP业务,保证AP的查询不影响TP流量的稳定性。同时在OLAP能力上,引入Push模型、Chunk执行、向量化、MPP并行计算等特性,可以满足TPC-H/TPC-DS等复杂查询的诉求,后续会专门开一篇文章介绍下PolarDB-X的MPP并行计算的代码设计和理念。以 TPC-H Q9为例:关键特性对比对比项子项PolarDB-XMySQL with Analytics EngineTiDBCockroachPrestoSparkOLTP强一致事务分布式单机分布式分布式//基于索引的点查支持支持支持支持//基于索引的Join关联支持支持支持支持//各种DML支持支持支持支持//OLAP执行模型Push+BatchVolcano+BatchVolcano+BatchVolcano+BatchPush+BatchPull+Batch执行方式vectorizedvectorizedvectorizedvectorizedvectorized (表达式有codegen)whole stage codegen (企业版有vectorized)并行计算MPPMPPSMP(MPP需外挂Spark)SMPMPPMPPHTAP资源隔离存储和计算存储和计算存储///数据一致性强一致弱一致(异步复制)强一致/// 备注:MySQL with Analytics Engine,是MySQL在20年12月2号在其官网推出分析引擎,极大增强MySQL自身的分析能力。OLTP场景会比较注重基于索引的优化,比如基于Index的point select 以及Index Nested Loop Join,在OLAP场景下会比较注重并行能力和执行效率的优化,比如SMP/MPP、vectorized/codegen等,最后针对HTAP场景,更多会注重资源隔离、数据一致性、查询入口是一个还是多个等。我们还在路上软件技术的发展总是和硬件技术的发展紧密结合在一起的,迎合硬件技术的发展而改变软件技术栈的相应策略能够使得设计的系统获得更大的受益。这些年PolarDB-X执行器经过多次版本的迭代,早已不再是单机版本的内存执行器了,我们既是一款和分布式事务相耦合的执行器,也可以是一款可以处理海量的数据处理,具备了落盘和MPP的计算能力的执行器。这一切都得益于我们从一开始就把自己定位成了一款HTAP引擎:是一款可以同时满足事务处理和工作负载分析的业务需求的执行器是一款分布式并行执行器,具备计算水平扩展的能力高度统一的执行器代码,真正做到了TP和AP执行器一体化支持大规模并行处理和复杂查询优化基于工作负载做资源管理,支持计算资源相互弹缩但我们知道这些还远远不够,技术永无止境。现在的我们仍会时刻关注业界最新的技术动态,我们会探索一切可能的东西,充分融入到我们PolarDB-X的系统里头去,不断加强我们HTAP数据库的能力,这个过程我们也期待你的加入!参考文档Graefe G..Volcano-an extensible and parallel query evaluation system[J].Knowledge & Data Engineering, IEEE Transactions on,1994,6 S. Wanderman-Milne and N. Li, “Runtime Code Generation in Cloudera Impala,” IEEE Data Eng. Bull., vol. 37, no. 1, pp. 31–37, 2014 向量化与编译执行浅析A. Kemper, P. Boncz, T. Kersten, V. Leis, A. Pavlo, and T. Neumann, “Everything you always wanted to know about compiled and vectorized queries but were afraid to ask,” Proc. VLDB Endow., vol. 11, no. 13, pp. 2209–2222, 2018. 【相关阅读】PolarDB-X 面向 HTAP 的 CBO 优化器如宝马3系和5系:PolarDB-X 与 DRDS 并驾齐驱PolarDB-X 存储架构之“基于Paxos的最佳生产实践”PolarDB-X 私有协议:提升集群的性能和稳定性技术解读 | PolarDB-X 分布式事务的实现技术解读 | PolarDB-X 强一致分布式事务PolarDB-X 一致性共识协议 (X-Paxos)
阿里云AnalyticDB(下文简称:ADB)满足了我们营销SAAS平台海量数据实时分析秒级返回的诉求,平台的用户和订单数据一直大规模增长,对分析型数据库的弹性要求较高,ADB的平滑弹性升降配对业务影响很小,在扩容升配的时候还能正常访问;ADB弹性模式的冷热存储分离能力帮我们节约了很大的数据存储成本。PolarDB-X解决了我们海量数据读写和存储扩展性的问题。 ----蜂创科技研发负责人吕军雷关于蜂创科技 北京蜂创科技有限公司于2016年成立,中国企业级数字化营销解决方案提供商。公司拥有品牌“极目云客”。旗下营销活动一体化管理SaaS平台、客户关系CRM管理平台、终端门店管理平台、精准营销投放平台等平台,领先于国内数字营销垂直领域。以SaaS管理平台、资源供应链、整合传播增值服务为一体化解决方案,为企业级客户精准实现市场目标提供高效助力。致力于企业的数字化营销,让企业在人、货、场上数据在线,并进行参与互动,数据智能和有效协作。业务快速发展,面临巨大技术挑战蜂创的业务飞速发展,数据量迅猛增长,给数据平台带来了巨大的技术挑战。查询速度慢极目云客平台单表数据量从数亿到数十亿不等,业务上需要做多表关联以及多维度及时分析查询。过去用普通关系数据库来支撑,查询耗时太长,不能快速返回业务查询结果,特别是对于交互式探索分析来说,用户体验影响更大;希望在毫秒/秒级实时响应,提高产品核心竞争力。降低技术复杂度基于传统数据仓库和以hadoop为代表的大数据技术方案,由于技术组件多、架构复杂、数据链路长、技术复杂度高,带来的问题是系统维护成本高,需要投入较大人力专门搭建平台和升级运维。由于学习成本高,人才培养难度大。同时由于数据需要经过多个技术组件,数据接入和数据ETL开发工作量都比较大,新增一个业务场景需要全链路开发,从数据产生到应用上线时间周期长。扩展性要求高近年来公司业务发展非常迅速,客户数越来越多,数据量越来越大,需要保证平台的读写和存储性能水平扩展,要求技术架构稳定,业务代码无需修改来应对业务增长。实时数仓解决方案针对蜂创的业务挑战和应用场景,采用AnalyticDB MySQL版(简称ADB)作为实时数仓引擎,构建云上湖仓一体的解决方案,整体技术架构如下图所示:业务数据通过DTS将PolarDB-X的数据同步到ADB,简化数据采集链路开发过程,解决数据传输实时性问题, 实现数据秒级延迟。基于AnalyticDB构建一体化的数据仓库,能在同一平台进行数据清洗、ETL计算和实时查询服务,数据无需搬迁,减少开发工作量。AnalyticDB支持数十亿数据毫秒/秒级查询实时响应,实现交互查询应用和报表实时化,极大提高了用户体验和产品竞争力。AnalyticDB支持对OSS等外部开放数据存储进行自由读写。同时AnalyticDB支持冷热数据分层功能,可以根据业务特征将数据定义为热数据、温数据和冷数据。热数据存储在ESSD介质上,查询性能好;将冷数据存储在OSS等介质上,查询性能稍弱,但存储成本降低;支持按照二级分区将同一张表的近期数据定义为热数据、远期数据定义为冷数据,热数据按时间周期滚动变为冷数据,既保证热点数据的查询性能,又能降低存储成本。冷热数据存储功能让存储总成本降低60%以上。AnalyticDB弹性能力AnalyticDB采用云原生的技术架构,天然具备原生的弹性能力。AnalyticDB支持集群资源横向水平扩展,在不中断业务的情况下随时动态扩容,降低运维成本。在蜂创的业务场景中,存在明显的波峰波谷特征,在上班高峰期早上9点至11点查询请求量是其他时段的数倍。过去为了应对查询高峰,需要最大化预留计算资源,但在业务低峰期,这些资源实际上是浪费的。AnalyticDB的分时弹性能力完美地解决了这个问题,可以按小时制定分析弹性计算资源计划,每天早上8点半准时弹出扩展计算资源,11点自动释放,平滑地解决了业务高峰资源瓶颈问题。分时弹性计算资源按小时计费,只需承担2小时的资源成本,比过去24小时持有计算资源的模式总体成本降低30%。未来展望大数据与数据库技术的融合是大势所趋,“湖仓一体”正成为企业数据仓库架构设计新方向。AnalyticDB 具备海量数据的存储和计算能力、支持开放的数据格式,同时具备数据库的数据实时写入更新、数据一致性,兼容标准SQL语法,与常用BI产品无缝对接。既具备大数据平台的能力又具备数据库的体验,是企业级数据仓库产品的最佳选择。
作者:徒南优化器技术被公认为数据库领域中最有挑战性的技术之一,同时也是对数据库性能影响最大的一个模块。优化器直接影响SQL具体如何运行的执行计划,好的执行计划可以在毫秒内完成计算,而坏的执行计划则可能是分钟级或小时级别,两者性能可以相差成千上百倍。这篇文章将会为大家介绍PolarDB-X优化器的技术选型理由、技术架构与核心特性,帮助大家更深入地了解PolarDB-X优化器。从技术历史发展的角度看,优化器技术演进经历了大致四个阶段:以INGRES、早期Oracle为代表的启发式优化技术,主要优化手段有过滤条件下推,列裁剪,之后再做基于Cardinality的Join顺序调整。纯启发式优点就是简单,缺点是当查询稍微复杂就会导致找不到好的执行计划,并且会依赖一些很trick的魔数来调优。以System-R、早期IBM DB2、以及绝大多数开源数据库(MySQL, PostgreSQL)为代表的启发式+基于代价的Join Reorder优化技术。优点相比于纯启发式优化,优化器针对(复杂查询)Join顺序,会基于代价利用自底向上的动态规划选择最优的Join顺序。缺点是搜索空间比较受限不一定能找到最优的执行计划,代价模型中需要显式考虑像排序的物理属性。以IBM STARBURST、DB2、Oracle为代表的基于转换规则及代价的优化器技术。相比于阶段2,除了考虑代价,这类优化器已经抽象出作用于关系代数算子的转换规则(可以通过DSL编写)。优点是从工程角度上更易理解,维护,测试覆盖。缺点是优化规则会存在复杂的依赖关系,应用顺序需要人为指定。以 Volcano/Cascades、SQL Server为代表的基于规则转换及代价的自顶向下动态规划优化技术(Volcano/Cascades模型)。相比于阶段3,它将优化的搜索过程做成了统一的框架,添加新的优化只需要关注优化规则本身,不需要关注规则的应用顺序,具有很高的扩展性。同时自顶向下的搜索具有一个很吸引人的特性就是搜索空间的剪枝,剪枝可以保证执行计划最优性的情况下减少搜索空间的搜索,提升优化效率。另外优化器作为一个数据库领域中很复杂的模块,任何的改动都可能涉及大量SQL的性能变化,从软件工程的角度,借助Volcano/Cascades模型的模块化及扩展性,优化器的性能可以被持续地调优和改进。正是基于上面的考虑,PolarDB-X优化器被设计成一款以Volcano/Cascades模型作为框架的基于代价的优化器,它可以为每一条SQL构造出搜索空间,并根据数据的统计信息,基数估计,算子代价模型为搜索空间中的执行机计划估算出执行所需要的代价(CPU/MEM/IO/NET),最终选出代价最小的执行计划作为SQL的具体执行方式。我们知道PolarDB-X作为一款云原生分布式数据库,具有在线事务及分析的处理能力(HTAP)、计算存储分离、全局二级索引等重要特性,PolarDB-X优化器在这些特性中扮演了非常核心的角色。优化器架构优化器接受到SQL后会将它解析、转换成由关系代数算子组成的逻辑执行计划。整个PolarDB-X优化器中的具体优化手段都通过转换规则(Transformation Rule)来表达,转换规则会匹配特定结构的关系代数算子并将其转成等价的算子。PolarDB-X的优化器的优化阶段主要分为三个:Query Rewriter,基于RBO的启发式优化阶段,处理传统的逻辑优化如:子查询去关联化,列裁剪,谓词推导与下推,启发式Join Reorder(超多张表)等。这个优化阶段的特性是不断启发式地对同一个执行计划做逻辑优化,优化通常很快,且只做收益非常明确的优化器。Plan Enumerator,基于CBO的Volcano/Cascades模型,是最为核心优化阶段,它会应用转换规则为计划生成搜索空间,既包含了逻辑优化如:Join Reorder(Bushy,Zig-Zag,Left Deep空间动态选择),算子交换,计算下推等,也包含物理优化如:全局二级索引选择,物理算法选择等。搜索空间被完全展开并搜索过后,每个物理执行计划都会根据具体的物理算子估算出执行所需要的代价(通过CPU/MEM/IO/NET表示)。最后代价最低的物理执行计划将会被选择出来。在整个优化过程中,这一步耗时占比最高,因为它需要考虑整个搜索空间。MPP Planner,多机并行计算优化阶段,处理并行算子生成,算子间Shuffle的选择与消除,RunTimeFilter的生成及下推等。这一优化阶段专门用于优化OLAP的查询,保证可以充分利用多个节点的计算资源。此外还有统计信息、代价模型、基数估计(Cardinality Estimation)等重要模块,好的优化效果依赖于准确的数据统计信息,PolarDB-X维护了丰富的统计信息用于辅助优化器,我们会为每张表维护行数,直方图,列长度,NDV值等统计信息。PolarDB-X的代价模型充分考虑了计算存储分离架构下的算子执行代价,与传统数据库相比会更精细地考虑网络的代价。核心特性HTAP在HTAP混合负载处理方面,PolarDB-X提供智能路由的能力。我们知道传统的OLTP和OLAP的解决方案是基于简单的读写分离或者ETL模型,它们存在存储成本高、实时性差、链路和维护成本高等劣势。通过PolarDB-X可以统一处理HTAP负载,保证TP事务低延迟,同时保证AP分析查询充分利用计算资源,且保证数据的强一致。优化器在HTAP的负载识别中起了关键的作用。优化器会基于代价分析出查询的CPU,内存,IO,网络等核心资源消耗量,将请求区分为OLTP与OLAP请求。OLTP请求被路由至主副本执行, 相比于传统的读写分离方案能够提供更低的延迟。而分析出的OLAP请求将会通过MPP并行优化阶段,生成多机分布式的执行计划,下发至只读计算集群计算,访问只读副本,提供物理隔离,同时可以利用只读副本一致性读能力,保证强一致读。通过智能路由,用户可以非常透明地使用PolarDB-X同时处理OLTP及OLAP的诉求。计算下推PolarDB-X支持Partition Aware的计算下推。我们知道在计算与存储分离的架构下,我们获得了几乎无限弹性扩展计算节点的scale out能力,但代价是计算与存储间的网络交互开销。为了尽可能避免这一开销,可以通过计算下推,减少网络交互,计算离数据更近,计算效率获得提升,因此计算下推成了非常重要的优化手段。PolarDB-X用户大量使用拆分表(及广播表),将数据根据拆分方式打散至不同的分片上。PolarDB-X可以基于代价充分考虑存储(如存储的计算模型)及数据(是否具有索引)等特性,将查询中的部分计算(如:Join,Agg,Sort)下推至存储层进行计算。以TP场景下的Join的下推为例:如果Join不下推,我们会面临一个网络Lookup,通过网络Lookup的性能会劣于本地的磁盘Lookup,而通过计算下推我们就可以获得弹性扩容的同时,享受单机数据库的性能体验。全局二级索引选择PolarDB-X为用户提供全局二级索引,并提供数据强一致性。在建立全局二级索引后,优化器可以立马感知到表存在全局二级索引,并为查询优化出更优的查询计划。以订单为例子,用户将订单表按照买家维度作拆分,以买家维度作为查询可以获得非常好的性能。但按卖家维度进行查询时,需要将所有数据分片查询一遍才能得到完整结果。而通过为订单表建立卖家维度的全局二级索引,优化器就可以优化出访问全局二级索引(回表)的执行计划,避免全分片扫描,提升性能。另外,全局二级索引支持覆盖索引,优化器结合列裁剪优化,当发现用户查询表的列被全局二级索引覆盖时,可以做到只访问全局二级索引,避免回表。全局二级索引还可以和Partition Aware的计算下推做共同优化,例如:两张表的Join的场景,两表的拆分方式不一致导致Join无法下推的时候,我们可以将表按照Join Key共同的维度建立全局二级索引,达到计算下推的目的。优化例子下面让我们一起通过一个TPCH Q3作为例子看看PolarDB-X如何优化一条SQL吧。首先TPCH Q3这条SQL会被转化为如下的逻辑执行计划(LogicalPlan)这个逻辑执行计划包含Agg、Join、Filter、Project、LogicalView等关系代数算子,PolarDB-X通过LogicalView算子来抽象下发至存储执行的Plan(所以下推算子在优化中就是将算子下推进LogicalVIew算子)。接着我们一步一步看一下RBO,CBO,MPP三个优化阶段分别做了哪些优化。RBO启发式阶段,可以看到经过RBO后的逻辑执行计划已经将Filter,Project等算子下推到了LogicalVIew,也就是完成了列裁剪,谓词下推等优化。同时我们可以看到原来Orders,Lineitem表也因为拥有相同拆分规则被下推到了同一个LogicalVIew,完成了计算的下推优化。CBO基于代价的优化阶段,可以看到逻辑执行计划变成了物理执行计划,其中Join和Agg分表选择使用HashJoin和HashAgg作为物理算子。在这背后,优化器实际上已经考虑了不同的Join顺序,不同的Join与Agg算法,被选择出来的就是具有最低代价的物理执行计划。MPP分布式优化阶段,可以看到在算子间多了Exchange算子,用于描述数据如何在多个计算节点间进行Shuffle。这使得执行器可以知道如何在多个计算节点中传输数据,并行计算。此外,还可以看到RunTimeFilter生成,并下推到存储节点执行提前过滤数据,减少网络传输开销。总结PolarDB-X 查询优化器主要基于 Volcano/Cascades 模型设计,是一个基于代价的优化器(CBO),优化过程主要分为查询改写、计划枚举、MPP 优化三个阶段。其中,查询改写阶段负责启发式优化规则(例如查询下推);计划枚举阶段负责索引选择和决定 Join Order 等,是最核心的阶段;如果优化器判断执行计划代价较高、需要 MPP 执行,则会通过 MPP 优化阶段将执行计划进一步优化为分布式执行计划。通过这样的设计,让 PolarDB-X 能很好的适应 HTAP 场景,兼顾 TP 与 AP 两种不同模式的流量。【相关阅读】如宝马3系和5系:PolarDB-X 与 DRDS 并驾齐驱PolarDB-X 存储架构之“基于Paxos的最佳生产实践”PolarDB-X 私有协议:提升集群的性能和稳定性技术解读 | PolarDB-X 分布式事务的实现技术解读 | PolarDB-X 强一致分布式事务PolarDB-X 一致性共识协议 (X-Paxos)
作者:梦实PolarDB-X 2.0(以下简称PolarDB-X)与DRDS(DRDS也称为PolarDB-X 1.0)都是阿里云上的分布式数据库产品。看起来她们都是Share-Nothing的架构,用水平扩展来解决单机数据库瓶颈问题。很多同学因此会有疑惑,她们俩到底有什么样的区别?DRDS,其本质是搭建在标准MySQL(阿里云上的RDS For MySQL)上的分库分表中间件,具有很高的灵活性。PolarDB-X是使用云原生技术的分布式数据库,具有一体化的数据库体验,其存储节点是经过了高度定制的MySQL,从而提供了大量中间件无法提供的能力(使用全局MVCC的强一致的分布式事务、私有RPC协议带来的性能提升、Follower上的一致性读能力等等)。本文带大家从各个角度剖析下,PolarDB-X与DRDS的异同。首先简单说下她们相似的地方:她们都能基于Share-Nothing的架构,具备极强的水平扩展能力她们都基于MySQL的生态体系,具有很高的MySQL兼容性她们使用同样的SQL引擎,具备相似的SQL执行能力她们均提供分布式事务、全局索引等常见中间件不具备的高阶能力她们都在阿里巴巴内部广泛使用,历经多年双十一的考验,稳定可靠接下来我们重点看下她们有哪些区别。使用体验抛开技术原理,我们先看下最直观的产品体验上有哪些异同。购买实例由于DRDS是一个中间件,所以其和MySQL的接线划分得比较清晰,DRDS本身不包含MySQL(RDS)资源,MySQL由用户单独购买。你需要在两个产品的控制台上单独进行购买,并在DRDS控制台上将其组装在一起。PolarDB-X提供的是一个整体的数据库服务,你只需要创建一个PolarDB-X实例即可,其中包含了所需要的计算资源、存储资源。建库DRDS中,建库需要在控制台完成,并且在建库过程中需要选择已有或者购买新的MySQL资源:PolarDB-X中,你可以像使用单机数据库一样,使用你习惯的工具进行连接,然后使用CREATE DATABASE指令创建数据库:扩容DRDS中,你需要评估每个MySQL的容量,并选择将哪些分库挪到新的MySQL存储上。PolarDB-X中,你只需要选择节点数,数据将自动均衡地分布在各个存储节点上。数据同步如果你要将DRDS中的数据同步到下游,很多时候你需要使用DTS来订阅其中的每一个MySQL实例,并仔细处理同一个逻辑表的不同分表之间例如表名的差异等细节,并且DDL操作会让这个同步链路中断。PolarDB-X提供一个统一的binlog服务,你可以使用DTS像订阅一个单机MySQL一样来订阅它。这个binlog服务完全兼容MySQL,其屏蔽了所有的分布式的细节,让下游服务认为它是一个普通的单机MySQL(例如PolarDB-X支持包括SHOW BINLOG EVENTS在内的所有BINLOG相关的指令)。读写分离DRDS中,你可以使用只读实例(备库)来进行一些高消耗的SQL,避免对在线业务产生影响。但是,你需要手动来判断这些SQL的类型,并通过HINT、不同的连接串等方式,将其放到正确的地方来执行。同时,你需要注意备库上存在延迟,你需要改造你的业务系统,使其能够容忍这种延迟。PolarDB-X中,应用使用一个连接串即可,你无需关注这些SQL的类型和代价(用不着给它们加HINT),它的优化器会自动识别这些SQL的代价,并且使用正确的资源池来执行它们,尽最大可能避免AP的SQL影响到TP的SQL。同时,PolarDB-X的存储节点,支持Follower上的一致性读,因此你不需要担心在备库上读取数据会读到老的数据,任何时候去读,都能读到最新的数据。运维由于DRDS允许使用你自己购买的MySQL实例进行组件,因此你拥有这些MySQL实例完整的运维权限,你可以对它们做任何你想做的事情,例如:* 负载不均衡时,单独对其中一个节点进行规格的升级* 将其中的某个存储节点给其他的业务使用* 使用任意版本的RDS(5.6、5.7、8.0均可)* 订阅任意一个RDS的binlog但是,这种灵活性也存在一定的风险,例如,我们没有办法阻止你直接删除其中的一个分库,这会导致DRDS无法正常访问这个分库上的数据。PolarDB-X对用户屏蔽了存储节点,你不能、也不需要直接访问其存储节点,它将一个数据库的整体视角呈现给用户,它通过自动的负载均衡、逻辑binlog、混合负载的HTAP等能力来减少你对存储节点直接访问的需求。目前PolarDB-X DN主要基于的的MySQL版本为5.7,后续8.0的支持也已经在规划中。架构差异以上的差异,很多由其架构决定,我们看下PolarDB-X与DRDS在架构上的差异点。这是DRDS的架构图:DRDS的架构中,大量功能依赖外围管控系统完成,例如:扩容,使用内部一个叫精卫的组件来进行。元数据,一个地域内会共享一个叫Diamond的存储主备探活、切换,依赖一个叫ADHA的组件等等这是PolarDB-X的架构图:PolarDB-X中,核心功能全部内聚到内核。PolarDB-X使用X-DB作为其DN(数据节点)。X-DB使用Paxos(PolarDB-X 一致性共识协议 —— X-Paxos)做到了RPO=0。PolarDB-X相比DRDS,引入了一个新的组件:GMS(Global Meta Service),他具备非常重要的作用:提供分布式事务所使用的全局自增的时间戳根据负载情况,调度数据的分布,使节点之间达到均衡提供统一的元数据,例如INFORMATION_SCHEMA对CN与DN进行管理,例如切换、上下线等DRDS的扩容基于binlog,依赖外围管控系统完成。PolarDB-X的扩容基于分布式事务,由内核完成。架构继续往下细化,我们可以看一下其数据的分布情况:DRDS下的RDS是传统的主备(或者三节点)架构,主备以实例级为单位,正常情况下备库不提供服务:PolarDB-X下的DN,均为三节点架构,Paxos组以分片为单位,一个节点可以同时是一个分片的Leader与另一个分片的Follower,资源利用率更高:事务模型事务的实现机制,是一个数据库最根本的特征,PolarDB-X与DRDS上的事务机制,有着非常巨大的差异。DRDS使用的是MySQL官方提供的XA事务。XA事务可以保证写入操作的原子性。但是,标准的XA存在一个问题是,可能会在一个分片读到已提交的数据,再另一个分片读到未提交的数据。例如,有两张空表t1(pk,name,addr) dbpartition by hash(pk),t2(pk,name,addr) dbpartition by hash(name)。假如应用在事务1中对两张表分别进行一个插入操作insert into t1 values (1,'sun','hz'),insert into t2 values (1,'sun','hz'):begin; insert into t1 (pk,name,addr) values (1,'sun','hz'); insert into t2 (pk,name,addr) values (1,'sun','hz'); prepare p1; prepare p2; commit p1; commit p2;注:*左右滑动阅览同时,有另一个只读事务,分别对t1与t2进行count操作,它们就可能读到不一样的结果。如下的时间线:其中t1时刻,在一个事务内对t1和t2表进行查询,会得到不一样的记录数,这是一个不一致的结果。DRDS中,为了解决这个问题,使用的是加锁的实现,在冲突多的情况下,有比较高的代价。PolarDB-X使用自研的全局MVCC事务,在两阶段提交(2PC)的基础上,增加了事务快照时间戳(snapshot_ts)和提交时间戳(commit_ts)的支持。时间戳来自于全局 TSO 的分配,因此能做到外部一致的事务保证,并且避免了额外的加锁。在上述例子中,t1的时间由于比commit的时间晚,因此一定能读到两张表都是1的结果。PolarDB-X的事务机制相对比较复杂,请大家参考:PolarDB-X 强一致分布式事务原理。性能提升PolarDB-X的性能相对DRDS有很大的提升,主要体现在几个方面。精简的网络结构DRDS连接RDS,使用的是RDS标准的访问链路,中间需要经过SLB的中转,会增加一跳的网络延迟:PolarDB-X的CN节点与DN节点均在一个物理网络中,中间是点对点的直连,不经过任何SLB/LVS等中转,具有最低的网络延迟,下图是一个CN到DN的网络拓扑示意:私有RPC协议DRDS使用标准的MySQL协议连接RDS,发送标准的SQL语句。但这里会有不少的开销,例如:SQL经过DRDS优化器的优化后,还需被MySQL的优化器再次优化,如果涉及到多个MySQL分片,重复的次数会更多。MySQL协议中有很多的冗余元素,例如结果集的头,里面存储了结果集每一行的名字、类型等信息,这些是不需要的。MySQL协议返回的数据与DRDS内部计算使用的数据并不是一个格式,这中间需要经过再次转换。DRDS使用连接池来连接MySQL,MySQL的连接与线程是绑定关系,同一个连接上同一时间只能执行一个SQL。这导致DRDS与RDS之间需要维持大量的连接。PolarDB-X为了解决DRDS存在的这些问题,对MySQL进入了大量定制,中间的通信才用了私有的RPC协议,与MySQL协议相比有以下几个优势:传递的不再是SQL,而是执行计划,避免MySQL重复对SQL进行解析、优化的代价。使用异步模型,连接与线程、连接与会话不在是一一绑定的关系,使用比较少的连接即可满足需求。精简了通信中不需要的信息,例如结果集的头等。传输的数据格式与CN计算使用的格式完全一致,避免数据的二次转换。通过使用私有的RPC协议,PolarDB-X相对于DRDS,在很多场景下得到了性能提升。sysbench-select1.6亿行数据300并发计算节点和存储节点规格均为16c64g+39%节点CPUQPSRT-AVGRT-MAXRT-95%PolarDB-X710.6%97067.203.09ms108.12ms6.53msDRDS1289%69787.344.30ms110.30ms10.67mssysbench-oltp1.6亿行数据150并发计算节点和存储节点规格均为16c64g+14.4%节点CPUQPSRT-AVGRT-MAXRT-95%PolarDB-X1139%22587.23119.52ms757.47ms471.02msDRDS1236%19732.12136.82ms798.47ms415.74msMPP引擎对分析类查询的加速DRDS中使用的是SMP(单机并行)技术,PolarDB-X中使用的是MPP(多机并行)技术。这使得PolarDB-X相对于DRDS,在面对复杂分析查询时,可以使用更多的资源来加速。这个性能差异体现在TPC-H上非常显著。下面是同资源情况下,DRDS与PolarDB-X在TPC-H上的对比:DRDS 总耗时386s,PolarDB-X总耗时274s。小结PolarDB-X与DRDS的差异是方方面面的,DRDS是分库分表中间件的代表,PolarDB-X是云原生分布式数据库。有一个形象的比喻,DRDS和PolarDB-X的关系相当于宝马3系和5系,她们将长期共存,为不同需求的用户提供服务。【相关阅读】PolarDB-X 存储架构之“基于Paxos的最佳生产实践”PolarDB-X 私有协议:提升集群的性能和稳定性技术解读 | PolarDB-X 分布式事务的实现技术解读 | PolarDB-X 强一致分布式事务PolarDB-X 一致性共识协议 (X-Paxos)
作者:七锋背景MySQL数据库从诞生以来就以其简单开放、易用、开源为其主打的特点,成为不少开发者首选的数据库系统。阿里在09年开始提出去IOE的口号,也选择基于开源MySQL进行深度发展,结合TDDL的技术完成了去IOE的工作,这也是早期的PolarDB-X发展的技术栈。2014年开始,随着业务高速的增长,以及“异地多活”的新需求驱动,基于MySQL的一致性协议技术X-Paxos在阿里集团得到了全面的发展和验证,可参见:PolarDB-X 一致性共识协议 (X-Paxos)。从19年开始,PolarDB-X 2.0结合了分布式SQL引擎和基于X-Paxos的数据库存储技术,基于阿里集团多年双十一的技术积累和稳定性验证,以云的方式提供云原生分布式数据库产品,为传统企业、金融业务数字化转型和去IOE过程提供更好的技术产品和服务。架构设计上图展示的是一个部署三个节点的存储集群,设计上引入了多分组X-Paxos技术替换传统的复制协议,上图中Paxos Group 0~N代表不同的X-Paxos分组,基于多分组技术可支持多点写入+多点可读的能力,同时分布式下多个分片可以属于不同的Paxos分组。多分组Paxos我们在设计上,在同一个物理节点中允许管理多个X-Paxos实例。每个节点上基于分布式的数据分区(Partition),将不同分区的数据绑定到某一个X-Paxos实例,通过将每个X-Paxos实例的Leader分散到多个物理节点中,从而提供不同分区数据的多点可写能力,解决了传统MySQL数据库下的单点写瓶颈的问题。多分组Paxos相比于单分组的Paxos,并不是简单的启动多份X-Paxos实例,而是需要做一定的合并优化,来降低多分组Paxos所带来的资源开销,主要包含三个模块的优化:消息服务、分区管理、协调者。1. 消息服务:多分组引出两个风险,连接风暴和消息风暴。连接风暴是指,假设沿用原有单分组X-Paxos架构的设计,如果一个集群有3个节点,每个节点有3个分组,那么单节点需要监听3个不同的端口,并维持6个独立的连接。多分组X-Paxos消息服务提供一套共享的网络层,多个分组之间复用同样的网络连接。消息风暴是指,假设同步的总量不变,数据由多节点分散写入会让单个网络包变小,同时每个分组的Leader还会定期维持租约心跳,当分组数变多之后,网络会充斥大量小包导致收发包质量降低。因此共享的消息服务需要提供日志聚合和心跳聚合的能力。此外,我们还可以通过共享Timer模块让同一节点上多个分组的Leader共享任期,减少Leader租约管理的成本。日志聚合、心跳聚合、统一Leader租约管理2. 分区管理:分区管理模块维护X-Paxos分组和数据分片的映射关系。多分组X-Paos对接分布式下的数据分区,存储上收到计算层发送的DML操作后,以物理库或表做为分区键(Partition Key)传递到Consensus层。Consensus层接收字符串类型的分区键,转换成对应X-Paxos分组的Group ID。其中Hash Table模块提供快速的查询能力,Meta Store模块负责映射关系的持久化。当映射关系出现改变时,Hash Table会把最新的变更同步给Meta Store。Meta Store提供统一的接口,数据持久化是借助InnoDB引擎的MySQL系统表来保证数据修改的原子性和持久性。如上图所示,Consensus层保留了独立性和通用性,不依赖分布式下的分区逻辑,管理分区键和Group ID的关系,并驱动元数据表的同步更新。3. 协调者:当集群需要负载均衡时,我们可能会增加新的X-Paxos分组,还会把一部分数据分片从某个分组切换到新的分组中。这些分区管理模块的修改行为称为分组变更(Group Change)。需要考虑以下几个问题:如何确保同一数据分片不会同时属于两个分组,即跨Group信息的分布式一致?如何让集群中多个实例的分区元数据一致,防止双写造成数据冲突?如何保证在做分组变更的过程中,任意分组内部的节点变更(Membership Change,如切主、加减节点)不会影响分组变更的正确性?这类问题一般有两种解决方案,集中式和分布式。前者是引入一个外部的配置中心(如Placement Driver/PD),通过集中化的单点管理规避掉分布式一致性的问题。该方案的缺点是有单点故障的风险,因此一般情况下,PD也会部署成主备甚至三节点的形式,借助冗余来提高可用性,这又进一步增加了系统交付的成本。在PolarDB-X中,我们采用的是分布式的方案,集群中每一个节点既是参与者,也可以是协调者。通过两阶段日志同步,解决跨X-Paxos分组分布式一致性的问题。此外,Paxos协议保证了参与者和协调者的高可用,传统2PC协议中协调者或参与者宕机引出的一系列问题都自然而然地规避掉了。事务提交和复制基于MySQL发展的存储节点DN,其复制流程是基于X-Paxos驱动Consensus日志进行复制。Leader节点在事务prepare阶段会将事务产生的日志收集起来,传递给X-Paxos协议层后进入等待。X-Paxos协议层会将Consensus日志高效地转发给集群内其他节点。当日志在超过集群半数实例上落盘后 X-Paxos会通知事务可以进入提交步骤。否则如果期间发生Leader变更,期间prepare的事务会根据Paxos日志的状态进行相应的回滚操作。Follower节点也使用X-Paxos进行日志的管理操作,为提升接收效率,收到Leader传递过来的日志以后将日志内容Append到Consensus Log末尾,Leader会异步地将达成多数派的日志的消息发送给Follower,Follower的协调者线程会负责读取达成多数派的日志并加以解析,并传递给各个回放工作线程进行并发的数据更新。Follower的并发回放可以有多种方式,包括按照Leader上的Group Commit维度或者是按照表级别的维度,未来会引入最新的writeset方式来精确控制最大并发。相比于传统的MySQL基于binlog的semi-sync复制模式,我们引入X-Paxos做了比较多的优化。1.异步化事务提交。传统的MySQL都是 One Thread per Connection的工作模式, 在引入线程池后是以一个线程池孵化一定数量的工作线程, 每个线程负责处理一次query的解析、优化、修改数据、提交、回网络包等等。集群需要跨地域部署下,一次事务的提交由于需要在集群之间同步事务日志,受限于网络的RTT的限制,会达到数十毫秒的级别,那么对于一个普通的写事务来说,大量的时间会耗费在同步节点日志等待事务提交的过程。在大压力下,很快数据库内部的工作线程会耗尽, 吞吐达到瓶颈。如果一味的放大数据库内部的工作线程数目,那么线程上下文的代价会大幅增加。如果将整个事务的提交异步化,将工作线程从等待X-Paxos日志同步中解放出来,去处理新的连接请求,在大负载下可以拥有更高的处理能力。异步化提交核心思想是将每个事务的请求分成两个阶段,提交之前一个阶段,提交和回包一个阶段。两个阶段都可以由不同的工作线程来完成。为了完成异步化的改造,我们增加了等待同步队列和等待提交队列,用于存储处于不同阶段的事务。前者队列中的事务是等待Paxos多数派日志同步的事务,后者是等待提交的事务。异步化流程:a. 当CN节点发起Commit/Rollback/Prepare时, 处理客户端连接的线程池Worker产生事务日志并将事务上下文存储到等待同步的队列中。b. 等待同步队列的消费由少量数目的worker线程来完成,其余工作线程可以直接参与其他任务的处理。事务等待多数派完成后会被推入等待提交队列。c. 等待提交队列里的事务都是可以被立即提交的,所有的worker线程发现该队列里有事务,就可以顺道取出来执行提交操作。这样一来,系统中只有少数的线程在等待日志同步操作, 其余的线程可以充分利用CPU处理客户端的请求,异步化提交结合MySQL的Group Commit逻辑,将原有需要等待的操作全部异步化,让Worker线程可以去执行新的请求响应。在测试中,异步化改造在同城部署的场景中相比同步提交有10%的吞吐率提升,跨区域的部署后有500%的吞吐提升。2.热点更新优化。热点更新原本就是数据库的一个难题,受制于行锁竞争,性能吞吐一直很难提升上去。X-Paxos下面对跨域场景下的长传网络更加是雪上加霜,提交的时间变长,事务占据行锁的时间也显著增加。为了解决此问题,PolarDB-X在AliSQL的热点功能之上优化了复制,使得在保证数据强一致的情况下,热点更新性能提升非常明显。如上图所示,针对热点行更新的基本思路是合并多个事务对同一行的更新。为了让批量的更新事务能够同时进行提交,PolarDB-X在存储引擎中增加了一种新的行锁类型——热点行锁。热点行锁下,热点更新的事务之间是相容的,为了保证数据的一致性,对同一批的热点更新事务日志打上特殊tag, X-Paxos会根据tag将这一整批事务的日志组成一个单独的网络包进行集群间的数据同步,保证这些事务是原子的提交/回滚。除此之外为了提升日志回放的效率,PolarDB-X将每个批次事务中对于热点行的更新日志也做了合并。多副本配置和部署PolarDB-X设计目标是支持跨地域部署,在多地域保证集群数据强一致,即使某个城市的机房全部宕机,只要保证集群多数派节点存活,所有的数据都不会丢失。我们在部署方式设计上也比较灵活,支持容灾成本和多样化的部署需求。选主优先级。应用往往对于容灾后新主节点是有要求的,比如机房流量均衡、以及应用和地域之间的关联性(比如新疆业务的应用,期望对应的分区数据在新疆机房)。针对这样的需求,PolarDB-X可以根据不同的分区优先级,在分区级别X-Paxos级别设置不同的优先级,在原有Leader节点故障时,选举Leader的顺序会按照集群存活节点的权重顺序进行,同时在运行过程中如果发现有权重更高的节点,会主动发起一次Leader Transfer将Leader角色过继过去。策略化多数派。一致性复制可分为两档,强复制和弱复制。可以搭配选主优先级,比如设置同城机房的节点为强同步复制,我们可以配置在规定日志复制到多数节点的基础上必须还要复制到了所有强复制的节点才可以推进状态机并返回客户端事务提交成功的响应。这是一个类Max protection模式的设计,如果检测到强一致节点宕机,可自动降级。日志型副本。默认三副本的机制,相比于传统的主备模式,会在存储成本上会多一份数据。PolarDB-X在副本设计上分为普通型(Normal)和日志型(Log)两类。日志型节点可以和普通型节点组成Paxos的一致性投票,不过它只有投票权,没有被选举权。通过日志型副本只记录Paxos日志,在满足RPO=0的前提下,也可以和普通的主备模式的存储成本对齐。Follower Read。针对只读Leaner节点在跨机房部署下,如果所有Leaner节点的日志都从Leader节点复制会对Leader节点造成过大的网络和IO瓶颈,PolarDB-X也支持Leaner挂载到Follower节点获取一致性数据。多副本一致性读。在分布式环境下,基于Paxos的副本相比于主副本会有一定的Apply延迟,如果要实现多副本的线性一致性读时,需要有一定的保证机制。PolarDB-X里会有两种一致性读的方案。第一种是基于Paxos的LogIndex来,首先在Leader节点上获取一下index,然后观察follower/leaner副本的LogIndex是否已经超过,如果超过说明所需要的数据已经在了,可以直接读取。第二种是基于TSO全局版本号,查询的时候获取一个全局版本,指定版本在follower/leaner副本中进行读取,在存储上我们支持版本的阻塞读能力,用户可以设置等待时间直到对应版本数据同步到该副本上。这两种方案,前者只满足RC级别的当前读,后者可满足RR级别的历史读,结合PolarDB-X的HTAP架构,可以分流部分AP查询到只读副本上,并满足事务RC/RR的隔离级别。推荐的两种部署:高可用检测和恢复PolarDB-X 引入Paxos的一致性协议,基于Paxos算法已有的租约自动选举的策略,可以避免“双主”的问题出现。如果当前Leader被网络隔离,其他节点在租约到期之后,会自动重新发起选主。而那个被隔离的Leader,发送心跳时会发现多数派节点不再响应,从而续租失败,解除Leader的状态。Follower约定在lease期间不发起新的选主,Leader先于Follower lease超时,从时序上最大程度上规避了“双主”问题的出现。除了常规的租约策略之外,考虑云环境的各种异常情况,我们还需要有更进一步的优化:1.状态机诊断。从数据一致性角度来说,选主流程结束后,新的Leader必须回放完所有的老日志才能接受新的数据写入。因此,成功选主并不等价于服务可用,实际的不可用时间是选主时间(10s)和日志回放追平时间之和。在遇到大并发和大事务场景下,Follower可能会产生比较大的回放延迟。假如此时恰好主库出现故障,新选主的节点由于回放延迟,服务不可用时间充满了不确定性。故障有可恢复和不可恢复之分,通过我们观察,除了那种机器宕机、磁盘坏块这类彻底恢复不了的场景,大部分故障都是短期的。比如网络抖动,一般情况下网络架构也是冗余设计的,可能过一小段时间链路就重新正常了。比如主库OOM、Crash等场景,mysqld_safe会迅速的重新拉起实例。恢复后的老主一定是没有延迟的,对于重启的场景来说,会有一个Crash Recovery的时间。这个时候,最小不可用时间变成了一个数学问题,到底是新主追回放延迟的速度快,还是老主恢复正常的速度快。因此,我们做了一个状态机诊断和主动切换的功能。在数据库内核中,通过状态机诊断接口,可以收集回放延迟、Crash Recovery、系统负载等状态。当状态机健康状况影响服务可用性时,会尝试找一个更合适的节点主动切换出去。主动切换功能和权重选主也是深度整合的,在挑选节点的时候,也会考虑选主权重的信息。最终,服务恢复可用后诊断逻辑会自动停止,避免在稳定Leader的情况下产生不必要的切换。2.磁盘探活。对于数据库这样有状态的服务来说,存储是影响可用性的重要因素之一。在本地盘部署模式下,数据库系统会遇到Disk Failure或者Data Corruption这样的问题。我们曾经遇到过这样的情况,磁盘故障导致IO卡住,Client完全无法写入新的数据。由于网络是连通状态,节点之前的选举租约可以正常维持,三节点自动容灾失效导致故障。有时候还会发生一些难以捉摸的事情,由于IO已经完全不正常了,进程在kernel态处于waiting on I/O的状态,无法正常kill,只有重启宿主机才能让节点间通信完全断掉触发选主。针对这类问题,我们实现了磁盘探活功能。对于本地盘,系统自动创建了一个iostate临时文件,定期向其中执行随机数据读写操作。对于云盘这类分布式存储,我们对接了底层的IO采样数据,定期来感知IO hang或者Slow IO的问题。探测失败次数达到某个阈值后,系统会第一时间断开协议层的网络监听端口,之后配合重启实例可以恢复。3.反向心跳。基于已有的策略,我们已经可以覆盖99%的常规可用性问题。在长时间的线上实践中,我们发现有些问题从节点内部视角发现不了,比如主库连接数被占满,open files limit配置不合理导致“Too many open files”报错,以及代码bug导致的各种问题......对于这些场景,从选举租约、状态机、磁盘探活的角度,都无法正确的检测故障,因此最好有一个能从App视角去建连接、执行业务查询和返回结果的全链路检测流程。因此催生了Follower反向心跳的需求,即Follower通过业务查询SQL接口去主动探测Leader的可用性。该设计有两个优势:首先是内核自封闭,借助三节点的其他非Leader节点,不依赖外部的HA agent进行选主判定,就不用再考虑HA agent本身的可用性问题;其次和内核逻辑深度整合,不破坏原有的选主逻辑。整个流程如图所示,假设Node 1是Leader,给其他两个Follower正常发送心跳,但是对外的App视角已经不可服务。当Node 2和Node 3通过反向心跳多次尝试发现Leader的SQL接口不可服务之后,这两个Follower不再承认Leader发来的Heartbeat续租消息。之后若Node 2的选举权重相对较高,他会首先超时,并用新的term发起requestVote由Node 3投票选成主,对外开始提供服务。这个时候Node 2作为新主,也会同时开始给Node 1和Node 3发续租心跳。Node 1在收到新主发来的心跳消息之后,发现term比自己当前term大,就会主动降级成Follower。整个三节点又能回到正常的状态。总结PolarDB-X融合了基于X-Paxos的数据库存储技术,通过经历阿里集团多年双十一的技术积累和稳定性验证,PolarDB-X在稳定性、易用性、高可用特性上都会有不错的表现。未来,我们也会在Paxos副本在多节点混部和迁移、跨地域容灾的Paxos Quorum自动降级、Geo-Partition特性、以及分布式热点分区优化上做更多的探索和尝试,给用户提供更好的分布式数据库体验。【相关阅读】PolarDB-X 私有协议:提升集群的性能和稳定性技术解读 | PolarDB-X 分布式事务的实现技术解读 | PolarDB-X 强一致分布式事务PolarDB-X 一致性共识协议 (X-Paxos)
作者:王伟(盏一)1. ADB PG 简介AnalyticDB for PostgreSQL是阿里云上的MPP数据仓库服务,其内核采用PostgreSQL引擎,支持标准SQL 2003,兼容PostgreSQL/Greenplum,高度兼容 Oracle 语法生态;具有存储计算分离,在线弹性平滑扩容的特点;既支持任意维度在线分析探索,也支持高性能离线数据处理;是面向互联网、金融、证券、保险、银行、数字政务、新零售等行业有竞争力的数据仓库方案。AnalyticDB for PostgreSQL采用MPP架构,实例由多个计算节点组成,存储容量随节点数线性扩展,且保持查询响应时间不变。ADB PG 的 CBO 优化器基于表的统计信息,为查询选择最佳的查询计划。本次发布的 Auto Analyze 功能解决了在 ADB PG 实例使用过程中,由于未能及时执行 ANALYZE 收集统计信息导致了 CBO 优化器生成计划退化进而导致业务分析变慢的问题。2. Analyze 重要性 当前 ADB PG 基于代价的优化器(以下简称 CBO),依赖于我们评估一个代价值用于衡量每种候选计划的代价,而代价的评估又依赖于收集的统计信息。在我们看来,CBO 和统计信息之间的关系犹如一把枪和弹药之间的关系,枪再好如果没有充足的弹药的话,那么无异于巧妇难为无米之炊。统计信息的收集就是为了给 CBO 提供足够多合理的信息,让 CBO 能够基于这些统计信息做出合理的决策。举个简单例子,假设我们有表 t 以及 idx_t_z 如下所示:create table t(i int , j int, z int); create index idx_t_z on t(z); insert into t select i, i, i from generate_series(1, 2) i; -- 1 insert into t select i, i, i from generate_series(1, 3333333) i; -- 2 insert into t select i, i, 20181218 from generate_series(1, 10) i;注:*左右滑动阅览这里第1处 insert 会触发 ADB PG AutoStats 机制,此时会对表 t 进行一次 ANALYZE,并收集相关统计信息。接着我们使用 EXPLAN ANALYZE 来执行一条简单查询并输出查询的执行计划:tmp=# explain analyze select * from t where z = 20181218; Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..2.02 rows=1 width=12) (actual time=287.952..743.833 rows=10 loops=1) -> Seq Scan on t (cost=0.00..2.02 rows=1 width=12) (actual ti me=287.428..287.430 rows=5 loops=1) Filter: (z = 20181218) Planning time: 1.242 ms (slice0) Executor memory: 59K bytes. (slice1) Executor memory: 42K bytes avg x 3 workers, 42K byt es max (seg0). Memory used: 128000kB Optimizer: Postgres query optimizer Execution time: 744.675 ms注:*左右滑动阅览可以看到,由于在表 t 创建之后,仅在第一次 insert 时触发了 ANALYZE,在数据更新后未能及时更新统计信息,使得优化器在优化时看到的统计信息中记录着表 t 总行数为 2,使得 CBO 优化器这时错认为 SeqScan 比 IndexScan 效率更高。但如果我们这里手动执行下 ANALYZE:tmp=# ANALYZE t; ANALYZE tmp=# explain analyze select * from t where z = 20181218; Gather Motion 3:1 (slice1; segments: 3) (cost=0.18..8.20 rows=1 width=12) (actual time=0.429..0.439 rows=10 loops=1) -> Index Scan using idx_t_z on t (cost=0.18..8.20 rows=1 widt h=12) (actual time=0.014..0.016 rows=5 loops=1) Index Cond: (z = 20181218) Planning time: 1.305 ms (slice0) Executor memory: 92K bytes. (slice1) Executor memory: 60K bytes avg x 3 workers, 60K byt es max (seg0). Memory used: 128000kB Optimizer: Postgres query optimizer Execution time: 1.322 ms注:*左右滑动阅览可以看到由于 CBO 使用了更精确的统计信息,所以其也生成更优的执行计划,使得查询执行时间从 700ms 降低到 1ms。更精准的统计信息除了能帮助优化器生成更高效的执行计划之外;也能够使得 ADB PG 近期发布的多维排序得到更好的排序效果,排序效果越好,对查询就有越明显的加速。3. AutoStats 介绍 如上所示,正因为 ANALYZE 重要性,为了提升用户体验,ADB PG 引入了 AutoStats 机制。AutoStats 机制有如下三种工作模式,受配置 gp_autostats_mode 控制。ON_NO_STATS。此时意味着在用户执行了 Insert/Update/Delete 等 DML 操作之后,ADB PG 会查询 DML 目标表在 DML 之前的状态,若目标表在 DML 之前是空表,那么在 DML 之后,此时 ADB PG 会在同一事务内对目标表触发一次 ANALYZE 操作。这也是当前 ADBPG 线上默认配置。ON_CHANGE。此时意味着在用户执行了 DML 操作之后,ADB PG 会判断本次 DML 操作影响到的行数,若影响行数超过一定阈值,便会对目标表触发一次 ANALYZE 操作。NONE。此时意味着关闭 AutoStats 系统。可以看到 AutoStats 判断是否对目标表触发 ANALYZE 操作只依据了最近一次 DML 操作的结果,所以 AutoStats 更适合 ETL 业务。但随着 ADB PG HTAP 能力的提升,以及与周边生态系统链路的打通,越来越多的用户倾向于以流式的方式将数据导入到 ADB PG 中,比如使用阿里云数据传输服务。这导致了 AutoStats 越来越乏力。从上面举的例子也可以看到,AutoStats 只会在第一次 Insert 之后触发一次 ANALYZE 操作,使得在第二、三次 insert 之后,表 t 的统计信息与实际情况完全不符,这也直接导致了 CBO 未能生成更优的执行计划。4. 引入 Auto Analyze 为此 ADB PG 开发了适合场景更广、对流式插入更友好的 Auto Analyze 系统。Auto Analyze 会为每张表记录自上次 Analyze 以来所有 Insert/Update/Delete 影响到行数的累加值,之后基于这个累加值,结合表本身大小,来决策是否需要为表执行 Analyze 操作。另外 Auto Analyze 会异步地执行 Analyze 操作,与 AutoStats 在用户业务事务内同步地执行 Analyze 操作相比,异步 Analyze 执行对用户业务基本上无感,也不会再有同步执行 Analyze 操作而可能导致的死锁等其他问题。而且秉承着拥抱、回馈、融合、回报的开源思想,ADB PG Auto Analyze 也已经贡献并合入了 Greenplum master 分支。在开启 Auto Analyze 的前提下,我们再一次模拟执行文章开头中举的例子,如下所示在执行完第3条 insert 之后,Auto Analyze 系统也触发了对表 t 的一次 ANALYZE 操作。tmp=# select objid::regclass, staactionname, stasubtype from pg_stat_last_operation where objid = 't'::regclass order by statime desc; objid | staactionname | stasubtype -------+---------------+------------ t | ANALYZE | AUTO t | CREATE | TABLE (2 rows)注:*左右滑动阅览此时用户不再需要手动执行 ANALYZE,也能使 CBO 生成更优的执行计划:Gather Motion 3:1 (slice1; segments: 3) (cost=0.18..8.20 rows=1 width=12) (actual time=0.765..0.773 rows=10 loops=1) -> Index Scan using idx_t_z on t (cost=0.18..8.20 rows=1 width=12) (actual time=0.013..0.015 rows=5 loops=1) Index Cond: (z = 20181218) Planning time: 1.034 ms (slice0) Executor memory: 92K bytes. (slice1) Executor memory: 60K bytes avg x 3 workers, 60K bytes max (seg0). Memory used: 128000kB Optimizer: Postgres query optimizer Execution time: 1.647 ms注:*左右滑动阅览5. Auto Analyze 实现 5.1 PG Auto Analyze 实现在介绍 ADB PG Auto Analyze 实现之前,我们首先看下 PostgreSQL 中是如何实现 Auto Analyze 的。PostgreSQL Auto Analyze 的实现依赖着两个组件:Statistics Collector,Autovacuum。其中 Autovacuum 组件,简单来说,就是周期性遍历每个库,对于库中每个表,根据 Statistics Collector 中这张表相关的统计信息来判断是否需要为这张表触发 Analyze, Vacuum 等操作。Statistics Collector 组件负责收集、保存、持久化 PG 运行中产生的各种 metric 信息,如表的增、删、改行数等。Statistics Collector 收集到的所有信息都保存在内存中。在 Statistics Collector 进程关闭时,会将内存中的统计信息持久化到磁盘文件中。在 Statistics Collector 进程启动时,也会从磁盘文件中读取之前持久化的统计信息到内存中。Statistics Collector 自身同时也是一个 udp server,监听着一个特定端口。在 PG 运行中,backend 会在适当时候将自身收集到的 metric 打包成 udp message 发送给 Statistics Collector。下面以 PgStat_StatTabEntry 中信息的收集为例演示一下此过程:其中 pgStatTabList 指针指向的结构等同于 LinkedList<PgStat_TableStatus>,每个 backend 都会将自身收集到表级别的 metric 存放在该数组相应 PgStat_TableStatus 中。PgStat_TableStatus::trans 指针指向的结构等同于 Vec<PgStat_TableXactStatus> ,其内通过 PgStat_TableXactStatus 存放着当前表在每个事务级别内的统计信息。PgStat_SubXactStatus 结构存放着一个特定事务级别内所有 PgStat_TableXactStatus 结构,存放着该事务级别内发生的所有统计信息。pgStatXactStack 总是指向着当前事务级别对应的 PgStat_SubXactStatus 结构。当在某个事务级别内 backend 打开表准备执行增删改时,会从 pgStatTabList 指向的数组中选择一个 PgStat_TableStatus 元素赋值给 RelationData::pgstat_info。之后在执行 IUD(Insert/Update/Delete) 操作时,backend 会更新当前事务级别上特定表对应的 PgStat_TableXactStatus 结构。每当一个子事务 commit/rollback 时都会将本级别事务内所有 PgStat_TableXactStatus 统计信息合并到父事务中。在顶层事务 commit/rollback 时会将 PgStat_TableXactStatus 的统计信息合并到 PgStat_TableStatus.t_counts 中。最后每当 backend 进入 idle 状态(或者退出)时,会将 pgStatTabList 中所有有效的 PgStat_TableStatus 打包成 udp message 发送给 Statistics Collector,之后将 pgStatTabList 中统计信息清零。5.2 ADB PG Auto Analyze 实现所以 ADB PG Auto Analyze 的实现主要便是在 Insert/Update/Delete 执行结束之后,收集各个计算节点返回的 Insert/Update/Delete 在各自节点内影响到的行数,之后累加得到 Insert/Update/Delete 影响到的总行数,之后将这个信息按照 PG Statistics Collector 中做法记录在对应的 PgStat_TableXactStatus 结构中。最后会在适当时候发送给 ADB PG master 节点的 Statistics Collector 进程。具体细节感兴趣的同学可以参考我们将 ADB PG Auto Analyze 贡献给社区时提的 Pull Request(https://github.com/greenplum-db/gpdb/pull/10515)。6. 未来展望Auto Analyze 的引入使得用户在使用 ADB PG 实例的过程中,统计信息总是能被及时地收集。这也使得用户的业务分析将总是能得到较优的执行计划,业务分析 SQL 的执行性能将不再会由于统计信息过时而急剧下降。另外在 Auto Analyze 搭建的基础设施上,结合着线上用户在使用 ADB PG 中遇到的一些问题,我们也有了接下来的目标:实现 Auto Vacuum 功能。与 Analyze 操作一样,Vacuum 在 ADB PG 也扮演着重要的角色,相信 Auto Vacuum 的引入也会进一步加强 ADB PG 的用户体验。有需要的同学可以扫码进入钉钉群“云原生数据仓库AnalyticDB PostgreSQL版交流群”在线沟通。
作者:辰宇私有协议是什么?PolarDB-X作为阿里巴巴自主研发的云原生分布式数据库,通过将数据拆分到多个基于MySQL发展而来的存储节点DN,每个存储节点DN承担合适的并发、数据存储和计算负载,计算节点处理分布式逻辑,最终得到一个具有稳定可靠、高度扩展性的分布式关系型数据库系统。计算节点和存储节点之间的通信协议,即集群内部通信协议,是系统的重要组成部分。PolarDB-X的前身为TDDL中间件,通过在应用端上采用 Sharding 技术,应对高速增长的数据量,由于后端统一为 MySQL,中间的通信协议为传统 MySQL 的查询协议。1.0时代的 DRDS+RDS 的组合也延续了之前中间件的技术路线,同样采用 MySQL 的 SQL 查询协议作为计算节点和存储节点的数据传输协议。虽然 SQL 表示查询具有简洁易读,不用关心后端存储的物理实现等优点,但对于进入2.0时代(云原生分布式数据库)的PolarDB-X来说,计算节点和存储节点存在更多的数据交互,存储节点也不仅仅承担存储和处理普通SQL查询的功能,这时传统的SQL查询协议便显得力不从心了。传统的SQL协议基于一问一答的阻塞式模型,是针对C/S模式的应用而设计的,没有考虑两个节点间承载大量会话的需要。而目前常见的分布式系统,特别是分布式数据库,为了保证事务性,节点间需要维持多条处于不同事物的会话。因此分布式数据库在集群内部往往都会实现一套自己的通信协议,通常都会选择RPC方式实现。例如OceanBase基于 Libeasy 库,实现了ObRPC,实现了集群内异步化通信。TiDB则直接使用了gRPC作为和TiKV的通信协议。为了充分发挥PolarDB-X底层存储的高级特性,同时解决传统SQL协议作为内部通信协议的局限性,私有协议应运而生。基于私有协议的加持,可以绕过传统的MySQL Server层的解析和优化开销,直接和底下的InnoDB/X-Engine存储引擎进行高效交互,同时扩展并支持了全局时钟协议交互、Plan计算下推等能力,这也使得PolarDB-X逐步脱离原有中间件的范畴,演变为云原生的分布式数据库。PolarDB-X的私有协议作为计算节点和存储节点之间的桥梁,主要实现查询下发和结果回传等基础功能,同时针对一些常用的场景进行了特殊优化。私有协议不仅具备传统SQL接口执行SQL查询的功能,还能直接执行自定义的执行计划,使得计算节点和存储节点之间的交互更加灵活可控。同时为了提升网络交互性能,改善高并发多连接场景下稳定性,私有协议设计时也借鉴了RPC的处理思路,采用了异步化协议设计、会话连接解耦、实例级连接池、流量控制等机制,解决了大部分使用传统连接池遇到的问题,使得PolarDB-X的性能和稳定性都有所提升。私有协议的设计和实现兼容性 & 扩展性聊到网络通信协议,首先需要考虑的就是协议兼容性。作为分布式系统,整个系统的变更都不可能是一步完成的,在升级或变配过程中,都可能出现多个不同版本的客户端或服务端。特别是在PolarDB-X中,存在计算节点和存储节点,在进行升级时,每个节点都可能存在升级前后两个版本,若通信协议发生了升级,则需要协议具备向前向后兼容的能力。PolarDB-X私有协议在设计时充分考虑了这种情况,除了定长的协议头,所有通信协议消息均使用protobuf进行序列化,即使新增了额外的字段,旧版本的节点也能正常工作。考虑到系统的演进,私有协议支持基于SQL语句的查询,实现对SQL接口的完全替换,这部分扩展了MySQL官方的MySQL X protocol的实现,提供最大的兼容性。如下展示了PolarDB-X私有协议中SQL执行的message定义,在原有StmtExecute基础上,增加了一些额外字段,提升了会话上下文恢复速度,可避免通过额外SQL进行设置。message StmtExecute { optional string namespace = 3 [ default = "sql" ]; required bytes stmt = 1; repeated Polarx.Datatypes.Any args = 2; optional bool compact_metadata = 4 [ default = false ]; // Extended fields for fast context restore. optional string schema_name = 5; repeated Polarx.Datatypes.SessionVariable session_variables = 6; optional string encoding = 7; optional int32 token = 8; optional bool reset_error = 9; optional uint64 snapshot_seq = 10; optional uint64 commit_seq = 11; }会话连接解耦 & 异步化 & 流量控制在传统SQL接口中,一个TCP连接只能运行一个SQL会话,考虑到传统单机MySQL是作为一个服务端对外提供服务的,这个设计是非常合理的。但在PolarDB-X内部,MySQL被作为内部存储,在分布式系统中,存在多个计算节点和存储节点,在多连接复杂查询的场景下,后端会话数会因为数据分片的情况被成倍的放大,这时一个TCP连接对应一个SQL会话这种实现反而会带来局限性。因为TCP连接存在三层握手、内核TCP协议栈开销、连接鉴权等原因,使得动态扩容连接数是非常重的操作,而保留大量连接不仅耗费了大量系统资源同时增加了维护连接可靠的成本。针对传统SQL接口连接池的局限性,PolarDB-X私有协议采用了会话连接解耦的策略,即采用在协议包头中添加SessionId字段,标记会话ID,实现在同一个TCP连接上并行运行多个SQL会话。struct { uint64 sid; // Session id. Default -1. uint32 length; uint8 message_type; opaque message_payload[Message.length - 1]; } Message;在PolarDB-X的存储节点上,使用一个独立的调度线程处理TCP上收到的消息,该线程负责会话的创建及生命周期管理,同时将解码后的消息放到对应的会话FIFO队列中,由MySQL的任务执行线程进行执行,指令执行完成后,由session所在TCP的socket返回执行结果。基于PolarDB-X存储节点请求处理的FIFO特性,PolarDB-X计算节点使用异步调用和请求流水线的方式,最大化降低多指令的延迟。多会话复用TCP连接,虽然降低了会话开销,但带来的额外的局限,最典型的问题则是多个session对于TCP通道的争抢。举个简单例子,一个TCP连接上,有一个点查和一个全表扫描的请求,计算节点全表扫描的消费速度没有发送快,导致该TCP通道被全表扫描的数据占据,点查结果无法及时发送过滤。针对这种场景,PolarDB-X私有协议设计了令牌机制,通过令牌对存储节点的生产者进行背压,而不是通过TCP的滑动窗口,从而保证短查询的时效性。同时在连接使用策略上,优先使用空闲连接,而不是优先复用,尽可能避免争抢的情况。执行计划传输SQL虽然能精简地描述一个查询,但这是需要额外使用解析器和优化器为代价的,在PolarDB-X中,计算节点和存储节点之间的数据传输指令也是用SQL实现的。但是在点查等简单的查询场景下,SQL的解析优化也成为制约存储节点吞吐量的一个瓶颈。PolarDB-X私有协议结合计算节点的特性,在优化阶段将下推执行的查询直接转换为特殊的执行计划。存储节点通过私有协议收到执行计划后,不用进行SQL解析和优化,直接进行数据的读取和处理。在实际测试中,同样吞吐量下,使用执行计划相对于使用SQL查询,存储节点的CPU的降低了50%,查询平均延迟也有近30%的下降。高级查询特性支持在OLAP任务中,常需要从存储节点拉取大量数据到计算节点进行处理,这对传输协议也是很大的挑战。PolarDB-X计算节点在内存中使用紧凑的列式数据存储结构,并借助向量化提升数据处理速度。PolarDB-X私有协议针对这种应用场景,通过自定义的传输格式的方式,可以直接以列式方式传输结果集,绕过传统传输协议行式传输带来的计算节点行转列的损失。同时针对列式数据格式的特性,后续也将具备压缩及压缩后计算的能力,进一步优化网络和内存的开销。在复杂查询中,不仅传输的数据量大,同时复杂join也是非常常见的情况。通过添加runtime filter是一种有效的优化方法,但是在基于传统MySQL作为存储单元的情况下,这种高级过滤难以通过SQL进行表示。PolarDB-X中使用私有协议实现了bloom filter的传递,直接在存储节点上对数据进行过滤,极大减少了网络传输的压力。更多有用的高级查询特性,例如存储节点数据按规则主动分发、状态推送等也将逐步增加到PolarDB-X私有协议之中。性能对比使用私有协议后,最直接效果则是性能的提升,在点查场景下,私有协议相对于走SQL协议能极大降低后端存储的压力,同时提升吞吐量。sysbench-select1.6亿行数据300并发计算节点和存储节点规格均为16c64g+39%sysbench-oltp1.6亿行数据150并发计算节点和存储节点规格均为16c64g+14.4%展望PolarDB-X私有协议实现了对MySQL传统SQL查询协议的替换,并针对分布式数据库的特殊场景进行了扩展和改造。未来将在查询性能、网络开销和功能扩展上进一步优化和改造,提升PolarDB-X计算节点和存储节点的交互效率,为高性能PolarDB-X奠定坚实的基础。参考文档TiKV 源码解析系列文章(一)序一文详解OceanBase通信协议层Introduction to gRPCCore concepts, architecture and lifecycle【相关阅读】技术解读 | PolarDB-X 分布式事务的实现技术解读 | PolarDB-X 强一致分布式事务PolarDB-X 一致性共识协议 (X-Paxos)
2021年06月
2021年05月
2021年04月
2021年03月
2021年02月