【问题标题】:Most efficient way of storing database information in memory using C#使用 C# 在内存中存储数据库信息的最有效方法
【发布时间】:2015-06-18 23:20:08
【问题描述】:

我目前正忙于用 C# 进行实验,以便可以根据自己的喜好构建 Web 服务器。 Web 服务器将专为我也在从事的项目而设计。希望它能比其他网络服务器运行得更好。

现在我一直在尝试做的是找到一种将数据库信息存储在应用程序内存中的有效方法。

到目前为止,我已经尝试创建一个类似于带有字段的表的类。为表中的每一行创建该类的新实例。然后创建类似于索引的某些 Dictionary 变量。就像一个用于 ID,一个用于用户名。它正在工作,但与数据库中的表相比,它消耗了大约 3 倍的内存。我所做的示例:

class User()
{
    public int Id;
    public string Name;
    //other columns, the same way
}

至于字典:

Dictionary<int, User> UserIDIndex = new Dictionary<int, User>();
User NewUser = new User();
NewUser.Name = "Something...";
UserIDIndex.Add(someId, NewUser);

我也尝试过 DataTable,但根本没有成功。它消耗的内存至少比类和字典方法多 50%。

我很好奇是否还有其他方式,一种更有效的信息存储方式?

这一切背后的原因:每秒需要更新和选择大量数据。恐怕直接从数据库中做太多了。所以起初(启动和某些条件)想在内存中加载一些东西。然后,在某些时候,更新内存中的值。然后一次这么多秒,更新数据库中的这些值。我想这也可以用 NodeJS 完成,但它使用 1 个核心,我觉得这有点难过。如果您遇到 CPU 问题,这并不是真正的未来证明。

【问题讨论】:

  • 我正在构建一个实时应用程序,在线的用户越多,需要完成的连接/查询就越多。每个用户每秒执行 2-4 个操作,它可以远远超过 200 个。在这种情况下,我确实相信需要一些缓存。我会看看你推荐的软件。
  • 与stackoverflow的数据库访问相比,我认为数据库内的查询无法处理如此大的请求量。就算是死锁了,修复一下还是不错的blog.codinghorror.com/deadlocked
  • 预期音量是多少?

标签: c# caching memory-management architecture


【解决方案1】:

从性能的角度来看,如果分组 SQL 请求的性能明显优于一系列单个请求的性能,那么您的方法可能会很好。

主要的不便之处在于应用程序在崩溃时的健壮性。可能需要实施一些恢复机制。

关于内存,很难通过观察可用 RAM 来评估内存需求,因为内存不会立即释放:它会在系统需要时根据需要完成。

要计算实际使用的内存,请评估对象的大小并乘以计数。

在处理用户输入时,您是否评估了平均计算时间 有和没有数据库更新?

【讨论】:

  • 不适用于实时功能。我还没有达到那部分,因为我一直在寻找一种我觉得它会起作用的方法。从我运行的一些测试中,我注意到 MySQL 数据库上的每秒连接数有相当严格的限制,即使在更改设置之后也是如此。大约 250/s,这就是它在大约 1.5 分钟的运行中出错的地方。该过程然后崩溃。这让我想到了另一种方法......
  • 如果我存储信息的方式没有问题,那么我想...我会使用大约 4GB 的 RAM。静态信息 0.6-1GB,在线用户 3GB,当然,如果我同时在线获得大量信息。时间会证明这是否会发生。至于恢复机制,我正在考虑不时将更新的信息更新到数据库中。就像确保内存中的信息和数据库中的信息永远不会 30 秒不同步一样。也许是重要案例的实时记录。所以万一发生崩溃我可以恢复。
  • 您是否设想将更新请求推送到内存列表中并在辅助线程上弹出所有列表,在弹出操作之间休眠 x 秒?
  • 事实上我正在考虑类似的事情。实际上,这可能是我的下一个目标/要做的事情之一。我已经在这里和那里学习了一些关于线程和任务的知识,但还没有真正应用它......所以我不确定当我开始使用它时会遇到什么问题。
【解决方案2】:

我建议使用在 Entity Framework Core (EF Core) 中使用的可观察集合。 (我没有深入了解,我观察到 .local 让我可以访问内存中的副本)

Public ObservableCollection<user> users = new ObservableCollection<user>();

由于使用 EF Core 在底层使用了相同的结构,因此您可以编写一些东西,让您可以轻松地在使用它们之间来回切换。棘手的部分是以一种非常容易使用 EF Core 或 InMemory 实现的方式定义事物。以下示例基于使用 EF Core。在下面的代码中,我创建了一个通用的基础来工作。我包装了 Ef Core 使用的 DbContext。包装器允许我访问驻留在实体框架中的本地内存数据。

现在数据在内存中,您只需将其保存到磁盘。由于您可以选择 EF Core,因此很容易比较适合您的环境的选项。为了快速测试,我序列化到文件。从同一个文件重新加载也很容易恢复到我保存的状态。

警告:我的包装器只是将整个数据库加载到内存中,因为在我的情况下它很小。 .ToList() 是确保将数据加载到 EF Core 中的内存的代码。

查看此链接以获取Entity Framework Core How To

代码:

using Blog.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;


namespace Blog
{
    public interface IData_Common
    {
        ObservableCollection<User> Users { get; }
        ObservableCollection<Other> Others { get; }
    }

    public interface IData_InMemoryContext : IData_Common
    {
        IData_InMemoryContext Create();
    }

    public class Data_InMemoryContext : IData_InMemoryContext
    {
        public ObservableCollection<User> Users { get; set; }
        public ObservableCollection<Other> Others { get; set; }

        public Data_InMemoryContext()
        {
            Users = new ObservableCollection<User>();
            Others = new ObservableCollection<Other>();
        }

        public IData_InMemoryContext Create()
        {
            Users = new ObservableCollection<User>();
            Others = new ObservableCollection<Other>();

            return new Data_InMemoryContext();
        }

    }

    public interface IData_Context
    {
        DbSet<User> Users { get; set; }
        DbSet<Other> Others { get; set; }

        IData_Context Create();
    }

    public class Data_Context : DbContext, IData_Context
    {
        public virtual DbSet<User> Users { get; set; }
        public virtual DbSet<Other> Others { get; set; }

        public IData_Context Create()
        {
            return new Data_Context();
        }

        public Data_Context()
        {
            //this.Configuration.LazyLoadingEnabled = false;
        }
    }

    // I wrap context so that I can use the InMemoryContext interface
    public class WrapData_Context : IData_InMemoryContext
    {
        private object _Context;
        private DbSet<User> _Users;
        private DbSet<Other> _Others;

        public WrapData_Context(Data_Context DbContext)
        {
            if (DbContext is null)
            {
                throw new NullReferenceException("DbContext can't be null");
            }
            _Context = DbContext;
            // Need to make sure the data is accessed so it is loaded to the local ObservableCollections
            DbContext.Users.ToList();
            _Users = DbContext.Users;
            DbContext.Others.ToList();
            _Others = DbContext.Others;
        }

        public IData_InMemoryContext Create()
        {
            var context = new Data_Context();
            return new WrapData_Context(context);
        }

        // Properties
            public ObservableCollection<User> Users => _Users.Local.ToObservableCollection();
            public ObservableCollection<Other> Others => _Others.Local.ToObservableCollection();

    }

}

示例用法:

Data_Context _c = new Data_Context();
WrapData_Context _context = new WrapData_Context(_c);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-06
    • 2010-11-09
    • 1970-01-01
    • 2021-08-19
    • 2010-09-22
    • 1970-01-01
    相关资源
    最近更新 更多