Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >更新操作的秘密

更新操作的秘密

作者头像
用户2936994
发布于 2022-07-21 05:41:50
发布于 2022-07-21 05:41:50
4070
举报
文章被收录于专栏:祝威廉祝威廉

进入主题前的一句唠叨

如果说,Delta带来的三大核心特性:

  1. 流批共享
  2. upsert/delete/overwrite等操作
  3. 版本回滚

让我选最核心的特性的话,我会选择第二个。在大数据领域,我们也是一步步进化的,从最早的数据存储采用纯文本,到后面ORC/Parquet等面向读的格式。但是他们都存在一个一个很大的问题,就是不可变,只增。但现实中的业务场景里太需要Upsert这个功能了。有了这个功能,对架构来说真的是如沐春风。

当然,单独的更新功能没啥值得骄傲的,像HBase,Kudu等等都有,但是Delta的更新功能是建立在流批共享表的基础上,同时还不增加额外复杂度,这种情况下就显得难能可贵了。

一起来探秘

更新有很多种类,这个章节我们只会介绍Delta是如何实现Upsert语义的操作。前面我们说,Delta表由两部分构成:

一堆的parquet文件和记录操作日志的json文件(以及checkpoint用的parquet文件)。在讲解upsert操作前,我们先看看如果要新增记录,文件的变化会是什么样子的:

往Delta写入新数据,主要有三个步骤(如图描述):

  1. 将新数据保存成新的parquet文件(图中灰色部分)。
  2. 进行commit操作,创建一个新的json文件(形成类似000...0012.json,图中灰色部分),这个json文件会比较新增的parquet文件是哪些。
  3. 此时相当于新增加了一个版本,其他的reader就可以读到最新的文件了。

我们看到,新增的过程不影响其他用户读Delta表。现在让我们看看如何实现Upsert语义:

假设delta表里面已经有a1,a2两个parquet文件,然后当前的版本号是11,你看图应该很容易看出来。现在有一批数据A要进来,这批数据有部分是新增,有部分是已经存在于a1,a2中的。我们会通过如下五个步骤来完整整个Upsert操作:

  1. 使用进来的数据A创建一个a3文件。 这个阶段,显然有数据重复了,因为A里部分数据是a1,a2已经存在的。
  2. 读取a1,a2的数据,和A求差集,也就是过滤掉A中在a1,a2里出现的数据,然后创建新的文件a3,a4,a5。这个时候所有的老数据都有了一份。
  3. 现在,我们标记删除所有的老数据在的parquet文件,也就是a1,a2。现在目录里应该只有a3,a4,a5三个文件。a3是A集合的数据,a4,a5是去掉和A重复的数据,他们构成了完整的一个数据集。
  4. 现在系统会检测当前的版本号是不是11,记得我们刚开始事务的时候么我们发现版本号是11,如果现在还是11,说明没有人更改过数据,所以我们可以放心的提交。否则,我们可能需要重新执行所有流程。
  5. 提交数据,生成000…012.json. 该文件表示版本号为12,同时里面会标记删除了a1,a2,同时a3,a4,a5为新增的parquet文件。

这个时候其他用户已经可以读取到版本12的所有数据了。

从上面描述我们可以看到,Delta做更新操作是个比较重的操作,他需要遍历所有数据找到和当前数据不重复的数据然后生成新的文件,然后删掉老的文件。我们也有一些优化空间可以做,比如如果用户的条件包含了分区字段,那么我们只要抽取集合A涉及到的分区,然后再去读对应的delta表中的文件,这可以避免全表扫描。

我们还可以得出下面几个结论,而这几个结论也是大家非常容易困惑的地方:

  1. 更新一条和1000条数据对delta来说,性能可能不会有什么差别。
  2. Delta不适合对单条数据做upsert,因为overhead 太高。最好是能一批一批的去做upsert.这和传统的比如hbase,MySQL等是不一样的。
  3. Delta本质上是在不断的产生新的文件,然后又因为是比较删除,所以文件增长会非常快。

1,2是没有太大办法解决的,因为设计就是如此。 第三点应该是很多用户会明确感受到的,对此也是有解决方案的。首先我们不会保留太多版本,则意味着我们可以清理掉老版本里所有被标记删除的文件,从而实现真正的减少文件。同时,我们也可以每次commit生成的文件数,避免产生过多小文件。

乐观锁

前面我们其实回避了一个问题,就是如果有多个写同时发生怎么办?对于并发,我们肯定还是回避不了锁的问题。Delta采用了乐观锁,乐观锁的概念是什么呢?就所有准备工作都做好了,数据也搞好了,只有在最后commit的时候才会检查写冲突。检查冲突其实只有两个结果

  1. 运气好,没有冲突,提交成功。
  2. 运气不好,冲突了

运气好咱没啥说的。现在我们看看,运气不好的时候咋办。如果冲突了,则意味文件已经被更改,问题是我们乐观锁是我们在检测冲突前,就把所有的工作都做了(比如整个更新流程),相当于花了大力气,现在核心的问题是,这些工作要不要重做。要不要重做取决于你是不是依赖于读取表里的数据。对于纯新增数据操作,我们是不读取原始表的数据的,所以我们发现冲突后,只要在冲突的版本上重试申请新的版本即可,并不需要重新写数据。但是对于upsert操作,因为我们读取了原始表的数据,现在别人原始表的数据已经变化了,这就表示你之前的工作需要重做,否则就相当于你覆盖掉了别人的操作(就是抢在你之前提交了数据的人),所以你必须重新做之前做过的一整套流程,然后再进行检测,直到提交成功。

通过这里我们可以看出,乐观锁在需要读取原始数据的情况下是其实是非常不适合并发操作的。这意味着,Delta是适合写少读多的场景。前面我们提到,因为upsert是个很重的操作,所以不适合一条一条执行,要一批一批执行,这是一个原因点。还有一个就是基于并发的考虑,用户想一条一条执行,为了能执行的更快,就会放到多线程里,这个时候因为delta又不适合并发写,会导致很多任务不断重试,而每个任务又是很重的操作,导致集群资源无意义的浪费并且严重降低了吞吐。

本章结束语

到目前为止,我们看到了Delta是如何支持更新的,以及使用乐观锁来解决并发写的问题。因为我们在原理探讨了上面的问题,所以我们知道了Delta如下几个特点:

  1. Delta支持更新语义,但是更新操作是个很重的操作。
  2. Delta的更新最好是一批一批更新,不要一条一条更新。基本上一条一条更新是你可以理解为不work的。
  3. Delta采用乐观锁,所以适合写少读多的场景
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-10-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Delta Lake如何自己实现更新操作加速(布隆过滤器)
Delta Lake 现阶段判定一条数据是不是已经存在是比较暴力的,直接做一次全表join(如果有分区会好点)。这样可以得到待更新记录所在的文件路径。在特定场景,这个开销会非常大。上次和一位朋友聊天,他对这个点也"耿耿于怀"。 尤其是做MySQL表同步的时候,通常是没有分区的,这就意味着每次都有一次全表扫描。
用户2936994
2022/01/07
7300
Delta Lake的竞争对手Hudi(Alpha版)
Delta Lake肯定不是第一个数据湖产品。对于存储这块,CarbonData也一直有雄心。不过今天我要重点讲讲Delta Lake 和Hudi的对比。因为Hudi我仅限于基本的浏览了写入和读取相关的代码,理解上算不得成熟,所以这篇文章我加了限定词Alpha版,后续可能会Alpha01....Beta,当然最后肯定是没有标记,那就表示我觉得我的理解差不多了,文章可能定型了。
用户2936994
2022/07/21
3720
数据湖Delta Lake、Hudi 与 Iceberg介绍 | 青训营笔记
数据湖是一类存储数据自然/原始格式的系统或存储,通常是对象块或者文件。数据湖通常是企业中全量数据的单一存储。 全量数据包括原始系统所产生的原始数据拷贝以及为了各类任务而产生的转换数据,各类任务包括报表、可视化、高级分析和机器学习。
鳄鱼儿
2024/05/21
1.6K0
数据湖Delta Lake、Hudi 与 Iceberg介绍 | 青训营笔记
快问快答,MySQL面试夺命20问
给面试官讲一下 MySQL 的逻辑架构,有白板可以把下面的图画一下,图片来源于网络。
敖丙
2021/07/27
9780
快问快答,MySQL面试夺命20问
快速理解脏读、不可重复读、幻读和MVCC
A事务读取B事务尚未提交的数据,此时如果B事务发生错误并执行回滚操作,那么A事务读取到的数据就是脏数据。就好像原本的数据比较干净、纯粹,此时由于B事务更改了它,这个数据变得不再纯粹。这个时候A事务立即读取了这个脏数据,但事务B良心发现,又用回滚把数据恢复成原来干净、纯粹的样子,而事务A却什么都不知道,最终结果就是事务A读取了此次的脏数据,称为脏读。
互扯程序
2019/06/25
64.9K14
快速理解脏读、不可重复读、幻读和MVCC
高并发系统中的常见问题
本文一共分析了三个案例,分别介绍并发系统中的共享资源并发访问、计算型密集型任务缓存访问 、单一热点资源峰值流量问题和解决方案。 Q1:订票系统,某车次只有一张火车票,假定有1w个人同时打开12306网站来订票,如何解决并发问题? A1: 首先介绍数据库层面的并发访问,解决的办法主要是乐观锁和悲观锁。 乐观锁 假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。 乐观锁使用一个自增的字段表示数据的版本号(或者timestamp),更新的时候检查版本号是否一致,比如数据库中版本号为4,更新时版本号使用
wangxl
2018/03/07
9800
高并发系统中的常见问题
精心为你准备的最全的20道Mysql面试题。
Mysql中事务的隔离级别分为四大等级:读未提交(READ UNCOMMITTED)、读提交 (READ COMMITTED)、可重复读 (REPEATABLE READ)、串行化 (SERIALIZABLE)。
用户4172423
2020/11/11
8660
精心为你准备的最全的20道Mysql面试题。
ElasticSearch并发操作之乐观锁的使用
绍了关于ES嵌套索引的增删改,本篇就接着上篇主题继续深入聊一下,上篇的添加和更新操作,其实是不安全的,所有的数据库db系统都会存在并发问题像关系型数据库MySQL,Oracle,SQL Server默认采用的是悲观锁。 在ElasticSearch中采用的乐观锁,下面先熟悉下什么是乐观锁和悲观锁: 悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到
我是攻城师
2018/05/14
1.7K0
读懂数据库中的乐观锁和悲观锁和MVCC
在数据库的实际使用过程中,我们常常会遇到不希望数据被同时写或者读的情景,例如秒杀场景下,两个请求同时读到系统还有库存1个,然后又先后把库存更新为0,这时候就会出现超卖的情况,这时候货物的实际库存和我们的记录就会对应不上了。
BUG弄潮儿
2020/07/30
8440
Java并发编程(05):悲观锁和乐观锁机制
多线程并发访问同一个资源问题,假如线程A获取变量之后修改变量值,线程C在此时也获取变量值并且修改,两个线程同时并发处理一个变量,就会导致并发问题。
PHP开发工程师
2021/05/18
6300
Java并发编程(05):悲观锁和乐观锁机制
Java并发编程(05):悲观锁和乐观锁机制
多线程并发访问同一个资源问题,假如线程A获取变量之后修改变量值,线程C在此时也获取变量值并且修改,两个线程同时并发处理一个变量,就会导致并发问题。
知了一笑
2020/06/23
2830
Java性能 -- CAS乐观锁
AtomicInteger是基于CAS实现的一个线程安全的整型类,Unsafe调用CPU底层指令实现原子操作
Java_老男孩
2019/12/02
9570
基于Apache Hudi 的CDC数据入湖
首先我们介绍什么是CDC?CDC的全称是Change data Capture,即变更数据捕获,它是数据库领域非常常见的技术,主要用于捕获数据库的一些变更,然后可以把变更数据发送到下游。它的应用比较广,可以做一些数据同步、数据分发和数据采集,还可以做ETL,今天主要分享的也是把DB数据通过CDC的方式ETL到数据湖。
ApacheHudi
2021/10/11
1.2K0
基于Apache Hudi 的CDC数据入湖
一致性协议算法-2PC、3PC、Paxos、Raft、ZAB、NWR超详细解析
在常见的分布式系统中,总会发生诸如机器宕机或网络异常(包括消息的延迟、丢失、重复、乱序,还有网络分区)等情况。
王知无-import_bigdata
2020/12/18
3.5K0
一致性协议算法-2PC、3PC、Paxos、Raft、ZAB、NWR超详细解析
分布式事务中限制数据的并发访问
乐观锁是一种常用的并发控制机制,适用于高并发读取、少量写入的场景。它的主要思想是,每次读取数据时都假设没有其他线程对数据进行修改,只有在更新数据时才会根据实际情况进行并发冲突的检测和处理。
一凡sir
2023/11/10
2390
分布式事务中限制数据的并发访问
Java面试集锦(一)之数据库(mysql)
第一范式:列不可分,eg:【联系人】(姓名,性别,电话),一个联系人有家庭电话和公司电话,那么这种表结构设计就没有达到 1NF; 第二范式:有主键,保证完全依赖。eg:订单明细表【OrderDetail】(OrderID,ProductID,UnitPrice,Discount,Quantity,ProductName),Discount(折扣),Quantity(数量)完全依赖(取决)于主键(OderID,ProductID),而 UnitPrice,ProductName 只依赖于 ProductID,不符合2NF; 第三范式:无传递依赖(非主键列 A 依赖于非主键列 B,非主键列 B 依赖于主键的情况),eg:订单表【Order】(OrderID,OrderDate,CustomerID,CustomerName,CustomerAddr,CustomerCity)主键是(OrderID),CustomerName,CustomerAddr,CustomerCity 直接依赖的是 CustomerID(非主键列),而不是直接依赖于主键,它是通过传递才依赖于主键,所以不符合 3NF。
凯哥Java
2022/12/16
3220
Java面试集锦(一)之数据库(mysql)
程序员过关斩将--数据库的乐观锁和悲观锁并非真实的锁
我们平时编写程序的时候,有很多情况下需要考虑线程安全问题,一个全局的变量如果有可能会被多个同时执行的线程去修改,那么对于这个变量的修改就需要有一种机制去保证值的正确性和一致性,这种机制普遍的做法就是加锁。其实也很好理解,和现实中一样,多个人同时修改一个东西,必须有一种机制来把多个人进行排队。计算机的世界中也是如此,多个线程乃至多个进程同时修改一个变量,必须要对这些线程或者进程进行排队。数据库的世界亦是如此,多个请求同时修改同一条数据记录,数据库必须需要一种机制去把多个请求来顺序化,或者理解为同一条数据记录同一时间只能被一个请求修改。
架构师修行之路
2019/10/22
4870
程序员过关斩将--数据库的乐观锁和悲观锁并非真实的锁
2020数据库面试题
常见的MySQL主要有两种结构:Hash索引和B+ Tree索引,我们使用的是InnoDB引擎,默认的是B+树
Tim在路上
2020/08/04
7490
Java中CAS详解
在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁 锁机制存在以下问题:
全栈程序员站长
2022/09/08
3060
一篇文章弄懂MySQL锁机制
对于行级锁与表级锁的折中,开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般
Java编程指南
2020/11/13
7310
一篇文章弄懂MySQL锁机制
相关推荐
Delta Lake如何自己实现更新操作加速(布隆过滤器)
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文