Sql server 长提交导致nHibernate的性能缓慢

Sql server 长提交导致nHibernate的性能缓慢,sql-server,asp.net-mvc,nhibernate,Sql Server,Asp.net Mvc,Nhibernate,我们在ASP web应用程序中使用了nHibernate,该应用程序具有每请求会话模式。用户能够执行的某些操作会导致数百条INSERT和UPDATE语句,这些语句需要几秒钟才能完成。这会导致糟糕的性能,因为在将整个事务提交到数据库之前,用户无法看到其操作的结果 顺便说一下,之前我们在会话中保存了结果,但这也不是一个好的解决方案。将内容写入会话会导致读写锁定,从而导致并发请求按顺序处理。因此,长请求会阻止其他请求,直到其完成。这就是为什么我们转而将结果写入数据库,但正如我所解释的,这会带来一系列问

我们在ASP web应用程序中使用了nHibernate,该应用程序具有每请求会话模式。用户能够执行的某些操作会导致数百条INSERT和UPDATE语句,这些语句需要几秒钟才能完成。这会导致糟糕的性能,因为在将整个事务提交到数据库之前,用户无法看到其操作的结果

顺便说一下,之前我们在会话中保存了结果,但这也不是一个好的解决方案。将内容写入会话会导致读写锁定,从而导致并发请求按顺序处理。因此,长请求会阻止其他请求,直到其完成。这就是为什么我们转而将结果写入数据库,但正如我所解释的,这会带来一系列问题

我们如何处理这个问题?我只能考虑在后台提交结果时,以某种方式将结果bakc发送到浏览器。如果可能的话,我不知道会话如何与来自浏览器的请求耦合。但可能吗?还是有可行的解决方案

编辑

  • 我们正在使用Guid.Comb标识符策略
  • 了解提交的大小:一次提交涉及2000多条INSERT和UPDATE语句,并且在操作运行期间会发出类似数量的SELECT语句。提交需要2-4分钟才能完成(有那么长吗?)

如果我没有提供足够的信息,请询问。我真的不知道现在在这里写什么会很有趣,因为我不知道我应该朝什么方向寻找解决方案。

您还没有发布任何代码,但在大多数情况下,您可以使用NHibernate中的批处理获得更好的性能。设置较大的批处理大小将减少到数据库的往返。但是,您可能需要重写一些代码

<property name="adonet.batch_size">1000</property>
1000
将配置您的批处理大小


第二种选择是使用IStatelessSession而不是ISession。您没有任何状态,但性能可以提高。

您没有发布任何代码,但在大多数情况下,使用NHibernate中的批处理可以获得更好的性能。设置较大的批处理大小将减少到数据库的往返。但是,您可能需要重写一些代码

<property name="adonet.batch_size">1000</property>
1000
将配置您的批处理大小


第二种选择是使用IStatelessSession而不是ISession。您没有任何状态,但性能可以提高。

我在这里写到了这个问题:

如果使用的是数据库中生成的
IDENTITY
GUID
s,则插入速度慢,无法批处理

当使用GUID插入SQL时,NHibernate会发出请求,首先获取新的GUID

您最终的交易如下所示:

- statement #1
begin transaction with isolation level: Unspecified

- statement #2
select newid()

- statement #3
select newid()

- statement #4
select newid()

- statement #5
INSERT INTO Fruit
            (Name,
             Id)
VALUES      ('Apple0' /* @p0_0 */,
             '269bc638-74b4-4568-85d1-45b6e537fcbd' /* @p1_0 */)

INSERT INTO Fruit
            (Name,
             Id)
VALUES      ('Apple1' /* @p0_1 */,
             'fc848779-b173-4c31-b8b6-0a7735c0c2dc' /* @p1_1 */)

INSERT INTO Fruit
            (Name,
             Id)
VALUES      ('Apple2' /* @p0_2 */,
             '232c8971-18c7-486d-9152-26c969c3b632' /* @p1_2 */)

- statement #6
commit transaction
同样,当您使用IDENTITY时,NHibernate需要从数据库中选择新Id以更新模型,当NHibernate需要在插入之前将此新对象与另一个对象关联时,这一点尤为重要

使用IDENTITY最终会产生如下事务:

- statement #1
begin transaction with isolation level: Unspecified

- statement #2
INSERT INTO People
            (FirstName,
             Surname)
VALUES      ('Phillip0' /* @p0 */,
             'Haydon' /* @p1 */);

select SCOPE_IDENTITY()

- statement #3
INSERT INTO People
            (FirstName,
             Surname)
VALUES      ('Phillip1' /* @p0 */,
             'Haydon' /* @p1 */);

select SCOPE_IDENTITY()

- statement #4
INSERT INTO People
            (FirstName,
             Surname)
VALUES      ('Phillip2' /* @p0 */,
             'Haydon' /* @p1 */);

select SCOPE_IDENTITY()

- statement #5
commit transaction
在一台相当不错的计算机上运行一些基本测试,我得到了以下时间的插入结果:

  • 身份28951ms
  • 新ID 30241ms
将其更新为使用HiLo或GuidComb,允许NHibernate自己生成标识

使用HiLo,NHibernate将发出下一个hi值的请求,然后更新hi值,一旦有hi,它可以生成一系列ID,直到lo满为止

这将导致插入,如下所示:

- statement #1
begin transaction with isolation level: Unspecified

- statement #2
Reading high value: 
select next_hi
from   hibernate_unique_key with (updlock, rowlock)

- statement #3
Updating high value: 
update hibernate_unique_key
set    next_hi = 3 /* @p0 */
where  next_hi = 2 /* @p1 */

- statement #4
INSERT INTO People
            (FirstName,
             Surname,
             Id)
VALUES      ('Phillip0' /* @p0_0 */,
             'Haydon' /* @p1_0 */,
             202 /* @p2_0 */)

INSERT INTO People
            (FirstName,
             Surname,
             Id)
VALUES      ('Phillip1' /* @p0_1 */,
             'Haydon' /* @p1_1 */,
             203 /* @p2_1 */)

INSERT INTO People
            (FirstName,
             Surname,
             Id)
VALUES      ('Phillip2' /* @p0_2 */,
             'Haydon' /* @p1_2 */,
             204 /* @p2_2 */)

- statement #5
commit transaction
同样,使用GuidComb,NHibernate将为您生成GUID:

- statement #1
begin transaction with isolation level: Unspecified

- statement #2
INSERT INTO Fruit
            (Name,
             Id)
VALUES      ('Apple0' /* @p0_0 */,
             'db902160-edbb-49c7-bf52-9f660018299a' /* @p1_0 */)

INSERT INTO Fruit
            (Name,
             Id)
VALUES      ('Apple1' /* @p0_1 */,
             '5e852528-3a6f-41d2-a6b1-9f660018299a' /* @p1_1 */)

INSERT INTO Fruit
            (Name,
             Id)
VALUES      ('Apple2' /* @p0_2 */,
             '2f63c6e8-e595-4393-ad15-9f660018299a' /* @p1_2 */)

- statement #3
commit transaction
这意味着在执行插入时,NHibernate不需要往返到数据库,这两种方法都允许进行批量插入,正如您在HiLo和GuidComb中所看到的那样,只向服务器发送一条语句,而不像NEWID/IDENTITY,在NEWID/IDENTITY中,为每个插入发送一条语句,或者为每个新id进行选择

这将大大缩短时间,如:

  • 希洛9287ms
  • GuidComb 9060ms

我希望这能有所帮助:)

我在这里写到了这个问题:

如果使用的是数据库中生成的
IDENTITY
GUID
s,则插入速度慢,无法批处理

当使用GUID插入SQL时,NHibernate会发出请求,首先获取新的GUID

您最终的交易如下所示:

- statement #1
begin transaction with isolation level: Unspecified

- statement #2
select newid()

- statement #3
select newid()

- statement #4
select newid()

- statement #5
INSERT INTO Fruit
            (Name,
             Id)
VALUES      ('Apple0' /* @p0_0 */,
             '269bc638-74b4-4568-85d1-45b6e537fcbd' /* @p1_0 */)

INSERT INTO Fruit
            (Name,
             Id)
VALUES      ('Apple1' /* @p0_1 */,
             'fc848779-b173-4c31-b8b6-0a7735c0c2dc' /* @p1_1 */)

INSERT INTO Fruit
            (Name,
             Id)
VALUES      ('Apple2' /* @p0_2 */,
             '232c8971-18c7-486d-9152-26c969c3b632' /* @p1_2 */)

- statement #6
commit transaction
同样,当您使用IDENTITY时,NHibernate需要从数据库中选择新Id以更新模型,当NHibernate需要在插入之前将此新对象与另一个对象关联时,这一点尤为重要

使用IDENTITY最终会产生如下事务:

- statement #1
begin transaction with isolation level: Unspecified

- statement #2
INSERT INTO People
            (FirstName,
             Surname)
VALUES      ('Phillip0' /* @p0 */,
             'Haydon' /* @p1 */);

select SCOPE_IDENTITY()

- statement #3
INSERT INTO People
            (FirstName,
             Surname)
VALUES      ('Phillip1' /* @p0 */,
             'Haydon' /* @p1 */);

select SCOPE_IDENTITY()

- statement #4
INSERT INTO People
            (FirstName,
             Surname)
VALUES      ('Phillip2' /* @p0 */,
             'Haydon' /* @p1 */);

select SCOPE_IDENTITY()

- statement #5
commit transaction
在一台相当不错的计算机上运行一些基本测试,我得到了以下时间的插入结果:

  • 身份28951ms
  • 新ID 30241ms
将其更新为使用HiLo或GuidComb,允许NHibernate自己生成标识

使用HiLo,NHibernate将发出下一个hi值的请求,然后更新hi值,一旦有hi,它可以生成一系列ID,直到lo满为止

这将导致插入,如下所示:

- statement #1
begin transaction with isolation level: Unspecified

- statement #2
Reading high value: 
select next_hi
from   hibernate_unique_key with (updlock, rowlock)

- statement #3
Updating high value: 
update hibernate_unique_key
set    next_hi = 3 /* @p0 */
where  next_hi = 2 /* @p1 */

- statement #4
INSERT INTO People
            (FirstName,
             Surname,
             Id)
VALUES      ('Phillip0' /* @p0_0 */,
             'Haydon' /* @p1_0 */,
             202 /* @p2_0 */)

INSERT INTO People
            (FirstName,
             Surname,
             Id)
VALUES      ('Phillip1' /* @p0_1 */,
             'Haydon' /* @p1_1 */,
             203 /* @p2_1 */)

INSERT INTO People
            (FirstName,
             Surname,
             Id)
VALUES      ('Phillip2' /* @p0_2 */,
             'Haydon' /* @p1_2 */,
             204 /* @p2_2 */)

- statement #5
commit transaction
同样,使用GuidComb,NHibernate将为您生成GUID:

- statement #1
begin transaction with isolation level: Unspecified

- statement #2
INSERT INTO Fruit
            (Name,
             Id)
VALUES      ('Apple0' /* @p0_0 */,
             'db902160-edbb-49c7-bf52-9f660018299a' /* @p1_0 */)

INSERT INTO Fruit
            (Name,
             Id)
VALUES      ('Apple1' /* @p0_1 */,
             '5e852528-3a6f-41d2-a6b1-9f660018299a' /* @p1_1 */)

INSERT INTO Fruit
            (Name,
             Id)
VALUES      ('Apple2' /* @p0_2 */,
             '2f63c6e8-e595-4393-ad15-9f660018299a' /* @p1_2 */)

- statement #3
commit transaction
这意味着在执行插入时,NHibernate不需要往返到数据库,这两种方法都允许进行批量插入,正如您在HiLo和GuidComb中所看到的那样,只向服务器发送一条语句,而不像NEWID/IDENTITY,在NEWID/IDENTITY中,为每个插入发送一条语句,或者为每个新id进行选择

这将大大缩短时间,如:

  • 希洛9287ms
  • GuidComb 9060ms

我希望这会有所帮助:)

我们已经在使用GuidComb了,所以这不是我们可以改进的