爱奇艺在Iceberg落地 性能优化与实践 演讲人:林豪-爱奇艺-助理研究员 爱奇艺OLAP团队 报表自助查询运营实时分析 ClickHouse明细查询秒级延迟 Trino灵活查询秒级延迟 SparkSQL 批处理/灵活查询分钟级延迟 Pilot 智能SQL引擎 应用 查询引擎 HDFS 离线数据 Kafka 实时数据 Iceberg 近实时数据 存储 目录 数据湖平台建设 02 业务落地 04 01 为什么要数据湖 03 性能优化 为什么要数据湖 数据湖技术加速数据流通 优缺点适用场景 离线 HDFS 离线数据开发平台 离线数仓 (Hive) new Pingback 投递 近实时 近实时数仓 (数据湖) 实时 小时级~天级延迟 成本低PB级容量 报表 核心指标 流数据服务平台 (Kafka) 实时计算平台 实时数仓 (Kafka) 分钟级延迟性价比高PB级容量 秒级延迟成本高 只存放最近几个小时数据 运营:广告、会员实时监控:直播大盘UG 实时广告实时推荐实时热度 Iceberg定义–新型表格式 Iceberg:一种新设计的开源表格式用于大规模数据集分析 -不是存储引擎:支持HDFS、对象存储 -不是文件格式:使用Parquet存储数据 -不是查询引擎:支持Spark/Flink/Presto/Hive ၞձۓක )OiQN/6SDUN 64/ັᧃක 3UHVWR/6SDUN64//+iYH ,FHEHUJ +D)6 زկғᛔਧԎ໒ୗහഝկғ3DUTXHW ਂؙ 表格式–Hive及其缺陷 SELECTcount(1)FROMhuge_table WHEREdt>='2021-11-01’ ANSdt<='2021-11-07’ ANDmetric='metric-name’; 如每天240分区,100文件/分区,7天命中24K个分区 0HWDVWRUH 0\64/ හഝପғᤒמ௳ғړ܄מ௳ғ හഝկ +D)6 dt=2021-11-07/ _–KRXU=20 _–_–ŏ _–KRXU=21 _–_–000000B0 _–_–ŏ _–_–000128B0 _–ŏ 设计核心 ●用目录树组织数据 ●Metastore记录元数据 优点:分区级过滤 缺点 ●元数据库容易成为瓶颈,与HDFS不一致 ●元信息不包含文件信息 ●执行计划需列举目录 ●O(N)次调用,N为命中的分区数 ●无法用文件级统计进行过滤 ●原子操作:仅添加分区是原子操作,且依赖文件系统移动为原子性 ●不支持修改:分区覆盖、分区重算 表格式–Iceberg IcebergCatalog数据库:db 表信息:table 当前快照: 元数据层 Metadata S0 Metadata S0 S1 Metadata S0S1S2 Manifest List Manifest List Manifest List R W 读写分离 Manifest File Manifest File Manifest File 数据层 DataFiles DataFiles DataFiles 核心:记录表的所有文件 -快照:表文件的完整列表 -写入:每次写入创建并提交一个新的快照 快照概念优点 ●读写分离:写不影响读行为 ●操作原子:跨分区修改,合并/重写文件 表格式–HiveVSIceberg 特性 Hive Iceberg 规模/成本 PB级、廉价 PB级、廉价 修改粒度 分区级覆盖 文件级更新,支持跨分区修改 提交机制 重命名数据文件,对象存储需复制 无需重命名 下游重新计算 修改涉及所有分区 增量拉取,仅快照间增量部分 时效性 离线,天级/小时级 近实时,5分钟级 制定执行计划列举目录调用数 O(N),N=查询命中分区数量,对象存储前缀列举 常数级,元文件+Snapshot文件 分区过滤 分区值 分区值、隐式分区 文件过滤 无 列值基于统计、字典等过滤 版本控制 无 可回滚到特定快照版本 行级更新 无 V2格式支持 数据湖平台建设 平台总览 数据湖平台 元数据管理 权限管理 ⽣命周期TTL、合并 监控、告警 Pilot Trino/SparkSQL Venus ⼴告 审核 Pingback 会员订单 业务 Iceberg 数据⽂件 Parquet 元⽂件 快照 查询 RCP 流批⼀体 Babel离线ETL 摄 ⼊ ⽇志 Venus ⽤户数据 Pingback 监控 Hubble 业务数据 DBIO 离线数据 HDFS 实时数据 Kafka 近实时数据 Iceberg 数据源 流式入湖 三步即可入湖 1.配置读取Kafka 2.配置处理逻辑 3.配置写Iceberg 魔镜 交互查询平台 出湖查询 ⼴告、会员等业务 Venus ⽇志平台 查询入口 ●Pilot:智能SQL引擎提供统一入口 Trino Pilot-智能SQL引擎 ●魔镜:交互式查询平台 Iceberg SparkSQL V2格式 ⾏级更新/删除 V1格式 AppendOnly 查询引擎 ●Trino:支持V1格式 ●SparkSQL:支持V2格式 指标 ●每天34K查询,耗时P9042秒 性能优化 小文件–生命周期 MySQL TTL配置 数据湖平台 定时任务 ⻚⾯配置 配置策略 线程池清理线程 业务●建表时分区表必须指定清理策略 1.dropPartition2.expireSnapshot3.removeOrphanFiles4.rewriteManifests 清理优化 ●Spark常驻模式:避免申请Yarn耗时 ●天级目录删除:递归删除孤儿文件慢/分区目录不被清理 ●回收站:添加回收机制、避免误操作 ●对于大表接入TTL:原先为一次性删除所有过期的分区,遇到任务执行过久一直失败,改为每次删除固定数量的分区 清理效果 ●每个表:4文件/commit*1commit/1分钟*60*24*7 分钟=40K文件 ●日志库从2亿Inode稳定在4千万 各分区 均⽅差 计算任务 小文件–智能合并 待合并分区 执⾏合并 定时合并 ●合并任务参数复杂,配置困难 ●合并时机、合并范围:譬如3小时后合并小时分区,一天后合并天分区 ●如合并范围过小:则小文件过多,查询性能下降 ●如合并范围过大:则有重复合并,写放大 智能合并 ●基于分区下文件大小均方差自动选择待合并分区 表权重 因⼦ 筛选分区 i"# ●𝑀𝑆�=∑� 2÷� 𝑇𝑎�����−𝐴𝑐����� ●微调:业务设置权重、执行失败权重降级 ●业务无需任何配置 参考:Netfilx-Optimizingdatawarehousestorage 各分区 均⽅差 计算任务 小文件–智能合并 待合并分区 执⾏合并 定时合并 ●合并任务参数复杂,配置困难 ●合并时机、合并范围:譬如3小时后合并小时分区,一天后合并天分区 ●如合并范围过小:则小文件过多,查询性能下降 ●如合并范围过大:则有重复合并,写放大 智能合并 ●基于分区下文件大小均方差自动选择待合并分区 表权重 因⼦ 筛选分区 i"# ●𝑀𝑆�=∑� 2÷� 𝑇𝑎�����−𝐴𝑐����� ●微调:业务设置权重、执行失败权重降级 ●业务无需任何配置 参考:Netfilx-Optimizingdatawarehousestorage 小文件–合并性能优化 DeleteFile合并后没有被删除 ●背景:ISSUE1028,最终在ISSUE2294修复 如果Delete之后紧跟RewriteDataFile,相应的DeleteFile不会被删除 ●背景:ISSUE4127,目前仍未修复,ISSUE6126在跟进 大表合并任务经常失败 ●Bucket分区:减少单次合并的数据量 ●BinPack合并:控制合并文件大小范围 小文件合并任务经常因冲突而失败 现象:Cannotcommit,foundnewpositiondeleteforreplaceddatafile 原因:ISSUE5404:判断待合并的DataFile没有新的DeleteFile时,Upper和Lower被截取了16bit,从而错误的判定datafile被引用修复:altertableiceberg_tablesettblproperties('write.metadata.metrics.default'='full’); 小文件–写入参数控制 假设:任务并行度=100,hour分区跨度=1 dt=2023-03-11 slot0 … Transform+Sink Source Transform+Sink Source KafkaFlink任务 slot99 Iceberg表 hour=00hour=01 … hour=23 默认策略:100个文件,小文件过多Hash策略:1个文件,容易写入阻塞Hash策略+bucket分区: ●通过bucket数量控制文件数 distribution-mode=none,每个sink都会写⽂件 ●建议文件大小在百MB KafkaFlink任务 Source Transform slot0 Transform Source Transform Source slotNslot99 Iceberg表 Sink Sink DistributionMode Bucket分区 每次Commit文件数 none none 任务并行度*时间跨度分区数=100 hash none 时间跨度分区数=1 hash bucket=10 时间跨度分区数*Bucket数=10 dt=2023-03-11 hour=00hour=01 … hour=23 Sink ShuffleByPartitionKey(dt=xx/hour=xx/bucket=0) distribution-mode=hash,按分区Key重分布 查询优化–ID查询慢 示例:指定订单ID查询明细 SELECT* timestamp order_id row0 T0 666 row1 T0 444 row2 T0 333 row0 T1 111 row1 T1 555 row2 T1 999 … … row0 TN 321 row1 TN 654 row2 TN 987 FROMorder_tableWHEREorder_id='555'; FileA FileB FileN 指定ID明细查询慢 ●Impala+Kudu:3秒 ●Spark+Iceberg:948秒 原因 ●Kudu对列有构建索引 ●IcebergMinMax、字典等索引对此不生效,几乎是全表扫描 思考 ●能否为Iceberg引入BloomFilter过滤能力? ●背景:ParquetSupportBloomFilterSince1.12 查询优化–开启BloomFilter timestamp order_id BF(order_id) row0 T0 666 row1 T0 444 row2 T0 333 row0 T1 111 row1 T1 555 row2 T1 999 … … row0 TN 321 row1 TN 654 row2 TN 987 FileA FileB FileN SELECT* 01000 0 1010 10000 1 0001 00110 0 1000 FROMorder_tableWHEREorder_id='555'; Iceberg表参数开启 ●write.parquet.bloom-filter-enabled.column.col1 Spark/Trino读取应用 ●可过滤:equals、in ●不可过滤:notequals、notin、les