Sql server 长提交导致nHibernate的性能缓慢
我们在ASP web应用程序中使用了nHibernate,该应用程序具有每请求会话模式。用户能够执行的某些操作会导致数百条INSERT和UPDATE语句,这些语句需要几秒钟才能完成。这会导致糟糕的性能,因为在将整个事务提交到数据库之前,用户无法看到其操作的结果 顺便说一下,之前我们在会话中保存了结果,但这也不是一个好的解决方案。将内容写入会话会导致读写锁定,从而导致并发请求按顺序处理。因此,长请求会阻止其他请求,直到其完成。这就是为什么我们转而将结果写入数据库,但正如我所解释的,这会带来一系列问题 我们如何处理这个问题?我只能考虑在后台提交结果时,以某种方式将结果bakc发送到浏览器。如果可能的话,我不知道会话如何与来自浏览器的请求耦合。但可能吗?还是有可行的解决方案 编辑Sql server 长提交导致nHibernate的性能缓慢,sql-server,asp.net-mvc,nhibernate,Sql Server,Asp.net Mvc,Nhibernate,我们在ASP web应用程序中使用了nHibernate,该应用程序具有每请求会话模式。用户能够执行的某些操作会导致数百条INSERT和UPDATE语句,这些语句需要几秒钟才能完成。这会导致糟糕的性能,因为在将整个事务提交到数据库之前,用户无法看到其操作的结果 顺便说一下,之前我们在会话中保存了结果,但这也不是一个好的解决方案。将内容写入会话会导致读写锁定,从而导致并发请求按顺序处理。因此,长请求会阻止其他请求,直到其完成。这就是为什么我们转而将结果写入数据库,但正如我所解释的,这会带来一系列问
- 我们正在使用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
- 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
- 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了,所以这不是我们可以改进的