kylin与superset集成实现数据可视化
2017-10-26 17:27
543 查看
Apache kylin是一个开源分布式引擎,提供Hadoop之上的SQL查询接口及多维分析(OLAP)能力以支持超大规模数据。而superset是airbnb开源的一款数据可视化工具。
kylin在超大数据规模下仍然可以提供秒级甚至毫秒级sql响应的OLAP多维分析查询服务。而且对服务器内存的要求也不像spark sql那么高,经过多方面的优化,数据膨胀率甚至可以控制在100%以内。它利用hive做预计算,然后建立多维的数据立方体,并存在hbase中,从而提供了实时查询的能力。
superset也就是早先的caravel,提供了丰富的图表供用户配置。只要连上数据源,勾几个简单的配置,或者写点sql。用户就可以轻易的构建基于d3、nvd3、mapbox-gl等的炫酷图表。
我厂也是选择了kylin和superset,遗憾的是superset支持多种数据源,包括druid、hive、impala、sparkSQL、presto以及多种主流关系型数据库,但是并不支持kylin。于是我们对其进行了改进。
首先观察superset的源码,它后台使用Flask App Builder搭建的,数据访问层用sqlalchemy实现。也就是说,它本质上可以支持所有数据源,只要实现一套kylin的dialect即可。而同时github上有一个pykylin项目,就是实现的这个dialect。这极大增强了我解决这个问题的信心。
正好前几周,superset出了一个新的prod版本airbnb_prod.0.15.5.0。装好它和pykylin之后,导入kylin数据源,成功!
但是点开sqllab想敲点sql验证一下时,却出了异常。
Debug了pykylin代码,发现get_table_names方法的入参connection实际已经是sqlalchemy的Engine对象了,这可能是最新sqlalchemy的版本升级造成的。总之,将原来的代码:
def get_table_names(self, connection, schema=None, **kw):
return connection.connection.list_tables()
改成:
def get_table_names(self, engine, schema=None, **kw):
connection = engine.contextual_connect()
return connection.connection.list_tables()
即可。
顺便我们看到这里它扩展了sqlalchemy的list_tables方法,sqllab左上方的table选择区还有列出所有schema的下拉框,于是我们顺带把list_schama方法也实现。connection.py添加:
def list_schemas(self):
route = 'tables_and_columns'
params = {'project': self.project}
tables = self.proxy.get(route, params=params)
return [t['table_SCHEM'] for t in tables]
dialect.py添加:
def get_schema_names(self, engine, schema=None, **kw):
connection = engine.contextual_connect()
return connection.connection.list_schemas()
之后执行sql还是有错:
pykylin在每次调用kylin的api时会首先登录,以获得JSESSIONID,并存入cookie中,这里是登录失败,检查代码,发现这里问题还挺多的,首先proxy.py中的login方法作者写的是self.password = user应改成password。dialect.py中create_connect_args方法改为:
def create_connect_args(self, url):
opts = url.translate_connect_args()
api_prefix = 'kylin/api/'
args = {
'username': opts['username'],
'password': opts['password'],
'endpoint': 'http://%s:%s/%s' % (opts['host'], opts['port'], api_prefix)
}
args.update(url.query)
return [], args
这样大部分sql查询没有问题,但是有的查询结果有部分值是null,这样也会出错。修改cursor.py的_type_mapped方法:
def _type_mapped(self, result):
meta = self.description
size = len(meta)
for i in range(0, size):
column = meta[i]
tpe = column[1]
val = result[i]
if val is None:
pass
elif tpe == 'DATE':
val = parser.parse(val)
elif tpe == 'BIGINT' or tpe == 'INT' or tpe == 'TINYINT':
val = int(val)
elif tpe == 'DOUBLE' or tpe == 'FLOAT':
val = float(val)
elif tpe == 'BOOLEAN':
val = (val == 'true')
result[i] = val
return result
这样在sqllab中执行sql基本没问题了。
下一步开始自定义slice,定制自己的可视化dashboard。
在这里再次遇到问题,superset会自动把count函数计算的列设置别名叫count,而count是kylin的关键字,因此导致查找失败。修改superset的models.py的sqla_col方法:
@property
def sqla_col(self):
name = self.metric_name
if name == 'count':
name = 'total_count'
return literal_column(self.expression).label(name)
另外在slice中还经常会遇到pandas抛出的KeyError异常。这是因为在superset里面所有的关键字都是小写,然而kylin返回的所有的数据metadata全是大写,导致superset在kylin的返回结果中查询关键字的时候出现找不到关键字的错误。
修改pykylin的cursor.py的execute方法。
def execute(self, operation, parameters={}, acceptPartial=True, limit=None, offset=0):
sql = operation % parameters
data = {
'sql': sql,
'offset': offset,
'limit': limit or self.connection.limit,
'acceptPartial': acceptPartial,
'project': self.connection.project
}
logger.debug("QUERY KYLIN: %s" % sql)
resp = self.connection.proxy.post('query', json=data)
column_metas = resp['columnMetas']
for c in column_metas:
c['label'] = str(c['label']).lower()
c['name'] = str(c['name']).lower()
self.description = [
[c['label'], c['columnTypeName'],
c['displaySize'], 0,
c['precision'], c['scale'], c['isNullable']]
for c in column_metas
]
self.results = [self._type_mapped(r) for r in resp['results']]
self.rowcount = len(self.results)
self.fetched_rows = 0
return self.rowcount
最后,我发现在查找的字段包含kylin中的date类型时也会出错。点击slice页面右上角的query按钮,可以查看superset最终发送的sql。
将它直接拷贝到kylin的insight页面去执行,发现报错。原来kylin的date类型只支持年月日,而superset在添加日期搜索条件时为了实现定时刷新图表而在sql的日期条件中都精确到了时分秒。后来我发现superset支持在列的设置页面为一个日期列添加自定义的格式转换函数
于是我在这里设置日期列格式
TO_DATE(‘{}’, ‘yyyy-MM-dd’)
然后可以看到slice这里sql中的该列都变成了to_date函数形式
最后的工作就是修改kylin源码,添加对日期函数的支持。hive sql是支持to_date等日期格式转换函数的,kylin凭什么不支持?
大致debug了一下kylin的源码,kylin处理sql的入口在server-base模块下的QueryController.java的query方法中。我发现在最终调用jdbc驱动执行sql之前,kylin会调QueryUtil类的massageSql方法来优化sql。主要是加上limit和offset参数。最后调内部类DefaultQueryTransformer的transform方法改掉sql中的一些通病,比如SUM(1)改成count(1)等。日期转换函数的处理放在这后面我觉得是最合适的。
添加正则表达式,以匹配日期函数:
private static final String TO_DATE = "(to_date|TO_DATE)\\(['|\"]([^'|\"]*)['|\"],\\s?['|\"]([^'|\"]*)['|\"]\\)";
private static final Pattern FN_TO_DATE = Pattern.compile(TO_DATE);
添加日期转行函数解析:
private static String executeFN(String sql) {
Matcher m;
while (true) {
m = FN_TO_DATE.matcher(sql);
if (!m.find())
break;
String dateTime = m.group(2);
String format = m.group(3);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date dt = null;
try {
dt = sdf.parse(dateTime);
} catch (ParseException e) {
logger.error("Parse date error", e);
}
sdf = new SimpleDateFormat(format);
String date = sdf.format(dt);
String begin = sql.substring(0, m.start());
String end = sql.substring(m.end(), sql.length());
sql = begin + "'" + date + "'" + end;
}
return sql;
}
然后在massageSql方法中调用这个函数,kylin就可以支持上面sql的执行了
kylin在超大数据规模下仍然可以提供秒级甚至毫秒级sql响应的OLAP多维分析查询服务。而且对服务器内存的要求也不像spark sql那么高,经过多方面的优化,数据膨胀率甚至可以控制在100%以内。它利用hive做预计算,然后建立多维的数据立方体,并存在hbase中,从而提供了实时查询的能力。
superset也就是早先的caravel,提供了丰富的图表供用户配置。只要连上数据源,勾几个简单的配置,或者写点sql。用户就可以轻易的构建基于d3、nvd3、mapbox-gl等的炫酷图表。
我厂也是选择了kylin和superset,遗憾的是superset支持多种数据源,包括druid、hive、impala、sparkSQL、presto以及多种主流关系型数据库,但是并不支持kylin。于是我们对其进行了改进。
首先观察superset的源码,它后台使用Flask App Builder搭建的,数据访问层用sqlalchemy实现。也就是说,它本质上可以支持所有数据源,只要实现一套kylin的dialect即可。而同时github上有一个pykylin项目,就是实现的这个dialect。这极大增强了我解决这个问题的信心。
正好前几周,superset出了一个新的prod版本airbnb_prod.0.15.5.0。装好它和pykylin之后,导入kylin数据源,成功!
但是点开sqllab想敲点sql验证一下时,却出了异常。
Debug了pykylin代码,发现get_table_names方法的入参connection实际已经是sqlalchemy的Engine对象了,这可能是最新sqlalchemy的版本升级造成的。总之,将原来的代码:
def get_table_names(self, connection, schema=None, **kw):
return connection.connection.list_tables()
改成:
def get_table_names(self, engine, schema=None, **kw):
connection = engine.contextual_connect()
return connection.connection.list_tables()
即可。
顺便我们看到这里它扩展了sqlalchemy的list_tables方法,sqllab左上方的table选择区还有列出所有schema的下拉框,于是我们顺带把list_schama方法也实现。connection.py添加:
def list_schemas(self):
route = 'tables_and_columns'
params = {'project': self.project}
tables = self.proxy.get(route, params=params)
return [t['table_SCHEM'] for t in tables]
dialect.py添加:
def get_schema_names(self, engine, schema=None, **kw):
connection = engine.contextual_connect()
return connection.connection.list_schemas()
之后执行sql还是有错:
pykylin在每次调用kylin的api时会首先登录,以获得JSESSIONID,并存入cookie中,这里是登录失败,检查代码,发现这里问题还挺多的,首先proxy.py中的login方法作者写的是self.password = user应改成password。dialect.py中create_connect_args方法改为:
def create_connect_args(self, url):
opts = url.translate_connect_args()
api_prefix = 'kylin/api/'
args = {
'username': opts['username'],
'password': opts['password'],
'endpoint': 'http://%s:%s/%s' % (opts['host'], opts['port'], api_prefix)
}
args.update(url.query)
return [], args
这样大部分sql查询没有问题,但是有的查询结果有部分值是null,这样也会出错。修改cursor.py的_type_mapped方法:
def _type_mapped(self, result):
meta = self.description
size = len(meta)
for i in range(0, size):
column = meta[i]
tpe = column[1]
val = result[i]
if val is None:
pass
elif tpe == 'DATE':
val = parser.parse(val)
elif tpe == 'BIGINT' or tpe == 'INT' or tpe == 'TINYINT':
val = int(val)
elif tpe == 'DOUBLE' or tpe == 'FLOAT':
val = float(val)
elif tpe == 'BOOLEAN':
val = (val == 'true')
result[i] = val
return result
这样在sqllab中执行sql基本没问题了。
下一步开始自定义slice,定制自己的可视化dashboard。
在这里再次遇到问题,superset会自动把count函数计算的列设置别名叫count,而count是kylin的关键字,因此导致查找失败。修改superset的models.py的sqla_col方法:
@property
def sqla_col(self):
name = self.metric_name
if name == 'count':
name = 'total_count'
return literal_column(self.expression).label(name)
另外在slice中还经常会遇到pandas抛出的KeyError异常。这是因为在superset里面所有的关键字都是小写,然而kylin返回的所有的数据metadata全是大写,导致superset在kylin的返回结果中查询关键字的时候出现找不到关键字的错误。
修改pykylin的cursor.py的execute方法。
def execute(self, operation, parameters={}, acceptPartial=True, limit=None, offset=0):
sql = operation % parameters
data = {
'sql': sql,
'offset': offset,
'limit': limit or self.connection.limit,
'acceptPartial': acceptPartial,
'project': self.connection.project
}
logger.debug("QUERY KYLIN: %s" % sql)
resp = self.connection.proxy.post('query', json=data)
column_metas = resp['columnMetas']
for c in column_metas:
c['label'] = str(c['label']).lower()
c['name'] = str(c['name']).lower()
self.description = [
[c['label'], c['columnTypeName'],
c['displaySize'], 0,
c['precision'], c['scale'], c['isNullable']]
for c in column_metas
]
self.results = [self._type_mapped(r) for r in resp['results']]
self.rowcount = len(self.results)
self.fetched_rows = 0
return self.rowcount
最后,我发现在查找的字段包含kylin中的date类型时也会出错。点击slice页面右上角的query按钮,可以查看superset最终发送的sql。
将它直接拷贝到kylin的insight页面去执行,发现报错。原来kylin的date类型只支持年月日,而superset在添加日期搜索条件时为了实现定时刷新图表而在sql的日期条件中都精确到了时分秒。后来我发现superset支持在列的设置页面为一个日期列添加自定义的格式转换函数
于是我在这里设置日期列格式
TO_DATE(‘{}’, ‘yyyy-MM-dd’)
然后可以看到slice这里sql中的该列都变成了to_date函数形式
最后的工作就是修改kylin源码,添加对日期函数的支持。hive sql是支持to_date等日期格式转换函数的,kylin凭什么不支持?
大致debug了一下kylin的源码,kylin处理sql的入口在server-base模块下的QueryController.java的query方法中。我发现在最终调用jdbc驱动执行sql之前,kylin会调QueryUtil类的massageSql方法来优化sql。主要是加上limit和offset参数。最后调内部类DefaultQueryTransformer的transform方法改掉sql中的一些通病,比如SUM(1)改成count(1)等。日期转换函数的处理放在这后面我觉得是最合适的。
添加正则表达式,以匹配日期函数:
private static final String TO_DATE = "(to_date|TO_DATE)\\(['|\"]([^'|\"]*)['|\"],\\s?['|\"]([^'|\"]*)['|\"]\\)";
private static final Pattern FN_TO_DATE = Pattern.compile(TO_DATE);
添加日期转行函数解析:
private static String executeFN(String sql) {
Matcher m;
while (true) {
m = FN_TO_DATE.matcher(sql);
if (!m.find())
break;
String dateTime = m.group(2);
String format = m.group(3);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date dt = null;
try {
dt = sdf.parse(dateTime);
} catch (ParseException e) {
logger.error("Parse date error", e);
}
sdf = new SimpleDateFormat(format);
String date = sdf.format(dt);
String begin = sql.substring(0, m.start());
String end = sql.substring(m.end(), sql.length());
sql = begin + "'" + date + "'" + end;
}
return sql;
}
然后在massageSql方法中调用这个函数,kylin就可以支持上面sql的执行了
相关文章推荐
- kylin与superset集成实现数据可视化
- Pentaho6.1中D3可视化库的集成及数据联动的实现
- superset 开源数据可视化工具(For Apache Kylin)使用说明
- Trafodion 集成R实现数据可视化
- 简单易学多维数据可视化R实现:神奇的卡通脸谱图Chernoff faces
- [Echarts可视化] php和ajax连接数据库实现动态数据可视化
- 可视化数据分析(三) 一个简单的散点图的实现
- 一种基于矩形块的颜色渲染方式实现二维数据可视化
- ECharts, PHP, MySQL, Ajax, JQuery 实现前后端数据可视化
- 12个帮助实现超棒数据可视化效果的Javascript框架
- 小项目-数据处理篇:租房信息整理,plotly实现数据可视化
- Jawbone Up 数据的按小时统计及可视化(R 语言实现)
- 以911新闻为例演示Python实现数据可视化的教程
- 教你用Cognos Analytics实现数据的可视化
- 如何使用zeppelin实现大数据可视化
- 用编程工具实现数据可视化的几个选择
- Android 以webview的方式集成Dcloud 5+SDK 实现携带数据跳转原生界面
- 开源数据可视化工具(For Apache Kylin)使用说明
- d3结合Spring和mybatis实现数据可视化中数据库内容的树型显示
- 基于睿思BI-开源商业智能系统实现数据快速可视化