目标场景
批量写入与追加:更偏向“一次写入,多次读取”的场景。
数据以批次为单位写入,生成新文件或版本。
更新操作通过写新版本实现,不支持高效的单行、实时写入
lance的技术问答
01.技术边界在哪里? 最大存多大,能存多少列,速度取决于什么?
每条数据称为“一帧”(frame一组),一组有几百列,
通常包含摄像头(图片)、激光雷达和传感器上报的数据,一帧数据的大小约20MB,通常一秒有若干帧
透明压缩: ZSTD 编码压缩点云数据达 70% 压缩率
而且Lance本身的压缩是定义在schema中的,对于数据的写入或者读取是无感的,透明的
透明压缩: 由存储系统自动完成,应用无感知,常见算法包括 LZ4、Zstd、zlib.透明压缩强调“自动且无感”
非透明压缩: 需应用显式调用压缩/解压接口 常见算法包括 gzip、Snappy
02.竞品的比较,作为存储格式的和parquet的比较,作为lancedb 和Hbase中的概念比较
HBase HBase的核心数据结构是列族(column family)
在实际应用中,一张表通常只设计 1 到 2 个列簇
通过行键(RowKey)快速定位到具体的Region和数 在线、实时读写
设计哲学、目标场景和底层实现有着根本性的不同
03.业务场景的匹配和处理方案
001.如何处理多写的情况,并发写等
支持ACID事务语义
002.增加列和减少列怎么处理
04.如何组织--谓词下推
将过滤条件(如 WHERE age > 18)尽可能推到数据读取的最底层,避免加载无关的数据。
列式文件(如 Parquet 的 Row Group、ORC 的 Stripe)会为每个列存储最小值、最大值、空值个数等统计信息
Lance 的 miniblock 也可以携带轻量统计
列式存储的谓词下推主要通过元数据统计(min/max/null count)和页级统计来实现“跳过不相关数据块”,
“行筛选”或 “行定位
因为每列独立存储。因此需要将过滤结果(符合条件的行号)传递到其他列的读取过程中。
生成一个行号列表或位图(bitmap)-使用位图批量提取值
投影列读取:根据这些行号,去其他列(如 name, address)的相应页中,只提取对应位置的值
说明
固定宽度列-可变宽度列
-嵌套层级数量、数据类型大小或所用压缩方式
Parquet 行组(row group) 列块(column chunk) 一个 页(page)
是列块中的一小段数据
业务场景说明
全扫描 :模型训练、批处理(列存擅长)
随机存取:向量检索、点查、RAG、多模态读取(列存不擅长)
适用场景
machine learning workflows
不适用的场景
技术说明
现代化列式数据格式,
01.统一管理多模态数据:
将非结构化数据(如图像)和结构化数据(如元数据、标签)存储在同一张表中
02. 高效的数据版本控制: 自动记录每一次数据变更,确保实验的可复现性
所有的数据写入、更新或删除操作都会自动生成一个新的、不可变的数据集版本
(新版本的数据处理,不干扰老版本的数据训练);
03.高性能的查询与搜索: 支持对数据过滤和向量相似度搜索
将 SQL 谓词直接下推到存储系统,可以显著减少扫描过程中的 I/O 负载
列式格式的一个显著特点,Lance 允许快速随机读取
结构
文件格式本身负责定义文件的整体结构和主要组件。
在格式内部,存在结构编码机制,用于控制如何将嵌套数据类型拆分为独立的数组或缓冲区。
编码- Lance 自适应结构编码
1. 大数据类型(≥128B,向量、图像、长文本)使用 Full Zip 全拉链编码
2. 小数据类型(<128B,数字、短字符串) 使用 Miniblock 迷你块编码
里面有真实数据的data目录,也管理版本的_versions目录,以及管理事务的_transactions目录
演示
概念补充
磁盘读取:顺序读取(sequntial read)是指顺序地读取磁盘上的页。
随机读取(random read)是指访问的页不是连续的,需要磁盘的磁头不断移
文件读取: 文件顺序读取 从开头读到结尾 随机读取(random read)是指访问的页不是连续的
随机读与批量扫描 随机访问模式(通常称为点查
列式存储的问题是否正在被解决
01.删除操作效率低下(Deletion Compliance 挑战--通过删除索引,
GDPR(欧盟通用数据保护条例)和CCPA(加州消费者隐私法案) 要求物理删除
Compact
lance.dataset.DatasetOptimizer.compact_files() 方法重写数据文件,
压缩过程中,Lance 还可以删除已删除的行。重写的片段将不再有删除文件
行是通过在单独的删除索引中标记为已删除来删除的。这比重写文件更快,也不会使指向这些文件的索引失效
02. parquet在10,000列时元数据解析需要52ms
03.操作
merge_insert
04.Schema Change:在数据集中添加、删除和修改列
parquet
1.data :
定长和变长
atomic和group
应用场景: 更适合分析型查询,查询通常涉及少量列
列存储由于数据更新成本比行存储高,一般适合读多写少的场景
2.data-model-Schema-数据模型
record记录--字段
Schema的最上层是message,里面包含字段。
每个字段有3个属性:重复性(repetition)、类型(type)和名称(name)
--字段类型和字段重复次数
data type
data reption{optional required repeated}
由于optional和repeated类型的存在.编码嵌套列 Definition Levels
使用 dremel 通过 definition levels 和 repetition levels 来实现
Repetition levels:用以表示在该字段路径上哪个节点进行了重复
Definition Levels:用以表示该字段路径上 有多少可选的字段 实际进行了定义
Schema 模式演进
3.data 数据编码
数据转换成编码形式
Plain编码 对数据没有压缩和其他处理
RLE编码 Run-Length encoding算法,针对连续重复的数据,记录重复次数及对应值
bit-packing 将多个值打包到一个空间中
Dictionary Encoding 编码
压缩
压缩率(同列数据类型一致)、更快的列扫描速度
4.data-store-format 存储模型
Parquet 的存储模型主要由 行组(Row Group)、列块(Column Chuck)、页(Page)组成
Parquet 格式支持 谓词下推,即在查询时将过滤条件直接应用于磁盘上的数据
Parquet 文件是由 文件头、元数据、数据块 等部分组成
File Header
Metadata 描述文件级别 schema(数据模式)、数据的列名称和类型
描述数据的各列 括列的名称、类型、数据的编码方式、压缩方式
描述数据数据块(page),每个数据块也会包含自己的元数据
Data Pages 数据页都有自己的元数据(如压缩格式、行数等),并按列进行存储
Checksum
File Footer 尾部元数据 包含了文件的索引和元数据的偏移量
5.设计选择
成本模型: 存储成本-开发运营成本--使用成本和收益
存储设计决策:如何布局行、何时进行压缩、基于什么进行分区。这些决策对成本和查询性能的影响
决定这些文件的存储位置以及由什么来读取它们
S3 兼容的存储中
查询引擎--模式演进、ACID(原子性、一致性、隔离性和持久性)事务以及自动分区管理等功能
实际业务
时间戳、标识符和数值
单用时间分区会导致热点--
二维分区:时间和空间-基于区域或设备类型等的自然分组方式 序列标识(series_id)的哈希值
衍生数据-预先计算更粗时间间隔的聚合数据
3.Parquet 是如何“知道”要跳过/扫描哪个行组?
每个 Parquet 文件都包含“有关数据的数据”,引擎首先读取元数据
建议单个 Parquet 文件的大小至少应为几百 MB。
正确分区能让引擎直接跳过无关文件和row group
如何找到对应列的相应行 --依赖行组对齐 + 扫描 + 位图物化的模式,并可辅以 Page Index 进行优化
同一个Row Group内各列的行数是相同的(按行对齐) 行组是数据的水平分片
过滤列(比如age)的读取 行索引列表(相对于Row Group内的偏移)
对于投影列(比如name)
V1: "page index"来定位行号所在的page,然后只读取该page并扫描到对应的
V2: "Column Index"和"Offset Index 允许根据行号快速定位到包含该行的page以及page内的偏移
先扫描过滤列得到位图,再扫描其他列并根据位图物化结果
Parquet并没有设计成支持高效点查询或按行号随机访问其他列,而是通过一次全扫描(只扫描需要的列)结合位图过滤来实现
Parquet通常采用基于行组的全扫描+位图过滤方式 行组和页机制巧妙地将“找”和“读”两个步骤解耦,利用顺序扫描的流式处理
4.大宽表
于Parquet这类格式,它的元数据(Metadata)记录了文件中所有列的信息
当表变得极宽时,仅仅是读取和反序列化这个庞大的元数据文件 Parquet基于Thrift的元数据结构
Parquet 将所有元数据(统计信息、编码信息)集中存储在文件末尾
5.Parquet 将数据首先按行切分成大的行组 (Row Group)。在一个行组内,所有列的行数必须完全一致,形成一个规整的矩形
lance 将每一列视为一个独立的数据流。它的切分单位是物理大小固定的“页” (Page)。
对于不同数据类型的列,一个页内包含的行数是不同的
page 页内部,Lance 也提供了不同的数据排布策略,以应对不同类型的数据
Mini-Block 编码:主要用于整数、小字符串等尺寸较小或可变的数据
Full Zip 编码 专为向量等尺寸较大的定长数据设计
Blob布局:这种布局用来存储更加大的数据,例如图片、视频等
lance的处理方式
Lance 格式的核心设计 :二维存储布局(行被划分为垂直片段,每个片段再水平划分为数据文件)
旨在高效处理机器学习工作负载中常见的 " 宽表 " 和模式演进问题 。
Lance 文件格式的 " 部分读取不要求加载整个文件
.lance 数据文件内部记录单个文件中的列分布,然后在数据集层面,通过独立的 Manifest(清单文件) 来整合所有文件的列
一个 .lance 文件 三级元数据体系 如文件整体的 Schema 定义
lance v2设计各列独立写,写入达到page size后刷到磁盘,各列按照独立固定合适大小写出
Manifest 表格式将多个 .lance 数据文件组织起来
数据集快照 (Snapshot) 列 Schema 定义
数据分片 (Fragments) 数据管理的逻辑单元
添加新列时只需创建新的 Manifest 版本,将新列数据写入新文件,并关联到对应的 Fragment 上,无需重写旧文件
追加写(Append-only)和不可变(Immutable)
Lance 的这种将物理信息(文件内的列页面位置)
与逻辑信息(数据集级别的列定义、数据类型)分离的设计,并辅以 Protobuf 的元数据格式
lance文件格式
二维存储布局(行被划分为垂直片段,每个片段再水平划分为数据文件)
lance 的 行级随机访问
物理行地址 (Row Address) 机制
每一行都有一个64位的逻辑行 ID (Row ID) 和物理行地址 (Row Address)
01.稳定的逻辑ID(如 _rowid)能保持不变
分配与管理状态记录在Manifest(清单文件)和Fragment的元数据
02. (fragment_id << 32) | local_row_offset 构成 存储在实际数据文件 (.lance) 的页元数据中
Lance 支持建立多种二级索引(如 B-Tree、向量索引等),索引直接引用 物理行地址 或 逻辑行 ID
lance 的存储模型主要由 Data Pages
lance 文件是由 数据页 等部分组成 构成
Data Pages Column Metadatas Column Metadata Offset Table Global Buffers Offset Table Footer
文件末尾是 Footer,包含 Column Metadata 和 Global Buffers,读取方式为先读 Footer,再按需定位并读取实际的数据 Page
读取步骤
01. loading the footer
02. Then parse the footer and read the rest of the metadata
03. scan through the pages for each column to determine which pages are needed.
Each page stores the row offset of the first row in the pag
data/ 存文件,_versions/ 存清单,_transactions/ 存事务日志
_indices/{UUID-*}/index.idx:二级索引目录。每个二级索引(如向量索引或标量索引)都有一个独立的目
lance 的表格式
Schema Format
all fields, their data types, and metada
Fragments
The Lance table format organizes datasets as
versioned collections of fragments, data files, deletion files, and indices
Rows are partitioned into fragments
数据格式
CIDR '25上发表了一篇名为《Bullion: A Column Store for Machine Learning》的论文
长序列稀疏特征与超宽表 (Sparse Features and Wide Tables) 能存储多少列??
ACID 事务:这是 Lakehouse 得以成为可靠数据源的根本。它保证了并发读写的一致性和隔离性,彻底解决了数据湖的可靠性问题。
数据版本控制(时间旅行):由于所有历史版本都记录在日志中,
用户可以轻松查询任意时间点的表状态,这对于审计、错误回滚和保证可复现的机器学习实验至关重要。
模式强制与演进 (Schema Enforcement & Evolution):
云计算
统一 BI 和 AI
数据存储层 (Data Storage) 原生的数据类型 VARIANT
元数据管理(Metadata) 端到端加密 缓存 (Caching)--“开放表格式
数据计算层 (Data compute)
解决方式--自动列式提取优化
对于这些高频字段,将半结构化数据在物理层面转换成了结构化数据。
对于那些稀疏或结构极其不固定的字段,则依然以序列化的形式存储。
参考
智驾场景 https://www.volcengine.com/docs/6491/1544103?lang=zh
火山引擎LAS基于Lance的PB级智驾数据湖方案 https://developer.volcengine.com/articles/7530564919285497919
胡津铭 https://zhuanlan.zhihu.com/p/1979139469253821854
浙公网安备 33010602011771号