Производительность LINQ2SQL с транзакциями - PullRequest
2 голосов
/ 13 мая 2009

У меня серьезная проблема с производительностью LINQ2SQL и транзакциями. Мой код выполняет следующие действия, используя сгенерированный IDE код LINQ2SQL:

Запустить проверку сохраненной процедуры для существующей записи Создать запись, если она не существует Запустите сохраненный процесс, который оборачивает свой собственный код в транзакцию

Когда я запускаю код без области транзакции, я получаю 20 итераций в секунду. Как только я заключаю код в область транзакции, он падает до 3-4 итераций в секунду. Я не понимаю, почему добавление транзакции на верхнем уровне значительно снижает производительность. Пожалуйста, помогите?

Псевдо сохраненный процесс с транзакцией:

begin transaction
update some_table_1;
insert into some_table_2;
commit transaction;

select some, return, values

Псевдо-код LINQ без транзакции:

var db = new SomeDbContext();
var exists = db.RecordExists(some arguments);

if (!exists) {
    var record = new SomeRecord 
    {
        // Assign property values
    };

    db.RecordsTable.InsertOnSubmit(record);
    db.SubmitChanges();

    var result = db.SomeStoredProcWithTransactions();
}

Псевдо-LINQ код с транзакцией:

var db = new SomeDbContext();
var exists = db.RecordExists(some arguments);

if (!exists) {
    using (var ts = new TransactionScope()) 
    {
        var record = new SomeRecord 
        {
            // Assign property values
        };

        db.RecordsTable.InsertOnSubmit(record);
        db.SubmitChanges();

        var result = db.SomeStoredProcWithTransactions();
        ts.Complete();
    }
}

Я знаю, что транзакция не передается в DTC, потому что я отключил DTC. SQL Profiler показывает, что некоторые запросы выполняются гораздо дольше при включенной транзакции, но я не уверен, почему. Запросы выполняются очень недолго, и у меня есть индексы, которые, как я проверял, используются. Я не могу определить, почему добавление родительской транзакции приводит к такому снижению производительности.

Есть идеи?

EDIT:

Я проследил проблему до следующего запроса в последней хранимой процедуре:

if exists 
(
    select * from entries where 
        ProfileID = @ProfileID and 
        Created >= @PeriodStart and 
        Created < @PeriodEnd
) set @Exists = 1;

Если бы я использовал (nolock), как показано ниже, проблема исчезнет.

if exists 
(
    select * from entries with(nolock) where 
        ProfileID = @ProfileID and 
        Created >= @PeriodStart and 
        Created < @PeriodEnd
) set @Exists = 1;

Однако я обеспокоен тем, что это может вызвать проблемы в будущем. Любой совет?

Ответы [ 3 ]

3 голосов
/ 13 мая 2009

Одна большая вещь, которая меняется, как только вы получаете транзакцию - уровень изоляции . Ваша база данных находится под сильным соперничеством? Если это так: по умолчанию TransactionScope находится на самом высоком «сериализуемом» уровне изоляции, который включает в себя блокировки чтения, блокировки диапазона ключей и т. Д. Если он не может сразу же получить эти блокировки, он замедлится, пока он заблокирован. Вы можете исследовать, уменьшив уровень изоляции транзакции (через конструктор). Например (но выберите свой уровень изоляции):

using(var tran = new TransactionScope(TransactionScopeOption.Required,
    new TransactionOptions { IsolationLevel = IsolationLevel.Snapshot })) {
    // code
    tran.Complete();
}

Тем не менее, выбор уровня изоляции является ... сложным; Сериализуемый является самым безопасным (следовательно, по умолчанию). Вы также можете использовать детальные подсказки (но не через LINQ-to-SQL), такие как NOLOCK и UPDLOCK, чтобы помочь управлять блокировкой определенных таблиц.


Вы также можете выяснить, связано ли замедление с с попыткой связаться с DTC. Включите DTC и посмотрите, ускорится ли он. LTM хорош, но я видел, как составные операции с одной базой данных переходят в DTC, прежде чем ...

0 голосов
/ 13 мая 2009

Несмотря на то, что вы используете один текстовый текстовый код, в вашем примере кода может использоваться более одного соединения, и это преобразует вашу транзакцию в распределенную транзакцию.

Попробуйте инициализировать ваш datacontext с явным подключением к базе данных или вызовите db.Connection.Open () сразу после создания datacontext. Это устраняет накладные расходы распределенных транзакций ...

0 голосов
/ 13 мая 2009

Участвует ли хранимая процедура, которую вы называете, в окружающей (родительской) транзакции? - вот в чем вопрос.

Вероятно, что хранимая процедура участвует в транзакции окружения, которая вызывает деградацию. Здесь есть статья MSDN , в которой обсуждается их взаимосвязь.

Из статьи:

"Когда объект TransactionScope присоединяется к существующей внешней транзакции, удаление объекта области может не завершить транзакцию, если только область не прерывает транзакцию. Если внешняя транзакция была создана корневой областью, только когда корневая область расположена из, выполняет ли вызов транзакцию. Если транзакция была создана вручную, транзакция завершается, когда она либо прерывается, либо фиксируется ее создателем. "

Существует также серьезный документ о вложенных транзакциях, который выглядит так, как будто он непосредственно применим, расположен на MSDN здесь .

Примечание:

«Если TransProc вызывается, когда транзакция активна, вложенная транзакция в TransProc в значительной степени игнорируется, а ее операторы INSERT фиксируются или откатываются на основе окончательного действия, предпринятого для внешней транзакции."

Я думаю, это объясняет разницу в производительности - это, по сути, стоимость обслуживания родительской транзакции. Предложение Кристофер может помочь уменьшить накладные расходы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...