0

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

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

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

编辑

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

请问我是否没有提供足够的信息。我真的不知道此时在这里写什么有趣,因为我不知道应该在哪个方向寻找解决方案。

4

2 回答 2

3

我在这里写过这个问题:

http://www.philliphaydon.com/2011/09/the-benefits-of-letting-the-orm-generate-the-identity-part-1/

如果您使用数据库中生成的IDENTITYor GUID,您将遭受插入速度慢和无法批处理的困扰。

当使用 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

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

  • 身份 28951 毫秒
  • NEWID 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 中看到的那样,只有 1 条语句被发送到服务器,不像 NEWID / IDENTITY 其中为每个插入发送一条语句,或者为每个新 id 进行选择。

这会导致更短的时间,例如:

  • HiLo 9287ms
  • GuidComb 9060ms

我希望这会有所帮助:)

于 2013-06-12T15:45:44.217 回答
0

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

<property name="adonet.batch_size">1000</property>

将配置您的批量大小。

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

于 2013-06-12T15:27:05.517 回答