SQL事务的实际成本是多少?

时间:2010-10-08 19:37:09

标签: sql sql-server ado.net

这需要一些背景知识。我正在创建一个Web应用程序(带有SQL Server后端的ASP.NET / VB.NET),它将接受一个应用程序来接收资金,这个表单有三个人的联系信息字段。因为所有三个人(姓名,电子邮件,电话)的信息都相同,所以我选择将联系信息存储在与应用程序分开的表中。在应用程序表中,这三个联系人中的每一个都有一个ID字段,指向联系人表中的联系人。

然而,这在我做CRUD的过程中提出了一个有趣的问题。我可以想到在一个SQL事务中为应用程序和所有三个联系人创建,更新和检索信息的唯一方法需要非常复杂的存储过程。换句话说,通过多个事务检索此信息会更简单(对我来说)。但是,由于这些信息永远不需要单独使用,我总是会做多个事务来获取一个应用程序的信息。

所以我的问题:

  1. 这个设计是否过度杀伤?每个应用程序永远不会有超过三个联系人,并且必须有三个。通过将此信息删除到单独的表格,我是否过于复杂?
  2. 执行多个SQL事务与编写复杂存储过程并且只需要一个事务的实际成本是多少?
  3. 一般来说,使用ADO.NET对Web应用程序进行SQL事务的成本是多少?
  4. 感谢您对这个冗长的解释感到满意。

    *的 修改 *

    在阅读了一些回复之后,似乎我使用了“交易”这个词。我对实际好奇的是在单个连接上执行多个查询的成本,而不是一个查询。对不起有误。

6 个答案:

答案 0 :(得分:8)

需要事务将数据库从一个一致状态移动到另一个一致状态。这里的“一致”适用于数据库的应用程序视图。典型的例子是两个账户之间的汇款:您必须借记一个账户并贷记另一个账户。在这两个操作之间,数据库是不一致的,有一些钱已经“消失”(从一个帐户中扣除的金额无处)。但在事务结束时,数据库再次保持一致。您需要一个事务来跨越这两个操作,以保护读者不要查看不一致的数据库,并在发生崩溃时确保数据库一致。

您说为了在单个事务中处理逻辑中的多个项目,您需要复杂的过程。事实并非如此,事务范围与请求范围正交:客户端可以启动事务,通过在3个调用中调用3个过程来执行3个操作,然后提交事务。 要求所有操作都在一个存储过程中完成。

因此,事务会导致重大的过程开销。事实上,一个程序将无视交易。在事务内部调用时以及在没有事务的情况下调用时,编写良好的过程应该正常运行。有关在事务例外情况下行为正常的过程的模板,请参阅Exception handling and nested transactions

最后,交易的真实成本是多少?事务数据(读取并不真正需要事务),因此他们锁定修改的行。尝试读取这些被阻塞的行时,读取通常会阻塞,然后事务越长,锁定行的时间越长,阻塞的读取越多。但是有一个非常简单的解决方案:快照读取。快照读取真的是一个神奇的小精灵粉尘,它们允许应用程序通过被阻止的行继续无阻碍,因为读者总是可以读取之前的行版本到阻塞的更新排。请参阅Using Row Versioning-based Isolation Levels

所以结论很简单:交易没有成本。仅仅因为没有替代交易。问题不是'X慢于Y',是'X是唯一正确替代'的问题。

<强>更新

编辑后:有多个请求与一个请求的费用可能显着。到服务器的往返(即,在开放连接上发出请求)具有固定成本。如果您进行多次往返,则需要在每次请求时支付此固定费用。如果您使用多个过程调用执行单个请求,则此固定成本仅支付一次。在非常热系统上,这种差异是可衡量的,并且在整体性能上有成本。但我在谈论真正的热门系统,就像每秒数千个请求一样。解决方案通常不会使过程复杂化,而是在一个请求中发出多个过程调用:

SqlCommand cmd = new SqlCommand(@"
exec usp_firstProc @arg1, @arg2;
exec usp_secondProc @arg2, @arg3;
exec usp_thirdProc @arg1, @arg3;", connection);
cmd.Parameters.Add("@arg1", someValue);
...
cmd.ExecuteNonQuery();

但我必须说除非你有非常热的工作负载,否则不需要这个解决方案。作为一个封套的规则,对于任何低于1000个请求/秒的内容,我会认为明确代码的好处超过了性能优势。

如果您必须为每个请求打开新连接,那么有一点不同。登录握手真的昂贵,可以测量几百毫秒。但解决方案很简单:连接池(在ADO.Net中默认启用)并且不会丢弃应用程序prematurel中的连接,保持连接并重复使用它直到整个工作单元完成。

答案 1 :(得分:1)

  1. 这不是矫枉过正。这是一个很好的规范化和良好的应用自由重复自己的原则。系统的要求可能会发生变化,您将来可能需要2或4个联系人,而不是3个。
  2. 您不需要执行任何复杂的存储过程。你这里没有做任何与众不同的事情。您根本不需要存储过程。
  3. 不知道。
  4. 当你说“交易”时,我认为你的意思是“查询”。当我想获取一个应用程序的数据时,我会使用两个单独的简单查询:

    SELECT *
      FROM application
    

    SELECT *
      FROM contact
    

    这是两个独立的查询,但很重要。更重要的部分是以有意义的方式设计数据库。

答案 2 :(得分:0)

你在这里使用“交易”这个词有点不对劲。我认为你所指的是一个简单的数据库查询,ACID交易助手。

那就是说,很难说。默认情况下启用的连接池将无需打开三个/四个单独的连接。

您甚至可能不需要多个查询来获取数据,并且可以连接这两个表来检索它,例如:

SELECT app.field1, app,field2, contact1.name, contact2.name, contact3.name
FROM Applications AS app
INNER JOIN Contacts AS contact1 ON app.contact1ID = contact1.id
INNER JOIN Contacts AS contact2 ON app.contact2ID = contact2.id
INNER JOIN Contacts AS contact3 ON app.contact3ID = contact3.id

或类似的东西,根据您的情况(从app向选择列表添加新字段,从每个联系表返回更多数据等。如果表格索引正确,查找速度非常快,可能不会影响性能一点都不。

如果您正在插入/更新数据,那么是的,您需要多个查询来执行此操作,但选择数据可以在一个中进行。

当然,另一种选择是将数据放回原始应用程序表中,首先不需要第二个表。此时,不需要额外的连接或其他任何东西。

答案 3 :(得分:0)

为你思考......

如果您当前的设计是两个表,则第一个表(applciation)为

ID,contact_id1,contact_id2,contact_id3,otherinfo

作为联系人的第二个表格只是

contact_id,info

此格式的最大优势是,如果您有多个应用程序上的1个联系人... Conatact'Bob'是8个不同应用程序上的contact_1意味着bob的信息仅记录一次,并且只有他的ID记录在应用程序表上。最大的限制是每个应用程序的3个联系人是永久编码的

或者......应用可以定义为 application_ID,信息 并且作为 application_ID,id_sequence,info

此设置的优点是可以为每个应用程序存储无限数量的联系人。

虽然一切都能满足您的需求......如果您总是有3个联系人,那么这里的第二个选项就是sorta unnessacary。如果您不打算周围有超过1000条记录,那么将所有信息存储在一个表中并不是最糟糕的想法(选项一主要是消除冗余数据)。回答你的问题1 ......在某些情况下它有点过分,但答案将取决于你当前的要求和未来的要求。从“最佳实践”设计来看,2表格设置是优惠的。

只是问题#2的评论 - 这将是一个更简单的存储过程......我看不出它对你的设置太复杂了。您可以创建一个可更新的视图(不确定可能出现的限制或问题)。

答案 4 :(得分:0)

除了纯处理成本之外,每个查询都会花费您一个客户端 - 数据库 - 服务器往返时间。因此,如果您设法批量处理简单的单个查询,则可以节省往返时间。

答案 5 :(得分:-1)

使用1个存储过程执行所有操作,因此您只需要打开1个sql连接。

Web表单中的每个变量都映射到存储过程中的参数(包括隐藏的联系人和应用程序键,对于新应用程序,默认为0)

在该过程中,如果记录键为0,则该过程将记录添加到数据库并使用scope_identity()快速检索键。如果向过程发送非零键,则更新记录。然后,该过程返回所有数据,并将所有存储过程变量声明为输出。

然后你可以将程序包装在sql开始/结束事务中,这样它就是一个全有或全无的sql命令。