ASP.NET并发处理

讲到并发处理,我们一般会分为两个方面来说:a)服务器级别的并发控制; b)程序级别的并发控制。

服务器级别的并发控制:

Ø  调整服务器应用程序池中的最大连接数。

对于Web 服务器,dudu在优化博客园的服务器的时候写过以下的文章。

dudu 写过让Windows Server 2008+IIS 7+ASP.NET支持10万个同时请求http://www.cnblogs.com/dudu/archive/2009/11/10/1600062.html

1. 调整IIS 7应用程序池队列长度

由原来的默认1000改为65535。

IIS Manager > ApplicationPools > Advanced Settings

Queue Length : 65535

2.  调整IIS 7的appConcurrentRequestLimit设置

由原来的默认5000改为100000。

appcmd.exe set config /section:serverRuntime /appConcurrentRequestLimit:100000

在%systemroot%/System32/inetsrv/config/applicationHost.config中可以查看到该设置。

3. 调整machine.config中的processModel>requestQueueLimit的设置

由原来的默认5000改为100000。

<configuration>
<system.web>
<processModel requestQueueLimit="100000"/>

4. 修改注册表,调整IIS 7支持的同时TCPIP连接数

由原来的默认5000改为100000。

reg add HKLM/System/CurrentControlSet/Services/HTTP/Parameters /v MaxConnections /t REG_DWORD /d 1000000 

完成上述4个设置,就可以支持10万个同时请求

Ø  对于DB服务器同样也可以调整最大连接数来做优化。

      在调整优化好最大连接数之后,就只有软硬件负载均衡了。硬件负载均衡能够直接通过智能交换机实现,处理能力强,而且与系统无关,但是价格贵,配置困难,不能区分实习系统与应用的状态。所以硬件负载均衡适用于一大堆设备,大访问量,简单应用。软件负载均衡是基于系统与应用的,能过更好地根据系统与应用的状况来分配负载。性价比高。PCL负载均衡软件,Linux下的LVS软件。

程序级别的并发控制:

当两个用户同时访问一个页面,一个用户可能更新的事另一个用户已经删除的记录。或者,在一个用户加载页面跟他点击删除按钮之间的时间里,另一个用户修改了这条记录的内容。

当多个用户同时更新同一数据的时候,由于更新可能导致数据的不一致性,使得程序的业务数据发生错误,这种情况可以称之为并发。在ADO .NET中,并发的处理可以通过三种方式来控制:保守式并发控制、开发式并发控制以及最后更新生效方式。 
— 保守式并发控制:数据从数据库取出之后,一直处于锁定的状态,其他用户不能获取该数据,直至数据更新完毕之后,用户才能取出该数据进行操作。此种控制方式对于性能和资源占用得很多,由于只能同时有一个用户对数据享用操作权,所以可能会在正常业务中,影响其他用户的处理进程。但此控制方式可以完全保证数据的完整性。该方式可以通过.NET提供的事务机制来实现,前提是数据源需要支持事务。

— 开发式并发控制:数据在更新之前都是可以被其他用户使用的,只有在更新的时候,才锁定记录。但更新的时候,会比对与查询之初的数据是否吻合,如果不一致,则不运行修改。此种控制方式也可以完全保证数据的完整性,其优点是不会占用其他用户访问该数据的权限,其缺点是由于其他用户可能已经更新了这些数据,导致本次更新可能不会完成。对于此种控制方式,多以开发人员通过程序本身的业务逻辑来实现。

— 最后更新生效方式:此种方式同上,只有在数据更新的时候,其他用户才不可使用,但更新的时候不检查是否与开始数据一致,而直接对其更新。此种方式对于更新的并发性有很大的支持,但缺点是可能引发前后数据的不一致。此种方式适合可以满足此需求的业务场景使用。

注意:数据库的并发处理并不是一成不变的,不同的业务场景对数据库的并发要求是不一样的,可以根据具体情况具体分析

当多个用户试图同时修改数据时,需要建立控制机制来防止一个用户的修改对同时操作的其他用户所作的修改产生不利的影响。处理这种情况的系统叫做“并发控制”。

并发控制的类型

通常,管理数据库中的并发有三种常见的方法:

  • 保守式并发控制 - 在从获取记录直到记录在数据库中更新的这段时间内,该行对用户不可用。
  • 开放式并发控制 - 只有当实际更新数据时,该行才对其他用户不可用。更新将在数据库中检查该行并确定是否进行了任何更改。如果试图更新已更改的记录,则将导致并发冲突。
  • 最后的更新生效 - 只有当实际更新数据时,该行才对其他用户不可用。但是,不会将更新与初始记录进行比较;而只是写出记录,这可能就改写了自上次刷新记录后其他用户所进行的更改。

保守式并发

保守式并发通常用于两个目的。第一,在某些情况下,存在对相同记录的大量争用。在数据上放置锁所费的成本小于发生并发冲突时回滚更改所费的成本。

在事务过程中不宜更改记录的情况下,保守式并发也非常有用。库存应用程序便是一个很好的示例。假定有一个公司代表正在为一名潜在的客户检查库存。您通常要锁定记录,直到生成订单为止,这通常会将该项标记为“已订购”状态并将其从可用库存中移除。如果未生成订单,则将释放该锁,以便其他检查库存的用户得到准确的可用库存计数。

但是,在断开的结构中无法进行保守式并发控制。连接打开的时间只够读取数据或更新数据,因此不能长时间地保持锁。此外,长时间保留锁的应用程序将无法进行伸缩。

注意    如果基础数据源支持事务,则可以通过在事务中更新数据来模拟保守式并发。有关更多信息,请参见 ADO.NET 中的事务

开放式并发

在开放式并发中,只有在访问数据库时才设置并保持锁。这些锁将防止其他用户在同一时间更新记录。除了进行更新这一确切的时刻之外,数据始终可用。有关更多信息,请参见开放式并发

当试图更新时,已更改行的初始版本将与数据库中的现有行进行比较。如果两者不同,更新将失败,并引发并发错误。这时,将由您使用所创建的业务逻辑来协调这两行。

最后的更新生效

当使用“最后的更新生效”时,不会对初始数据进行检查,而只是将更新写入数据库。很明显,可能会发生以下情况:

  • 用户 A 从数据库获取一项记录。
  • 用户 B 从数据库获取相同的记录,对其进行修改,然后将更新后的记录写回数据库。
  • 用户 A 修改“旧”记录并将其写回数据库。

在上述情况中,用户 A 永远也不会看到用户 B 作出的更改。如果您计划使用并发控制的“最后的更新生效”方法,则要确保这种情况是可以接受的。

ADO.NET 和 Visual Studio .NET 中的并发控制

因为数据结构基于断开的数据,所以 ADO.NET 和 Visual Studio .NET 使用开放式并发。因此,您需要添加业务逻辑,以利用开放式并发解决问题。

如果您选择使用开放式并发,则可以通过两种常规的方法来确定是否已发生更改:版本方法(实际版本号或日期时间戳)和保存所有值方法。

版本号方法

在版本号方法中,要更新的记录必须具有一个包含日期时间戳或版本号的列。当读取该记录时,日期时间戳或版本号将保存在客户端。然后,将对该值进行部分更新。

处理并发的一种方法是仅当 WHERE 子句中的值与记录上的值匹配时才进行更新。该方法的 SQL 表示形式为:

UPDATE Table1 SET Column1 = @newvalue1, Column2 = @newvalue2
WHERE DateTimeStamp = @origDateTimeStamp

或者,可以使用版本号进行比较:

UPDATE Table1 SET Column1 = @newvalue1, Column2 = @newvalue2
WHERE RowVersion = @origRowVersionValue

如果日期时间戳或版本号匹配,则表明数据存储区中的记录未被更改,并且可以安全地使用数据集中的新值对该记录进行更新。如果不匹配,则将返回错误。您可以编写代码,在 Visual Studio .NET 中实现这种形式的并发检查。您还必须编写代码来响应任何更新冲突。为了确保日期时间戳或版本号的准确性,您需要在表上设置触发器,以便在发生对行的更改时,对日期时间戳或版本号进行更新。

保存所有值方法

使用日期时间戳或版本号的替代方法是在读取记录时获取所有字段的副本。ADO.NET 中的 DataSet 对象维护每个修改记录的两个版本:初始版本(最初从数据源中读取的版本)和修改版本(表示用户更新)。当试图将记录写回数据源时,数据行中的初始值将与数据源中的记录进行比较。如果它们匹配,则表明数据库记录在被读取后尚未经过更改。在这种情况下,数据集中已更改的值将成功地写入数据库。

对于数据适配器的四个命令(DELETE、INSERT、SELECT 和 UPDATE)来说,每个命令都有一个参数集合。每个命令都有用于初始值和当前值(或修改值)的参数。

注意    由于不存在初始记录,添加新记录(INSERT 命令)只需要当前值;移除记录(DELETE 命令)只需要使用初始值来定位要删除的记录。

以下示例显示一个数据集命令的命令文本,该命令更新一个典型的客户表。该命令是为动态 SQL 和开放式并发而指定的。

UPDATE Customers SET CustomerID = @currCustomerID, CompanyName = @currCompanyName, ContactName = @currContactName,
ContactTitle = currContactTitle, Address = @currAddress, City = @currCity,
PostalCode = @currPostalCode, Phone = @currPhone, Fax = @currFax
WHERE (CustomerID = @origCustomerID) AND (Address = @origAddress OR @origAddress IS NULL AND Address IS NULL) AND (City = @origCity OR @origCity IS NULL AND City IS NULL)
AND (CompanyName = @origCompanyName OR @origCompanyName IS NULL AND CompanyName IS NULL) AND (ContactName = @origContactName OR @origContactName IS NULL AND ContactName IS NULL) AND (ContactTitle = @origContactTitle OR @origContactTitle IS NULL AND ContactTitle IS NULL)
AND (Fax = @origFax OR @origFax IS NULL AND Fax IS NULL) AND (Phone = @origPhone OR @origPhone IS NULL AND Phone IS NULL) AND (PostalCode = @origPostalCode OR @origPostalCode IS NULL AND PostalCode IS NULL);
SELECT CustomerID, CompanyName, ContactName, ContactTitle, Address, City,
PostalCode, Phone, Fax
FROM Customers WHERE (CustomerID = @currCustomerID)

请注意,九个 SET 语句参数表示将写入数据库的当前值,而九个 WHERE 语句参数则表示用于定位初始记录的初始值。

前九个 SET 语句参数对应于参数集合中的前九个参数。这些参数会将其 SourceVersion 属性设置为 Current

接着的九个 WHERE 语句参数用于开放式并发。这些占位符对应于参数集合中接着的九个参数,这些参数的每一个都将其 SourceVersion 属性设置为Original

SELECT 语句用于在发生更新后刷新数据集。它是您在“高级 SQL 生成选项”对话框中设置“刷新数据集”选项时生成的。

注意    上面的 SQL 使用命名参数,而 OleDbDataAdapter 命令则使用问号 (?) 作为参数占位符。

默认情况下,如果您在“数据适配器配置向导”中选择“开放式并发”选项,Visual Studio 将为您创建这些参数。此时将由您根据自己的业务要求来添加错误处理代码。ADO.NET 提供了一个 DBConcurrencyException 对象,它返回违反并发规则的行。有关更多信息,请参见处理并发错误

原文地址:https://www.cnblogs.com/sheseido/p/4719341.html