分区
对表进行分区有两个作用:减少 Athena 需要扫描的数据量,并让数据湖能够保留数据库中已经 不存在的历史数据。
如果这里不做任何配置,所有表都会被直接复制——在表变大之前,这样也能很好地工作。
为什么要分区
Section titled “为什么要分区”一张按年和月分区的表,在 S3 中会变成一层层目录:
catalog/{cluster}/vendas.pagamentos/ano=2024/mes=03/*.parquet当查询包含 WHERE ano = 2024 AND mes = 3 时,Athena 只会读取 那一个目录。没有分区
时,它会读取整张表,而这部分开销由你承担。
在 Partições(分区)中,选择集群、数据库和表来创建一份配置。然后定义分区列:一个 用于计算取值的表达式,以及生成的列名。
year(CAST(payment_date AS TIMESTAMP)) 记为 anomonth(CAST(payment_date AS TIMESTAMP)) 记为 mes顺序很重要:由粗到细。年在月之前,月在日之前。
该用几个层级
Section titled “该用几个层级”每一种不同的取值组合都会生成一个目录,目录过多会拖慢写入,甚至耗尽作业的内存。
Data Pump 会根据可用内存和表的宽度自动计算能容纳多少个目录——一张 40 列的表能支撑的 目录数远少于一张 3 列的表。超出上限时,它会分批写入,而不是直接失败。
即便如此,仍值得有意识地选择:对一张有数年历史的表按天分区会产生成千上万个目录,而查询 很少需要这种粒度。通常按年和月就足够了。
排序字段确实存在,但代价很高:在写出第一行之前必须对整张表排序。Parquet 本身已经记录了 每个块的最小值和最大值,因此即使不排序,Athena 也能有效过滤。只有在你明确知道原因时 才使用它。
替换直接复制
Section titled “替换直接复制”默认情况下,一张分区表在数据湖中会出现 两次:整表复制的版本和分区后的版本并存。 这是有意为之——便于在迁移期间对比两者。
当你只需要分区版本时,开启 替换直接复制。
开启 保留长期历史 后,该表会被写入一个不会在每次执行时清空的区域。
正是这一点让你能够 从 Postgres 清理旧数据而不丢失它们:从数据库中移除的月份在数据湖
中依然可查。catalog/ 反映数据库当下的状态,longterm/ 则持续累积。
每次执行只会重写该次导出中包含的分区,数据库中已不再产出的分区保持原样。
更新发生变化的行
Section titled “更新发生变化的行”单靠历史保留,对于行一旦写入就不再改变的事务型表已经够用。但如果已接入的某一行在数据库 中被修正,数据湖中仍会保留旧版本——而且这一行可能位于一个已经不再出现在导出中的分区里。
开启 更新发生变化的行,并指定:
- 键列 — 用于标识行,从而确定要替换哪一行
- 如何识别变化 — 一个条件,用于在导出中筛选出自上次接入以来发生变更的行
(例如
updated_at > current_date - 7)
Data Pump 会定位包含这些行的文件,并用新版本替换旧版本。
每次替换之前,先前的状态都会被保存到 longterm/overrides/{export}/。这是关于被替换
内容的唯一记录:一旦某次替换出错,就要从这里恢复。重新处理同一个导出不会覆盖这份记录。
重复执行同一次接入
Section titled “重复执行同一次接入”同一个导出执行两次是安全的。写入会在重写前先删除对应分区,替换则按键列匹配——最终结果 与只执行一次完全相同。