首页 > 解决方案 > EF 核心与 Devart 提供程序,光标共享问题

问题描述

我们的 WEB API 使用了大约 2 年,突然一些高负载使我们的一个 API 工作非常缓慢,DBA 说这是因为游标共享问题,这本身就是因为 NLS 参数正在改变。

API 仅使用 EF Core 2.1 和 Devart 提供程序 (9.6.621) 供 oracle 进行简单查询:

public IQueryable<NotificationEndUserViewEntity> InboxQuerable(string userId, string clientId)
    {
        var client = _dbContext.Clients.FirstOrDefault(x => x.ClientId == clientId);
        var inboxQuerable = _dbContext
                                 .NotificationsUserView
                                    .Where(nots =>
                                        !nots.IsCancelled &&
                                        !nots.IsDeleted &&
                                        nots.IsOutGoing &&
                                        nots.ActiveFrom < DateTime.Now &&
                                        (nots.ActiveTill == null || nots.ActiveTill > DateTime.Now) &&
                                        nots.ClientId == client.Id &&
                                        nots.RecipientId == userId);
        return inboxQuerable.AsNoTracking();
    }

 var firstMastRead = await InboxQuerable(userId, clientId)
                                    .OrderByDescending(x => x.ActiveFrom)
                                    .FirstOrDefaultAsync(x => x.IsMustRead && !x.IsRead);

这里有什么问题?DBA 说这个查询有超过 300-500 个游标。是参数绑定问题,EF 还是 Devart(我们使用旧版本,但在更新之前我们必须确定),如何控制 NLS 设置?

更新:DBA 看到的。

    SELECT nots.Id,
        nots.ActiveFrom,
        nots.ActiveTill,
        nots.BodyEn,
        nots.BodyKa,
        nots.ClientId,
        nots.HasAttachment,
        nots.IsCancelled,
        nots.IsDeleted,
        nots.IsMustRead,
        nots.IsNoReplay,
        nots.IsOutGoing,
        nots.IsRead,
        nots.RecipientId,
        nots.SubjectEn,
        nots.SubjectKa
   FROM VW_USER_NOTIFICATIONS nots
  WHERE     (    (    (    (    (    (    (nots.IsCancelled = :"SYS_B_0")
                                      AND (nots.IsDeleted = :"SYS_B_1"))
                                 AND (nots.IsOutGoing = :"SYS_B_2"))
                            AND (nots.ActiveFrom < CURRENT_DATE))
                       AND (   nots.ActiveTill IS NULL
                            OR (nots.ActiveTill > CURRENT_DATE)))
                  AND (nots.ClientId = :p__client_Id_0))
             AND (nots.RecipientId = :p__userId_1))
        AND ((nots.IsMustRead = :"SYS_B_3") AND (nots.IsRead = :"SYS_B_4")) 
ORDER BY nots.ActiveFrom DESC FETCH FIRST :"SYS_B_5" ROWS ONLY

这是由 Linq 生成的:

SELECT 
nots.Id, 
nots.ActiveFrom, 
nots.ActiveTill, 
nots.BodyEn, 
nots.BodyKa, 
nots.ClientId, 
nots.HasAttachment, 
nots.IsCancelled, 
nots.IsDeleted, 
nots.IsMustRead, 
nots.IsNoReplay, 
nots.IsOutGoing, 
nots.IsRead, 
nots.RecipientId,
nots.SubjectEn, 
nots.SubjectKa  FROM VW_USER_NOTIFICATIONS nots WHERE (((((((nots.IsCancelled = 0)
    AND (nots.IsDeleted = 0)) 
    AND (nots.IsOutGoing = 1)) 
    AND (nots.ActiveFrom < CURRENT_DATE))
    AND (nots.ActiveTill IS NULL OR (nots.ActiveTill > CURRENT_DATE))) 
    AND (nots.ClientId = :p__client_Id_0)) 
    AND (nots.RecipientId = :p__userId_1)) 
    AND ((nots.IsMustRead = 1) AND (nots.IsRead = 0)) ORDER BY nots.ActiveFrom DESC FETCH FIRST 1 ROWS ONLY

标签: c#oracleentity-frameworkentity-framework-coredevart

解决方案


//DBA 说这个查询有超过 300-500 个游标。//

作为开发人员,您(恕我直言)......应该始终查看生成的 SQL,尤其是在创建非平凡查询时。

所以这里有一个面包屑来连接它。

https://github.com/granadacoder/oracle-ef-issues-demo/blob/master/src/DataLayer.EntityFramework/Contexts/EfPlaygroundDbContext.cs#L66

这将允许您共享生成的 SQL。

如果您记录并研究您的日志,您希望非常注意以下评论,例如

“无法翻译,将在当地进行评估。”

我的猜测是,由于这个问题,您可能会获得超多游标。

在我的示例中,您可以在下面的面包屑中看到这些警告。

https://github.com/granadacoder/oracle-ef-issues-demo/blob/master/src/ConsoleOne/Program.cs#L160

============================

另外,我注意到了这一点:

nots.ActiveTill > DateTime.Now

尝试创建一个局部变量并使用它.......与拥有 DateTime.Now “内联”。

例子:

DateTime myNow = DateTime.Now;

然后

nots.ActiveTill > myNow

我不记得确切,但那个“内联”的东西正在响起我内心的声音。


稍微偏离主题,考虑使用 IDateTimeProvider 与直接使用 DateTime。

https://www.nuget.org/packages/Chronos.Abstractions/1.0.2

============================

另外,我没有使用过 Devart 提供程序,但是 Oracle.EF 驱动程序遇到了一个巨大的问题,即我所说的“列别名错误”。

我在这里的例子:

https://github.com/granadacoder/oracle-ef-issues-demo

错误报告在这里:

https://support.oracle.com/rs?type=bug&id=31234903

https://support.oracle.com/rs?type=bug&id=31240915

(“讨论”在这里: https ://community.oracle.com/thread/4327311 )

附言

Oracle 3.1 EF 驱动程序看起来比 2.1 版本好很多。我认为 3.1 的认证过程一定要严格得多。也许是 Oracle 3.1 EF 驱动程序花了这么长时间的原因(我猜想)(我猜因为它处于测试阶段,它实际上“花了这么长时间”).. 走出门。

https://www.nuget.org/packages/Oracle.EntityFrameworkCore/

当然,这可能就是为什么 devart 是一个受欢迎的选择。

总之。

知道如何查看生成的 SQL。

了解如何调试/跟踪日志以获取有关生成的 SQL 的警告。

调整您的 C# 代码以避免出现警告。

ORM 有时是“善变的”。归根结底,他们创建了 SQL,但需要批判性地查看该 SQL。


推荐阅读