MySQL wird gesperrt, während CABATE TABLE AS SELECT erstellt wird


10

Ich führe die folgende (Dummy-) Abfrage aus

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

Angenommen, die Abfrage dauert 10 Minuten. Wenn Sie versuchen, die Werte in den Tabellen a, b oder c während der Ausführung zu aktualisieren, warten Sie, bis die obige Abfrage zuerst abgeschlossen ist. Ich möchte diese Sperre vermeiden (Datenkonsistenz ist nicht von Interesse). Wie kann ich das erreichen?

Verwenden von: MySQL 5.1.41- und InnoDB-Tabellen

ps SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; ergibt keine Verhaltensänderung

Aktualisieren Während die Abfrage ausgeführt wird, lautet die Ausgabe von SHOW ENGINE INNODB STATUS wie folgt (ich habe hier absichtlich eine sehr langsame Abfrage durchgeführt).

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

Update 2

Wenn Sie versuchen, entweder b, c oder d zu aktualisieren, während die Abfrage ausgeführt wird, lautet INNODB STATUS wie folgt:

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

Und da ist die aktuelle offene Prozessliste

Prozessliste

Antworten:


10

Ich sehe diese Abfrage in Ihrem 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 

Diese Abfrage macht mir Angst, weil sie drei Dinge kombiniert, an die Sie vielleicht nicht gedacht haben:

  • InnoDB ist basierend auf Ihrer ursprünglichen Prämisse beteiligt: Using: MySQL 5.1.41 and InnoDB Tables
  • MyISAM ist ebenfalls beteiligt. Warum ist MyISAM beteiligt? ALLE INTERNEN TEMP-TABELLEN SIND MyISAM !!! Der resultierende Join ist eine MyISAM-Tabelle, die in InnoDB konvertiert werden muss, wenn die temporäre Tabelle gefüllt wurde. Was ist die Standardsperrstufe für MyISAM-Tabellen? Sperren auf Tabellenebene.
  • DDL ist beteiligt, da eine neu erstellte Tabelle ins Leben gerufen werden muss. Diese neue Tabelle wird erst angezeigt, wenn die temporäre Tabelle ausgefüllt, in InnoDB konvertiert und schließlich umbenannt wurde 1_temp_foo.

Es gibt noch einen weiteren bemerkenswerten Nebeneffekt. Wenn Sie das tun

CREATE TABLE tblname AS SELECT ...

Die resultierende Tabelle enthält keine Indizes.

Ich habe etwas, das Sie vielleicht hilfreich finden, um das Problem mit dem Sperren zu umgehen. Dabei wird die Tabelle zuerst als separate Abfrage erstellt und dann ausgefüllt. Es gibt zwei Möglichkeiten, um Ihre temporäre Tabelle zu erstellen:

OPTION 1 : Versuchen Sie, die Tabelle mit demselben Layout zu erstellen

CREATE TABLE 1_temp_foo LIKE crm_companies;

Dadurch wird die Tabelle so erstellt 1_temp_foo, dass sie genau dieselben Indizes und Speicher-Engines wie die ursprüngliche Tabelle aufweist crm_companies.

OPTION 2 : Versuchen Sie, die Tabelle nur mit derselben Speicher-Engine, aber ohne Indizes zu erstellen.

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

Nachdem Sie die Tabelle erstellt haben (wie auch immer Sie sich entscheiden), können Sie die Tabelle nun wie folgt füllen:

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
;

Diese Abfrage sollte nun Sperren auf Zeilenebene erzeugen, damit Daten für wiederholbare Lesevorgänge verfügbar sind. Mit anderen Worten, dies ist eine Transaktionsabfrage.

VORBEHALT

OPTION 2 hat Vorteile gegenüber OPTION 1

  • Vorteil Nr. 1 : Wenn crm_companies Fremdschlüsseleinschränkungen aufweist, ist OPTION Nr. 1 nicht wirklich möglich. Der Einfachheit halber müssten Sie OPTION 2 wählen.
  • Vorteil Nr. 2 : Da OPTION Nr. 2 eine Tabelle ohne benutzerdefinierte Indizes erstellt, sollte die Tabelle schneller geladen werden, als wenn die Tabelle über OPTION Nr. 1 erstellt worden wäre.

2

Zusätzlich zum Festlegen der Transaktionsisolationsstufe auf READ COMMITTED (oder READ UNCOMMITTED) muss Ihr binäres Protokollformat auf MIXED oder ROW eingestellt sein. Die auf STATEMENT basierende Replikation sperrt diese Art von Anweisung, um sicherzustellen, dass alles "sicher" ist. Sie können auch vorübergehend innodb_locks_unsafe_for_binlog = 1 setzen, aber Sie könnten einen Slave erhalten, der auf diese Weise nicht synchron ist.

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

Leider funktioniert das auch nicht :(
Clops

1
Was ist die Ausgabe von SHOW ENGINE INNODB STATUS, wenn die Anweisung CREATE TABLE ausgeführt wird?
Aaron Brown

1
Stellen Sie außerdem sicher, dass Sie READ COMMITTED und nicht READ UNCOMMITTED verwenden. Sie könnten auf diesen Fehler stoßen, der erst am 5.1.47
Aaron Brown

1
Das ist seltsam. Gibt es Fremdschlüssel? Können Sie INNODB STATUS posten, während Sie versuchen, die Zeile zu aktualisieren? Haben Sie dies bei einer neueren Version von MySQL versucht? (Ich vermute, dass Sie 5.1.41 verwenden, weil es mit Ihrer Distribution geliefert wird?)
Aaron Brown

1
Sind Sie sicher, dass Ihre Benutzertabelle InnoDB ist? Das Update wird nicht in der Ausgabe von SHOW ENGINE INNODB STATUS angezeigt, und in der Prozessliste wird es als gesperrt angezeigt, was normalerweise auf MyISAM-Tabellen zurückzuführen ist. Ich bin froh, dass die Antwort von @ RolandoMySQLDBA Ihr Problem gelöst hat, aber ich denke, hier ist noch etwas los.
Aaron Brown
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.