Performance 大集合上的慢速查询

Performance 大集合上的慢速查询,performance,ravendb,Performance,Ravendb,我正在做一个审计日志,它将会话保存在RavenDB中。最初,用于查询审计日志的网站响应速度足够快,但随着记录的数据量的增加,搜索页面变得不可用(在使用默认设置返回之前,它会超时-无论使用的是何种查询)。目前,表中有大约4500万个会话被查询,但稳定状态下的文档预计约为1.5亿个 问题是,有了这么多实时数据,到处玩测试变得不切实际。我希望有人能给我一些想法,什么是最有效的调查领域 索引如下所示: public AuditSessions_WithSearchParameters() { M

我正在做一个审计日志,它将会话保存在RavenDB中。最初,用于查询审计日志的网站响应速度足够快,但随着记录的数据量的增加,搜索页面变得不可用(在使用默认设置返回之前,它会超时-无论使用的是何种查询)。目前,表中有大约4500万个会话被查询,但稳定状态下的文档预计约为1.5亿个

问题是,有了这么多实时数据,到处玩测试变得不切实际。我希望有人能给我一些想法,什么是最有效的调查领域

索引如下所示:

public AuditSessions_WithSearchParameters()
{
    Map = sessions => from session in sessions
                      select new Result
                      {
                          ApplicationName = session.ApplicationName,
                          SessionId = session.SessionId,
                          StartedUtc = session.StartedUtc,
                          User_Cpr = session.User.Cpr,
                          User_CprPersonId = session.User.CprPersonId,
                          User_ApplicationUserId = session.User.ApplicationUserId
                      };

    Store(r => r.ApplicationName, FieldStorage.Yes);
    Store(r => r.StartedUtc, FieldStorage.Yes);
    Store(r => r.User_Cpr, FieldStorage.Yes);
    Store(r => r.User_CprPersonId, FieldStorage.Yes);
    Store(r => r.User_ApplicationUserId, FieldStorage.Yes);
}
查询的本质是这样的:

// Query input paramters
var fromDateUtc = fromDate.ToUniversalTime();
var toDateUtc = toDate.ToUniversalTime();

sessionQuery = sessionQuery
        .Where(s =>
            s.ApplicationName == applicationName &&
            s.StartedUtc >= fromDateUtc &&
            s.StartedUtc <= toDateUtc
        );

var totalItems = Count(sessionQuery);
var sessionData =
    sessionQuery
    .OrderByDescending(s => s.StartedUtc)
    .Skip((page - 1) * PageSize)
    .Take(PageSize)
    .ProjectFromIndexFieldsInto<AuditSessions_WithSearchParameters.ResultWithAuditSession>()
    .Select(s => new
    {
        s.SessionId,
        s.SessionGroupId,
        s.ApplicationName,
        s.StartedUtc,
        s.Type,
        s.ResourceUri,
        s.User,
        s.ImpersonatingUser
    })
    .ToList();
//查询输入参数
var fromDateUtc=fromDate.ToUniversalTime();
var toDateUtc=toDate.ToUniversalTime();
sessionQuery=sessionQuery
。其中(s=>
s、 ApplicationName==ApplicationName&&
s、 StartedUtc>=fromDateUtc&&
s、 StartedUtc s.StartedUtc)
.Skip((第1页)*页面大小)
.Take(页面大小)
.ProjectFromIndexFieldsInto()项目
。选择(s=>new
{
s、 会话ID,
s、 会话组ID,
s、 应用程序名称,
s、 启动UTC,
s、 类型,
s、 ResourceUri,
s、 用户,
s、 模拟用户
})
.ToList();
首先,为了确定结果的页数,我使用以下方法计算查询中的结果数:

private static int Count<T>(IRavenQueryable<T> results)
{
    RavenQueryStatistics stats;
    results.Statistics(out stats).Take(0).ToArray();
    return stats.TotalResults;
}
private静态整数计数(IRavenQueryable结果)
{
拉文奎尔统计数据;
results.Statistics(out stats).Take(0.ToArray();
返回stats.TotalResults;
}
这本身就非常昂贵,因此优化在这里和查询的其余部分都是相关的

查询时间与结果项的数量没有任何相关关系。如果我对
applicationName
参数使用与任何结果不同的值,那么速度也一样慢

改进的一个方面是在会话中使用顺序ID。由于与本文无关的原因,我发现使用基于guid的ID是最实用的。我不确定是否可以轻松地更改现有值的ID(有这么多数据),我更希望不删除数据(但如果预期的影响足够大,可能会删除)。我知道顺序ID会使索引的b树表现更好,但我不知道其影响有多大

另一种方法是在id中包含时间戳,并查询id以字符串开头的文档,匹配足够的时间来过滤结果。示例id可以是
AuditSessions/2017-12-31-24-31-42/bc835d6c-2fba-4591-af92-7aab96339d84
。这还需要我更新或删除所有现有数据。当然,这也有大部分顺序ID的优点

第三种方法是随着时间的推移将旧数据移动到不同的集合中,因为您通常会查看最新的数据。这需要后台作业和跨收集时间边界查询的支持。它还存在一个问题,即如果需要访问旧会话的集合,该集合仍然很慢


我希望有比这些解决方案更简单的解决方案,例如以避免大量工作的方式修改查询或索引字段。

乍一看,它可能与
StartedUtc
上的范围查询有关。 我假设你使用的是精确的数字,所以你有很多不同的值。 如果可以的话,您可以通过将索引更改为秒/分钟粒度的索引(这通常是您要查询的),然后使用
标记
,这允许我们使用数字范围查询,从而显著降低成本

                      StartedUtcTicks = new Datetime(session.StartedUtc.Year, session.StartedUtc.Month, session.StartedUtc.Day, session.StartedUtc.Hour, session.StartedUtc.Minute, session.StartedUtc.Second).Ticks,

然后按日期标记进行查询。

启用查询计时,查看实际花费时间的位置。