最近做了一点数据分析的工作,比如分析用户留存率,但是留存率需要先自己统计和计算,用的是公司内部的大数据平台。有时真觉得所谓大数据,数据量上去了,但是质量未必上去,比如前几天我分析的移动应用内埋点数据,数据量可谓超大,但是很多埋点其实是完全无意义的,埋点的初衷是为了分析应用内各个页面、按钮等点击访问情况,但是无的放矢的埋点,导致很多埋点根本不会去分析,这样将产生大量冗余数据,这样的大数据又有什么意义呢,占据大量磁盘空间,检索查询成本也高,说难听点这样的大数据是费电的大数据。好了,还是回到本文话题。公司的大数据平台不知道用的hive什么版本,没有update命令,也就是不支持行级更新。当然很多场景是用不到更新的,但是要统计类似第N日留存率这样的需求,也就是根据今天的数据或者昨天的数据,统计前指定天数的留存率,势必要更新数据。问同事该如何做,她的做法是每天全量更新,从某个指定时间节点,依次把每天留存率全算一遍,这样固然可以,但有太多的重复计算了。以前做图像算法,想的都是如何快,用什么快速算法。而这样的全量更新,耗时又耗费计算资源。 想来想去只好曲线实现行级更新,我们可以采用insert overwrite和insert into组合实现更新,即一个数据覆盖操作和一个数据插入操作。其实很简单,具体操作为,比如我要更新A表,那么首先对表A做个拆分,将待更新的那行数据查询出来保存到临时表A_swap2,其余不更新的数据保存到临时表A_swap1,然后把A_swap1数据使用insert overwrite命令重写回表A,此时A内的数据会被覆盖,然后对A_swap2做一些更新操作后在使用insert into命令将新数据插入到表A,这样就完成了数据更新操作。简单示例代码如下:

--拆分表
drop table tmp.user_day_retention_rate_swap1;
create table tmp.user_day_retention_rate_swap1 as
select * from db.user_day_retention_rate
where dt!=date_sub('${biz_date}', 1);

drop table tmp.user_day_retention_rate_swap2;
create table tmp.user_day_retention_rate_swap2 as
select * from db.user_day_retention_rate
where dt=date_sub('${biz_date}', 1);

--不更新的数据重写回原表
insert overwrite table db.user_day_retention_rate
select * from tmp.user_day_retention_rate_swap1 ;

--更新的数据重新添加至原表
insert into table db.user_day_retention_rate
select
    0.5,
    b.user_retention_rate_3d as user_retention_rate_3d,
    b.user_retention_rate_7d as user_retention_rate_7d,
    b.user_retention_rate_14d as user_retention_rate_14d,
    b.user_retention_rate_30d as user_retention_rate_30d,
    b.dt as dt
from tmp.user_day_retention_rate_swap2 b;


这样就基本实现了行级更新,觉得应该有更好的方法,欢迎大家留言、指导。




       上面用到了时间函数,不同的工程师在数据库设计时在时间格式的定义上也是五花八门,有的定义成年月日时分秒,有的定义成一个大整数毫秒,我倾向前者,后者可读性较差,需要转换才行。格式的不统一,在使用时势必要进行一些转换。这里简单罗列几个时间函数,可以对时间进行转换及运算。


--毫秒与年月日时分秒相互转换
select from_unixtime(floor(1478169393635/1000), 'yyyy-MM-dd');
select unix_timestamp('2016-11-03', 'yyyy-MM-dd')
--日期加减
select date_add('2016-11-03' , 5)
select date_sub('2016-11-03' , 5)