Есть задача: в справочник (ID int primary key, Value nvarchar(4000)) нужно вставлять строки и получить на выходе ID новой строки или существующей (если есть таковая). Ограничение одно - строки не должны повторяться!
И тут настает сложность: поле Value - нельзя проиндексировать и установить уникальность на уровне индекса (ограничение SQL - 900 байт на индексируемое поле), а искать по нему надо.
Сложность задачи еще в том, что это будет высоконагруженный блок с большим кол-вом данных (млн+) в котором данные льются с большой скоростью. И вставка должна быть максимально быстрой....
Чтобы ускорить поиск по текстовому полю - мы ввели поле ValueHash bigint (индексируем это поле). Теперь поиск получается по ValueHash быстрым - но это не гарантия выбора единственной записи...
В итоге - Если использовать IF (NOT EXISTS(...)) INSERT INTO... (или MERGE - что похоже) то все равно складывается ситуация, когда 2 потока могут вставить запись с одинаковым Value (при уровне изоляции транзакции меньше чем SERIALIZABLE - а если SERIALIZABLE - то дэдлок).
Помогите решить вопрос - наверняка кто-то сталкивался с такой задачей в больших системах... Нужно организовать вставку с получением ID без задвоения Value и без дэдлоков...
Прикладываю хранимку на вставку...
И тут настает сложность: поле Value - нельзя проиндексировать и установить уникальность на уровне индекса (ограничение SQL - 900 байт на индексируемое поле), а искать по нему надо.
Сложность задачи еще в том, что это будет высоконагруженный блок с большим кол-вом данных (млн+) в котором данные льются с большой скоростью. И вставка должна быть максимально быстрой....
Чтобы ускорить поиск по текстовому полю - мы ввели поле ValueHash bigint (индексируем это поле). Теперь поиск получается по ValueHash быстрым - но это не гарантия выбора единственной записи...
В итоге - Если использовать IF (NOT EXISTS(...)) INSERT INTO... (или MERGE - что похоже) то все равно складывается ситуация, когда 2 потока могут вставить запись с одинаковым Value (при уровне изоляции транзакции меньше чем SERIALIZABLE - а если SERIALIZABLE - то дэдлок).
Помогите решить вопрос - наверняка кто-то сталкивался с такой задачей в больших системах... Нужно организовать вставку с получением ID без задвоения Value и без дэдлоков...
Прикладываю хранимку на вставку...
CREATE PROCEDURE dbo.ModuleNameInsertValue @value nvarchar(4000), @valueHash bigint AS SET NOCOUNT ON; -- переменная для возврата значения declare @Id int SET @Id = 0 --SET TRANSACTION ISOLATION LEVEL SERIALIZABLE BEGIN TRANSACTION BEGIN TRY -- сливаем записи в @hashed - точность хеша не 100%, но сюда попадется очень МАЛО (чаще всего одна) записей полюбасу declare @hashed table (Id int, [Value] nvarchar(4000), [ValueHash] bigint) INSERT INTO @hashed SELECT Id, [Value], [ValueHash] FROM [dbo].[ModuleName] WHERE [valueHash] = @valueHash -- т.к. записей мало - то даже поиск по nvarchar(4000) будет быстрым SELECT @Id = Id FROM @hashed WHERE [ValueHash] = @valueHash AND [Value] = @value -- тут уже точно одна IF (@Id = 0) BEGIN INSERT INTO dbo.[ModuleName] ([Value], [ValueHash]) VALUES (@value, @valueHash) SET @Id = SCOPE_IDENTITY(); END IF @@TRANCOUNT > 0 COMMIT TRANSACTION END TRY BEGIN CATCH BEGIN IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION; THROW; END END CATCH; RETURN @Id GO