удалить выбранные строки в таблице (Как мне увеличить скорость) - PullRequest
2 голосов
/ 09 августа 2009

Предыстория У меня есть таблица, в которой хранится кэшированное время, и в настоящее время около 1 млн строк И когда я обновляю таблицу новыми версиями кэшированных элементов, мне нужно удалить старые кэшированные элементы (около 3 тысяч элементов). Не критично, что эти элементы обнаруживаются сразу, но я бы предпочел, чтобы, когда клиенты извлекали кэшированные элементы, я хотел, чтобы они получили новейшую версию.

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

DELETE FROM cache where cache_event_id = X

Мой вопрос становится: Могу ли я сделать запрос быстрее (я ожидаю, что таблица кеша только увеличится в размерах, поэтому эта проблема усугубится)? Должен ли я сделать удаление sql запустить свой собственный поток, и жить с фактом, что пользователи могут на некоторое время старые элементы?

Pr запрашивает остальную информацию для таблицы.

CREATE TABLE [dbo].[cache](
    [cache_id] [int] IDENTITY(1,1) NOT NULL,
    [cache_name] [nchar](128) NOT NULL,
    [cache_event_id] [int] NOT NULL,
    [cache_encounter_id] [int] NOT NULL,
    [cache_type_id] [tinyint] NOT NULL,
    [cache_creation_date] [datetime] NOT NULL,
    [cache_data] [varbinary](max) NOT NULL
) ON [PRIMARY]

Все индексы создаются профайлером сервера SQL, похоже, мне нужно вручную удалить старый индекс Индекс 1:

CREATE NONCLUSTERED INDEX [_dta_index_cache_6_366624349__K2_K3_K5_K4_7] ON [dbo].    [cache] 
(
    [cache_name] ASC,
    [cache_event_id] ASC,
    [cache_type_id] ASC,
    [cache_encounter_id] ASC
)
INCLUDE ( [cache_data]) 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) ON [PRIMARY]
GO

Индекс 2: // Возможно, он не используется

CREATE NONCLUSTERED INDEX [_dta_index_cache_6_366624349__K5_1_2_3_4_6_7] ON [dbo].[cache] 
(
    [cache_type_id] ASC
)
INCLUDE ( [cache_id],
[cache_name],
[cache_event_id],
[cache_encounter_id],
[cache_creation_date],
[cache_data]) 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) ON [PRIMARY]
GO

Индекс 3 (я полагаю, это одно удаление)

CREATE NONCLUSTERED INDEX [_dta_index_cache_6_366624349__K3] ON [dbo].[cache] 
(
    [cache_event_id] 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) ON [PRIMARY]
GO

Данные вставляются в таблицу с помощью класса BulkCopy

Данные извлекаются (это самая важная часть)

SqlCommand cmd = new SqlCommand("GetPageCache", connection);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@event_id", EventID); // int 
cmd.Parameters.AddWithValue("@encounter_id", EncounterID); // int 
cmd.Parameters.AddWithValue("@type_id", (int)CacheType); //int 
cmd.Parameters.AddWithValue("@cachename", CacheName); // Required in some cases, but 90% this is just a fallback

Ответы [ 5 ]

4 голосов
/ 09 августа 2009

Хорошая новость заключается в следующем: если оператор DELETE всегда удаляет около 3000 строк, ситуация может не ухудшиться по мере увеличения таблицы.

Структура вашей таблицы может сильно влиять на продолжительность операции DELETE и на то, как она напрямую влияет на пользователей из-за блокировок.

Индекс «помогает», упрощая определение локатора строк из ~ 3000 обреченных строк. Однако эти строки должны быть расположены во всей таблице (и в каждом индексе таблицы) и затем удалены. Вероятная причина такой медлительности заключается в том, что эти 3000 строк распределены по таблице (и индексам) на отдельных страницах данных.

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

Если вы разместите операторы CREATE TABLE и CREATE INDEX для [cache], у меня могут быть конкретные предложения вместо обобщений.

Дополнительные замечания:

Вот еще несколько мыслей.

Есть ли у вас ограничение PRIMARY KEY? Если нет, то у вас нет кластеризованного индекса, и это означает, что ваша таблица хранится в виде кучи. Это не хорошо, особенно для стола, который подвергается большой активности. Хотя у меня нет всех деталей, я также согласен с Dems ниже. Это должно помочь включить первичный ключ (который должен быть кластеризован) (cache_event_id, cache_id).

Еще одним узким местом могут быть сами данные кеша. Вы включили его в три индекса, поэтому вы храните его в четырех местах! Я только догадываюсь, но очень маловероятно, что у вас есть запросы, которые возвращают столбец cache_data из нескольких строк одновременно. В результате вы можете избежать хранения cache_data только в кластерном индексе (по умолчанию кластерный индекс включает все столбцы). Советник по настройке базы данных хорош, чтобы дать вам идеи, но не всегда хорошая идея делать именно то, что он говорит.

Насколько велик типичный столбец cache_data? Если он почти всегда большой (более 8 КБ), это вызовет большую активность на страницах переполнения больших объектов. Я не эксперт по настройке рабочей нагрузки, когда есть много LOB-активности, но, вероятно, есть несколько хороших ресурсов с советами. Единственное, что нужно учитывать (пока вы не попробуете улучшить индексы и на самом деле посмотреть на использование памяти, попадания в кеш и т. Д.), Это рассмотреть изменения, которые позволят разместить на странице больше строк таблицы:

  1. Пересмотрите, нужен ли вам тип nchar (128) для cache_name. (Вы может, но подумай об этом. Это всегда около 128 байт данных? Является использование Unicode необходимо и стоит дополнительное место? Если нет, может быть nvarchar (128) или varchar (128) является хорошо.)

  2. Подумайте, может ли это быть полезным установить "большие значения типов из опция строки для ВКЛ. По умолчанию ВЫКЛ, и это может привести к иметь только одну строку таблицы на данные страница в среднем, но без сокращения необходимость в страницах переполнения больших объектов. Посмотрите на результат sp_spaceused или sys.dm_db_partition_stats, чтобы попробовать оценить это. Если у вас есть только 1 или 2 строки на странице, это может помочь изменить настройку.

2 голосов
/ 09 августа 2009

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

У меня была похожая проблема (но в моем случае мне нужно было убедиться, что старые записи не были видны) и закончил тем, что добавил битовое поле с именем hidden. Таким образом, подпрограмма «delete» была фактически просто оператором обновления, чтобы установить для hidden значение true, а поиск был изменен, чтобы игнорировать скрытые записи.

Тогда я мог бы удалить скрытые записи в фоновом режиме, не затрагивая пользователей.

1 голос
/ 09 августа 2009

Очистка этих данных обязательно должна выполняться асинхронно (через запланированное задание sql, службу, задание, которое заполняет данные и т. Д.). Если вы беспокоитесь о том, что старые элементы возвращаются в запросах, прежде чем у вас есть возможность их удалить, вы можете реализовать схему управления версиями, которая будет возвращать только самые новые элементы.

1 голос
/ 09 августа 2009

Конечным пользователям ждать вызова удаления, который выполняет очистку кэша, кажется ненужным. Это, безусловно, должно быть фоновое задание / поток.

В качестве альтернативы, вы можете использовать что-то вроде memcached , которое предназначено для обработки чтения и истечения срока действия кэша.

0 голосов
/ 09 августа 2009

Я думаю, что частью проблемы является дизайн, но при условии, что мы ТОЛЬКО хотим ускорить удаление и больше ничего не менять?

Индекс с только «cache_event_id» действительно используется при удалении, но не так, как вы можете ожидать. Запустите удаление с планом выполнения, и после использования индекса вы увидите, что он также использует первичный ключ (при условии, что он кластеризован). Индекс в основном работает как кратчайший путь для поиска первичных ключей, которые необходимо удалить. Первичный ключ (или любой кластерный индекс) позволяет СУБД физически знать, где находятся записи, поэтому они могут быть удалены.

Кроме того, при удалении записи все индексы нуждаются в обновлении. В зависимости от того, сколько у вас индексов и как они настроены, это могут быть labourios.

Итак, мои две рекомендации:
1. Убедитесь, что первичный ключ или кластеризованный индекс имеет "cache_event_id" в качестве первого поля
2. Рационализируйте количество индексов, если это возможно, это может потребовать переписывания некоторых запросов

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