HTAP(Hybrid Transactional/Analytical Processing)系统之统一存储的实时之道

文章目录

  • HTAP与时俱进
  • LASER中的存储
    • 关键知识
      • LSM(Log-Structured Merge Tree)
      • SkipList(跳表)
      • CDC(Changed Data Capture)
      • SST(Sorted Sequence Table)
    • 特性
      • 列组(Column Group)
      • 部分列更新
  • LASER存储的实现
    • 数据插入流程
    • 部分列更新流程
      • 初始化LEVELs
      • 插入一条新记录并更新一条旧记录(合并L0和L1)
      • 插入一条新记录并更新一条旧记录(不合并)
    • 范围查询
    • 部分列的Compaction
  • LASER存储的性能
    • 整体性能
    • 插入性能
    • 检索性能
  • LASER存储的问题
    • 写放大
    • 点查放大
    • 范围查询放大
    • 更新放大
  • 总结
  • 思考

HTAP与时俱进

在线联机事务处理(OLTP)和在线联机分析处理(OLAP)这两类数据处理分析场景,是公司日常工作中不可不说的内容,尤其是在大数据时代的当下,说它们决定了公司的成败也不为过,因此诞生了各类成熟且高效地分布式计算、存储系统,如计算侧的MapReduce、Spark、Flink、Trino等,存储侧的Oracle、RocksDB、Clickhouse等。

但正是各类计算和存储系统的遍地开花,也导致了在实际中很难将不同的系统归一、数据统一,导致各类负担,尤其是流系统、批系统的天然隔阂,因此近年来大家都是力求找到或开发出一个系统,能够同时很好应付日常工作中的绝大部分OLTP/OLAP的业务就行了,就像Snowflake那样,实现一个相对完善的HTAP系统

但要想做好一个HTAP系统,不可避免地需要要结合计算、存储这两个层面的特性来进行设计,虽然我们在实际的工作中经常强调要存算分离,保证集群系统能够至少满足BASE(Basically Available、Soft States、Eventually Consistent原则,也可以说是AP原则吧)原则,但这仅仅是强调使用上的注意事项,而要实现这样的系统,却不能分开计算而谈存储,反之亦然。

人都是“贪婪”的,一旦有了开发了一个工具,不管是使用者还是开发者,都希望随着技术的进步,这个工具能够变得更好,比如说时间,为别人/自己节省了多少时间,实时性达到秒级等,说到这里,这篇博客也就随着这篇论文Real-Time LSM-Trees for HTAP Workloads来看看学习和思考前人的成果,以帮助解决当下或未来的问题。

LASER中的存储

关键知识

LSM(Log-Structured Merge Tree)

很多文章都介绍这个概念了,大家可以自行查找一下,当然也有不少系统基于此原理实现了自己的存储,如Google LevelDB、Clickhouse、Flink Table Store等

SkipList(跳表)

这个概念也有不少的大佬分析了其理论与实践,例如Redis中的应用,还有Real-Time LSM-Trees for HTAP Workloads论文中的提到的LASER系统。

CDC(Changed Data Capture)

实际上对应了数据库中的INSERT、DELETE、UPDATE操作,更细节知识自行查阅吧。

SST(Sorted Sequence Table)

可以认为就是持久化到磁盘上的数据文件,文件中的数据行都是按排序KEY有序的。

特性

列组(Column Group)

为了兼并行存和列存的优势,LASER在不同的存储等级(LEVEL)上定义不了同列组规则,一个列组就是一个数据行中的部分或全部字段,对应一个单独的存储文件,例如在下面的图中显示的,在Level 0层,文件是按行存储的,文件中的一行就对应了一条完整的Record;而在Leve 1层,会存储两个文件,分别保存(A, B)列以及(C, D)列;在Level 3层,一个列,就是一个单独的文件。(这里说一个文件并不准确,实际上应该是一类文件,毕竟文件一般会按大小被切分成多个)
图-1 列组
图-1 列组的定义与组织

部分列更新

LASER存储的实现

数据插入流程

下图展示了LASER中的基本存储流程,图中也展示了一些配套的索引技术,如BLOOM FILTERS用于加速文件的查找、SkipList查找新记录的待插入位置。
描述
一条新数据记录(Record)完整地经历CDC过程的简单描述如下:

  1. 决定Record的操作类型:Server接收到Record后,根据指定的排序Key、唯一Key,在内存中的SKIPLIST以及磁盘中的LEVEL文件(这里指SST文件)查找,看是否存在相同的数据记录。如果存在则更新这条RECORD的操作类型为UPDATE,否则为INSERT
  2. 插入到内存中的MUTABLE SKIPLIT:通过跳表可以很快地确认这条新的数据记录的插入位置,因此就将其插入到内存。
  3. Flush到磁盘:如果新的数据记录插入后,到达了一定的阈值,则系统会尝试将MUTABLE的数据刷新到磁盘,但在Flush之前,需要将新记录插入的内存数据表标记为IMMUTABLE,以保证数据写出时,不会发生 变更。
  4. 写出数据到Level0Level 0只定义了一个文件,因此会首先尝试将新的RECORD,按行格式,插入到此文件中。
  5. Compaction数据文件:如果新的RECORD插入Level 0后,导致文件的大小超过了阈值,则会触发Compaction行为,将Level 0的文件,下沉到更下层,达到优化存储的目的,因此这里会首先将Level 0的文件,转存到Level 1。文件由Level 0插入到Level 1的过程,实际上是一个归并排序的过程,需要保证文件的有序性,因此这里可以采用二分查找来确认需要合并Level 1中的哪些文件,例如Level 0文件的SORT KEY值范围为[22, 66],那么需要与Level 1中的21-5051-88的两个SST文件进行合并。
  6. 列组映射:上面的图只显示了文件的插入过程,但没有展示出列出的合并逻辑,这里简单说一下:

图-1可以知道每一个Level的列组划分是不同的,而Level 0中的文件中的一行可能包含了所有列值(如A、B、C、D四个列),而在Level 1中数据文件只有两类(A, B)和(C, D),因此需要将0层的文件中的数据行按列拆分成两个文件,分别以前两个列为一行和后两个列为一行,再分别进行合并,最终生成两类文件。

部分列更新流程

一般地,SQL中的UPDATE语句会更新部分列的历史值,因此LASER也需要有能力支持。

初始化LEVELs

Level 0:数据文件行式存储,因此文件中的一行,包含了全部列,A、B、C、D。
Level 1: 两个文件,即两人上Column Group,左边文件包含A、B列;右边文件包含C、D列
注意到每一行记录之前有一个特殊的整数,例如106: a6, b6, c6, d6中的106,表示的是数据记录排序键对应的值,可以看到在每一个文件中,所有的数据记录都是按此值有序。

在这里插入图片描述

插入一条新记录并更新一条旧记录(合并L0和L1)

插入一条新记录:99: a9, b9, c9, d9
更新一条旧记录:107: -, -, c9, d9,其中-表示不更新,即保留A, B列的原有值
注意到,这里插入新记录后,导致LEVEL 0超过存储阈值,因此会触发L0的文件下沉到L1,因此下面的图展示的是合并后的结果。
在合并L0和L1的过程中,可以看到,原本在L0的行文件中的记录106: a6, b6, c6, d6,下沉到L1后,被纵向拆分到了两个Column Group文件中;而新的更新记录107: -, -, c9, d9最终只会在CG:<C, D>有值,而不会添加记录107: -, -到CG:<A, B>中,节约了存储空间。

在这里插入图片描述

插入一条新记录并更新一条旧记录(不合并)

插入一条新记录:50: a0, b0, c0, d0
更新一条旧记录:108: a1, b1, -, -,其中-表示不更新,即保留C, D列的原有值
可以看到由于新插入的数据后,被首先Flush到Level 0,但Level 0的数据大小没有达到阈值,因此不会发生Compaction,新的数据就以行格式保留在L0中。
在这里插入图片描述

范围查询

ColumnMergingIterators:用于合并ColumnGroup,一个Iterator实例只会作用于同一个Level,因此不会真正的合并新旧数据,而是将要所有要检索的列(这里是A、B、C、D列)拼接在一起。
LevelMergingIterators:用于合并来自不同Level的数据,这些数据经过ColumnMergingIterators后返回了一个"临时表",包含了所有要检索的列,同时会进行新、旧列值的覆盖
图-2 部分列的查询
查询流程简述如下:

  1. SQL解析:接收SELECT * FROM tbl WHERE sort_key >= 50 and sort_key <= 108,产生要返回的结果列的投影信息,即返回A、B、C、D。
  2. 确认数据所有层级:发现sort_key的取值范围是[50, 108],在3个Level中都存在数据,因此需要遍历每一层的数据文件。
  3. 遍历每一层的数据文件:为每一个LEVEL创建ColumnMergingIterators实例,遍历满足条件的数据文件,返回的结果是一个临时表且它们的Layout相同,均为A、B、C、D,例如对于sort_key = 107的数据记录,通过列拼接,最终得到在临时表中的对应行107: -, -, c9, d9
  4. 合并每一层的临时表:通过LevelMergingIterators实例,合并每一层的返回结果,同时进行数据记录的更新/删除动作,例如对于sort_key = 107的数据记录,发现它的旧值为107: a7, b7, c7, d7,新值为107: -, -, c9, d9,因此通过覆盖后的最终结果为107: a7, b7, c9, d9
  5. 返回最终结果:最终结果集包含了所有要检索的列,以及包含了旧记录中的列的最新值。

部分列的Compaction

通过后台的Compaction线程,可以并行地在不同的ColumnGroup上进行Compaction,因为在数据下沉的过程中,越往向,列组越小,并且互相不影响。

如下图所示,当前一共有两个Compaction任务在执行,第一个任务是合并L1和L2中的CG:<A, B>;第二个任务是合并L2和L3中的CG: <C>
图-3 部分列的合并
但这里有一个潜在的问题:为什么选择L1中的<A, B>下沉,以及L2中的<C>下沉?

简单来说,LASER为每一个Level配置了不同的Quota,例如为L1配置了上限记录数为2,而当前L1中一共存在3条记录,因此需要合并L1和L2;同时注意到CG: <A, B> 的占比最多,因此优先选择此CG下沉,故就对应了任务1,同理生成任务2

最终,经过部分列上的下沉,可以避免对它列的影响,在一定程度上能够减缓由于数据下沉,导致在这些列上的检索时间变长的问题,当然也可以结合一些冷热策略可以更精细地控制正常过程。

LASER存储的性能

rocksdb:行存
rocksdb-col:列存
HTAP-simple:行、列混合存储,25%数据行存,其它列存。
Postgress:行存
MySQL:行存
MyRocks:行存
MonetDB:列存
Hyper:全内存列存

整体性能

如下图所示,在同时进行INSERT、UPDATE、SELECT操作时,LASER的整体性能是最好的,尤其是在设置了ColumnGroup的大小为6(6列)、15(15列)的场景下,而次强的则是HTAP-simple和rocksdb-col(它们完全是基于内存的)
图-4 整体性能

插入性能

如下图所示,当仅执行INSERT操作时,LASER表现最好,尤其是设置CG的大小为2和3时,而HTAP-simple和rocksdb将之。
图-5 插入性能

检索性能

𝑄1: INSERT INTO R VALUES (𝑎0, 𝑎1, …, 𝑎𝑐 )
𝑄2: SELECT 𝑎1, 𝑎2, …, 𝑎𝑘 FROM R WHERE 𝑎0 = 𝑣
𝑄3: UPDATE R SET 𝑎1 = 𝑣1, …, 𝑎𝑘 = 𝑣𝑘 WHERE 𝑎0 = 𝑣
𝑄4: SELECT 𝑎1 + 𝑎2 + … + 𝑎𝑘 FROM R WHERE 𝑎0 ∈ [𝑣𝑠 , 𝑣𝑒 )
𝑄5: SELECT 𝑀𝐴𝑋 (𝑎1), …, 𝑀𝐴𝑋 (𝑎𝑘) FROM RWHERE𝑎0 ∈ [𝑣𝑠 , 𝑣𝑒 )

如下图所示,分别执行不同Query时的延迟统计图,从中可以看到当前执行Q1、Q2、Q3时,LASER能够达到其它优势引擎的最好性能;而执行Q4的算术运算时,Hyper表现最好,比LASER快5倍(而MonetDB比LASER慢20倍);而执行Q5的聚合运算时,MonetDB和Hyper比LASER快5倍,这是由于Hyper和Monet存储的数据记录都是按列连续的,因此不需要像LASER那样需要先合并数据。
图6 检索性能
因此整体上看,在高负载,和能用场景下,LASER的表是所有列式引擎、行式引擎中综合表现最好的,也更加活动地通过CG的大小来适配不再的场景。

LASER存储的问题

下面提到的这些问题,都是论文中有提到的,同时也给出了估算公式,但是着实需要细致分析每一个算法才能更好地理解架构设计的精妙,这里就不展开分析了,也怕功力不够,引发解读错误,那就栽了!!!

写放大

不难想象,当我们更新更新或插入数据时,至少需要读取索引数据、旧的的数据记录,以确定当前数据行的操作类型;当发生数据合并(Compaction过程)时,需要将新旧数据写出到一个新的数据文件,同时保证旧的数据文件依然在此期间可以为查询作业提供服务,因此这么大的倍数与写入或更新的数据模型有关。

为了缓解此问题,可以基于ColumnGroup机制,同时为每一个Level制定不同的数据下沉策略。

点查放大

仅仅是等值查询,最坏情况下,需要在内存遍历,同时需要检查所有Level中的数据范围,以确定数据是否存在。

范围查询放大

比点查更坏,最坏情况下,要查询的数据在每一个Level中都存在,因此遍历记录每一层的数据文件的信息,来确定要读取的数据。

更新放大

在点查放大问题的基础之上,需要将新数据写出到Level 0,同时很可能会引发Compcation过程。

总结

Real-Time LSM-Trees for HTAP Workloads介绍了一个支持实现写入的、基于LSM的、支持HTAP场景的存储系统,LASER。论文提出了ColumnGroup存储规范,能在兼并行存、列存的优点,以相对最好的性能同时支持OLTP和OLAP事务,为打造流批一体计算&存储系统提供了借鉴,非学值得我们细细口味。

思考

  1. 使用什么的索引或算法,能够快速定位范围所包含的数据文件?
  2. 时间旅行?
  3. 并发写事务的支持?
  4. 如何支持插入入新的列?
  5. 。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/608507.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Arthas,你真是Java程序员的大力丸

您好&#xff0c;我是码农飞哥&#xff08;wei158556&#xff09;&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精…

印章管理详解|契约锁帮助提前预防99%的印章风险

传统实体印章不仅存在私刻私盖、盗用乱用、易伪造等安全隐患&#xff0c;此外&#xff0c;线下面签的方式也不便于异地、非工作时间用印&#xff0c;分公司用印常常两地来回跑。组织的印章到底怎么“管”才能保障安全和使用效率&#xff1f; 一、 印章管理风险有哪些&#xff…

系统概要设计说明书

系统概要设计说明书 1.整体架构 2.功能架构 3.技术架构 4.运行环境设计 5.设计目标 6.接口设计 7.性能设计 8.运行设计 9.出错设计 全文档获取进主页

面试算法107:矩阵中的距离

题目 输入一个由0、1组成的矩阵M&#xff0c;请输出一个大小相同的矩阵D&#xff0c;矩阵D中的每个格子是矩阵M中对应格子离最近的0的距离。水平或竖直方向相邻的两个格子的距离为1。假设矩阵M中至少有一个0。 例如&#xff0c;图&#xff08;a&#xff09;是一个只包含0、1的…

Docker安装Jenkins,配置Maven和Java

前言 这是一个java的springboot项目&#xff0c;使用maven构建 安装准备 需要将maven和jdk安装在服务器上&#xff0c;Jenkins需要用到&#xff0c;还有创建一个jenkins的目录&#xff0c;安装命令如下&#xff1a; docker run -d -uroot -p 9095:8080 -p 50000:50000 --n…

时间序列数据库选型: influxdb; netdiscover列出docker实例们的ip

influxdb influxdb: 有收费版本、有开源版本 docker run -itd --name influxdb-dev -p 8086:8086 influxdb #influxdb的web客户端(端口8003)被去掉了 #8006是web-service端口docker pull chronograf docker run -d -p 8888:8888 --name chronograf-dev chronografsudo netst…

对比fwrite、mmap、DirectIO 的内存、性能开销,剖析 Page Cache

背景 如上图所示&#xff1a;应用程序写文件有三种形式。 fwrite : 应用程序 -> fwrite(Buffered IO) -> File System -> Page Cache -> Block IO Layer -> Device & Disk etc.mmap : 应用程序 -> mmap -> Page Cache -> Block IO Layer -> De…

【LLM】vLLM部署与int8量化

Acceleration & Quantization vLLM vLLM是一个开源的大型语言模型&#xff08;LLM&#xff09;推理和服务库&#xff0c;它通过一个名为PagedAttention的新型注意力算法来解决传统LLM在生产环境中部署时所遇到的高内存消耗和计算成本的挑战。PagedAttention算法能有效管理…

虾皮商品标题:如何创建有效的虾皮商品标题

虾皮&#xff08;Shopee&#xff09;平台是一个非常受欢迎的电商平台&#xff0c;为卖家提供了一个广阔的销售渠道。在虾皮上&#xff0c;一个有效的商品标题是吸引潜在买家注意力的关键元素之一。一个好的商品标题能够吸引更多的点击和浏览量&#xff0c;从而提高销售机会。下…

什么是API网关代理?

带有API网关的代理服务显着增强了用户体验和性能。特别是对于那些使用需要频繁创建和轮换代理的工具的人来说&#xff0c;使用 API 可以节省大量时间并提高效率。 了解API API&#xff08;即应用程序编程接口&#xff09;充当服务提供商和用户之间的连接网关。通过 API 连接&a…

【PostgreSQL】在DBeaver中实现序列、函数、视图、触发器设计

【PostgreSQL】在DBeaver中实现序列、函数、触发器、视图设计 基本配置一、序列1.1、序列使用1.1.1、设置字段为主键&#xff0c;数据类型默认整型1.1.2、自定义序列&#xff0c;数据类型自定义 1.2、序列延申1.2.1、理论1.2.2、测试1.2.3、小结 二、函数2.1、SQL直接创建2.1.1…

Python教程37:使用turtle画一个戴帽子的皮卡丘

---------------turtle源码集合--------------- Python教程36&#xff1a;海龟画图turtle写春联 Python源码35&#xff1a;海龟画图turtle画中国结 Python源码31&#xff1a;海龟画图turtle画七道彩虹 Python源码30&#xff1a;海龟画图turtle画紫色的小熊 Python源码29&a…

基于宝塔搭建Discuz!论坛

一、安装宝塔 我是在我的虚拟机上安装图的宝塔 虚拟机版本&#xff1a;Ubuntu 18.04 wget -O install.sh https://download.bt.cn/install/install-ubuntu_6.0.sh && sudo bash install.sh 6dca892c安装完成之后在浏览器输入你的地址 https://你的域名&#xff08;或…

基于JavaWeb+BS架构+SpringBoot+Vue校园一卡通系统的设计和实现

基于JavaWebBS架构SpringBootVue校园一卡通系统的设计和实现 文末获取源码Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 文末获取源码 Lun文目录 第一章 概述 4 1.1 研究背景 4 1.2研究目的及意义 4 1.3国内外发展现状 4 1…

网络安全之你的浏览器记录真的安全吗?

密码是每个人最私密的东西&#xff0c;轻易是不会展示给他人的&#xff0c;那么我如何能知道你电脑上浏览器里保存的密码呢&#xff1f;浏览器是大家在网上冲浪最常用的软件&#xff0c;在登录一些网站填写账号密码后&#xff0c;浏览器为了方便大家使用&#xff0c;会提示是否…

unity小程序websocket:nginx配置https (wss)转http (ws)及其他问题解决

目录 前言 实际运用场景 处理流程如下 nginx配置ssl和wss 配置过程中遇到的问题 1、无法连接服务器 2、通过IP可以访问&#xff0c;域名却不行 问题描述 解决 3、如何判断该域名是否备案了 前言 为了服务器网络的通用性&#xff0c;我们在实现移动端的游戏转微信小程序…

Python教程38:使用turtle画动态粒子爱心+文字爱心

Turtle库是Python语言中的一个标准库&#xff0c;它提供了一种有趣的方式来介绍编程和图形绘制的基本概念。Turtle库使用一个虚拟的“海龟”来绘制图形。你可以控制海龟的方向、速度和位置&#xff0c;通过向前移动、向左转或向右转等命令来绘制线条、圆弧多边形等图形。 -----…

Java 反射(一)

反射 1.反射的介绍 1.反射机制允话程序在执行期间借助于Refelction API取得任何类的信息&#xff08;比如成员变量&#xff0c;构造器&#xff0c;成员方法等&#xff09;并能操作对象的属性及方法&#xff0c;反射在设计模式和框架底层都会用到 2.加载完类之后&#xff0c;在…

API调试怎么做?Apipost快速上手

前言 Apipost是一款支持 RESTful API、SOAP API、GraphQL API等多种API类型&#xff0c;支持 HTTPS、WebSocket、gRPC多种通信协议的API调试工具。除此之外&#xff0c;Apipost 还提供了自动化测试、团队协作、等多种功能。这些丰富的功能简化了工作流程&#xff0c;提高了研发…

CodeWave智能开发平台--03--目标:应用创建--08联系人管理

摘要 本文是网易数帆CodeWave智能开发平台系列的第11篇&#xff0c;主要介绍了基于CodeWave平台文档的新手入门进行学习&#xff0c;实现一个完整的应用&#xff0c;本文主要完成08联系人管理 CodeWave智能开发平台的11次接触 CodeWave参考资源 网易数帆CodeWave开发者社区…