Kyligence Copilot - AI 数智助理,以 AI 变革企业经营与管理! 立即了解更多

【技术帖】将 OLAP 用到极致—— Mondrian 非功能优化

苏志锋
2018年 2月 06日

作者 | 苏志锋

苏志锋,中国建行银行厦门开发中心工程师,主要从事建行金融大数据服务平台的建设。

多维数据分析的过程,是指数时间复杂度的立方体计算过程。因此,多维分析系统是 CPU 和 Memory 密集型系统。

本文介绍了影响 Mondrian 非功能指标的几个典型问题,主要包括:资源异常使用问题、资源未被充分使用问题、线程死锁问题、长时间占用资源问题、热点方法问题等。

01 资源异常使用问题

现象:当并发数超过系统最大查询线程池的处理能力时,触发流量控制,系统直接返回超出处理能力异常。但是,存在一种场景,即并发数瞬间增大,持续一段时间之后,停止发送请求,却存在一段时间的 CPU 空跑时间,在此期间并没有新的请求,并且 CPU 空跑时间的长短与并发持续时间成正比。

原因:对于每个请求,系统生成一个执行任务,并将执行任务添加到执行任务列表和执行线程池。超出流量控制的请求不会被添加到执行线程池,却被添加到了执行任务列表。从而造成执行任务引用的对象,需要等待其生命周期结束后才能被释放,造成了内存浪费,并且增加了执行任务列表的循环执行开销。原代码如下:

tasks.add(pair);

try {

executor.execute(task);

return task.get();

} catch (Throwable e) {

解决:对于超出流量控制的请求,不添加到执行任务列表。修改后的代码如下:

try {

executor.execute(task);

tasks.add(pair);

return task.get();

} catch (Throwable e) {

02 资源未被充分使用问题

现象:随着并发用户数的增大,CPU 维持在 10% 以下,TPS 明显偏低。

原因:系统中 SchemaPool,MonitorMap,MemberReader 等相关实现代码,存在着大量的临界区操作,造成了资源争用和线程等待。

解决:改造成读写锁,或者修改实现方式,提升系统吞吐量。

03 线程死锁问题

现象:存在某一次的清理 Segment 缓存操作,导致系统无法处理任何请求。

原因:系统将与 Segment 相关的操作命令,比如添加,更新,删除等操作命令,添加到一个执行队列。如果清理 Segment 缓存的命令,对 SchemaPool 加锁后,被添加到执行队列,因为其不在队列头而无法执行;与此同时,队列头的命令要运行,必须获取 SchemaPool 锁,则队列头命令等待,从而造成死锁。

解决:清理 Segment 缓存操作命令不再加入执行队列,直接执行,避免获取锁之后,被放入队列,而又无法立即执行,使正在执行的操作命令无法获取锁的情况。

04 长时间占用资源问题

现象:每个 MDX 查询都设置了最大执行时间,但是,存在查询,其执行时间明显超过最大执行时间,程序却并没有报告超时异常,长时间占用 CPU 和内存资源。

原因:一个 MDX 查询可能需要多次 SQL 查询,或者除了 SQL 查询之外,还需要大量的内存计算。系统在每个 SQL 执行前做超时检查,却没有给 SQL 执行设置最大执行时间,因此,存在 QL 执行时间超时,却没有被取消的情况。

解决:动态计算并设置 SQL 的最大执行时间。代码如下:

statement.setQueryTimeout(queryTimeout);

05 热点方法问题

现象:在压力测试中,成员的 getName方法占据超过 40% 的 CPU 资源。

原因:getName 是一个被高频调用的方法,但是,其内部实现却有一段临界区计算。

解决:成员名称在主题加载之后就不会变化,因此,定义成员名称为私有变量,主题加载之后第一次调用给变量赋值,此后调用直接返回变量值。类似的还有系统的 Properties 配置项,采用了 HashTable 加上动态计算的实现方式,影响了系统的非功能指标,改造成 Zookeeper 加本地缓存的实现方式。

本文介绍了几个影响 Mondrian 非功能指标的典型问题。其实,除了从技术角度解决 Mondrian的 非功能问题外,少用 strToSet、strToMember 等函数,甚至简单修改业务实现思路,有时候会事半功倍。

添加企微

kyligence
关注我们

kyligence