MySQL блокирует пока CREATE TABLE AS SELECT

10

Я выполняю следующий (фиктивный) запрос

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

ps УСТАНОВИТЕ УРОВЕНЬ ИЗОЛЯЦИИ СДЕЛКИ СЧИТАЕТСЯ НЕЗАВИСИМЫМ; не дает никаких изменений в поведении

Обновление Во время выполнения запроса вывод команды 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
============================

И есть фактический список открытых процессов

Список процессов

clops
источник

Ответы:

10

Я вижу этот запрос в вашем 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 участвует? ВСЕ ВНУТРЕННИЕ ТАБЛИЦЫ ТЕМПЕРАТУРЫ 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 имеет какие-либо ограничения внешнего ключа, OPTION # 1 на самом деле не представляется возможным. Вы должны были бы выбрать ВАРИАНТ № 2 ради простоты.
  • Преимущество # 2 : поскольку OPTION # 2 создает таблицу без пользовательских индексов, таблица должна загружаться быстрее, чем если бы таблица была создана с помощью OPTION # 1.
RolandoMySQLDBA
источник
2

Помимо установки уровня изоляции транзакции READ COMMITTED (или READ UNCOMMITTED), вы также должны иметь двоичный формат журнала, установленный на MIXED или ROW. Репликация на основе ЗАЯВЛЕНИЯ блокирует этот тип оператора, чтобы убедиться, что все «безопасно». Вы также можете временно установить innodb_locks_unsafe_for_binlog = 1, но в итоге вы можете получить ведомого, который не синхронизирован таким образом.

SET binlog_format = ROW;
CREATE TABLE ... SELECT ...
Аарон Браун
источник
К сожалению, это тоже не работает :(
Clops
1
Что выводит SHOW ENGINE INNODB STATUS, когда выполняется оператор CREATE TABLE?
Аарон Браун
1
Кроме того, убедитесь, что вы используете READ COMMITTED, а не READ UNCOMMITTED. Вы могли столкнуться с этой ошибкой, которая не была исправлена ​​до 5.1.47 bugs.mysql.com/bug.php?id=48607
Аарон Браун
1
Странно. Есть ли внешние ключи? Можете ли вы опубликовать INNODB STATUS при попытке обновить строку? Вы пробовали это на более свежей версии MySQL? (я предполагаю, что вы используете 5.1.41, потому что он поставляется с вашим дистрибутивом?)
Аарон Браун
1
Вы уверены, что ваша таблица пользователей InnoDB? Обновление не появляется в выходных данных SHOW ENGINE INNODB STATUS, а в списке процессов оно отображается как заблокированное, что обычно является результатом таблиц MyISAM. Я рад, что ответ @ RolandoMySQLDBA решил вашу проблему, но я думаю, что здесь происходит что-то еще.
Аарон Браун