У меня есть таблица, содержащая некоторые данные о покупке/продаже, в ней около 8 миллионов записей:
CREATE TABLE [dbo].[Transactions](
[id] [int] IDENTITY(1,1) NOT NULL,
[itemId] [bigint] NOT NULL,
[dt] [datetime] NOT NULL,
[count] [int] NOT NULL,
[price] [float] NOT NULL,
[platform] [char](1) NOT NULL
) ON [PRIMARY]
Каждые X минут моя программа получает новые транзакции для каждого itemId, и мне нужно его обновить. Мое первое решение - это два шага DELETE+INSERT:
delete from Transactions where platform=@platform and itemid=@itemid
insert into Transactions (platform,itemid,dt,count,price) values (@platform,@itemid,@dt,@count,@price)
[...]
insert into Transactions (platform,itemid,dt,count,price) values (@platform,@itemid,@dt,@count,@price)
Проблема в том, что этот оператор DELETE занимает в среднем 5 секунд. Это слишком долго.
Второе решение, которое я нашел, — использовать MERGE. Я создал такую хранимую процедуру, которая принимает табличный параметр:
CREATE PROCEDURE [dbo].[sp_updateTransactions]
@Table dbo.tp_Transactions readonly,
@itemId bigint,
@platform char(1)
AS
BEGIN
MERGE Transactions AS TARGET
USING @Table AS SOURCE
ON (
TARGET.[itemId] = SOURCE.[itemId] AND
TARGET.[platform] = SOURCE.[platform] AND
TARGET.[dt] = SOURCE.[dt] AND
TARGET.[count] = SOURCE.[count] AND
TARGET.[price] = SOURCE.[price] )
WHEN NOT MATCHED BY TARGET THEN
INSERT VALUES (SOURCE.[itemId],
SOURCE.[dt],
SOURCE.[count],
SOURCE.[price],
SOURCE.[platform])
WHEN NOT MATCHED BY SOURCE AND TARGET.[itemId] = @itemId AND TARGET.[platform] = @platform THEN
DELETE;
END
Эта процедура занимает около 7 секунд для таблицы с 70 тыс. записей. Так что с 8M это, вероятно, займет несколько минут. Узким местом является «Когда не совпадает» — когда я прокомментировал эту строку, эта процедура выполняется в среднем 0,01 секунды.
Итак, вопрос: как улучшить производительность оператора удаления?
Удалить необходимо, чтобы убедиться, что эта таблица не содержит транзакцию, которая была удалена в приложении. Но в реальности это случается очень редко, т.к. истинная потребность в удалении записей составляет менее 1 на 10000 обновлений транзакций.
Мой теоретический обходной путь состоит в том, чтобы создать дополнительный столбец, такой как «бит транзакцииУдаленный», и использовать UPDATE вместо DELETE, а затем выполнять очистку таблицы пакетным заданием каждые X минут или часов и выполнять
delete from transactions where transactionDeleted=1
Это должно быть быстрее, но мне нужно будет обновить все операторы SELECT в других частях приложения, чтобы использовать только записи transactionDeleted=0, и поэтому это также может повлиять на производительность приложения.
Знаете ли вы какое-нибудь лучшее решение?
ОБНОВЛЕНИЕ: Текущие индексы:
CREATE NONCLUSTERED INDEX [IX1] ON [dbo].[Transactions]
(
[platform] ASC,
[ItemId] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 50) ON [PRIMARY]
CONSTRAINT [IX2] UNIQUE NONCLUSTERED
(
[ItemId] DESC,
[count] ASC,
[dt] DESC,
[platform] ASC,
[price] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
when not matched
вы называете узким местом? Есть два.. - person Martin Smith   schedule 02.10.2011ID
внешним ключом в некоторых других таблицах? - person Mikael Eriksson   schedule 03.10.2011