Идеи по логике / алгоритму и как предотвратить гонку в многопоточных записях в SqlServer - PullRequest
3 голосов
/ 31 декабря 2010

У меня следующая логика:

public void InQueueTable(DataTable Table)
{
    int incomingRows = Table.Rows.Count;

    if (incomingRows >= RowsThreshold)
    {
        // asyncWriteRows(Table)

        return;
    }

    if ((RowsInMemory + incomingRows) >= RowsThreshold)
    {
        // copy and clear internal table
        // asyncWriteRows(copyTable)
    }

    internalTable.Merge(Table);
}

Существует одна проблема с этим лагоритом:

  • Дано RowsThreshold = 10000

  • Если incomingRows ставит RowsInMemory свыше RowsThreshold: (1) асинхронно записывать данные, (2) объединить входящие данные

  • Если incomingRows больше RowsThreshold, асинхронная запись входящие данные

Но что, если ??? Предположим, что второй поток раскручивается и вызывает asyncWriteRows (xxxTable); также, что каждый поток, владеющий асинхронным методом, будет писать в одну и ту же таблицу в SqlServer: Обрабатывает ли SqlServer этот вид многопоточной функциональности записи в одну и ту же таблицу?

Последующие действия
По предложению Грега Д.:

using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connectionString, 
                                              sqlBulkCopyOptions.KeepIdentity | SqlBulkCopyOptions.UseInternalTransaction))
{
    // perform bulkcopy
}

Несмотря на это, у меня все еще есть проблема сигнализации asyncWriteRows (copyTable). Алгоритм должен определить необходимость идти вперед и скопировать internalTable, clear internalTable и asyncWriteRows (copyTable). Я думаю, что мне нужно перевести вызов internalTable.Copy() на собственный метод:

private DataTable CopyTable (DataTable srcTable)
{
    lock (key)
    {
        return srcTable.Copy();
    }
}

... а затем следующие изменения в методе InQueue:

public void InQueueTable(DataTable Table)
{
    int incomingRows = Table.Rows.Count;

    if (incomingRows >= RowsThreshold)
    {
        // asyncWriteRows(Table)

        return;
    }

    if ((RowsInMemory + incomingRows) >= RowsThreshold)
    {
        // copy and clear internal table
        // asyncWriteRows(CopyTable(Table))
    }

    internalTable.Merge(Table);
}

... наконец, добавьте метод обратного вызова:

private void WriteCallback(Object iaSyncResult)
{
    int rowCount = (int)iaSyncResult.AsyncState;

    if (RowsInMemory >= rowCount)
    {
        asyncWriteRows(CopyTable(internalTable));
    }
}

Это то, что я определил как решение. Есть отзывы?

Ответы [ 2 ]

1 голос
/ 31 декабря 2010

Есть ли причина, по которой вы не можете использовать транзакции?

0 голосов
/ 31 декабря 2010

Теперь я признаю, что я не эксперт в этой области.

С транзакциями и курсорами вы получите повышение блокировки, если ваша операция велика. Например. Ваша операция начнет блокировку строки, затем страницы, а затем таблицы, если это необходимо, что не позволит функционировать другим операциям. Идиот, которого я предполагал, что SQL Server просто поставит эти заблокированные операции в очередь и будет ждать освобождения блокировок, но он просто возвращает ошибки, и программисту API приходится повторять попытки (кто-то исправит меня, если я ошибаюсь, или если это исправлено в более поздней версии). Если вы рады, что читаете, возможно, старые данные, которые затем копируете, как мы, мы изменили наш режим изоляции, чтобы излишне блокировать операции блокировки сервера. ALTER DATABASE [имя_базы] SET READ_COMMITTED_SNAPSHOT ON;

Вы также можете изменить свою вставку, чтобы использовать NOLOCK. Но, пожалуйста, прочтите это.

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