RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 608416
Accepted
tretetex
tretetex
Asked:2020-12-27 18:16:04 +0000 UTC2020-12-27 18:16:04 +0000 UTC 2020-12-27 18:16:04 +0000 UTC

优化数据库条目。实体框架

  • 772

有一个 SQLite 基础,带有表 -Messages和Users. 包含大约 100,000 名用户。

您需要添加/更新帖子以及重新发布的用户。每 100 个帖子,大约有 6000 个转发用户。如果所有重新发布的用户还不在数据库中(这正常吗?),现在添加 1 个帖子(大约 300 次重新发布)需要大约 14 秒。

我这样做:

using (AppDbContext db = new AppDbContext())
{
    // Получаем всех пользователей, чтобы сравнивать с репостнувшими пользователями
    usersFromDb = db.Users.ToList();

    /*****
     Т.к. пользователей в базе много, получение всех пользователей не оптимально.
     Но лучше ли будет делать много запросов (для каждого пользователя), вместо этого одного?
    *****/
}

using (AppDbContext db = new AppDbContext())
{
    foreach (var message in messages)
    {
        if (message.RepostUsers != null)
        {
            // Проходим по списку репостнувших пользователей

            /*****
             Эта часть занимает много времени
             Можно ли оптимизировать?
            *****/
            foreach (var user in message.RepostUsers)
            {
                // Ищем по пользователям из базы
                var dbUser = usersFromDb.Find(u => u.Id == user.Id);
                if (dbUser == null)
                {
                    // Если не нашли, помечаем новым
                    db.Entry(user).State = EntityState.Added;
                }
                else if (dbUser.Name != user.Name)
                {
                    // Если нашли и есть изменения, помечаем измененным
                    db.Entry(user).State = EntityState.Modified;
                }
                else
                {
                    // Если нашли и изменений нет, помечаем не измененным
                    db.Entry(user).State = EntityState.Unchanged;
                }
            }
        }

        // Получаем Message из базы
        var dbMessage = db.Messages.AsNoTracking().FirstOrDefault(p => p.Id == message.Id);

        // Message всегда либо новое, либо измененное. Помечаем
        db.Entry(message).State = dbMessage != null ? EntityState.Modified : EntityState.Added;
    }

    db.Messages.AddRange(messages);
    db.SaveChanges();
}

问题
一 数据库中有很多用户,获取所有用户并不是最优的。但是提出许多请求(分别针对每个转发的用户)而不是一个更好吗?

问题 2:
当我通过 更改用户状态时db.Entry(user).State,是否会缓存对象以跟踪更改?那些。AsNoTracking没有考虑?如果是这样,是否应该更改以及如何更改?

c#
  • 3 3 个回答
  • 10 Views

3 个回答

  • Voted
  1. RusArt
    2020-12-27T18:47:06Z2020-12-27T18:47:06Z

    我只回答优化问题。每次查找 Id 时,您都会遍历 usersFromDb。为此,您需要使用字典。这是代码:

    Dictionary<int,User> usersFromDb;
    ...
    usersFromDb = db.Users.ToDictionary(x=>x.Id, x=>x);
    ...
    // Ищем по пользователям из базы
    if (usersFromDb.ContainsKey(userId))
    {
        var dbUser = usersFromDb[userId];
    ...
    

    该字典允许在恒定的 O(1) 时间内进行搜索。搜索工作表发生在线性时间 O(n) 内。

    • 1
  2. Александр Пузанов
    2020-12-27T19:30:43Z2020-12-27T19:30:43Z

    考虑 PostgreSQL,因为 SQLite 是一个关系数据库。我不细说。一方面简单,但是数据库中的记录越多,查询越重。将数据库中的记录数加倍,看看它是否仍然值得使用。

    • 0
  3. Best Answer
    tretetex
    2020-12-30T02:33:49Z2020-12-30T02:33:49Z

    那么发生了什么:

    • 不是获取所有用户(1-2 秒),而是获取特定用户(~0.01 秒)
    • 写入/更新100条消息(约5000-7000个转发用户)的速度为5-7秒。

    有几个问题:

    1. 如果数据库已经包含要添加的记录,则该方法AddRange不起作用,因为 AddRange设置记录的状态Added
    2. 为了检查用户(是否需要添加或更改),每次都从数据库中提取所有记录,并与之进行比较。使用where. 但这里的一切都不是那么简单 - 如果您添加 10 条消息,则进行选择会更快。如果每条消息有100条,那么用户很多,选择的时间比通过 获取所有记录的时间要长ToList()。
    3. 很多时间花在了改变记录的状态上db.Entry(user).State =,因为 这导致对内部算法DetectChanges的昂贵执行调用

    更正:

    1. 我们删除AddRange
    2. 我们从数据库中选择用户,而不是获取所有用户
    3. 禁用机制db.Configuration.AutoDetectChangesEnabled = false;

    结果:

    using (AppDbContext db = new AppDbContext())
    {
        // Получаем всех пользователей, чтобы сравнивать с репостнувшими пользователями
        usersFromDb = db.Users.Where(x => allUsers.Keys.Contains(x.Id)).ToList();
    }
    
    using (AppDbContext db = new AppDbContext())
    {
        db.Configuration.AutoDetectChangesEnabled = false;
    
        foreach (var message in messages)
        {
            if (message.RepostUsers != null)
            {
                // Проходим по списку репостнувших пользователей
                foreach (var user in message.RepostUsers)
                {
                    User dbUser = null;
    
                    // Ищем по пользователям из базы
                    if (usersFromDb.ContainsKey(user.Id))
                        dbUser = usersFromDb[user.Id];
    
                    if (dbUser == null)
                    {
                        // Если не нашли, помечаем новым
                        db.Entry(user).State = EntityState.Added;
                    }
                    else if (dbUser.Name != user.Name)
                    {
                        // Если нашли и есть изменения, помечаем измененным
                        db.Entry(user).State = EntityState.Modified;
                    }
                    else
                    {
                        // Если нашли и изменений нет, помечаем не измененным
                        db.Entry(user).State = EntityState.Unchanged;
                    }
                }
            }
    
            // Получаем Message из базы
            var dbMessage = db.Messages.AsNoTracking().FirstOrDefault(p => p.Id == message.Id);
    
            // Message всегда либо новое, либо измененное. Помечаем
            db.Entry(message).State = dbMessage != null ? EntityState.Modified : EntityState.Added;
        }
    
        db.Configuration.AutoDetectChangesEnabled = true;
        db.SaveChanges();
    }
    

    特别感谢@Ruslan Artamonov提供的提示和答案。

    • 0

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    如何停止编写糟糕的代码?

    • 3 个回答
  • Marko Smith

    onCreateView 方法重构

    • 1 个回答
  • Marko Smith

    通用还是非通用

    • 2 个回答
  • Marko Smith

    如何访问 jQuery 中的列

    • 1 个回答
  • Marko Smith

    *.tga 文件的组重命名(3620 个)

    • 1 个回答
  • Marko Smith

    内存分配列表C#

    • 1 个回答
  • Marko Smith

    常规赛适度贪婪

    • 1 个回答
  • Marko Smith

    如何制作自己的自动完成/自动更正?

    • 1 个回答
  • Marko Smith

    选择斐波那契数列

    • 2 个回答
  • Marko Smith

    所有 API 版本中的通用权限代码

    • 2 个回答
  • Martin Hope
    jfs *(星号)和 ** 双星号在 Python 中是什么意思? 2020-11-23 05:07:40 +0000 UTC
  • Martin Hope
    hwak 哪个孩子调用了父母的静态方法?还是不可能完成的任务? 2020-11-18 16:30:55 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    Arch ArrayList 与 LinkedList 的区别? 2020-09-20 02:42:49 +0000 UTC
  • Martin Hope
    iluxa1810 哪个更正确使用:if () 或 try-catch? 2020-08-23 18:56:13 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5