0

我的应用程序包括客户端、Web 层(负载平衡)、应用程序层(负载平衡)和数据库层。Web 层向客户端公开服务,并将调用转发到应用程序层。然后应用层对数据库执行查询(使用 NHibernate)并返回结果。

数据主要是读取,但写入却相当频繁,尤其是当新数据进入系统时。通常情况下,数据是聚合的,这些聚合返回给客户端 - 而不是原始数据。

通常,用户会对最近数据的聚合感兴趣——比如过去一周的数据。因此,对我来说,引入一个包含过去 7 天所有数据的缓存是有意义的。我不能只在加载实体时缓存它们,因为我需要聚合一系列实体,并且该范围由客户端以及其他复杂情况(例如过滤器)决定。我需要知道 - 在给定的时间范围内 - 该范围内的所有数据是否都在缓存中。

在我理想的幻想世界中,我的服务根本不需要改变:

public AggregationResults DoIt(DateTime starting, DateTime ending, Filter filter)
{
    // execute HQL/criteria call and have it automatically use the cache where possible
}

将有一个单独的过滤层连接到 NHibernate 中,并智能且透明地确定 HQL/条件查询是否可以针对缓存执行,并且仅在必要时才进入数据库。如果所有数据都在缓存中,它会查询缓存数据本身,有点像内存数据库。

然而,在第一次检查中,NHibernate 的二级缓存机制似乎并不适合我的需要。我想做的是:

  1. 将其配置为始终在缓存中保留最近 7 天的数据。例如。“对于这个表,缓存该字段在 7 天前和现在之间的所有记录。”
  2. 具有手动维护缓存的能力。当新数据进入系统时,如果我可以直接将其放入缓存而不是等到缓存失效,那就太好了。同样,当数据超出时间段时,我希望能够从缓存中提取它。
  3. 让 NHibernate 智能地了解它何时可以直接从缓存中提供查询,而不是完全访问数据库。例如。如果用户请求过去 3 天的数据聚合,则该聚合应直接从缓存中计算,而不是接触数据库。

现在,我很确定#3 要求太多了。即使我可以在缓存中填充所需的所有数据,NHibernate 也不知道如何有效地查询这些数据。它实际上必须遍历所有实体以区分哪些与查询相关(老实说,这可能很好)。此外,它还需要实现 NHibernate 的查询引擎,该引擎针对对象而不是数据库执行。但我可以做梦,对吧?

假设 #3 要求太多,我需要在我的服务中使用一些逻辑,如下所示:

public AggregationResults DoIt(DateTime starting, DateTime ending, Filter filter)
{
    if (CanBeServicedFromCache(starting, ending, filter))
    {
        // execute some LINQ to object code or whatever to determine the aggregation results
    }
    else
    {
        // execute HQL/criteria call to determine the aggregation results
    }
}

这并不理想,因为每个服务都必须是缓存感知的,并且必须复制聚合逻辑:一次用于通过 NHibernate 查询数据库,一次用于查询缓存。

也就是说,如果我至少可以将相关数据存储在 NHibernate 的二级缓存中,那就太好了。这样做将允许其他服务(不进行聚合)透明地从缓存中受益。如果我决定在系统的其他地方需要二级缓存,它还将确保我不会在缓存的实体上加倍(一次在二级缓存中,一次在我自己的单独缓存中)。

我怀疑如果我可以ICache在运行时获得实现,我需要做的就是调用该Put()方法将我的数据粘贴到缓存中。但这可能是在危险的地方...

谁能提供关于 NHibernate 的二级缓存机制是否可以满足我的任何要求的任何见解?还是我应该推出自己的解决方案并完全放弃 NHibernate 的二级缓存?

谢谢

PS。我已经考虑过使用多维数据集更快地进行聚合计算,但这仍然让我将数据库作为瓶颈。除了缓存之外,我还可以使用多维数据集,但缺少缓存是我现在最关心的问题。

4

3 回答 3

2

停止使用事务性 (OLTP) 数据源进行分析 (OLAP) 查询,问题就会消失。

当一个领域重大事件发生时(例如一个新实体进入系统或被更新),触发一个事件(一个领域事件)。为事件连接一个处理程序,该处理程序获取创建或更新实体的详细信息并将数据存储在一个非规范化的报告存储中,该存储专门设计为允许报告您想要的聚合(很可能将数据推送到星型模式中)。现在您的报告只是沿着预定义的轴查询聚合(甚至可能是预先计算的),只需要一个简单的选择和一些连接。查询可以使用 L2SQL 之类的东西,甚至是简单的参数化查询和数据读取器来执行。

性能提升应该是显着的,因为您可以优化读取端以跨许多标准进行快速查找,同时优化写入端以通过 id 快速查找并减少写入时的索引负载。

当您迁移到这种方法后,还可以获得额外的性能和可扩展性,然后您可以物理分离您的读取和写入存储,以便您可以为每个写入存储运行 n 个读取存储,从而允许您的解决方案横向扩展以满足增加的读取需求而写入需求以较低的速度增加。

于 2010-03-02T19:36:57.773 回答
1

定义 2 个缓存区域“aggregation”和“aggregation.today”,过期时间较长。将这些分别用于前几天和今天的聚合查询。

DoIt()中,使用可缓存查询在请求范围内每天进行 1 次 NH 查询。在 C# 中组合查询结果。

使用后台进程填充缓存,该进程DoIt()定期调用您需要缓存的日期范围。此过程的频率必须低于聚合缓存区域的到期时间。

当今天的数据发生变化时,清除缓存区域“aggregation.today”。如果您想快速重新加载此缓存区域,请立即执行此操作,或者让另一个更频繁的后台进程DoIt()在今天调用。

当您启用查询缓存时,NHibernate 将尽可能从缓存中提取结果。这基于查询和参数值。

于 2010-03-01T16:14:13.657 回答
0

在分析 NHibernate 缓存详细信息时,我记得读过一些您不应该在缓存中中继的内容,这似乎是一个不错的建议。

我认为滚动您自己的数据/缓存管理策略可能更合理,而不是试图让您的 O/R 映射器覆盖您的应用程序需求。

此外,您谈论的 7 天缓存规则听起来像是与业务相关的事情,女巫是 O/R 映射器不应该知道的事情。

总之,让您的应用程序在没有任何缓存的情况下工作,而不是使用分析器(或更多 - .net、sql、nhibernate 分析器)来查看瓶颈在哪里,并通过最终添加缓存或任何其他优化来开始改进“红色”部分.

PS:关于缓存的一般性-根据我的经验,一个缓存点很好,两个缓存在灰色区域,你应该有充分的理由分开,两个以上是自找麻烦。

希望能帮助到你

于 2010-03-01T21:34:37.937 回答