背景
AWS S3储存是面向对象的储存,该服务并没有提供S3桶文件的最后访问日期的记录,但是在S3智能分层中又会根据文件最后访问日期进行分层。为了能分析出哪些文件适合智能分层,还有排查出僵尸文件,就需要统计S3桶文件的最后访问日期表。
AWS中的S3桶目前有2个审计日志
- 桶所有文件的审计日志
- 桶文件的访问审计日志
结合这两个审计日志,就能得到每一个文件的最后访问日期
桶所有文件的审计日志
目前是通过桶的【管理】 —> 【库存配置】进行每日生成
可以通过建立外部表,使用presto引擎对生成的审计日志文件进行查询
※注意,目前生成的文件并不能使用spark进行查询,会报错
org.apache.orc.FileFormatException: Malformed ORC file s3://xxx/symlink.txt. Invalid postscript. (state=,code=0)
生成库表的代码需要咨询AWS侧技术人员
CREATE EXTERNAL TABLE s3_inventory_log(
`bucket` string,
`key` string,
`size` bigint,
`last_modified_date` timestamp,
`storage_class` string)
PARTITIONED BY (
`dt` string)
ROW FORMAT SERDE
'org.apache.hadoop.hive.ql.io.orc.OrcSerde'
STORED AS INPUTFORMAT
'org.apache.hadoop.hive.ql.io.SymlinkTextInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION
'审计日志所在文件'
TBLPROPERTIES (
'transient_lastDdlTime'='创建表的时间戳')
因为该审计日志是具有分区的,所以需要先修复分区再进行查询
msck repair table s3_inventory_log;
然后对表进行查询,只需要抽出所需的数据
- key:文件路径
- last_modified_date:文件创建日期,只需要精确到日期
- 排除%$folder$文件,这个是创建路径后都会存在的 0b 文件
select key, from_unixtime(last_modified_date, 'yyyy-MM-dd') as last_modified_date
from s3_inventory_log
where dt = '日期分区'
and key not like '%$folder$'
limit 1;
访问审计日志
-
移动审计日志文件
审计日志是在桶的【属性】—>【服务器访问日志记录】里开启,并指定路径储存数据文件。
但是这个是txt格式的数据格式,所以如果直接进行查询,会出现扫描全表的情况,为了减少运行数据量,对前一天的审计日志复制到另外的路径上
先创建数据临时存放的路径
hdfs dfs -mkdir s3://审计日志所在路径/analysis
用以下脚本移动文件
#!/bin/bash
y_day=`date -d yesterday +%Y-%m-%d`
echo $y_day
s3_log_files=`hdfs dfs -ls s3://审计日志所在路径/ |awk '{print $8}'|grep $y_day`
for s3_log_file in $s3_log_files
do
#echo $s3_log_file
hdfs dfs -cp $s3_log_file s3://审计日志所在路径/analysis/
done
注意:
当执行完最后的SQL后,要记得把这部分文件给删了,不然会导致审计日志的储存量翻倍
-
创建审计日志表
使用上面的数据进行查询,生成外部表的代码需要咨询AWS侧技术人员
CREATE EXTERNAL TABLE s3_access_log_origin(
`bucketowner` string COMMENT '',
`bucket` string COMMENT '',
`requestdatetime` string COMMENT '',
`remoteip` string COMMENT '',
`requester` string COMMENT '',
`requestid` string COMMENT '',
`operation` string COMMENT '',
`key` string COMMENT '',
`requesturi_operation` string COMMENT '',
`requesturi_key` string COMMENT '',
`requesturi_httpprotoversion` string COMMENT '',
`httpstatus` string COMMENT '',
`errorcode` string COMMENT '',
`bytessent` bigint COMMENT '',
`objectsize` bigint COMMENT '',
`totaltime` string COMMENT '',
`turnaroundtime` string COMMENT '',
`referrer` string COMMENT '',
`useragent` string COMMENT '',
`versionid` string COMMENT '',
`hostid` string COMMENT '',
`sigv` string COMMENT '',
`ciphersuite` string COMMENT '',
`authtype` string COMMENT '',
`endpoint` string COMMENT '',
`tlsversion` string COMMENT '')
ROW FORMAT SERDE
'org.apache.hadoop.hive.serde2.RegexSerDe'
WITH SERDEPROPERTIES (
'input.regex'='([^ ]*) ([^ ]*) \\[(.*?)\\] ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) \\\"([^ ]*) ([^ ]*) (- |[^ ]*)\\\" (-|[0-9]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) (\"[^\"]*\") ([^ ]*)(?: ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*))?.*$')
STORED AS INPUTFORMAT
'org.apache.hadoop.mapred.TextInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION
's3://审计日志所在路径/analysis/'
TBLPROPERTIES (
'transient_lastDdlTime'='时间戳');
然后对表进行查询,只需要抽出所需的数据,sparksql如下
select replace(key, '%253D', '=') as key, substr(requestdatetime,1,11) as requestdatetime
from s3_access_log_origin
where key not like '%%2524folder%2524%'
group by 1,2
- replace(key,'%253D','='):等号在key中会被转化成【%253D】,所以需要转回去
- substr(requestdatetime,1,11):直接截取前11位字符串,只留下请求日期
- key not like '%%2524folder%2524%':排除%$folder$文件,这个是创建路径后都会存在的 0b 文件
- group by 1,2:将数据除重
输出结果为
+--------------------+-------------------------------+
| key | requestdatetime |
+--------------------+-------------------------------+
| S3文件路径 | DD/MMM/YYYY |
+--------------------+-------------------------------+
两表关联
创建文件最后访问表
create table if not EXISTS s3_last_access_log
(
`key` string COMMENT '文件路径' ,
`last_modified_date` timestamp COMMENT '文件最后修改日期',
`last_access_date` string COMMENT '文件最后访问日期'
)
comment '记录S3桶文件最后访问日期'
partitioned by (
`datestamp` string comment '时间分区'
)
接下来只需要每日的文件审计数据与文件访问审计表相关联就能得到每个文件最终访问时间
insert overwrite table s3_last_access_log partition (datestamp=前一天的日期)
select a.key, a.last_modified_date, b.last_access_date
from (
select key, from_unixtime(last_modified_date, 'yyyy-MM-dd') as last_modified_date
from s3_inventory_log
where dt = '前一天时间分区'
and key not like '%$folder$'
) a
left join (
select replace(key, '%253D', '=') as key
, substr(requestdatetime, 1, 11) as last_access_date
from s3_access_log_origin
where key not like '%%2524folder%2524%'
group by 1,2
) b
on a.key = b.key
增量查询
上面关联的只是前一天的s3桶文件访问记录,而每天生成的S3桶文件审计日志为前一天S3桶中所有的文件记录,所以需要以最新一天的文件审计日志表作为主表。
为了让文件都能记录上最后访问日期,需要将前一天的S3文件审计日志与前两天的最后访问日志表 join 一次
逻辑如下图
最终SQL如下
insert overwrite table s3_last_access_log partition (datestamp=前一天的日期)
select a.key, a.last_modified_date, max(last_access_date)
from (
select key, from_unixtime(last_modified_date, 'yyyy-MM-dd') as last_modified_date
from s3_inventory_log
where dt = '前一天分区'
and key not like '%$folder$'
) a
left join (
select key, last_modified_date, last_access_date
from s3_last_access_log
where dt = '前两天分区'
) b
on a.key = b.key
left join (
select replace(key, '%253D', '=') as key
, substr(requestdatetime, 1, 11) as last_access_date
from s3_access_log_origin
where key not like '%%2524folder%2524%'
group by 1, 2
) c
on a.key = c.key
注意
对于 ORC 和 Parquet 格式的清单文件,不支持使用 Apache Hive 和 Apache Spark 读取 symlink.txt。