报表系统需要支持伸缩性,需要使用分布式部署和协同工作,必须有一定的技术根源支持,本节主要针对报表系统的缓存模块进行说明,本节不会描述在报表系统中如何个实现负载均衡等等信息,只讨论如何实现缓存以及缓存机制的作用。

缓存设计的首要任务是搞清楚需要缓存什么样的数据以及如何进行缓存。报表系统需要缓存的数据如下:

1、报表的基础定义信息:XML文本信息,使用键值直接访问,键值就是报表的唯一ID,报表系统认为,在系统中报表的ID总是唯一的,如果存在相同ID的不同报表定义,则后者“很有可能”覆盖前面录入的报表定义,这里存在一个通知用户的问题和报表定义覆盖的问题。报表系统不对定义进行特殊处理,直接覆盖。报表基础定义的XML缓存应该支持分布式缓存。报表基础定义的缓存理论上应该分成用户自定义报表和全局共享报表池的缓存两个部分。

2、报表系统所使用的字典定义信息:字典定义信息和报表基础定义信息一致,可以在不同的报表中复用,使用键值进行访问,键值即是字典定义的唯一ID。

3、报表字典的数据信息:缓存报表字典的实际字典值。使用报表的字典定义的键值访问,字典的值可能会有所变化。

4、报表使用的数据库链接信息缓存,和报表字典信息一致,使用数据库的唯一标识作为键值,数据库定义可能从Spring中取得,在这种情况下,Spring注入的Bean不再做缓存,这会带来分布访问的问题(需要后继的工作仔细研究。)。

5、报表数据:在Eris以前的版本中,我们使用了唯一Hash键值作为报表的数据的缓存,这个是整个应用中最复杂的部分。

在上面的列表中,我们可以看到,所有的缓存都是有键值的访问,以下列出访问各个缓存和清除各个缓存的规则。

  报表定义 字典定义 字典数据 数据库 报表数据
数量 256 64 和字典定义相当 8 1024
存储均值 5K(冗余) 1K(冗余) 5K(保守) 0.3K(保守) 5K(偏小,保守)
Insert缓存 初始化的时候插入文件定义报表
数据库定义的按需插入
初始化的时候插入 使用的时候按需插入 初始化的时候插入 按需插入
更新频率 非常低 非常低 非常低
删除频率 非常低 非常低 非常低
生命周期 应用生命周期 应用生命周期 应用生命周期/可调 应用生命周期 按照常用的策略
缓存策略 unlimited unlimited unlimited/LruCache unlimited LruCache

报表数据缓存策略:

在报表数据的缓存中,最重要的问题是如何对缓存数据进行键值Hash。注意,缓存的报表数据是:

  1. 数据只缓存原生的数据,即是直接从数据源中取得的数据,系统可能会提供最终结果的缓存(等待协商确定)
  2. 缓存的数据是没有经过二次过滤的数据。
  3. 缓存数据中无分页信息,对有分页的报表使用全部数据缓存的机制处理
  4. 缓存的数据中不会进行行集权限过滤,是原生的所有数据。
  5. 缓存的数据中不包含格式信息
  6. 缓存的数据是没有经过字典转换的原始数据。

报表数据缓存的键值由以下几个部分组成:

  1. 报表定义ID
  2. 当前登录的用户(不是必须的)
  3. 查询的参数键值对,对应为“参数名=参数值”的列表。
  4. 报表定义的Hash值。

在进行数据缓存的过程中,如果提供最终结果(渲染之后的结果)的缓存策略,需要从以下角度判断是否是缓存原生数据还是结果数据。

  1. 数据的容量大小,从报表结果中可以取得。
  2. 数据的各部分操作所占用的比例(从报表性能统计中可以取得),例如如果后期渲染处理的时间远远大于数据源生成数据的时间则应该按照最终结果缓存,反之应该按照原生数据缓存。

原生数据缓存数据的优势在于

  1. 支持分布式的缓存。虽然缓存最终结果也可以支持,但是无法对报表进行二次过滤等高级操作。
  2. 支持多样化的呈现,可以在相同的数据缓存上做复杂的操作。

从以上两点可以得到,我们建议还是缓存原生数据,而非最终的结果数据。

缓存报表数据对报表访问带来的影响是增加了取得数据的时候的步骤,即是取得数据的时候判断是否有缓存,如存在缓存则从缓存中取得,否则从数据源中取得数据。

缓存功能的附加功能

  1. 缓存的清除功能
  2. 缓存的性能统计功能

嗯,功能都不大,呵呵,很好实现。Open-mouthed

缓存的权衡,是应用缓存还是报表缓存

在特定的情况下,应用可能已经生成了对报表系统数据的缓存机制(例如分表、分库、Partition、物化视图等等),在这种情况之下,可以不使用报表缓存功能,报表数据缓存的唯一用处就是:尽可能的对慢报表提高相应速度。

以下列出在本版本中准备加入的报表缓存支持

  1. 支持OSCache
  2. 支持MemCache
  3. 尝试支持MemCacheDb(From Sina )

一句话,我在BI项目中觉得这个东西是可以使用的,但是在实际的OLTP中因为数据量小,这个功能反倒没有什么用处,这次主要是为了丰富功能,将缓存加入到实现中,呵呵,这个缓存应该在4.1版本中进行发布。