Cara mengecilkan / membersihkan file ibdata1 di MySQL

561

Saya menggunakan MySQL di localhost sebagai "alat permintaan" untuk melakukan statistik dalam R, yaitu, setiap kali saya menjalankan skrip R, saya membuat database baru (A), membuat tabel baru (B), mengimpor data ke B , kirimkan pertanyaan untuk mendapatkan apa yang saya butuhkan, dan kemudian saya menjatuhkan B dan menjatuhkan A.

Ini berfungsi dengan baik untuk saya, tetapi saya menyadari bahwa ukuran file ibdata meningkat dengan cepat, saya tidak menyimpan apa pun di MySQL, tetapi file ibdata1 sudah melebihi 100 MB.

Saya menggunakan kurang lebih pengaturan MySQL default untuk pengaturan, apakah ada cara agar saya dapat secara otomatis mengecilkan / menghapus file ibdata1 setelah periode waktu yang tetap?

lokheart
sumber

Jawaban:

778

Yang ibdata1tidak menyusut adalah fitur MySQL yang sangat mengganggu. The ibdata1file tidak dapat benar-benar akan menyusut kecuali Anda menghapus semua database, menghapus file dan kembali dump.

Tetapi Anda dapat mengkonfigurasi MySQL sehingga setiap tabel, termasuk indeksnya, disimpan sebagai file terpisah. Dengan cara ibdata1itu tidak akan tumbuh besar. Menurut komentar Bill Karwin, ini diaktifkan secara default pada versi 5.6.6 dari MySQL.

Beberapa waktu yang lalu saya melakukan ini. Namun, untuk mengatur server Anda untuk menggunakan file terpisah untuk setiap tabel Anda perlu mengubah my.cnfuntuk mengaktifkan ini:

[mysqld]
innodb_file_per_table=1

http://dev.mysql.com/doc/refman/5.5/en/innodb-multiple-tablespaces.html

Karena Anda ingin mendapatkan kembali ruang dari ibdata1Anda sebenarnya harus menghapus file:

  1. Lakukan mysqldumpdari semua basis data, prosedur, pemicu dll kecuali mysqldan performance_schemabasis data
  2. Hapus semua basis data kecuali 2 basis data di atas
  3. Hentikan mysql
  4. Hapus ibdata1dan ib_logfile
  5. Mulai mysql
  6. Kembalikan dari dump

Ketika Anda memulai MySQL pada langkah 5 ibdata1dan ib_logfile akan dibuat kembali.

Sekarang Anda siap untuk pergi. Saat Anda membuat database baru untuk analisis, tabel akan ditempatkan di ibd*file terpisah , bukan di ibdata1. Karena Anda biasanya menjatuhkan basis data segera setelahnya, ibd*file - file tersebut akan dihapus.

http://dev.mysql.com/doc/refman/5.1/en/drop-database.html

Anda mungkin telah melihat ini:
http://bugs.mysql.com/bug.php?id=1341

Dengan menggunakan perintah ALTER TABLE <tablename> ENGINE=innodbatau OPTIMIZE TABLE <tablename>satu dapat mengekstrak data dan indeks halaman dari ibdata1 ke file terpisah. Namun, ibdata1 tidak akan menyusut kecuali Anda melakukan langkah-langkah di atas.

Mengenai itu information_schema, itu tidak perlu atau tidak mungkin untuk jatuh. Ini sebenarnya hanya sekelompok tampilan read-only, bukan tabel. Dan tidak ada file yang terkait dengan mereka, bahkan direktori basis data. The informations_schemamenggunakan memori db-mesin dan dijatuhkan dan regenerasi pada stop / restart mysqld. Lihat https://dev.mysql.com/doc/refman/5.7/en/information-schema.html .

John P.
sumber
16
@JordanMagnuson Jangan repot-repot menjatuhkan informasi_schema. Ini sebenarnya hanya sekelompok tampilan read-only, bukan tabel. Dan tidak ada file yang terkait dengannya. Bahkan tidak ada direktori untuk database. Information_schema menggunakan memory db-engine dan dijatuhkan dan dibuat ulang saat stop / restart mysqld. Lihat dev.mysql.com/doc/refman/5.5/id/information-schema.html . Mengenai performance_schema saya belum menggunakan skema itu sendiri.
John P
4
Saya tidak tahu apakah ini hal yang baru tetapi setelah opsi innodb_file_per_table diaktifkan, Anda dapat menjalankan "ALTER TABLE <tablename> ENGINE = InnoDB" (bahkan jika itu sudah InnoDB) dan akan memindahkan tabel ke file individualnya . Tidak perlu drop database dan semacamnya.
CR.
3
+1 FWIW, MySQL 5.6 diaktifkan innodb_file_per_tablesecara default.
Bill Karwin
3
Ya, ibdata1 diharapkan hadir bersama file lainnya. File ibdata1 masih akan menyimpan metadata tentang tabel, log undo dan buffer.
John P
1
Saya kehabisan ruang di server saya karena file ibdata1, jadi saya bahkan tidak bisa membuang databasenya. Apakah sama dengan memindahkan file di / var / lib / mysql (kecuali "mysql", "ibdata1", "ib_logfile0" dan "ib_logfile1") dan kemudian ikuti langkah-langkahnya? Lihat stackoverflow.com/questions/2482491/…
Sophivorus
47

Menambah jawaban John P ,

Untuk sistem linux, langkah 1-6 dapat dilakukan dengan perintah-perintah ini:

  1. mysqldump -u [username] -p[root_password] [database_name] > dumpfilename.sql
  2. DROP DATABASE [database_name];
  3. sudo /etc/init.d/mysqld stop
  4. sudo rm /var/lib/mysql/ibdata1
    sudo rm /var/lib/mysql/ib_logfile (dan hapus ib_logfile lainnya yang mungkin dinamai ib_logfile0, ib_logfile1dll ...)
  5. sudo /etc/init.d/mysqld start
  6. create database [database_name];
  7. mysql -u [username]-p[root_password] [database_name] < dumpfilename.sql

Peringatan: instruksi ini akan menyebabkan Anda kehilangan database lain jika Anda memiliki database lain pada instance mysql ini. Pastikan langkah 1,2 dan 6,7 dimodifikasi untuk mencakup semua basis data yang ingin Anda simpan.

Vinay Vemula
sumber
6
Anda harus mengulangi 1,2, dan 6 untuk setiap database yang memiliki tabel InnoDB.
Marquis of Lorne
4
Anda memerlukan beberapa langkah lagi di antara # 5 dan # 6. Anda harus membuat ulang basis data dan menetapkan kembali izin. Jadi dari mysql client command prompt create database database_name;dan kemudiangrant all privileges on database_name.* to 'username'@'localhost' identified by 'password';
fred
1
@ Fred Saya tidak perlu memberikan hak istimewa saat melakukan ini. Mungkin karena saya membuat ulang database dengan nama yang sama?
crmpicco
2
Untuk mengetikkan kata sandi saat Password:diminta (yang merupakan praktik yang lebih aman), cukup masukkan -ptanpa kata sandi yang sebenarnya.
ADTC
Pemicu, acara, dan rutinitas / fungsi tidak dibuang kecuali Anda memberi tahu mysqldump untuk melakukannya. Tambahkan --triggers, --events dan --routine juga jika ada database Anda yang mengandungnya. Juga, cukup buang dengan --all-databases untuk membuang semua database sekaligus bukan satu per satu.
Friek
34

Ketika Anda menghapus tabel innodb, MySQL tidak mengosongkan ruang di dalam file ibdata, itu sebabnya ia terus bertambah. File-file ini hampir tidak pernah menyusut.

Cara mengecilkan file ibdata yang ada:

http://dev.mysql.com/doc/refman/5.5/en/innodb-resize-system-tablespace.html

Anda dapat membuat skrip ini dan menjadwalkan skrip untuk dijalankan setelah jangka waktu tertentu, tetapi untuk pengaturan yang dijelaskan di atas tampaknya beberapa tablespace adalah solusi yang lebih mudah.

Jika Anda menggunakan opsi konfigurasi innodb_file_per_table, Anda membuat beberapa tablespace. Artinya, MySQL membuat file terpisah untuk setiap tabel, bukan satu file bersama. File-file terpisah ini disimpan dalam direktori database, dan mereka dihapus ketika Anda menghapus database ini. Ini harus menghapus kebutuhan untuk mengecilkan / membersihkan file ibdata dalam kasus Anda.

Informasi lebih lanjut tentang beberapa tablespace:

http://dev.mysql.com/doc/refman/5.5/en/innodb-multiple-tablespaces.html

titanoboa
sumber
tautan pertama rusak, pertandingan terdekat yang bisa saya temukan: dev.mysql.com/doc/refman/5.5/en/…
BlackICE
14

Jika Anda menggunakan mesin penyimpanan InnoDB untuk (sebagian) tabel MySQL Anda, Anda mungkin sudah menemukan masalah dengan konfigurasi default-nya. Seperti yang mungkin Anda perhatikan di direktori data MySQL Anda (di Debian / Ubuntu - / var / lib / mysql) terdapat file bernama 'ibdata1 ′. Ini memegang hampir semua data InnoDB (itu bukan log transaksi) dari contoh MySQL dan bisa menjadi cukup besar. Secara default file ini memiliki ukuran awal 10Mb dan secara otomatis meluas. Sayangnya, dengan desain file data InnoDB tidak dapat menyusut. Itu sebabnya HAPUS, TRUNCATE, DROP, dll. Tidak akan mengklaim kembali ruang yang digunakan oleh file.

Saya pikir Anda dapat menemukan penjelasan dan solusi yang bagus di sana:

http://vdachev.net/2007/02/22/mysql-reducing-ibdata1/

Vik
sumber
11

Cepat menuliskan prosedur jawaban yang diterima di bash:

#!/usr/bin/env bash
DATABASES="$(mysql -e 'show databases \G' | grep "^Database" | grep -v '^Database: mysql$\|^Database: binlog$\|^Database: performance_schema\|^Database: information_schema' | sed 's/^Database: //g')"
mysqldump --databases $DATABASES -r alldatabases.sql && echo "$DATABASES" | while read -r DB; do
    mysql -e "drop database \`$DB\`"
done && \
    /etc/init.d/mysql stop && \
    find /var/lib/mysql -maxdepth 1 -type f \( -name 'ibdata1' -or -name 'ib_logfile*' \) -delete && \
    /etc/init.d/mysql start && \
    mysql < alldatabases.sql && \
    rm -f alldatabases.sql

Simpan sebagai purge_binlogs.shdan jalankan sebagai root.

Tidak termasuk mysql, information_schema, performance_schema(dan binlogdirektori).

Mengasumsikan Anda memiliki kredensial administrator /root/.my.cnfdan bahwa database Anda hidup di /var/lib/mysqldirektori default .

Anda juga dapat membersihkan log biner setelah menjalankan skrip ini untuk mendapatkan kembali lebih banyak ruang disk dengan:

PURGE BINARY LOGS BEFORE CURRENT_TIMESTAMP;
Pierre-Alexis de Solminihac
sumber
Masih tidak yakin mengapa, tapi hari ini beberapa tabel InnoDB saya rusak selama proses yang sama, jadi saya tidak akan menghapus alldatabases.sqlsebelum memeriksa ulang apakah semua tabel sehat. Adapun beberapa perbaikan: atur innodb_fast_shutdown=0sebelum shutdown, atur autocommit=0sebelum mengimpor file SQL, jalankan COMMITdan setel autocommit=1setelah mengimpor file SQL, gunakan mysqlcheck --all-databasessebelum menghapus cadangan.
Victor
6

Jika tujuan Anda adalah untuk memonitor ruang kosong MySQL dan Anda tidak dapat menghentikan MySQL untuk mengecilkan file ibdata Anda, dapatkan melalui perintah status tabel. Contoh:

MySQL> 5.1.24:

mysqlshow --status myInnodbDatabase myTable | awk '{print $20}'

MySQL <5.1.24:

mysqlshow --status myInnodbDatabase myTable | awk '{print $35}'

Kemudian bandingkan nilai ini dengan file ibdata Anda:

du -b ibdata1

Sumber: http://dev.mysql.com/doc/refman/5.1/id/show-table-status.html

Cyno
sumber
4

Dalam versi baru dari resep mysql-server di atas akan menghancurkan basis data "mysql". Dalam versi lama itu berfungsi. Dalam beberapa tabel baru beralih ke jenis tabel INNODB, dan dengan demikian Anda akan merusaknya. Cara termudah adalah:

  • buang semua database Anda
  • uninstall mysql-server,
  • tambahkan tetap my.cnf:
    [mysqld]
    innodb_file_per_table=1
  • hapus semua di / var / lib / mysql
  • instal mysql-server
  • mengembalikan pengguna dan basis data
adjustable_wrench
sumber
1

Seperti yang sudah disebutkan, Anda tidak dapat menyusutkan ibdata1 (untuk melakukannya Anda perlu membuang dan membangun kembali), tetapi sering kali juga tidak perlu.

Menggunakan autoextend (mungkin pengaturan ukuran paling umum) ibdata1 mengalokasikan penyimpanan, bertambah setiap kali hampir penuh. Itu membuat menulis lebih cepat karena ruang sudah dialokasikan.

Ketika Anda menghapus data itu tidak menyusut tetapi ruang di dalam file ditandai sebagai tidak terpakai. Sekarang ketika Anda memasukkan data baru itu akan menggunakan kembali ruang kosong dalam file sebelum menumbuhkan file lebih jauh.

Jadi itu hanya akan terus tumbuh jika Anda benar-benar membutuhkan data itu. Kecuali Anda benar-benar membutuhkan ruang untuk aplikasi lain, mungkin tidak ada alasan untuk mengecilkannya.

steveayre
sumber
66
Saya pikir Anda sedikit terlalu meremehkan kebutuhan untuk membebaskan ruang.
drewish
2
Saya memiliki partisi Solid State 60Gig. Saya kehabisan ruang cepat, karena saya bekerja dengan basis data 4 + pertunjukan. Saya ingin segera memindahkan mysql ke partisi lain, tetapi pertanyaan ini dan jawabannya akan membantu saya sementara waktu
NullVoxPopuli
3
Terima kasih atas jawaban ini, ini sangat membantu. Saya telah menghapus beberapa tabel dari data lawas ... ada baiknya mengetahui bahwa ukuran pada disk tidak akan tumbuh lagi dalam waktu dekat.
Brad
1
Saya memiliki file ibdata1 500G - tetapi hampir semua data yang disimpan di dalamnya sekarang disimpan dalam file per-database. Saya sangat perlu mengecilkan pemborosan ruang kolosal ini!
Frankrank
2
Omong kosong! File yang terus membengkak perlu dipangkas apakah Anda kehabisan ruang atau tidak . Saya akan menyebutnya a storage leak.
ADTC