继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

揭秘Fluss核心功能 - 底层存储和查询

匆匆那年9239
关注TA
已关注
手记 31
粉丝 3
获赞 1

大家好,我是大圣。

Fluss 提供了可靠的底层存储设计与灵活的查询更新机制。然而,这一切听起来似乎很复杂,里面有太多看似晦涩的技术名词——比如日志表(LogTablet)、键值表(KvTablet)、Tablet、TabletServer 等等。

那么,Fluss 的存储到底是怎么运作的?本文将从一个具体的数据例子出发,带你逐步了解 Fluss 的底层存储逻辑,以及查询和更新数据时,系统背后的变化过程。


从一个具体的例子开始

假设我们在 Fluss 中创建了一个表,用于记录用户的行为数据。这张表的结构如下:

CREATE TABLE user_activity (
  user_id BIGINT,
  activity STRING,
  event_date DATE,
  score INT,
  PRIMARY KEY (user_id, event_date) NOT ENFORCED
)
PARTITIONED BY (event_date)
WITH ('bucket.num' = '2');
  • 字段解释
    • user_id:用户的唯一标识。
    • activity:用户的行为类型(如登录、浏览)。
    • event_date:行为发生的日期。
    • score:行为得分。
  • 配置说明
    • 主键是 user_idevent_date,这意味着每个用户在每一天的行为记录是唯一的。
    • 表按 event_date 进行分区,每个分区包含 2 个桶(bucket.num=2)。

接下来,我们向表中插入以下三条记录,模拟一些用户行为:

user_id activity event_date score
1 login 2023-01-01 10
2 browse 2023-01-01 15
1 purchase 2023-01-02 50

1. 数据写入时的存储过程

Fluss 是如何存储这些数据的?下面我们从 分区存储、分桶存储、日志表存储键值表存储 四个角度详细说明。

1.1 分区存储

Fluss 按照 event_date 字段对数据进行分区,将数据分为不同的分区。我们的数据中有两个不同的 event_date

  • 分区 event_date=2023-01-01

    user_id=1, activity=login, score=10
    user_id=2, activity=browse, score=15
    
  • 分区 event_date=2023-01-02

    user_id=1, activity=purchase, score=50
    

分区存储的好处在于,可以按照数据的某些维度(如时间、地域等)将数据进行逻辑划分,提高查询效率和管理方便性。

1.2 分桶存储

每个分区中的数据会进一步按照 哈希算法 分配到不同的 中。假设我们将 user_id 作为分桶的依据,并设置每个分区有 2 个桶(bucket.num=2)。

  • 对于 user_id=1,哈希值是 1,1 % 2 = 1,所以它被分配到 桶 1
  • 对于 user_id=2,哈希值是 2,2 % 2 = 0,所以它被分配到 桶 0

这样,分区和分桶的最终数据分布如下:

  • 分区 event_date=2023-01-01
    • 桶 0:user_id=2, activity=browse, score=15
    • 桶 1:user_id=1, activity=login, score=10
  • 分区 event_date=2023-01-02
    • 桶 1:user_id=1, activity=purchase, score=50

通过 分桶,Fluss 实现了数据的并行处理,使得每个桶的数据可以单独进行处理,极大地提升了并发性能。

1.3 存储到日志表(LogTablet)

Fluss 使用日志表(LogTablet)记录所有的 数据变更(插入、更新、删除)。每条数据变更都会按顺序写入 .log 文件,并生成对应的 .index 文件,便于快速定位。

  • 日志表(.log 文件) 存储的内容:

    [Offset=0] user_id=1, event_date=2023-01-01, activity=login, score=10
    [Offset=64] user_id=2, event_date=2023-01-01, activity=browse, score=15
    [Offset=128] user_id=1, event_date=2023-01-02, activity=purchase, score=50
    
  • 日志表的索引(.index 文件) 内容:

    Offset=0 -> user_id=1, event_date=2023-01-01
    Offset=64 -> user_id=2, event_date=2023-01-01
    Offset=128 -> user_id=1, event_date=2023-01-02
    

日志表保证了每一条数据变更都被记录下来,并且通过索引文件可以高效定位到变更记录。

1.4 存储到键值表(KvTablet)

键值表(KvTablet)用于存储 每个主键的最新状态,即每个用户在每一天的最新行为记录。在 Fluss 中,键值表的底层存储使用了 RocksDB,它基于 LSM 树 的设计,支持高效的查询和更新操作。

键值表中存储的数据如下:

Key Value
1-2023-01-01 {"activity":"login", "score":10}
2-2023-01-01 {"activity":"browse", "score":15}
1-2023-01-02 {"activity":"purchase", "score":50}

通过键值表,我们可以快速查询某个主键的最新数据,例如查询 user_id=12023-01-02 的行为记录,直接从键值表中获取 {"activity":"purchase", "score":50}


2. Fluss 中的 Tablet 和 TabletServer

在 Fluss 中,TabletTabletServer 是关键组件,负责管理和存储数据。接下来我们将详细讲解这两个概念如何与数据存储和查询流程结合。

2.1 Tablet
  • Tablet 是 Fluss 中的最小存储单元。每个 Tablet 存储一定范围的数据,这些数据根据 分区(如 event_date分桶(如 user_id 进行划分。
  • 数据存储到 Tablet 中时,根据 分区字段(例如 event_date)和 桶字段(例如 user_id)进行分配。

例如:

  • Tablet 1 存储分区 event_date=2023-01-01 的数据,并按 user_id 哈希值分配到桶中:
    • 桶 0:user_id=2, activity=browse, score=15
    • 桶 1:user_id=1, activity=login, score=10
  • Tablet 2 存储分区 event_date=2023-01-02 的数据,并将所有数据分配到桶 1:
    • 桶 1:user_id=1, activity=purchase, score=50

每个 Tablet 可以包含多个桶的数据,它们是数据存储的 逻辑单元,存储在物理硬盘上,支持高效的并行存储和查询。

2.2 TabletServer

TabletServer 是 Fluss 中的服务端组件,负责管理和处理多个 Tablet。每个 TabletServer 管理一定数量的 Tablet,确保数据的存储和查询能够顺利进行。

  • TabletServer 的工作方式
    • 当客户端发起查询请求时,TabletServer 根据请求中的分区字段(如 event_date)和桶字段(如 user_id),快速定位到目标 Tablet,并从中检索数据。
  • TabletServer 如何处理查询
    • 假设我们查询 user_id=1event_date=2023-01-01 的数据。TabletServer 会首先定位到 Tablet 1,然后根据哈希值找到桶 1,获取数据 user_id=1, activity=login, score=10

3. 数据查询和更新流程

查询流程:

假设我们查询 user_id=12023-01-01 的行为数据。

  1. 查询键值表

    • Fluss 首先会查询 键值表,通过 user_id=1event_date=2023-01-01 直接查找对应的键值。
    • 返回结果:{"activity":"login", "score":10}
  2. 查询日志表

    • 如果需要查看历史变更,Fluss 会查询 日志表。日志表通过 .index 文件快速定位到 .log 文件中的变更记录。

    • 查找到 Offset=0,读取日志记录:

      [Offset=0] user_id=1, event_date=2023-01-01, activity=login, score=10
      
  3. Tablet 和 TabletServer 的作用

    • 在查询过程中,TabletServer 会根据查询条件(如分区字段 event_date)定位到对应的 Tablet,并从该 Tablet 中返回数据。
    • 例如,查询 user_id=1, event_date=2023-01-01 时,TabletServer 定位到 Tablet 1,从中返回数据。
更新流程:

假设我们更新 user_id=12023-01-01score20

  1. 写入日志表

    • 追加一条更新记录到 .log文件:

      [Offset=192] UPDATE user_id=1, event_date=2023-01-01, activity=login, score=20
      
  2. 更新键值表

    • 在键值表中更新对应的键值对:

      Key: `1-2023-01-01`
      Value: `{"activity":"login", "score":20}`
      
  3. Tablet 和 TabletServer 的作用

    • 在更新过程中,TabletServer 会根据数据的位置(由分区字段和桶字段决定),定位到对应的 Tablet,进行数据的更新操作。

总结

Fluss 的底层存储和查询更新流程通过 分区分桶日志表键值表 的协同工作,保证了高效的数据存储、查询与更新:

  1. 分区存储:数据按 event_date 进行分区,方便管理和查询。
  2. 分桶存储:每个分区的数据根据 user_id 划分到不同的桶,支持并行查询和处理。
  3. 日志表存储:记录所有变更数据,支持数据追溯和恢复。
  4. 键值表存储:存储最新的主键值,支持高效查询和更新。
  5. Tablet 和 TabletServer:通过 Tablet 来管理数据,TabletServer 管理多个 Tablet,确保数据存储和查询操作的高效执行。

写在最后

上面的内容通过一个具体数据的例子,简单梳理了 Fluss 的底层存储和数据查询、更新的过程。后面的文章会陆续更新 Fluss 的文章,让大家对 Fluss 这个后起之秀有更直观的认识,最后欢迎大家关注"大圣数据星球"微信公众号,一起来讨论大数据技术。

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP