O MySQL bloqueia enquanto CREATE TABLE AS SELECT


10

Estou executando a seguinte consulta (fictícia)

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

Suponha que a consulta leve 10 minutos para ser executada. Tentar atualizar os valores nas tabelas a, b ou c enquanto estiver em execução aguardará a conclusão da consulta acima. Quero evitar esse bloqueio (a consistência dos dados não é de interesse). Como posso conseguir isso?

Usando: MySQL 5.1.41 e Tabelas InnoDB

ps SET NÍVEL DE ISOLAMENTO DA TRANSAÇÃO LIDO SEM COMPROMISSO; não produz mudança de comportamento

Atualização Enquanto a consulta está sendo executada, a saída de SHOW ENGINE INNODB STATUS é a seguinte (fiz uma consulta muito lenta aqui de propósito)

=====================================
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
============================

Atualização 2

Ao tentar atualizar b, c ou d enquanto a consulta está executando o INNODB STATUS, é o seguinte:

=====================================
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
============================

E existe a lista de processos abertos real

Lista de processos

Respostas:


10

Eu vejo essa consulta no seu 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 

Essa consulta me dá arrepios porque combina três coisas que você talvez não tenha pensado:

  • O InnoDB está envolvido com base em sua premissa inicial: Using: MySQL 5.1.41 and InnoDB Tables
  • O MyISAM também está envolvido. Por que o MyISAM está envolvido? TODAS AS TABELAS INTERNAS DE TEMP SÃO MYISAM !!! A junção resultante é uma tabela MyISAM que deve ser convertida no InnoDB quando a tabela temporária for preenchida. Qual é o nível de bloqueio padrão para as tabelas MyISAM? Bloqueio no nível da tabela.
  • O DDL está envolvido, pois uma tabela recém-criada deve ser criada. Essa nova tabela não seria manifestada até que a tabela temporária seja preenchida, convertida em InnoDB e finalmente renomeada 1_temp_foo.

Há outro efeito colateral digno de nota. Quando você faz

CREATE TABLE tblname AS SELECT ...

A tabela resultante não possui índices.

Tenho algo que você pode achar útil para contornar o problema de bloqueio. Envolve tornar a tabela primeiro como uma consulta separada e, em seguida, preenchê-la. Existem duas opções para criar sua tabela temporária:

OPÇÃO 1 : Tente criar a tabela com o mesmo layout

CREATE TABLE 1_temp_foo LIKE crm_companies;

Isso criará a tabela 1_temp_foopara ter exatamente os mesmos índices e mecanismo de armazenamento que a tabela original crm_companies.

OPÇÃO 2 : Tente criar a tabela apenas com o mesmo mecanismo de armazenamento, mas sem índices.

CREATE TABLE 1_temp_foo SELECT * FROM crm_companies WHERE 1=2;
ALTER TABLE 1_temp_foo ENGINE=InnoDB;

Depois de criar a tabela (da maneira que você escolher), agora você pode preencher a tabela assim:

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
;

Agora, essa consulta deve produzir bloqueios no nível da linha para manter os dados disponíveis para leituras repetíveis. Em outras palavras, essa é uma consulta transacional.

EMBARGO

A OPÇÃO 2 tem vantagens sobre a OPÇÃO 1

  • Vantagem # 1 : Se crm_companies tiver alguma restrição de chave estrangeira, a OPÇÃO # 1 não será realmente possível. Você teria que escolher a OPÇÃO # 2 por uma questão de simplicidade.
  • Vantagem # 2 : Como a OPÇÃO # 2 cria uma tabela sem índices definidos pelo usuário, a tabela deve carregar mais rapidamente do que se a tabela fosse feita via OPÇÃO # 1.

2

Além de definir o nível de isolamento da transação como READ COMMITTED (ou READ COMMITTED), você também deve ter o formato de log binário definido como MIXED ou ROW. A replicação baseada em STATEMENT bloqueia esse tipo de instrução para garantir que tudo esteja "seguro". Você também pode definir innodb_locks_unsafe_for_binlog = 1 temporariamente, mas pode acabar com um escravo que está fora de sincronia dessa maneira.

SET binlog_format = ROW;
CREATE TABLE ... SELECT ...

Infelizmente isso também não funciona :(
clops 23/03

11
Qual é a saída do SHOW ENGINE INNODB STATUS quando a instrução CREATE TABLE está em execução?
Aaron Brown

11
Além disso, verifique se você está usando READ COMMITTED, não READ UNCOMMITTED. Você poderia estar em execução para este bug que não foi fixado até 5.1.47 bugs.mysql.com/bug.php?id=48607
Aaron Brown

11
Isso é estranho. Existem chaves estrangeiras? Você pode postar INNODB STATUS enquanto tenta atualizar a linha? Você já tentou isso em uma versão mais recente do MySQL? (o meu palpite é que você está usando 5.1.41 porque ele vem com sua distribuição?)
Aaron Brown

11
Você tem certeza de que sua tabela de usuários é o InnoDB? A atualização não está aparecendo na saída SHOW ENGINE INNODB STATUS e na lista de processos está aparecendo como Bloqueada, o que geralmente é resultado das tabelas MyISAM. Estou feliz que a resposta de @ RolandoMySQLDBA tenha resolvido seu problema, mas acho que há algo mais acontecendo aqui.
Aaron Brown
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.