我在s3中有一个数据集,正在通过Athena查询。 每x分钟写入一次新版本的分区中的分区(Boto3 / Lambda)。 最终,我需要通过Athena SQL有效地查询数据的“最新”版本。
目前为止有效的策略,但是有“问题” ....
现在所有这些工作正常,但是我担心随着时间的推移会发生切换。 使用Workbench / J(在Lambda中使用时可能更少,但是使用相同的主体)
因此,至少要等待2秒,我的数据集才会产生不正确的结果。 或者,如果我先执行“ current = No”,然后添加新分区,则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
答案 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 等内容非常有用,因为版本控制和时间点查询都已排除在外盒子。