У меня две таблицы 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"
Для обновления около 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 ;
Чанкинг - это правильный путь. Однако, кусок на tableA
PRIMARY KEY
tableA
.
Я предлагаю всего 1000 строк за раз.
Следуйте приведенным здесь советам
Вы сказали, что PK of tableA является варчаром? Нет проблем. См. Второй аромат кода в этой ссылке; он использует ORDER BY id LIMIT 1000,1
чтобы найти конец следующего фрагмента, независимо от типа данных id
(PK).
Ваша операция 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 в выбранном запросе.
where a.id between 1 and 10000
- тогда, where a.id between 10001 and 20000
и так далее.
Привет, я не уверен, но вы можете сделать работу cron. process: в таблице tableA вам нужно добавить еще одно поле (например) is_update установить его значение по умолчанию 0, установить задание cron каждый мин. когда cron работает: например, он выбирает первый раз 10000 записей, имеющих значение поля is_update 0 и записи обновления, а set is_update is1, во второй раз его выбор следующий 10000 имеет is_update 0 и т.д. Надеюсь, это поможет вам.
cron
не так хорош, как просто иметь единственную проблему со «сном» между взаимодействиями. Одна потенциальная проблема с cron возникает, когда один экземпляр случайно попадает в следующий.