我使用了很多模型来连接到数据库,在我的上一个项目中,我使用c# &实体框架,我为db连接创建了静态类,但是我有打开和关闭连接的问题,当超过10-15个请求聚集在一起时给我错误,我通过改变连接到db的方法解决了这个问题,我现在每个请求都连接,并删除了所有静态方法和类。
现在我想知道,
进行连接的最佳模型是什么?
- 我应该在每次查询后关闭它并在使用前打开它吗? 静态类中的连接是一个很好的模型(我不需要)每次都创造它)?
- 对于这个问题是否有一个好的设计模式?
- 所有这些都是为了同一个问题什么是最好的方法建立数据库连接(静态的、抽象的、每个请求的…)?
例如我在一个短信发送者网络面板上工作,我应该每秒发送100K短信,这些短信与他人收集,并制作一个包,每个包有1~20条短信,然后我需要发送5K~100K包每秒钟,当我发送一个包,我应该做这些步骤:
- 将单条短信更新为已发送或未发送
- 在useraccounts表中删除用户余额
- 更新用户表短信发送次数
- 更新手机号码表短信发送次数
- 更新短信发送数表中的短信发送数
- 包表 中已发送和未发送短信的更新包
- 在包表 中更新线程如何发送此包的包
- 更新线程表,查看当前线程发送了多少条短信,失败了多少条
- 在AccountDocument表 中为这些事务添加账户文档
所有的步骤和许多其他的事情,如日志,用户界面和监控小部件,应该做的,我需要DB连接来做每一个事务。
现在,连接数据库的最佳模型是什么?通过人类请求或线程请求或每个单独的事务…
回答你的问题:
-
关闭它。net在底层为你做连接池。
-
创建它。每次使用using (Connection conn = new ....) -这样,您将充分利用。net池机制。
-
你可以使用。net ThreadPool(或者你自己的自定义ThreadPool),定义ThreadPool来并行使用10个线程,并一个接一个地Enqueue工作项。这样,在同一时间内使用的连接不会超过10个,而且可能会更快。关于自定义线程池的更多信息:自定义线程池实现
每个实例。
这是我对架构的建议:
-
为待发短信创建数据库表(队列)
-
每行将包含短信所需的所有信息+当前状态。
-
创建一个工作进程,可能是一个windows服务,它将不断地对该表进行采样——比方说,每5秒采样一次。它将选择状态= '待发送'的TOP ~20条短信(应该表示为int)。并将状态更新为'正在发送'
-
每条短信将使用windows服务端的自定义线程池发送。
-
在进程结束时,所有已处理的sms状态将使用CTE(通用表表达式-您可以发送带有所有刚刚被处理的sms行id的CTE来进行"批量更新"到"完成"状态)更新为"完成"。
-
你可以使状态更新存储过程与'getpending'相同。这样,您就可以在没有锁的情况下选择-for-update,从而使数据库工作得更快。
-
这样,你可以有不止一个处理器服务在运行(但是你必须松开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
.
管理连接的注意事项:(取自上一个链接)
- 对象上下文将打开在操作之前尚未打开的连接。如果对象上下文在操作期间打开了连接,它将总是在操作完成时关闭连接。
- 如果您手动打开连接,对象上下文不会关闭它。调用Close或Dispose将关闭连接。
- 如果对象上下文创建了连接,那么当上下文被丢弃时,该连接将始终被丢弃。
- 在长时间运行的对象上下文中,您必须确保在不再需要上下文时处置上下文。
希望有帮助。
我认为每个请求缩放最好。使用线程安全的连接池,并使连接范围与工作单元一致。让负责事务行为和工作单元的服务检出连接,使用它,并在提交或回滚工作单元时将其返回到池中。
更新:10-12秒提交状态更新?你还做错了什么。你写的问题不足以提供一个合适的答案。
纳斯达克每日交易量为13亿笔交易,在8小时的一天中,每秒约有45K笔交易。你的成交量是纳斯达克的2倍。如果你试图在一台机器上完成它,我会说纳斯达克使用了不止一台服务器。
我也想知道你是否可以不使用ACID更新状态。毕竟,星巴克不使用两阶段承诺。也许更好的解决方案是使用带有阻塞队列的生产者/消费者模式,以便在发送状态后更新这些状态。