雅典娜-制作当前/最新分区

时间:2020-09-25 08:56:41

标签: amazon-s3 amazon-athena

我在s3中有一个数据集,正在通过Athena查询。 每x分钟写入一次新版本的分区中的分区(Boto3 / Lambda)。 最终,我需要通过Athena SQL有效地查询数据的“最新”版本。

目前为止有效的策略,但是有“问题” ....

  1. 数据以包括'/ stamp = YYYY-MM-DD hh:mm:ss /'的索引写入S3
  2. 雅典娜查询将新的分区添加到表中以获取“邮票”值,但还包括一个额外的分区“ Current = Yes”
  3. 雅典娜查询将以前的分区从“ Current = Yes”更改为“ Current = No”

现在所有这些工作正常,但是我担心随着时间的推移会发生切换。 使用Workbench / J(在Lambda中使用时可能更少,但是使用相同的主体)

  • 添加新分区需要1.18-2.56秒。
  • 将先前的分区从“ Current = Yes”更改为“ Current = No”需要相同的时间(2秒)

因此,至少要等待2秒,我的数据集才会产生不正确的结果。 或者,如果我先执行“ current = No”,然后添加新分区,则2秒钟将没有当前数据。

听起来不多,但是

  1. 迟早要等到有人打电话消费数据的2秒钟。
  2. 此过程必须针对多个父/子分区运行,因此在更广泛的顶层数据集中,随时可能有数十个此类开关在进行-意味着更广泛的数据集将永远不会“完整”。

因此,问题-是否有更好的方法来实现这一目标? 选择数据的“最新”版本的能力,而无需复杂的SQL来确定子查询中每个分区的max(stamp)。

-- first partition (earlier date)
ALTER TABLE mydb.getresources
 ADD PARTITION (ac='123456789012', `current` = 'Yes', stamp='2020-09-25T07_44_50.301984', region='us-east-1')
  LOCATION 's3://mybucket/api/resourcegroupstaggingapi/getresources/ac=123456789012/stamp=2020-09-25T07_44_50.301984/region=us-east-1/'
-- runtime 2.26s

-- second partiton (later date)
ALTER TABLE mydb.getresources
 ADD PARTITION (ac='123456789012', `current` = 'Yes', stamp='2020-09-25T08_02_50.925047', region='us-east-1')
  LOCATION 's3://mybucket/api/resourcegroupstaggingapi/getresources/ac=123456789012/stamp=2020-09-25T08_02_50.925047/region=us-east-1/'
 -- runtime 1.18s
  
-- rename old
ALTER TABLE mydb.getresources
  PARTITION (ac='123456789012', `current` = 'Yes', stamp='2020-09-25T07_44_50.301984', region='us-east-1') 
  RENAME TO PARTITION (ac='123456789012', `current` = 'No', stamp='2020-09-25T07_44_50.301984', region='us-east-1');
-- runtime 1.51s

3 个答案:

答案 0 :(得分:0)

不幸的是,Glue数据目录API中没有原子操作,Athena使用该原子操作来存储有关表和分区的元数据。无法以事务方式同时修改多个分区。

但是,您可以采取一些措施来缩短可能出现不一致的时间:直接使用Glue Data Catalog API而不是Athena的SQL接口。通过Athena进行操作比直接使用API​​要慢得多。

使用CreatePartition API调用,您可以添加新的分区,并使用UpdatePartition可以修改先前的分区-这与您当前的操作相对应,但是会保留较短的时间两个分区标记为当前分区。

使用BatchUpdatePartition可以做得更好:通过添加当前分区设置为false的新分区,然后批量更新当前分区和先前分区以交换该标志,您可以将持续时间缩短到最短可能是这样-即使据我所知无法保证查询不会看到两个当前分区或没有当前分区,但也不保证API是原子的。


但是,使用这样的分区键标记最新的分区有点麻烦。使用微秒分辨率时间戳作为分区键也使我怀疑您要实现的目标。雅典娜不是一个低延迟的数据库,通常在包含小文件的许多分区中表现都很差。用它来查找最后写入的文件绝不是一件好事。

我怀疑您的每个分区都包含一个文件,如果是这种情况,那么仅查看S3列表并抓取对象还是选择S3会不会更容易,更高效?

如果这不是一个选项,那么运行查询的进程可以在查询之前进行API调用吗?在这种情况下,它可以在Glue Data Catalog API中查找最新分区,或列出S3,或者您可以将最新分区写入Parameter Store并从那里读取。我认为有很多方法可以改善此问题,但是我没有信息可以帮助您。

如果您详细描述您要实现的目标,也许我们可以帮助您找到更有效的解决方案。

答案 1 :(得分:0)

您可以尝试以下解决方法。建议采用以下方法,假设您不非常频繁地更新分区,可能不少于5分钟一次(有争议)

不要更新现有表上的分区。

假设您有一张桌子A1。带有分区x0,x1,x2,x3 ... xn。

第一件事不是直接从表中查询,而是从视图查询中说view_A,其定义非常简单

CREATE OR REPLACE VIEW view_A AS SELECT * FROM db.A1

现在,如果您需要添加新分区,删除现有分区或同时删除两者,请执行以下操作

使用CTAS查询创建一个新的空表A2,该表具有与A1相同的架构。因此,A2将有一些来自A1的{xi}分区,并将添加新的y1,y2 ... ym分区。 通过ALTER语句(您需要A1所需的所有分区以及需要添加的任何新分区集)将分区列表添加到A2中,或如@Theo所述,将Glue api用于BatchPartitionUpdate。

现在更新view_A的视图定义以指向新表

CREATE OR REPLACE VIEW view_A AS SELECT * FROM db.A2

从视图中查询时,我现在看不到任何问题。准备好A2后,将视图切换为指向A2。

您可以删除旧表(如A1),因为更新视图后Athena不会删除基础数据。

注意由于它们都是DDL命令,并且不会扫描任何数据,因此,这些都不会产生任何额外费用。

答案 2 :(得分:0)

这些解决方案真是太棒了!我还建议采用更具影响力的方法,因为您将采用存储维护库作为新的合并引擎,但查看 Delta Lake 和 Hudi 等内容非常有用,因为版本控制和时间点查询都已排除在外盒子。