建立数据库连接的最佳方法是什么(静态、抽象、每个请求,…)

本文关键字:抽象 请求 静态 数据库连接 最佳 方法 是什么 建立 | 更新日期: 2023-09-27 18:08:19

我使用了很多模型来连接到数据库,在我的上一个项目中,我使用c# &实体框架,我为db连接创建了静态类,但是我有打开和关闭连接的问题,当超过10-15个请求聚集在一起时给我错误,我通过改变连接到db的方法解决了这个问题,我现在每个请求都连接,并删除了所有静态方法和类。

现在我想知道,

进行连接的最佳模型是什么?

  1. 我应该在每次查询后关闭它并在使用前打开它吗?
  2. 静态类中的连接是一个很好的模型(我不需要)每次都创造它)?
  3. 对于这个问题是否有一个好的设计模式?
  4. 所有这些都是为了同一个问题什么是最好的方法建立数据库连接(静态的、抽象的、每个请求的…)?

例如我在一个短信发送者网络面板上工作,我应该每秒发送100K短信,这些短信与他人收集,并制作一个包,每个包有1~20条短信,然后我需要发送5K~100K包每秒钟,当我发送一个包,我应该做这些步骤:

  1. 将单条短信更新为已发送或未发送
  2. 在useraccounts表中删除用户余额
  3. 更新用户表短信发送次数
  4. 更新手机号码表短信发送次数
  5. 更新短信发送数表中的短信发送数
  6. 包表
  7. 中已发送和未发送短信的更新包
  8. 在包表
  9. 中更新线程如何发送此包的包
  10. 更新线程表,查看当前线程发送了多少条短信,失败了多少条
  11. 在AccountDocument表
  12. 中为这些事务添加账户文档

所有的步骤和许多其他的事情,如日志,用户界面和监控小部件,应该做的,我需要DB连接来做每一个事务。

现在,连接数据库的最佳模型是什么?通过人类请求或线程请求或每个单独的事务…

建立数据库连接的最佳方法是什么(静态、抽象、每个请求,…)

回答你的问题:

  1. 关闭它。net在底层为你做连接池。

  2. 创建它。每次使用using (Connection conn = new ....) -这样,您将充分利用。net池机制。

  3. 你可以使用。net ThreadPool(或者你自己的自定义ThreadPool),定义ThreadPool来并行使用10个线程,并一个接一个地Enqueue工作项。这样,在同一时间内使用的连接不会超过10个,而且可能会更快。关于自定义线程池的更多信息:自定义线程池实现

  4. 每个实例。

这是我对架构的建议:

  1. 为待发短信创建数据库表(队列)

  2. 每行将包含短信所需的所有信息+当前状态。

  3. 创建一个工作进程,可能是一个windows服务,它将不断地对该表进行采样——比方说,每5秒采样一次。它将选择状态= '待发送'的TOP ~20条短信(应该表示为int)。并将状态更新为'正在发送'

  4. 每条短信将使用windows服务端的自定义线程池发送。

  5. 在进程结束时,所有已处理的sms状态将使用CTE(通用表表达式-您可以发送带有所有刚刚被处理的sms行id的CTE来进行"批量更新"到"完成"状态)更新为"完成"。

  6. 你可以使状态更新存储过程与'getpending'相同。这样,您就可以在没有锁的情况下选择-for-update,从而使数据库工作得更快。

  7. 这样,你可以有不止一个处理器服务在运行(但是你必须松开nolock)。

记住要尽量避免锁。

顺便说一下,这也很好,因为您可以从系统中的任何位置发送SMS,只需在待定的SMS表中添加一行即可。

还有一件事,我不建议使用实体框架,因为它有太多的引擎盖下进行。对于这种任务,您只需要调用3-4个存储过程,就可以了。也许可以看看Dapper-dot-NET——它是一个非常轻量级的MicroDal框架,在大多数情况下比EF(实体框架)快10倍以上

1. Should i close it after every query?

。Net为你做了这些,所以让它处理它,这是一个垃圾收集器任务。所以不要费心手动处理对象,Jon Skeet给出了一个很好的答案:https://stackoverflow.com/a/1998600/544283。然而,您可以使用using(IDisposable){ }语句来强制GC完成它的工作。这里有一篇关于资源重新分配的好文章:http://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About.

2. A connection in static class is good?

永远不要使数据上下文静态!数据上下文不是线程安全或并发安全。

3. Is there a good design pattern for this problem?

正如Belogix提到的依赖注入和工作单元模式是伟大的,实际上实体框架就是本身就是一个工作单元。不过,DI和UoW被高估了,如果你是第一次处理IoC容器,那么它并不容易实现,如果你要走这条路,我建议使用Ninject。另一件事是,如果你不打算运行测试,你并不真的需要DI,这些模式的神奇之处在于解耦,所以你可以轻松地测试和模拟。

简而言之:如果你要对你的代码运行测试,请使用这些模式。如果没有,我将为您提供一个示例,说明如何在您想要的服务之间共享数据上下文。这就是你第四个问题的答案。

4. What is the best method for making database connection (static, per request)?

上下文服务:

public class FooContextService {
    private readonly FooContext _ctx;
    public FooContext Context { get { return _ctx; } }
    public FooContextService() {
        _ctx = new FooContext();
    }
}
其他服务:

public class UnicornService {
    private readonly FooContext _ctx;
    public UnicornService(FooContextService contextService) {
        if (contextService == null)
            throw new ArgumentNullException("contextService");
        _ctx = contextService.Context;
    }
    public ICollection<Unicorn> GetList() {
        return _ctx.Unicorns.ToList();
    }
}
public class DragonService {
    private readonly FooContext _ctx;
    public DragonService(FooContextService contextService) {
        if (contextService == null)
            throw new ArgumentNullException("contextService");
        _ctx = contextService.Context;
    }
    public ICollection<Dragon> GetList() {
        return _ctx.Dragons.ToList();
    }
}

控制器:

public class FantasyController : Controller {
    private readonly FooContextService _contextService = new FooContextService();
    private readonly UnicornService _unicornService;
    private readonly DragonService _dragonService;
    public FantasyController() {
        _unicornService = new UnicornService(_contextService);
        _dragonService = new DragonService(_contextService);
    }
    // Controller actions
}

重新考虑(几乎是编辑):如果您需要上下文不为您的实体创建代理,因此也不需要延迟加载,您可以重载您的上下文服务,如下所示:

public class FooContextService {
    private readonly FooContext _ctx;
    public FooContext Context { get { return _ctx; } }
    public FooContextService() : this(true) { }
    public FooContextService(bool proxyCreationEnabled) {
        _ctx = new FooContext();
        _ctx.Configuration.ProxyCreationEnabled = proxyCreationEnabled;
    }
}

注意:

  • 如果你设置代理创建enabled为false,你将而不是延迟加载。
  • 如果你有api控制器,你不希望处理任何完整的对象图。
编辑:

先阅读:

  • 此链接涉及EF6的预发布版本:实体框架和异步。
  • Scott Allen在他的博客:Async in Entity Framework 6.0。
  • 如果你打算使用工作单元,我建议你阅读这个:用UnitOfWorkScope创建DbContext环境。
  • Darin Dimitrov关于在ASP中进行异步操作的回答。.NET MVC使用。NET 4中ThreadPool中的线程。

Get this done:

(_context as IObjectContextAdapter).ObjectContext.Connection.Open();

这是一篇关于管理连接和事务的好文章。

实体框架通过Connection属性公开EntityConnection。读作:public sealed class EntityConnection : DbConnection .

管理连接的注意事项:(取自上一个链接)

  • 对象上下文将打开在操作之前尚未打开的连接。如果对象上下文在操作期间打开了连接,它将总是在操作完成时关闭连接。
  • 如果您手动打开连接,对象上下文不会关闭它。调用CloseDispose将关闭连接。
  • 如果对象上下文创建了连接,那么当上下文被丢弃时,该连接将始终被丢弃。
  • 在长时间运行的对象上下文中,您必须确保在不再需要上下文时处置上下文。

希望有帮助。

我认为每个请求缩放最好。使用线程安全的连接池,并使连接范围与工作单元一致。让负责事务行为和工作单元的服务检出连接,使用它,并在提交或回滚工作单元时将其返回到池中。

更新:

10-12秒提交状态更新?你还做错了什么。你写的问题不足以提供一个合适的答案。

纳斯达克每日交易量为13亿笔交易,在8小时的一天中,每秒约有45K笔交易。你的成交量是纳斯达克的2倍。如果你试图在一台机器上完成它,我会说纳斯达克使用了不止一台服务器。

我也想知道你是否可以不使用ACID更新状态。毕竟,星巴克不使用两阶段承诺。也许更好的解决方案是使用带有阻塞队列的生产者/消费者模式,以便在发送状态后更新这些状态。