1.监控与新特性
Kylin 查询指标监控
Kylin 已开启查询 metric 监控,定时将监控数据写入 hive 表,如: KYLIN.HIVE_METRICS_QUERY_QA 等;
需要开启参数:
kylin.web.dashboard-enabled=true
kylin.server.query-metrics2-enabled=true
同时,KYLIN_SYSTEM 项目下创建了 5个cube,如下图
![](https://img.haomeiwen.com/i7207268/0ec4840252a7256e.png)
通过 kylin server 上配置的定时任务,定时触发执行 cube 构建(可通过 crontab -l 查看);
- 第一执行
$KYLIN_HOME/bin/system-cube.sh cron
即可, - 如果有一次 cube 阻塞会阻塞后续的构建;暂时需要人工监控;
新增 intersect_value UDAF 函数 - bitmap 求值函数
kylin 中一个典型场景:用户留存分析,可以借助 intersect_count 轻松实现留存率计算;
intersect_count 实现 bitmap 交集运算,
intersect_value 实现 bitmap 求值,留存分析除了关心留存率,还需要关心留存的用户具体是谁;
这个 feature 有官方 pull request,只需要将 request 集成即可
- 需要 jdk 1.8+
- 官方 jira :
KYLIN-4314
2.问题列表 Q&A
Q1: kylin 查询页面卡顿
执行有语法错位的 sql 后,再次执行查询会卡顿一分钟,之后才可以继续操作;
卡顿日志如下:
![](https://img.haomeiwen.com/i7207268/e273f5d74f1b97bb.png)
原因:同时开启了 memcache 查询缓存,和重复查询拦截检测(同一个查询,60秒内未返回,则等待,不重复执行)
kylin.query.lazy-query-enabled=true
kylin.query.lazy-query-waiting-timeout-milliseconds=60000
查询失败后,未清除缓存状态。导致再次查询会盲等 60s;
解决: Kylin 的 bug ,打补丁 KYLIN-4432
查询失败 清除缓存;
Q2: Kylin hive limit offset 实现差异
hive 执行以下 sql 会按照 rank 倒序排序,kylin 执行则为乱序显示;
select a.*,
row_number() over(order by a.orders desc) as "rank"
from
(select
product_id,
product_name,
count(distinct msisdn) orders,
sum(pv) pv
from mgwh_rd_dwm.dwm_music_vring_content_order_d
where dayid between '20200501' and '20200501' and is_videocring='1' and operate_type='0'
group by product_id,product_name
) a
limit 100
总结问题如下
- kylin hive 查询引擎,查询优化的差异,hive 基于 hive hdfs,kylin 基于 hbase;
- kylin limit 应该是启用了 limit pushdown 优化,只返回了局部数据;
- hive 对包含有开窗函数的查询取消 limit pushdown ,而 kylin 未关闭
如何取消 limit pushdown 优化,在遇到需要全局排序的场景时,limit pushdown 优化会关闭, 所以只需要加一个 order by 即可;改造如下:
select a.*,
row_number() over() as "rank"
from
(select
product_id,
product_name,
count(distinct msisdn) orders,
sum(pv) pv
from mgwh_rd_dwm.dwm_music_vring_content_order_d
where dayid between '20200501' and '20200501' and is_videocring='1' and operate_type='0'
group by product_id,product_name
order by orders desc
) a
limit 100
还没完,又遇到分页问题,业务方试图通过 limit 100 offset 10 的方式实现分页;kylin 可以使用 offset 指定 limit 的开始偏移量;但是结果不如预期,rank 还是从 1 开始编号,而不是从 10 开始编号;
解决:在原查询上再套一层,基于此 limit xx offset xx;
select * from (select a.*, row_number() over() as "rank"
from
(select
product_id,
product_name,
count(distinct msisdn) uv,
count(*) pv
from mgwh_rd_dwm.dwm_music_vring_content_order_d
where dayid between '20200501' and '20200501' and is_videocring='1' and operate_type='0'
group by product_id,product_name order by uv desc
)a ) limit 10 offset 10
todo :查看执行计划;
Q3: Case when 导致的执行报错
执行以下 sql 会报错,提示 cast 字段报错
select period_id,
case grouping(a.company_name) when 1 then '总体' else a.company_name end company_name,
case grouping(a.province) when 1 then '全国' else a.province end province,
case grouping(a.user_type) when 1 then '总体' else a.user_type end user_type,
case grouping(a.new_flag) when 1 then '总体' else a.new_flag end new_flag,
case grouping(a.channel_type) when 1 then '总体' else a.channel_type end channel_type,
count(distinct puid) as index1 --'活跃用户数'
from (
select monthid as period_id,
case company_id
when '1' then '咪咕数媒' when '2' then '咪咕动漫' when '3' then '咪咕视讯' when '4' then '咪咕音乐' when '5' then '咪咕互娱' when '7' then '咪咕+'
end as company_name,
province_name as province,
case puid_type when 'msisdn' then '手机用户' when 'tpa' then '第三方账号' else '游客' end as user_type,
case new_flag when '1' then '新增' when '0' then '存量' end as new_flag,
case channel_type_id
when '1' then '自有渠道' when '2' then '省公司渠道' when '3' then '社会渠道' when '-998' then '其他'
end as channel_type,
puid
from rpt_culture_active_m
where monthid between '201910' and '201910' and flag=1
) a --数据准备,统计口径+归口命名
group by grouping sets(
(a.period_id,a.company_name,a.province,a.user_type,a.new_flag,a.channel_type),
(a.period_id,a.company_name,a.province,a.user_type,a.new_flag),
(a.period_id,a.company_name,a.province,a.user_type),
(a.period_id,a.company_name,a.province)
)
报错提示:mismatch type
![](https://img.haomeiwen.com/i7207268/a111abaa6974e5fd.png)
user_type 一个为 not null 一个允许 null
解决: 找个不存在的值 case 'xx' then null else '' 让字段可为 null (巧妙骗过 sql 解析器)
select period_id,
case grouping(a.company_name) when 1 then '总体' else a.company_name end company_name,
case grouping(a.province) when 1 then '全国' else a.province end province,
case grouping(a.user_type) when 1 then '总体' else a.user_type end user_type,
case grouping(a.new_flag) when 1 then '总体' else a.new_flag end new_flag,
case grouping(a.channel_type) when 1 then '总体' else a.channel_type end channel_type,
count(distinct puid) as index1 --'活跃用户数'
from (
select monthid as period_id,
case company_id
when '1' then '咪咕数媒' when '2' then '咪咕动漫' when '3' then '咪咕视讯' when '4' then '咪咕音乐' when '5' then '咪咕互娱' when '7' then '咪咕+'
end as company_name,
province_name as province,
case puid_type when 'msisdn' then '手机用户' when 'tpa' then '第三方账号' when 'fuck1111' then null else '游客' end as user_type,
case new_flag when '1' then '新增' when '0' then '存量' end as new_flag,
case channel_type_id
when '1' then '自有渠道' when '2' then '省公司渠道' when '3' then '社会渠道' when '-998' then '其他'
end as channel_type,
puid
from rpt_culture_active_m
where monthid between '201910' and '201910' and flag=1
) a --数据准备,统计口径+归口命名
group by grouping sets(
(a.period_id,a.company_name,a.province,a.user_type,a.new_flag,a.channel_type),
(a.period_id,a.company_name,a.province,a.user_type,a.new_flag),
(a.period_id,a.company_name,a.province,a.user_type),
(a.period_id,a.company_name,a.province)
)
Q4: cube 新建失败,构建失败,MR 日志 OOM
构建过程中,总是 oom 报错,调整内存参数也无效;日志提示:too many combinations
![](https://img.haomeiwen.com/i7207268/51b9f0420c3b8c84.png)
主要问题在于:维度过多,组合过多,cubeid 爆炸,维度优化不够,这样的 cube 必须优化;
cube 的特征:
- 维度过多,20+ 维度
- 存在大量 bool 类型的维度, 列基数 = 2(列基数:列的 count distinct 值)
维度优化
- 设置强制维度(mandatory),维度组合数减半
- 除了相关的维度,也可以把列基数小的维度设为联合维度(joint),维度组合数大大减少;
- 将 省-市-县 类的维度设为层次维度
这里业务同事有一个担心:将不相关的维度设为联合维度,会导致查询结果出错,kylin 查询如果没有命中的 cube 会基于 base cubeid 在线计算,所以不需要担心这个问题;
列基数低,设为联合维度,在线 rollup 聚合也很快
![](https://img.haomeiwen.com/i7207268/b4432f80c1cccbba.png)
Q5: kylin rollup 时统计结果不准的 bug 已提 issue
select period_id,
case grouping(a.company_name) when 1 then '总体' else a.company_name end company_name,
case grouping(a.province) when 1 then '全国' else a.province end province,
case grouping(a.new_flag) when 1 then '总体' else a.new_flag end new_flag,
case grouping(a.busi_type) when 1 then '总体' else a.busi_type end busi_type,
case grouping(a.channel_type) when 1 then '总体' else a.channel_type end channel_type,
case grouping(a.pay_type) when 1 then '总体' else a.pay_type end pay_type,
case grouping(a.fee_type) when 1 then '总体' else a.fee_type end fee_type,
case grouping(a.combine_tag) when 1 then '总体' else a.combine_tag end combine_tag,
count(distinct user_id) as index2 --'用户数'
from (
select monthid as period_id,
case company_id
when '1' then '咪咕数媒' when '2' then '咪咕动漫' when '3' then '咪咕视讯' when '4' then '咪咕音乐' when '5' then '咪咕互娱' when '7' then '咪咕+'
end as company_name,
province_name as province,
case new_user_flag when '1' then '新增' when '2' then '存量' when '3' then '存量' end as new_flag,
case order_type when '1' then '包月' when '2' then '包月' when '3' then '点播' end as busi_type,
case channel_type_id
when '1' then '自有渠道' when '2' then '省公司渠道' when '3' then '社会渠道' when '-998' then '其他'
end as channel_type,
case payment_type when '1' then '话费' when '2' then '第三方支付' when '-998' then '其他' end as pay_type,
case order56_tag when '1' then '新六' when '2' then '老五' when '3' then '基地' when '-998' then '其他' end as fee_type,
case combine_tag when '1' then '自有业务' when '2' then '联合业务' when '3' then '合作业务' when '-998' then '其他' end as combine_tag,
user_id
from rpt_culture_order_m
where monthid between '202005' and '{202005}' and order_flag=2 and company_id=4
) a --数据准备,统计口径+归口命名
group by grouping sets(
(a.period_id,a.company_name,a.province,a.new_flag,a.busi_type,a.channel_type,a.pay_type,a.fee_type,a.combine_tag),
(a.period_id,a.company_name,a.new_flag),
(a.period_id,a.company_name))
kylin 在 多 grouping sets + count distinct 的计算上有 bug,提的 issue 链接:https://issues.apache.org/jira/browse/KYLIN-4582#
Q5: kylin 明细查询性能问题
为了使用 kylin 做明细查询,cube 构建全维度都设置为强制维度,这样最终的 cuboid 只有一个。
只命中几行的简单查询耗时 4s+,远远达不到预期,sql 代码样例如下:
select xxx from table where xx and
CASE
combine_tag
WHEN '0' THEN
'全部业务'
WHEN '1' THEN
'自有业务'
WHEN '2' THEN
'新型联合业务'
WHEN '3' THEN
'存量联合业务'
WHEN '4' THEN
'合作业务' ELSE '其他'
END IN ( '全部业务' )
AND
CASE
order_type
WHEN '0' THEN
'全部计费模式'
WHEN '1' THEN
'包月'
WHEN '2' THEN
'点播'
END IN ( '全部计费模式' )
AND
CASE
new_user_flag
WHEN '0' THEN
'全部计费属性'
WHEN '1' THEN
'新增付费'
WHEN '2' THEN
'续费'
END IN ( '全部计费属性' )
AND payment_type = '0'
AND order56_tag = '0'
AND charge_side = '0'
查看执行日志:查询一行扫描的行数达 25w+
将 where 条件中的 switch when 改成写死的
WHERE
dayid between '20201001' and '20201030'
AND province_id = 'all'
AND province_name = 'all'
AND channel_type_id = '0'
AND combine_tag in ('0','1','2')
AND company_id in ('3','1','2')
AND order_type = '0'
AND new_user_flag = '0'
AND payment_type = '0'
AND order56_tag = '0'
AND charge_side = '0'
扫描的行数降至 800 多行,响应速度明显的提升了,kylin 的 sql 解析引擎对这种写死的 case when 语句没有做优化,也能理解,尽量把 where 条件明确,不然会导致扫描行数变大。
但是为什么查一行维度固定的数据还需要扫描 800行,不满意性能,继续优化。
发现 sql 在 where 条件中 漏了一个维度,导致查询时,构造 hbase rowkey 是有一个维度是模糊查询,最终,将该漏的维度加上,扫描的行数优化为 1 行,性能初步达到预期。
Kylin 查询基本原理
kylin 构建的cube 最终的结果数据都写到HBase中存储
Rowkey 即为维度组合,Value 为聚合结果值
HBase rowkey 构造:
cuboid + 维度编码值排序组合
cuboid 根据维度进行0 1 编码
例如 11 个维度,全部都是强制维度
cuboid:11111111111 = 2047,如下所示,全维度,命中的 cuboid 为 2047
第1,2个维度的组合
cuboid:11000000000
cubo Ids: [2047]
total scan count : 12
total scan bytes: 783
result row count: 12
优化建议
HBase rowkey 排序很重要
Q6:kylin 元数据备份超时失败
执行 bin/metastore.sh backup 失败,看到请求超时相关日志,元数据中只有 dict 数据较大,单独执行
bin/metastore.sh list /dict 也报同样的错。
![](https://img.haomeiwen.com/i7207268/44dc6e8e5a397022.png)
可以看到提示 h1024 这台 regionserver 响应超时,callTimeoutException waitTime=10001 说明等待了 10000ms 后超时直接失败。
查看这台 regionserver 日志也可以看到2点:
1.提示 bucket cache 无法写入,数据太大 30M 左右 (见下图)
![](https://img.haomeiwen.com/i7207268/b5e8e679405bf0ff.png)
2.client 主动关闭了 connection
详细阅读日志(这点很重要,耐心一点从上往下读,找线索)查看到 kylin 的 hbase client 连接参数:
hbase connection
[key=kylin:kylin_metadata@hbase,hbase.client.scanner.timeout.period=10000,hbase.rpc.timeout=5000,hbase.client.retries.number=1 in connPoll]
kylin的配置中没搜到 hbase 关于这几个配置,应该是读到了默认配置,搜了下关键词,发现kylin配置hbase client 的参数
-
kylin.metadata.hbase-client-scanner-timeout-period
: 默认 10000 (ms),扫描数据的超时时间 -
kylin.metadata.hbase-rpc-timeout
: 默认5000(ms)rpc 超时时间 -
kylin.metadata.hbase-client-retries-number
:重试次数,默认 1 次
修改 kylin.properties 新增配置
kylin.metadata.hbase-client-scanner-timeout-period=20000 (scan 时间增大道20s)
重新执行成功优化后 再次构建成功;
Q7. cube 构建 hbase region is not online 等问题
hbase 出问题后 hbase region 发生了转移,构建cube 的时候报错 region is not online, 报错日志如下:
Thu Dec 03 09:46:02 GMT+08:00 2020, RpcRetryingCaller{globalStartTime=1606959962923, pause=100, retries=1}, org.apache.hadoop.hbase.NotServingRegionException: org.apache.hadoop.hbase.NotServingRegionException: Region kylin:kylin_metadata,/dict/MGWH_RD_RPT.RPT_CULTURE_ORDER_D/CP_NAME/1e771852-bddc-216d-3619-1bede28ccdf3.dict,1602642541356.b2de31d52e1813f534b9858e1a84b596. is not online on h1004.dm.migu.cn,16020,1606750977093
出现问题的背景: HBase 出过问题,部分 regionserver 重启,部分表的 region 重新上下线。
Build Dimension Dictionary 这一步需要加载 metadata 中的字典数据,发现需要的这个 region not online (h1004 这台regionserver)
同事反应,这个 region 已经转移到了 h1059 了。
所以可以断定 kylin 请求 hbase 元数据读的还是之前老的 region 信息。了解到 hbase client 的读取逻辑,会把第一次加载的 region 信息缓存下来。解释如下:
hbase client 客户端的逻辑是,当客户端没有缓存的时候(第一次请求),会加载 region 的location 信息到客户端,后面直接使用 cache 中的信息,如果出现重试,则会重新获取 location 信息,更新客户端的cache。
所以将 重试次数改为 3 可以降低 由于 region 上下线导致 请求失败的问题。
参考:
https://issues.apache.org/jira/browse/KYLIN-4711
https://blog.csdn.net/Gloria_y/article/details/83755460
网友评论