关于业务系统数据化的思考

一个发券系统为例,该怎么发券。用 5W1H 分析法尝试提问:

  1. Why 理由:为什么发券,为了获益。比如激活沉睡用户来消费,比如无脑群发拉一波流量,用户来了就是赚了。为什么用这个系统发券,因为这个系统能问答如下的问题。
  2. What 物品:优惠券,应该发放什么类型的优惠券。
  3. When 时间:应该在什么时间点发放优惠券。
  4. Where 地点:应该往哪个渠道发放优惠券。
  5. Who 人物:应该向什么样的人群发放优惠券。

这些问题,需要软件用数据(统计数据)来回答,而不是靠参差不齐的运营人员主观臆断。

数据模型该怎么设计,先分析出这些问题中的实体是什么。

实体与实体画像

以实体为中心,及与其他实体的交互,构建实体的画像。实体之间互相接触,实体A产生关于实体B的画像(标签),比如用户画像有时间相关的标签。

优惠券、物品

构建物品画像。

一张优惠券的画像,相当于评定优惠券发放效果好坏的结论报告。

时间点

构建时间画像。

渠道

构建渠道画像。相当于评定发放渠道的效果好坏的结论报告。

用户属性

性别比例、城市分布。

用户

构建用户画像。

基础属性

比如微信授权给到的信息,性别、城市等。

物品属性

比如优惠券领取率,优惠券的核销率、优惠券类型偏好。

渠道属性

渠道偏好。

时间属性

各领取时间段统计、领取时间段偏好、核销时间段偏好。

开发画像

T + 1 统计即可,统计数据对差一天的数据并不敏感。

按照用户画像的常用套路:

  1. 定义标签元数据。
  2. 竖表的方式开发新标签,新标签的开发通过增加开发者水平扩展。
  3. 根据标签元数据,将竖表转为横表。
  4. 加快多维度查询速度,可以使用ElasticSearch加速。

使用画像

基于标签元数据和每日生成的画像数据,可以,

精准投放

使用用户画像圈定人群。人群画像有时间偏好,选择发放时间,有渠道偏好,选择发放渠道,有优惠券偏好,选择优惠券类型。

或者换个方式,先选发放时间,根据时间画像,得知性别分布等,再筛选人群。

BI 可视化

各实体的可视化,各维度可筛选条件。

实体画像是用户画像的抽象,前端开发基于这种抽象开发,可适配不断新增的实体,减少重复劳动,免开发。

这就像一份知识库,作为运营人员的学习材料。比如时间的 BI 可视化,运营人员可学习到 xxx 时间段,是领取率最高的。

小结

一个发券系统,如何有别于其他产品。

  1. 充分收集和保留数据。
  2. 利用数据。使用统计数据,就能让产品很不同。
  3. 去发掘新的实体,丰富画像。
  4. 如果可以,在数据基础上加入机器学习模型。

未完待续。

实验说明 Golang HTTP 连接池参数

连接相对于其他对象,创建成本较高,资源也有限。如果没有连接池,在高并发场景下,连接关闭又新建,很快就会因为过多的TIME_WAIT(连接主动关闭方)导致无法创建更多连接了,程序被压死。

net/http 连接池参数

Go (测试版本 go 1.14)的net/http包是有连接池功能的,具体地,是 Transport 用于连接池化。

Transport is an implementation of RoundTripper that supports HTTP, HTTPS, and HTTP proxies (for either HTTP or HTTPS with CONNECT).

By default, Transport caches connections for future re-use.

	// MaxIdleConns controls the maximum number of idle (keep-alive)
	// connections across all hosts. Zero means no limit.
	MaxIdleConns int

	// MaxIdleConnsPerHost, if non-zero, controls the maximum idle
	// (keep-alive) connections to keep per-host. If zero,
	// DefaultMaxIdleConnsPerHost is used.
	MaxIdleConnsPerHost int

	// MaxConnsPerHost optionally limits the total number of
	// connections per host, including connections in the dialing,
	// active, and idle states. On limit violation, dials will block.
	//
	// Zero means no limit.
	MaxConnsPerHost int

	// IdleConnTimeout is the maximum amount of time an idle
	// (keep-alive) connection will remain idle before closing
	// itself.
	// Zero means no limit.
	IdleConnTimeout time.Duration

默认值

MaxIdleConns=100,MaxIdleConnsPerHost=2(=DefaultMaxIdleConnsPerHost),MaxConnsPerHost=0(不限制)。

Metabase Impala Driver 时区问题

问题

实际日期是上午11点,而Metabase显示是下午7点。差8小时。

select CURRENT_TIMESTAMP(),hour(CURRENT_TIMESTAMP())

../../images/image-20200714113556806.png

分析

参考:

  1. Handling timezones in Metabase
  2. troubleshooting-guide/timezones

有几个时区有关的设置:

  1. Database TimeZone,其中 impalad 启动环境是东八区,TIMESTAMP 不存储时区信息,存储的是东八区本地时间。
  2. Metabase TimeZone = JVM TimeZone = OS TimeZone,使用的是东八区。
  3. “Report Time Zone” setting,对 Impala 是不起作用的。

同样的SQL在MySQL上试了下,没问题。问题定位基本在metabaseimpala的Driver层。

发现从impala读取TIMESTAMP的值,TIMEZONE写死了UTC,应该是东八区。(Metabase SparkSQL Driver 也有这个问题,因为代码是从那copy的。)

../../images/image-20200714113928339.png

https://github.com/XUJiahua/metabase-impala-driver/commit/0963f9890a9213d53511c61b53870476f8f82cf4

metabase东八区启动,用北京时间显示,就会多加8小时。

../../images/image-20200714131930694.png

Impala TIMESTAMP 时区处理

现象

HiveParquet数据存储格式写入的 TIMESTAMP字段内容,Impala读取,时间差8小时。

../../images/image-20200713162929821.png

Hive写入

../../images/image-20200713163001445.png

Impala读取差8小时,北京时间与UTC差8小时。

原因

HiveParquet数据文件,TIMESTAMP先规范化到UTC格式再存储。而Impala直接读取数据文件中的内容,不会进行TIMEZONE的调整。

When Hive writes to Parquet data files, the TIMESTAMP values are normalized to UTC from the local time zone of the host where the data was written. On the other hand, Impala does not make any time zone adjustment when it writes or reads INT96 TIMESTAMP values to Parquet files. This difference in time zone handling can cause potentially inconsistent results when Impala processes TIMESTAMP values in the Parquet files written by Hive.

Metabase 客户隔离

之前有讨论,在不改动代码的情况下,可以使用分组功能来做客户隔离。但是该方案面临的问题是,每个客户一套模板,模板可维护性低。

问题的关键就是如何共享SQL模板并客户隔离。需要改动源代码。

目标

  1. 代码复用。同一套SQL代码。不用copy/paste,好维护。一个dashboard所有客户都能看。
  2. 客户隔离,保证数据安全。不同客户只能看到自己的数据。

实现

执行dashboard/chart中SQL代码时,动态插入客户隔离标识。

首先来看下一次SQL Query的生命周期。

../../images/image-20200616151301507.png

找了个比较接近的处理逻辑,substitute-parameters用于将{ {xxx} }替换为widget中选的值。模仿之,SQL中定义一个##client_id##占位符,写了一个substitute-placeholder方法使用用户的client-id替代占位符。

code: https://github.com/XUJiahua/metabase/pull/4/files

DEMO

效果

使用Sample Data数据集测试。

select * from PEOPLE
where STATE={{STATE}}
and SOURCE=##client_id##

返回的SQL中,可见占位符被替换为Google。

../../images/image-20200616150506812.png

因为还没有集成SSO系统,client-id临时用first-name来代替。

../../images/image-20200616150614876.png