一、背景
前面系列文章介绍了用户画像的概念、用户画像的标签加工、用户画像的应用。本篇文章主要介绍一些画像的技术细节,让大家更加详细的了解画像数据存储和处理的逻辑
举个现实中的例子:
例子1:因为疫情原因,上线一个平台(和疫情相关),然后用户关注了疫情这个平台,有一个标签来标识用户是否关注疫情,但是随着政策放开,这个标签显然对公司来说是没有用随着疫情放开之后,而且还浪费存储成本,需要单独弄一个字段来进行标识
例子2:一个账号在购物或者看视频的时候会登录很多个设备,比如说 电商平台网页版、手机版(安卓、ios)或者pad 版本等等,但是可能随着用户换设备(换手机、电脑或者pad),之前的设备信息再进行存储也没有意义,毕竟那个手机或者电脑可能已经不在使用了,至少不挂在这个账号体系下了
例子3:某个用户是非会员,有这样一个标签预测成为会员的概率,但是某天成为会员之后,这个数据明显就没有价值了,应该清理掉
通过上面上个例子,能看到有这样一些标签,随着时间的推移,对业务没有价值浪费存储空间,甚至因为没有过期造成误解
二、解决思路
那有没有这样一个方案呢?对这样的标签进行过期处理呢?
比如说:认为某个账号下一个设备半年不更新或者某个标签半年不更新就对这个标签进行删除呢?
上述流程图介绍了标签的写入流程以及标签过期的处理流程。
标签过期:需要读取所有的画像数据,对每个标签进行判断,如果当前时间—标签的更新时间>标签的TTL时间,需要对该标签进行删除。
即:整个流程支持数据库列级别的TTL,要求能获取到标签更新的时间,也就是列的更新时间
三、具体实现
目前整体采用Hbase +Mysql来实现,Hbase支持更新列的时候设置更新时间,同时支持获取列的读取到列的更新时间,通过Mysql配置标签的TTL信息即可完成整体流程
hbase写入时设置列的时间 @Test public void insert() throws IOException { Calendar calendar = Calendar.getInstance(); calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH) - 1, 0, 0, 0); long preZero = calendar.getTime().getTime(); System.out.println(preZero); Connection connection = createConnection(); Table table = connection.getTable(TableName.valueOf("tmp_test_info")); ArrayList<Put> puts = new ArrayList<>(); Put put4 = new Put("0005".getBytes()); put4.addColumn("f1".getBytes(), "name".getBytes(), preZero, Bytes.toBytes("小杰")); put4.addColumn("f1".getBytes(), "age".getBytes(), Bytes.toBytes(24)); //不设置则用当前时间 puts.add(put4); table.put(puts); table.close(); connection.close(); }
@Test public void scan() throws IOException { Map<String, Long> cellTTL = new HashMap<>(); cellTTL.put("name", 1L);//单位天 cellTTL.put("age", 2L);//单位天 List<Delete> deleteList = new ArrayList<>(); long currentTime = System.currentTimeMillis(); Table table = createConnection().getTable(TableName.valueOf("tmp_test_info")); Scan scan = new Scan(); scan.withStartRow("0001".getBytes()); scan.withStopRow("0008".getBytes()); ResultScanner scanner = table.getScanner(scan); for (Result result : scanner) { List<Cell> cells = result.listCells(); for (Cell cell : cells) { String rk = Bytes.toString(CellUtil.cloneRow(cell)); String family = Bytes.toString(CellUtil.cloneFamily(cell)); String column = Bytes.toString(CellUtil.cloneQualifier(cell)); long timestamp = cell.getTimestamp(); if (column.equals("name")) { String value = Bytes.toString(CellUtil.cloneValue(cell)); System.out.println(rk + ":" + family + ":" + column + ":" + value + ":" + timestamp); } else { int value = Bytes.toInt(CellUtil.cloneValue(cell)); System.out.println(rk + ":" + family + ":" + column + ":" + value + ":" + timestamp); } if (cellTTL.containsKey(column)) { if (currentTime - timestamp > cellTTL.get(column)*24*60*60*1000) { //判断列标签是否过期 Delete delete = new Delete(Bytes.toBytes(rk)); delete.addColumn(Bytes.toBytes(family), Bytes.toBytes(column)); deleteList.add(delete); } } } } if (!deleteList.isEmpty() && deleteList.size() > 0) { table.delete(deleteList); } table.close(); }
上面给出了一个简单版本的过期策略处理代码