Как обновить миллионы записей в MySql?

0

У меня две таблицы tableA и tableB. tableA имеет 2 миллиона записей, а tableB имеет более 10 миллионов записей. tableA имеет более тридцати столбцов, тогда как tableB имеет только два столбца. Мне нужно обновить столбец в таблице A из таблицы B, соединяя обе таблицы.

UPDATE tableA a 
INNER JOIN tableB b  ON a.colA=b.colA
 SET a.colB= b.colB 

colA в обеих таблицах индексируется.

Теперь, когда я выполняю запрос, требуется несколько часов. Честно говоря, я никогда не видел, чтобы это было завершено, и макс я ждал 5 часов. Это их способ завершить этот запрос в течение 20-30 минут. Какой подход я должен принять.

EXPLAIN в SQL Query

"id" "_type" "table" "type" "possible_" "key" "key_len"   "ref"   "rows" "Extra"
"1" "SIMPLE" "a"     "ALL"  "INDX_DESC" \N    \N          \N   "2392270"  "Using where"
"1" "SIMPLE" "b"     "ref"  "indx_desc" "indx_desc" "133" "cis.a.desc" "1"  "Using where"
  • 0
    у вас есть какой-то идентификатор в таблицах ??
  • 0
    У меня есть идентификатор в таблице A, которая является PK, но нет идентификатора в tableB. Объединенные столбцы имеют тип varchar и содержат текст.
Показать ещё 16 комментариев
Теги:
sql-update
indexing

4 ответа

0

Для обновления около 70 миллионов записей одной таблицы MySQL я написал хранимую процедуру для обновления таблицы порциями по 5000. На завершение ушло примерно 3 часа.

DELIMITER $$
DROP PROCEDURE IF EXISTS update_multiple_example_proc$$
CREATE PROCEDURE update_multiple_example_proc()
BEGIN
DECLARE x  bigint;

SET x = 1;

WHILE x  <= <MAX_PRIMARY_KEY_TO_REACH> DO
UPDATE tableA A
   JOIN tableB B
   ON A.col1 = B.col1
SET A.col2_to_be_updated = B.col2_to_be_updated where A.id between x and x+5000 ;
SET  x = x + 5000;
END WHILE;

END$$
DELIMITER ;
0

Чанкинг - это правильный путь. Однако, кусок на tableA PRIMARY KEY tableA.

Я предлагаю всего 1000 строк за раз.

Следуйте приведенным здесь советам

Вы сказали, что PK of tableA является варчаром? Нет проблем. См. Второй аромат кода в этой ссылке; он использует ORDER BY id LIMIT 1000,1 чтобы найти конец следующего фрагмента, независимо от типа данных id (PK).

0

Ваша операция UPDATE выполняет одну транзакцию на десять миллионов строк большой таблицы. (СУБД хранит достаточно данных, чтобы отбросить весь запрос UPDATE если он по какой-либо причине не завершен). Транзакция такого размера медленна для вашего сервера.

Когда вы обрабатываете целые таблицы, операция не может использовать индексы, а также, если она имеет очень избирательные WHERE.

Несколько вещей, чтобы попробовать:

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

    UPDATE tableA a 
INNER JOIN tableB b  ON a.colA=b.colA
       SET a.colB = b.colB
     WHERE a.colB <> b.colB 

2) Сделайте обновление в кусках нескольких тысяч строк и повторите операцию обновления до тех пор, пока не будет обновлена вся таблица. Я полагаю, tableA содержит столбец id. Вы можете использовать его для упорядочивания блоков строк для обновления.

    UPDATE tableA a 
INNER JOIN tableB b  ON a.colA=b.colA
       SET a.colB = b.colB
     WHERE a.id IN  (
             SELECT a.id
               FROM tableA
              INNER JOIN tableB ON a.colA = b.colA
              WHERE a.colB <> b.colB
              LIMIT 5000
      ) 

Подзапрос находит значения id из 5000 строк, которые еще не были обновлены, и запрос UPDATE обновляет их. Повторяйте этот запрос, пока он не изменит строки, и все готово. Это ускоряет работу, потому что сервер должен обрабатывать только небольшие транзакции.

3) Не выполняйте обновление вообще. Вместо этого, когда вам нужно получить значение colB, просто присоединитесь к tableB в выбранном запросе.

  • 0
    Для кусков лучше использовать диапазон первичных ключей: where a.id between 1 and 10000 - тогда, where a.id between 10001 and 20000 и так далее.
  • 0
    Я попытался второе решение, и я выполнил его только для 60000 записей в таблице А с чеком 5000 записей. Сначала выдает ошибку, что «эта версия mariadb еще не поддерживает подзапрос limit & in / all / any / some». Поэтому я изменил запрос на: - ОБНОВИТЬ таблицу А - ВНУТРЕННЮЮ СОЕДИНЕННУЮ таблицу В b ВКЛЮЧИТЬ ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВНУТРЕННЕГО СОСТАВА a.colA = b.ColA (ВЫБЕРИТЕ ИДЕНТИФИКАТОР ИЗ АППАРАТА ВНУТРЕННЕГО ОБЪЕДИНЕНИЯ Ограничение colB 5000) AS c ON a.id = c.id SET a.colB = b.colB
Показать ещё 1 комментарий
-1

Привет, я не уверен, но вы можете сделать работу cron. process: в таблице tableA вам нужно добавить еще одно поле (например) is_update установить его значение по умолчанию 0, установить задание cron каждый мин. когда cron работает: например, он выбирает первый раз 10000 записей, имеющих значение поля is_update 0 и записи обновления, а set is_update is1, во второй раз его выбор следующий 10000 имеет is_update 0 и т.д. Надеюсь, это поможет вам.

  • 0
    cron не так хорош, как просто иметь единственную проблему со «сном» между взаимодействиями. Одна потенциальная проблема с cron возникает, когда один экземпляр случайно попадает в следующий.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню