MySQL Locks sementara CREATE TABLE AS SELECT

10

Saya menjalankan kueri (dummy) berikut

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

Misalkan kueri membutuhkan waktu 10 menit untuk dijalankan. Mencoba memperbarui nilai dalam tabel a, b atau c saat sedang berjalan akan menunggu kueri di atas selesai terlebih dahulu. Saya ingin menghindari kunci ini (konsistensi data tidak menarik). Bagaimana saya bisa mencapainya?

Menggunakan: Tabel MySQL 5.1.41 dan InnoDB

ps SET TINGKAT ISOLASI TRANSAKSI BACA TIDAK DIKOMITMASI; tidak menghasilkan perubahan perilaku

Pembaruan Saat kueri dieksekusi, output dari SHOW ENGINE INNODB STATUS adalah sebagai berikut (saya sengaja membuat permintaan yang sangat lambat di sini)

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

Perbarui 2

Saat mencoba memperbarui b, c atau d saat kueri menjalankan INNODB STATUS adalah sebagai berikut:

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

Dan ada daftar proses terbuka yang sebenarnya

Daftar proses

Clops
sumber

Jawaban:

10

Saya melihat pertanyaan ini di email Anda 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 

Kueri ini membuat saya merinding karena menggabungkan tiga hal yang mungkin tidak Anda pikirkan:

  • InnoDB terlibat berdasarkan pada premis awal Anda: Using: MySQL 5.1.41 and InnoDB Tables
  • MyISAM juga terlibat. Mengapa MyISAM terlibat? SEMUA TABEL TEMP INTERNAL ADALAH MyISAM !!! Gabung yang dihasilkan adalah tabel MyISAM yang harus dikonversi ke InnoDB ketika tabel temp telah diisi. Apa tingkat kunci default untuk tabel MyISAM? Penguncian Tingkat Meja.
  • DDL terlibat karena tabel yang baru dibuat harus ada. Tabel baru itu tidak akan dimanifestasikan sampai tabel temp diisi, dikonversi ke InnoDB, dan akhirnya diganti namanya 1_temp_foo.

Ada efek samping lain yang perlu diperhatikan. Saat kamu melakukan

CREATE TABLE tblname AS SELECT ...

Tabel yang dihasilkan tidak memiliki indeks.

Saya memiliki sesuatu yang mungkin membantu untuk mem-bypass masalah penguncian. Ini melibatkan membuat tabel terlebih dahulu sebagai kueri yang terpisah, lalu mengisinya. Ada dua opsi untuk membuat tabel temp Anda:

OPSI # 1 : Coba buat tabel dengan tata letak yang sama

CREATE TABLE 1_temp_foo LIKE crm_companies;

Ini akan membuat tabel 1_temp_foountuk memiliki indeks dan mesin penyimpanan yang sama persis dengan tabel aslinya crm_companies.

OPSI # 2 : Coba buat tabel dengan mesin penyimpanan yang sama saja, tetapi tidak ada indeks.

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

Setelah membuat tabel (dengan cara apa pun yang Anda pilih), Anda sekarang dapat mengisi tabel seperti ini:

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
;

Sekarang, kueri ini harus menghasilkan kunci tingkat baris demi ketersediaan data untuk dibaca berulang. Dengan kata lain, ini adalah permintaan transaksional.

CAVEAT

OPSI # 2 memiliki kelebihan dibandingkan OPSI # 1

  • Keuntungan # 1 : Jika crm_companies memiliki batasan kunci asing, OPTION # 1 tidak benar-benar mungkin. Anda harus memilih OPSI # 2 demi kesederhanaan.
  • Keuntungan # 2 : Karena OPTION # 2 membuat tabel tanpa indeks yang ditentukan pengguna, tabel harus memuat lebih cepat daripada jika tabel dibuat melalui OPTION # 1.
RolandoMySQLDBA
sumber
2

Selain mengatur tingkat isolasi transaksi untuk BACA BERKOMITMEN (atau BACA TIDAK TERKADANG), Anda juga harus mengatur format log biner Anda ke CAMPURAN atau BARIS. Replikasi berbasis pernyataan mengunci jenis pernyataan ini untuk memastikan semuanya "aman." Anda juga dapat mengatur innodb_locks_unsafe_for_binlog = 1 sementara, tetapi Anda bisa berakhir dengan seorang budak yang tidak sinkron seperti itu.

SET binlog_format = ROW;
CREATE TABLE ... SELECT ...
Aaron Brown
sumber
Sayangnya ini tidak berhasil :(
clops
1
Apa output dari STATUS SHOW ENGINE INNODB ketika pernyataan CREATE TABLE berjalan?
Aaron Brown
1
Juga, pastikan bahwa Anda menggunakan READ COMMITTED, bukan READ UNCOMMITTED. Anda bisa mengalami bug ini yang tidak diperbaiki sampai 5.1.47 bugs.mysql.com/bug.php?id=48607
Aaron Brown
1
Itu aneh. Apakah ada kunci asing? Bisakah Anda memposting INNODB STATUS saat mencoba memperbarui baris? Sudahkah Anda mencoba ini pada versi MySQL yang lebih baru? (Dugaan saya adalah bahwa Anda menggunakan 5.1.41 karena dikirimkan bersama distribusi Anda?)
Aaron Brown
1
Apakah Anda yakin bahwa tabel pengguna Anda adalah InnoDB? Pembaruan tidak muncul di output SHOW ENGINE INNODB STATUS dan dalam daftar proses itu muncul sebagai Terkunci yang biasanya merupakan hasil dari tabel MyISAM. Saya senang bahwa jawaban RolandoMySQLDBA memecahkan masalah Anda, tetapi saya pikir ada sesuatu yang terjadi di sini.
Aaron Brown