MySQL блокируется при CREATE TABLE AS SELECT
Вопрос
Я выполняю следующий (фиктивный) запрос
CREATE TABLE large_temp_table AS
SELECT a.*, b.*, c.*
FROM a
LEFT JOIN b ON a.foo = b.foo
LEFT JOIN c ON a.bar = c.bar
Предположим, что выполнение запроса занимает 10 минут.Попытка обновить значения в таблицах a, b или c во время работы приведет к ожиданию первого завершения вышеуказанного запроса.Я хочу избежать этой блокировки (консистентность данных не интересует).Как я могу этого добиться?
С использованием:MySQL 5.1.41 и таблицы InnoDB
п.с.УСТАНОВИТЬ УРОВЕНЬ ИЗОЛЯЦИИ ТРАНЗАКЦИИ ЧТЕНИЕ НЕЗАВЕРШЕНО;не приводит к изменению поведения
ОбновлятьВо время выполнения запроса вывод SHOW ENGINE INNODB STATUS следующий (я специально сделал очень медленный запрос):
=====================================
120323 15:26:29 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 8 seconds
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 1470, signal count 1468
Mutex spin waits 0, rounds 7525, OS waits 112
RW-shared spins 803, OS waits 364; RW-excl spins 1300, OS waits 959
------------
TRANSACTIONS
------------
Trx id counter 0 3145870
Purge done for trx's n:o < 0 3141943 undo n:o < 0 0
History list length 22
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 0 0, not started, OS thread id 2958192640
MySQL thread id 7942, query id 69073 localhost root
SHOW ENGINE INNODB STATUS
---TRANSACTION 0 3145869, ACTIVE 20 sec, OS thread id 2955325440, thread declared inside InnoDB 343
mysql tables in use 1, locked 1
6 lock struct(s), heap size 1024, 162 row lock(s)
MySQL thread id 7935, query id 69037 localhost root Copying to tmp table
CREATE TABLE 1_temp_foo AS
SELECT SQL_NO_CACHE
a.*
FROM
crm_companies AS a
LEFT JOIN users b ON a.zipcode = b.uid
LEFT JOIN calc_base_materials c ON a.zipcode = c.material_id
LEFT JOIN calc_base_material_langtext d ON a.zipcode = d.material_id
LEFT JOIN crm_people e ON a.zipcode = e.telephone1_number
ORDER BY a.country, a.name1
--------
FILE I/O
--------
I/O thread 0 state: waiting for i/o request (insert buffer thread)
I/O thread 1 state: waiting for i/o request (log thread)
I/O thread 2 state: waiting for i/o request (read thread)
I/O thread 3 state: waiting for i/o request (write thread)
Pending normal aio reads: 0, aio writes: 0,
ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0
Pending flushes (fsync) log: 0; buffer pool: 0
27579 OS file reads, 613 OS file writes, 392 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 0.00 writes/s, 0.00 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 5, seg size 7,
0 inserts, 0 merged recs, 0 merges
Hash table size 34679, node heap has 9 buffer(s)
0.00 hash searches/s, 0.00 non-hash searches/s
---
LOG
---
Log sequence number 1 2030837110
Log flushed up to 1 2030837110
Last checkpoint at 1 2030837110
0 pending log writes, 0 pending chkp writes
231 log i/o's done, 0.00 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total memory allocated 21060366; in additional pool allocated 1048576
Dictionary memory allocated 2897304
Buffer pool size 512
Free buffers 0
Database pages 503
Modified db pages 0
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages read 36022, created 166, written 504
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
Buffer pool hit rate 1000 / 1000
--------------
ROW OPERATIONS
--------------
1 queries inside InnoDB, 0 queries in queue
1 read views open inside InnoDB
Main thread id 2957578240, state: waiting for server activity
Number of rows inserted 2022, updated 7, deleted 13, read 528536
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 8.00 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================
Обновление 2
При попытке обновить b, c или d во время выполнения запроса INNODB STATUS выглядит следующим образом:
=====================================
120323 16:12:58 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 27 seconds
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 2959, signal count 2957
Mutex spin waits 0, rounds 27587, OS waits 426
RW-shared spins 1321, OS waits 516; RW-excl spins 2578, OS waits 1855
------------
TRANSACTIONS
------------
Trx id counter 0 3145998
Purge done for trx's n:o < 0 3145994 undo n:o < 0 0
History list length 0
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 0 0, not started, OS thread id 2958602240
MySQL thread id 7990, query id 69621 localhost root
SHOW INNODB STATUS
---TRANSACTION 0 3145997, ACTIVE 35 sec, OS thread id 2955325440, thread declared inside InnoDB 227
mysql tables in use 1, locked 0
MySQL thread id 7984, query id 69594 localhost root Copying to tmp table
CREATE TABLE 1_temp_foo AS
SELECT SQL_NO_CACHE
a.*
FROM
crm_companies AS a
LEFT JOIN users b ON a.zipcode = b.uid
LEFT JOIN calc_base_materials c ON a.zipcode = c.material_id
LEFT JOIN calc_base_material_langtext d ON a.zipcode = d.material_id
LEFT JOIN crm_people e ON a.zipcode = e.telephone1_number
ORDER BY a.country, a.name1
Trx read view will not see trx with id >= 0 3145998, sees < 0 3145998
--------
FILE I/O
--------
I/O thread 0 state: waiting for i/o request (insert buffer thread)
I/O thread 1 state: waiting for i/o request (log thread)
I/O thread 2 state: waiting for i/o request (read thread)
I/O thread 3 state: waiting for i/o request (write thread)
Pending normal aio reads: 0, aio writes: 0,
ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0
Pending flushes (fsync) log: 0; buffer pool: 0
54447 OS file reads, 1335 OS file writes, 509 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 0.00 writes/s, 0.00 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 5, seg size 7,
584 inserts, 584 merged recs, 4 merges
Hash table size 34679, node heap has 1 buffer(s)
0.00 hash searches/s, 0.00 non-hash searches/s
---
LOG
---
Log sequence number 1 2060137545
Log flushed up to 1 2060137545
Last checkpoint at 1 2060137545
0 pending log writes, 0 pending chkp writes
338 log i/o's done, 0.00 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total memory allocated 20799534; in additional pool allocated 1047808
Dictionary memory allocated 2897304
Buffer pool size 512
Free buffers 0
Database pages 511
Modified db pages 0
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages read 70769, created 661, written 3156
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
Buffer pool hit rate 1000 / 1000
--------------
ROW OPERATIONS
--------------
1 queries inside InnoDB, 0 queries in queue
2 read views open inside InnoDB
Main thread id 2957578240, state: waiting for server activity
Number of rows inserted 2022, updated 66643, deleted 13, read 626517
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 7.59 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================
И есть фактический список открытых процессов
Решение
Я вижу этот запрос в вашем SHOW INNODB STATUS\G
CREATE TABLE 1_temp_foo AS
SELECT SQL_NO_CACHE
a.*
FROM
crm_companies AS a
LEFT JOIN users b ON a.zipcode = b.uid
LEFT JOIN calc_base_materials c ON a.zipcode = c.material_id
LEFT JOIN calc_base_material_langtext d ON a.zipcode = d.material_id
LEFT JOIN crm_people e ON a.zipcode = e.telephone1_number
ORDER BY a.country, a.name1
Этот запрос меня пугает, потому что он сочетает в себе три вещи, о которых вы, возможно, даже не задумывались:
- InnoDB задействован на основе вашей первоначальной предпосылки:
Using: MySQL 5.1.41 and InnoDB Tables
- MyISAM также участвует.Почему MyISAM участвует?ВСЕ ВНУТРЕННИЕ ТАБЛИЦЫ TEMP — MyISAM!!!Полученное соединение представляет собой таблицу MyISAM, которую необходимо преобразовать в InnoDB после заполнения временной таблицы.Каков уровень блокировки по умолчанию для таблиц MyISAM?Блокировка уровня таблицы.
- DDL задействован, поскольку необходимо создать вновь созданную таблицу.Эта новая таблица не будет проявляться до тех пор, пока временная таблица не будет заполнена, преобразована в InnoDB и, наконец, не переименована.
1_temp_foo
.
Стоит отметить еще один побочный эффект.Когда ты это делаешь
CREATE TABLE tblname AS SELECT ...
Полученная таблица не имеет индексов.
У меня есть кое-что, что может вам помочь, чтобы обойти проблему блокировки.Он предполагает сначала создание таблицы в виде отдельного запроса, а затем ее заполнение.Есть два варианта создания временной таблицы:
ОПЦИЯ 1 :Попробуйте создать таблицу с таким же макетом.
CREATE TABLE 1_temp_foo LIKE crm_companies;
Это создаст таблицу 1_temp_foo
иметь те же индексы и механизм хранения, что и исходная таблица crm_companies
.
ВАРИАНТ №2 :Попробуйте создать таблицу только с тем же механизмом хранения, но без индексов.
CREATE TABLE 1_temp_foo SELECT * FROM crm_companies WHERE 1=2;
ALTER TABLE 1_temp_foo ENGINE=InnoDB;
После создания таблицы (какой бы способ вы ни выбрали), вы можете заполнить ее следующим образом:
INSERT INTO 1_temp_foo
SELECT SQL_NO_CACHE a.*
FROM
crm_companies AS a
LEFT JOIN users b ON a.zipcode = b.uid
LEFT JOIN calc_base_materials c ON a.zipcode = c.material_id
LEFT JOIN calc_base_material_langtext d ON a.zipcode = d.material_id
LEFT JOIN crm_people e ON a.zipcode = e.telephone1_number
ORDER BY a.country, a.name
;
Теперь этот запрос должен создавать блокировки на уровне строк, чтобы данные были доступны для повторного чтения.Другими словами, это транзакционный запрос.
ПРЕДОСТЕРЕЖЕНИЕ
ВАРИАНТ №2 имеет преимущества перед ВАРИАНТОМ №1.
- Преимущество №1 :Если crm_companies имеет какие-либо ограничения внешнего ключа, ВАРИАНТ № 1 на самом деле невозможен.Для простоты вам придется выбрать ВАРИАНТ №2.
- Преимущество №2 :Поскольку ВАРИАНТ №2 создает таблицу без определяемых пользователем индексов, таблица должна загружаться быстрее, чем если бы таблица была создана с помощью ВАРИАНТА №1.
Другие советы
В дополнение к установлению уровня изоляции транзакций для чтения совершенного (или прочтения незавершенного), вы также должны установить свой двоичный формат журнала в смешанную или строку. Репликация на основе операторов блокирует этот тип оператора, чтобы убедиться, что все «безопасно». Вы также можете установить innodb_locks_unsafe_for_binlog= 1 временно, но вы можете получить раб, который не синхронизирован таким образом.
SET binlog_format = ROW;
CREATE TABLE ... SELECT ...