Pembuatan indeks MySQL gagal di atas meja sudah penuh

10

UPDATE: tl; dr: Masalahnya adalah MySQL menggunakan TMPDIRsaat membuat indeks. Dan saya TMPDIRadalah orang yang kehabisan ruang disk.

Q asli:

Saya mencoba menambahkan indeks ke tabel InnoDB, dan mendapatkan table is full error. Saya memiliki ruang disk yang cukup dan konfigurasi MySQL memiliki file-per-table = 1. Tabel data adalah 85GB dan saya berasumsi indeksnya akan sekitar 20GB - 30GB dan saya memiliki lebih banyak ruang disk dari itu. Saya juga menggunakan ext3 jadi saya tidak merasa ada masalah dengan batas ukuran file dari sudut pandang OS.

Kesalahan yang dicatat terlihat seperti ini:

140616 13:04:33  InnoDB: Error: Write to file (merge) failed at offset 3 1940914176.
InnoDB: 1048576 bytes should have been written, only 970752 were written.
InnoDB: Operating system error number 0.
InnoDB: Check that your OS and file system support files of this size.
InnoDB: Check also that the disk is not full or a disk quota exceeded.
InnoDB: Error number 0 means 'Success'.
InnoDB: Some operating system error numbers are described at
InnoDB: http://dev.mysql.com/doc/refman/5.5/en/operating-system-error-codes.html
140616 13:04:33 [ERROR] /usr/libexec/mysqld: The table 'my_table' is full

Apa yang menyebabkan ini dan bagaimana saya bisa menyelesaikannya?

Tabel buat:

`CREATE TABLE `my_table` (
 `uid_from` bigint(11) NOT NULL,
 `uid_to` bigint(11) NOT NULL,
 `counter` int(11) NOT NULL,
 `updated` date NOT NULL,
 PRIMARY KEY (`uid_to`,`uid_from`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8`

Seperti Data 87.4GB dan saya memperkirakan ada sekitar 1,5B baris.

SHOW GLOBAL VARIABLES LIKE 'tmpdir';
Variable_name   Value
tmpdir  /tmp

[root@web ~]# df -h /tmp
Filesystem      Size  Used Avail Use% Mounted on
/dev/xvda1       40G   24G   14G  64% /
Noam
sumber
1
"InnoDB: Periksa apakah OS dan sistem file Anda mendukung ukuran ini. InnoDB: Periksa juga bahwa disk tidak penuh atau kuota disk terlampaui." - sudahkah kamu memeriksanya? Apakah Anda menggunakan ext2? ext3? xfs? Memeriksa kuota untuk pengguna?
Philᵀᴹ
@Phil Memeriksa keduanya. Saya menggunakan ext3 dan saya hampir yakin tidak ada masalah dengan ruang disk. Firasat itu ada hubungannya dengan log yang mencapai batasnya atau sesuatu, tetapi tidak tahu bagaimana cara memverifikasi dan memperbaikinya.
Noam
Saya ingin tahu file apa "(gabungan)" itu?
akuzminsky
@akuzminsky hmm saya tidak tahu. Saya hanya menjalankan pembuatan indeks baru dari PHPMyAdmin.
Noam
+1 untuk TMPDIR, itu masalah di server saya juga.
Chad E.

Jawaban:

8

Tolong jangan tertipu oleh pesan kesalahan The table 'my_table' is full. Skenario langka ini sama sekali tidak ada hubungannya dengan ruang disk. Kondisi lengkap tabel ini berkaitan dengan pipa ledeng internal InnoDB.

Pertama, lihat diagram Arsitektur InnoDB ini

Arsitektur InnoDB

Harap dicatat bahwa tablespace sistem (file ibdata) hanya memiliki 128 Rollback Segments dan 1023 Rollback Slots Per Rollback Segment. Ini membatasi ukuran kapasitas kemunduran transaksi. Dengan kata lain, jika satu segmen rollback membutuhkan lebih dari 1023 slot untuk mendukung transaksi, transaksi akan mencapai table is fullkondisi itu.

Pikirkan restoran Red Lobster di New Jersey . Mungkin memiliki kapasitas 200 orang. Jika restoran penuh, barisan orang mungkin pergi ke luar untuk menunggu. Jika orang-orang di telepon menjadi tidak sabar, mereka mungkin pergi karena restoran penuh. Jelas, solusinya tidak akan membuat New Jersey lebih besar (atau mendapatkan lebih banyak ruang disk). Solusinya adalah membuat restoran Red Lobster lebih besar. Dengan begitu Anda dapat meningkatkan kapasitas tempat duduk, katakanlah, 240. Bahkan dengan itu, garis dapat terbentuk di luar jika lebih dari 240 orang memutuskan untuk datang ke Red Lobster.

Sebagai contoh, saya punya klien dengan 2TB sistem tablespace dan innodb_file_per_table dinonaktifkan. (346G untuk ibdata1, reset untuk ibdata2). Saya menjalankan kueri ini

SELECT SUM(data_length+index+length) InnoDBDataIndexSpace
FROM information_schema.tables WHERE engine='InnoDB';

Selanjutnya, saya mengurangi InnoDBDataIndexSpace dari jumlah filesizes untuk ibdata1 dan ibdata2. Saya mendapat dua hal yang mengejutkan saya

  • Ada 106GB yang tersisa di dalam tablespace sistem.
  • Saya mendapat Table is Fullkondisi yang sama

Ini berarti bahwa 106GB digunakan untuk pipa internal InnoDB. Klien menggunakan ext3 pada saat itu.

Solusi bagi saya adalah menambahkan ibdata3. Saya membahas ini di posting lama saya. Bagaimana mengatasi "Tabel ... sudah penuh" dengan "innodb_file_per_table"?

Saya telah membahas ini di postingan lain juga

Perlu diingat bahwa kondisi ini dapat terjadi bahkan jika innodb_file_per_table diaktifkan. Bagaimana? Rollbacks dan Undo Logs adalah sumber lonjakan yang tidak terkontrol adalah pertumbuhan untuk ibdata1 .

PERTANYAAN SEBENARNYA

Karena Anda menambahkan indeks ke tabel dan mendapatkan Table is Full, tabel tersebut harus besar dan tidak dapat masuk ke dalam segmen rollback tunggal. Anda harus melakukan hal berikut:

LANGKAH 01

Dapatkan data ke dalam file dump

mysqldump --no-create-info mydb mytable > table_data.sql

LANGKAH 02

Login ke MySQL dan jalankan ini

USE mydb
CREATE TABLE mytable_new LIKE mytable;
ALTER TABLE mytable_new ADD INDEX ... ;
ALTER TABLE mytable RENAME mytable_old;
ALTER TABLE mytable_new RENAME mytable;

LANGKAH 03

Muat tabel dengan indeks tambahan dengan data

mysql -Dmydb < table_data.sql

Itu saja.

Lihat, masalahnya adalah bahwa ALTER TABLE akan mencoba menyuntikkan semua baris dalam tabel besar Anda sebagai satu transaksi. Menggunakan mysqldump akan memasukkan data ke dalam tabel (sekarang dengan indeks baru) ribuan baris sekaligus, tidak semua baris dalam satu transaksi.

Jangan khawatir tentang what if this doesn't work?Tabel asli akan dinamai mytable_oldjika ada sesuatu. itu bisa berfungsi sebagai cadangan. Anda dapat menjatuhkan cadangan saat Anda tahu tabel baru berfungsi untuk Anda.

Cobalah !!!

UPDATE 2014-06-16 11:13 EDT

Jika Anda khawatir tentang dump data yang lebih besar, cukup gzip saja.

Anda bisa melakukan langkah yang sama, tetapi sebagai berikut

LANGKAH 01

Dapatkan data ke dalam file dump

mysqldump --no-create-info mydb mytable | gzip > table_data.sql.gz

LANGKAH 02

Login ke MySQL dan jalankan ini

USE mydb
CREATE TABLE mytable_new LIKE mytable;
ALTER TABLE mytable_new ADD INDEX ... ;
ALTER TABLE mytable RENAME mytable_old;
ALTER TABLE mytable_new RENAME mytable;

LANGKAH 03

Muat tabel dengan indeks tambahan dengan data

gzip -d < table_data.sql.gz | mysql -Dmydb

atau

gunzip < table_data.sql.gz | mysql -Dmydb

UPDATE 2014-06-16 12:55 EDT

Saya hanya memikirkan aspek lain berkenaan dengan masalah ini.

Karena Anda melakukan DDL dan bukan DML, ada kemungkinan bahwa ini bukan pipa saluran internal InnoDB. Karena DDL tidak dapat mengembalikan untuk InnoDB , masalahnya harus menjadi saluran pipa eksternal. Di mana pipa eksternal ini tersumbat? Saya curiga folder temp untuk OS. Mengapa?

140616 13:04:33  InnoDB: Error: Write to file (merge) failed at offset 3 1940914176.
InnoDB: 1048576 bytes should have been written, only 970752 were written.
InnoDB: Operating system error number 0.
InnoDB: Check that your OS and file system support files of this size.
InnoDB: Check also that the disk is not full or a disk quota exceeded.
InnoDB: Error number 0 means 'Success'.
InnoDB: Some operating system error numbers are described at
InnoDB: http://dev.mysql.com/doc/refman/5.5/en/operating-system-error-codes.html
140616 13:04:33 [ERROR] /usr/libexec/mysqld: The table 'my_table' is full

Lihat disk quota exceeded? Di mana kuota disk ini diberlakukan?

Jalankan kueri ini

SHOW GLOBAL VARIABLES LIKE 'tmpdir';

Anda mengatakan itu /var/lib/mysqltmp

Sekarang, jalankan ini di OS

df -h /var/lib/mysqltmp

Sesuatu memberi tahu saya bahwa ruang untuk /var/lib/mysqltmpkehabisan Setelah semua, Anda hanya memiliki 14G gratis. Di mata DDL (untuk membuat indeks), kemunduran terjadi, bukan pada file ibdata1, tetapi di tmpdirlokasi. Jika /var/lib/mysqltmptidak dipasang di mana saja, maka data temp sedang ditulis di partisi root. Jika /var/lib/mysqltmpdipasang di suatu tempat, maka mount itu sedang diisi dengan data baris. Dalam kedua kasus, tidak ada cukup ruang untuk menyelesaikan DDL.

Anda memiliki dua opsi di sini

PILIHAN 1

Anda juga bisa membuat disk besar (mungkin dengan 100 + GB) dan pasang /var/lib/mysqltmpdi disk besar itu.

PILIHAN 2

Saran 3 langkah saya akan tetap berfungsi, bahkan dengan ruang disk terbatas yang Anda miliki

KOMENTAR

Sayang sekali pesan kesalahan yang Anda posting mengatakan

InnoDB: Operating system error number 0.
InnoDB: Error number 0 means 'Success'.

Ini berarti OS baik-baik saja. Itu juga tidak ada nomor kesalahan terkait untuk situasi ini.

Ada hal lain yang harus Anda ketahui. Dokumentasi MySQL mengatakan bahwa Pembuatan Indeks Cepat untuk InnoDB masih masuk ke disk :

Selama pembuatan indeks, file ditulis ke direktori sementara ($ TMPDIR pada Unix,% TEMP% pada Windows, atau nilai variabel konfigurasi --tmpdir). Setiap file sementara cukup besar untuk menampung satu kolom yang membentuk indeks baru, dan masing-masing dihapus segera setelah digabungkan ke dalam indeks akhir.

Karena keterbatasan MySQL, tabel disalin, daripada menggunakan "Pembuatan Indeks Cepat" saat Anda membuat indeks pada TABEL SEMENTARA. Ini telah dilaporkan sebagai Bug MySQL # 39833 .

UPDATE 2014-06-16 13:58 EDT

[mysqld]
datadir                         = /mnt/cbsvolume1/var/lib/mysql
tmpdir                          = /mnt/cbsvolume1/var/lib/mysql

Pastikan /mnt/cbsvolume1/var/lib/mysqlmemiliki 100G atau lebih gratis

RolandoMySQLDBA
sumber
Butuh berminggu-minggu untuk membangun tabel di tempat pertama dari dump (data 85GB). Apakah ada cara lain selain melakukan ini lagi? Apakah Anda akan merekomendasikan untuk mem-partisi tabel?
Noam
Mempartisi tidak akan membantu karena ALTER TABLE untuk menambahkan indeks masih akan mencoba memuat semuanya dalam satu transaksi. Ingat, Anda melawan Arsitektur InnoDB, bukan ruang disk. Jawaban saya akan membantu Anda dalam kasus ini karena mysqldump menyuntikkan beberapa ribu baris per INSERT, bukan memasukkan semua atau tidak sama sekali ke tabel temp dengan kemungkinan rollback.
RolandoMySQLDBA
Tetapi membuat tabel ini akan (lagi) memakan waktu selamanya. Adakah cara untuk menjalankan indeks buat sambil menonaktifkan opsi rollback?
Noam
Membuat indeks adalah DDL bukan DML. Anda tidak dapat mengubah perilaku DDL. Itu sebabnya posting saya menggunakan teknik DML.
RolandoMySQLDBA
BTW berapa banyak baris yang dimiliki tabel ???
RolandoMySQLDBA