Как ускорить массовое обновление кластеризованного столбца? - PullRequest
7 голосов
/ 19 июня 2009

У меня довольно большая таблица: 20+ миллионов строк, и мне нужно обновить около 5% от этого - или 1 миллион строк.

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

Мой вопрос: Какой самый быстрый способ обновить эти строки?

Я попытался обновить строки напрямую:

update t1
set t1.groupId = t2.groupId
from
    table t1
join newtable t2 on t1.email = t2.email

но это занимает слишком много времени (я остановил его через 3 часа)

Я предполагаю, что это потому, что вся строка (которая имеет 2 datetime, 2 varchars и 2 ints) перемещается для каждого обновления.

Что, если я сначала отбросил кластерный индекс, затем сделал обновления, а затем заново создал кластерный индекс? Это будет быстрее?

Примечание: у меня есть некластеризованный индекс по электронной почте, на тот случай, если кто-то посчитает, что это медленная часть запроса. Это не.

Ответы [ 3 ]

8 голосов
/ 19 июня 2009

Вот что я сделал (и это было намного быстрее):

  1. Я сбросил кластерный индекс.
  2. Я также сбросил внешние ключи ссылки (две другие инт столбцы).
  3. Я запустил оператор обновления
  4. Я пересоздал индекс, который оказался быстрее, чем ожидалось. (Это первоначальная причина, по которой я сначала спросил ТАК).

Это снизило весь процесс до считанных секунд. Да, ~ 1 миллион строк за 15 секунд.

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

Количество физических чтений утроилось из-за этих поисков внешнего ключа.

Я не уверен, почему SQL Server должен это делать, но я предполагаю, что он все еще выполняет проверку целостности, даже если я не обновляю этот столбец, но перемещаю всю строку (обновление кластеризованного столбца).


В качестве примечания я также попытался запустить обновление партиями:

update top(1000) t1
set t1.groupId = t2.groupId
from
    table t1
join newtable t2 on t1.email = t2.email

Это было нормально (и казалось, что оно масштабировалось примерно до 10K на партию), но все равно было порядка 1-2 минут на каждую партию.


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

2 голосов
/ 19 июня 2009

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

Потому что

Кластерные индексы сортируют и сохраняют строки данных в таблице на основе их ключевые значения (источник msdn),

вам может быть лучше просто удалить кластерный индекс (сохраняйте индекс по электронной почте) После завершения операции воссоздайте кластерный индекс. Пока groupid не участвует ни в каких других индексах, я бы их не трогал. Если идентификатор группы участвует в других индексах, тогда отбросьте их. Я бы оставил хотя бы индекс по электронной почте, просто чтобы быстро присоединиться.

0 голосов
/ 19 июня 2009

Почему бы вам не сделать следующее:

  • Создайте другой столбец (один из столбцов) кластерного индекса
  • Создать индекс для вашего groupId
  • обновление
  • Затем обратный процесс.

Это должно быть быстрее.

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