2009-12-11 3 views
2

Я вижу поведение, которое выглядит так, как подсказка READPAST устанавливается в самой базе данных.SQL Server READPAST hint

The rub: Я не думаю, что это возможно.

У нас есть таблица foo (идентификатор первичного ключа id int, имя varchar (50), но не уникальное);

У меня есть несколько потоков, которые делают, в основном

id = select id from foo where name = ? 
if id == null 
    insert into foo (name) values (?) 
id = select id from foo where name = ? 

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

READ_COMMITTED_SNAPSHOT ВКЛЮЧЕН, транзакционная изоляция специально настроена на ЧИТАТЬ, COMMITTED, используя Connection.setTransactionIsolation (Connection.TRANSACTION_READ_COMMITTED);

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

Бросьте мне кость здесь?

+0

несколько предложений, чтобы обернуть в сделке - Прости, что я забыл включить, что мы делаем это (через подключение # setAutocommit (ложь)) –

+0

еще один момент - мы опускаем уникальный индекс по имени, и все работает. (Кроме того, мы не получаем повторяющиеся данные.) Это не может быть реальным решением. –

ответ

1

Вы находитесь на неправильном уровне изоляции. Помните, что происходит с уровнем изоляции моментального снимка. Если одна транзакция вносит изменения, никакие другие параллельные транзакции не видят эту транзакцию. Период. Другие транзакции будут видеть ваши изменения только после того, как вы их совершили, но только если они НАЧАТЬ после вашего фиксации. Решением этого является использование другого уровня изоляции. Оберните свои заявления в транзакцию и установите УРОВЕНЬ ПЕРЕВОДА ПО СЕРИАЛИЗАЦИИ. Это гарантирует, что ваши другие параллельные транзакции работают так, как будто все они запускаются серийно, что вам, похоже, нужно здесь.

+0

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

+0

Тогда я думаю, что у вас есть большая, более фундаментальная проблема. Я не думаю, что то, что вы думаете, происходит на самом деле. Это не случай намеков или уровней изоляции, если вы уверены, что транзакция совершена, и читатель не может видеть зафиксированную строку. Он либо каким-то образом изменен кем-то, либо никогда не был совершен. Попробуйте использовать средство просмотра журнала транзакций, например, материалы Red-Gate, если вам действительно нужно посмотреть, что происходит. –

+0

@ Dave - Спасибо, ты абсолютно прав. У нас была более глубокая проблема, которую я интерпретировал не так. Мы не могли прочитать значения для определенных строк, но мы могли бы для других. Тот факт, что «плохие» значения были теми, которые обрабатывались отдельными потоками, было только совпадением. Проблема, которую мы видим, похожа на приведенную ниже. Удаление уникального индекса «исправило» проблему. Конечно, мы не можем оставить уникальный индекс без каких-либо ограничений, но мы снова перемещаемся: http://social.msdn.microsoft.com/Forums/en/sqldatabaseengine/thread/03f734cd-1c01-4767-b434- 4be824c254b2 Спасибо! –

0

Похоже, вы не завершаете выбор и не вставляете транзакцию?

В качестве решения, вы можете:

insert into foo (col1,col2,col3) values ('a','b','c') 
where not exists (select * from foo where col1 = 'a') 

После этого @@rowcount будет 1, если можно проверить, если строка была вставлена.

+0

хорошее предложение, но исходный выбор не удается, даже для строк, которые были в базе данных, так как до загрузки приложения. Мы ищем способы, чтобы строка могла быть скрыта от оператора select. –

+0

@dg: строки не могут быть скрыты от операторов select – Andomar

0
SELECT SCOPE_IDENTITY() 

должен сделать трюк здесь ...

плюс обертывание в сделке, как предыдущий плакат упоминалось.

+0

, которые были бы более эффективными, но исключение возникает при вставке, потому что первый выбор не нашел строку, которую он искал. SCOPE_IDENTITY будет работать, если вставка выполнена успешно. –

0

Мораль этой истории полностью объясняется в моем сообщении в блоге "You can't hold onto nothing", но короткой версией этого является то, что вы хотите использовать подсказку HOLDLOCK. Я использую шаблон:

INSERT INTO dbo.Foo(Name) 
SELECT TOP 1 
    @name AS Name 
FROM (SELECT 1 AS FakeColumn) AS FakeTable 
WHERE NOT EXISTS (SELECT * FROM dbo.Foo WITH (HOLDLOCK) 
        WHERE [email protected]) 
SELECT ID FROM dbo.Foo WHERE [email protected] 
Смежные вопросы