Delta 初探

文章目录

    • Delta Lake
    • Transactional metada 实现
      • 并发控制
      • 乐观并发控制(optimistic concurrency control)
    • 使用
    • 结语

Delta Lake

在说 Delta Lake 之前,不得不说下 Data Lake ,Data Lake 的主要思想是将企业中的所有数据进行统一管理。例如基于 Hadoop 的 Data Lake 方案可以非常低成本的存储所有类型的数据。


显而易见的,它只支持批量插入,用户读取时无法获取当前更新的数据,多用户写还可能会发生异常,带来脏数据的问题,并不是非常可靠。又比如在 hadoop 生态中,困难的更新/删除操作 (需要整块重写),无法保证数据一致性 (例如 spark 读取时缓存了 parquet 元数据,若元数据变化需要进行 refresh)


Delta Lake 不仅能解决上述问题,还能对数据进行各种增强,例如 time travel, index 等。

Delta Lake 在近期开源 项目地址,发布了其第一个 release 版本,使用 parquet 文件格式,在其上通过 transaction log 记录更新操作从而提供了 ACID 事务等能力,通过与 spark 集成,得以处理大量的元数据。

Delta Lake 可以更简单的理解为构建在 Data Lake 上的一层数据库。巧妙的地方在于,它不是一种 file format 而是 table format ,这意味着你不需要修改底层的存储文件,他解放了用户花费在数据一致性上的大量时间,分析时也无需关心表中的数据是来自流数据还是历史数据。此外,通过时间旅行的功能,可能解决很多非常麻烦的事情,例如事件回放。

Delta 初探_第1张图片

Delta lake 相关特性在官网上已经说的非常详细,不再赘述,根据笔者自己的理解简单描述一下五个比较重要的特性:

  1. ACID transactions:在数据上的操作变的可靠。
  2. Schema enforcement: 自动处理表变化,例如 shema 发生变化的数据插入会被拒绝。
  3. Streaming and batch unification:统一了流和批处理,在以前,通过 spark streaming 可以非常容易的处理当前事件,但是无法处理历史数据或是进行一些复杂的机器学习分析。这个时候大家很容易就能想到 lambda 架构,在 Delta Lake 中,不论表是流式追加还是静态,不同的用户同时对它进行读写操作,都是透明的,不会发生异常和并发问题。这意味着你可以直接查询当前最新的数据或是历史的数据。
  4. Scalable metadata handling:随着数据的增加,元数据也会变得非常大,甚至可能会超过数据本身。显而易见会带来元数据恢复的问题(可以想象一下 hdfs namenode 启动时缓慢的元数据加载过程),因此把超大的元数据存放在 hive metastore 或是其他的系统中并不现实,同时非常难以管理。因此可以通过 Spark 强大的分布式处理能力来解决这个问题,例如轻松处理pb级表的所有元数据和数十亿个文件(类似读取 parquet 的分区发现)。
  5. Time travel:时间旅行,这样说可能有点抽象,它意味着你可以提供时间戳回朔到当时的数据状态。这在机器学习之类的事件回放场景太有用了(不用担心由于数据更新导致多次运行时 input 数据不一致)。其他的一些场景例如数据版本回滚(类似 git reset),完整的历史审计日志跟踪等。

Transactional metada 实现

在文件上增加一个日志结构化存储(transaction log ),该日志有序(ordered)且保持原子性(atomic)

增加或者删除数据时,都会产生一条描述文件,采用乐观并发控制 (optimistic concurrency control) 保证用户并发操作时数据的一致性

并发控制

Delta Lake 在读写中提供了 ACID 事务保证。这意味着:

  • 跨多集群的并发写入,也可以同时修改数据集并查看表的一致性快照,这些写入操作将按照串行执行
  • 在作业执行期间修改了数据,读取时也能看到一致性快照。

乐观并发控制(optimistic concurrency control)

Delta Lake 使用 optimistic concurrency control 机制提供写数据时的事务保证,在这种机制下,写过程包含三个步骤:

  1. Read: 读取表最新可用版本,以确定哪些文件需要被重新定义
  2. Write: 将所有的修改写成新的数据文件
  3. Validate and commit: 在提交变更之前,检查自读取该快照以来,和其他并发提交的变更是否有冲突。如果没有冲突,所有的变更会被提交并生成一个新版本快照,写操作成功。如果发现了冲突,该写入操作会因为并发定义异常失败,而不会像开源版的 spark 一样破坏数据。

使用

在 spark 中,为了提高查询性能会缓存 parquet 的元数据,如果此时表的 schema 被更新必须要手动刷新元数据才能保证数据的一致性,例如 spark.sql(refresh table xxx)

因为 delta Lake 的特性,数据更新后也无需调用 refresh,目前仅支持 HDFS 作为底层存储

// 将已有 parquet 数据转化为 delta table 
val df = spark.read.parquet("/path/to/your/data")
df.write.format("delta").save("/delta/delta_table")
spark.sql("CREATE TABLE delta_table USING DELTA LOCATION '/delta/delta_table'")

delta 表根目录 _delta_log 下会生成 json 格式的事务文件

hdfs dfs -cat /delta/delta_table/_delta_log/00000000000000000000.json

{"commitInfo":{"timestamp":1555989622032,"operation":"WRITE","operationParameters":{"mode":"ErrorIfExists","partitionBy":"[]"}}}
{"protocol":{"minReaderVersion":1,"minWriterVersion":2}}
{"metaData":{"id":"f33f4754-7c0c-43b8-87a2-ddc21e1311ea","format":{"provider":"parquet","options":{}},"schemaString":"{\"type\":\"struct\",\"fields\":[{\"name\":\"id\",\"type\":\"integer\",\"nullable\":true,\"metadata\":{\"comment\":\"\"}}]}","partitionColumns":[],"configuration":{},"createdTime":1555989621467}}

结语

Delta Lake 的理念给了大数据处理太多的想象空间。
目前大数据处理现在都朝着流批一体,ACID 事务,HTAP,AI,API易用性前进,得益于 databricks 优秀的商业策略,spark 无疑是其中走的最稳最好的。

你可能感兴趣的:(Spark,Delta,Lake,生产环境中的spark)