Cách thu nhỏ / lọc tệp ibdata1 trong MySQL


561

Tôi đang sử dụng MySQL trong localhost làm "công cụ truy vấn" để thực hiện thống kê trong R, nghĩa là mỗi khi tôi chạy tập lệnh R, tôi tạo cơ sở dữ liệu mới (A), tạo bảng mới (B), nhập dữ liệu vào B , gửi truy vấn để nhận những gì tôi cần, sau đó tôi thả B và thả A.

Nó hoạt động tốt với tôi, nhưng tôi nhận ra rằng kích thước tệp ibdata đang tăng nhanh, tôi không lưu trữ gì trong MySQL, nhưng tệp ibdata1 đã vượt quá 100 MB.

Tôi đang sử dụng cài đặt MySQL mặc định ít nhiều cho thiết lập, có cách nào để tôi có thể tự động thu nhỏ / lọc tệp ibdata1 sau một khoảng thời gian cố định không?


Câu trả lời:


778

Cái đó ibdata1 không thu hẹp là một tính năng đặc biệt khó chịu của MySQL. Các ibdata1tập tin có thể không thực sự được thu nhỏ trừ khi bạn xóa tất cả các cơ sở dữ liệu, loại bỏ các tập tin và tải lại một bãi chứa.

Nhưng bạn có thể cấu hình MySQL để mỗi bảng, bao gồm các chỉ mục của nó, được lưu trữ dưới dạng một tệp riêng biệt. Theo cách đóibdata1 sẽ không phát triển lớn. Theo nhận xét của Bill Karwin, điều này được bật theo mặc định kể từ phiên bản 5.6.6 của MySQL.

Đó là một thời gian trước đây tôi đã làm điều này. Tuy nhiên, để thiết lập máy chủ của bạn sử dụng các tệp riêng biệt cho mỗi bảng, bạn cần thay đổi my.cnfđể kích hoạt tính năng này:

[mysqld]
innodb_file_per_table=1

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

Khi bạn muốn lấy lại không gian từ ibdata1 bạn thực sự phải xóa tệp:

  1. Thực hiện một mysqldumptrong tất cả các cơ sở dữ liệu, quy trình, trình kích hoạt, ngoại trừ mysqlperformance_schema cơ sở dữ liệu
  2. Bỏ tất cả cơ sở dữ liệu trừ 2 cơ sở dữ liệu trên
  3. Dừng mysql
  4. Xóa ibdata1ib_log tập tin
  5. Bắt đầu mysql
  6. Khôi phục từ bãi chứa

Khi bạn khởi động MySQL ở bước 5, ibdata1ib_logcác tệp sẽ được tạo lại.

Bây giờ bạn phù hợp để đi. Khi bạn tạo một cơ sở dữ liệu mới để phân tích, các bảng sẽ được đặt trong ibd*các tệp riêng biệt chứ không phải trong ibdata1. Vì bạn thường bỏ cơ sở dữ liệu ngay sau đó, các ibd*tệp sẽ bị xóa.

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

Bạn có thể đã thấy điều này:
http://bugs.mysql.com/orms.php?id=1341

Bằng cách sử dụng lệnh ALTER TABLE <tablename> ENGINE=innodbhoặc OPTIMIZE TABLE <tablename>người ta có thể trích xuất dữ liệu và các trang chỉ mục từ ibdata1 để tách các tệp. Tuy nhiên, ibdata1 sẽ không thu nhỏ trừ khi bạn thực hiện các bước trên.

Về information_schemađiều đó, không cần thiết cũng không thể bỏ. Thực tế nó chỉ là một loạt các khung nhìn chỉ đọc, không phải các bảng. Và không có tệp nào liên quan đến chúng, thậm chí không có thư mục cơ sở dữ liệu. Việc informations_schemasử dụng bộ nhớ db-engine và được loại bỏ và được tạo lại khi dừng / khởi động lại mysqld. Xem https://dev.mysql.com/doc/refman/5.7/en/inatures-schema.html .


16
@JordanMagnuson Đừng bận tâm bỏ thông tin_schema. Thực tế nó chỉ là một loạt các khung nhìn chỉ đọc, không phải các bảng. Và không có tập tin liên quan đến chúng. Thậm chí không có một thư mục cho cơ sở dữ liệu. Thông tin_schema đang sử dụng bộ nhớ db-engine và được loại bỏ và được tạo lại khi dừng / khởi động lại mysqld. Xem dev.mysql.com/doc/refman/5.5/en/inatures-schema.html . Về Performance_schema Tôi đã không sử dụng lược đồ đó cho mình.
John P

4
Tôi không biết đây có phải là một điều gần đây không nhưng khi tùy chọn innodb_file_per_table được bật, bạn có thể chỉ cần chạy "ALTER TABLE <tablename> Engine = InnoDB" (ngay cả khi đó đã là InnoDB) và nó sẽ di chuyển bảng vào tệp riêng lẻ của nó . Không cần phải bỏ cơ sở dữ liệu và như vậy.
CR.

3
+1 FWIW, MySQL 5.6 cho phép innodb_file_per_tabletheo mặc định.
Bill Karwin

3
Có, ibdata1 dự kiến ​​sẽ có mặt cùng với các tệp khác. Tệp ibdata1 vẫn sẽ giữ siêu dữ liệu về các bảng, nhật ký hoàn tác và bộ đệm.
John P

1
Tôi đã hết dung lượng trong máy chủ của mình vì tệp ibdata1, vì vậy tôi thậm chí không thể kết xuất cơ sở dữ liệu. Sẽ giống như vậy khi chỉ di chuyển các tệp tại / var / lib / mysql (ngoại trừ "mysql", "ibdata1", "ib_logfile0" và "ib_logfile1") và sau đó làm theo các bước? Xem stackoverflow.com/questions/2482491/ từ
Sophivorus 17/03 '

47

Thêm vào câu trả lời của John P ,

Đối với hệ thống linux, các bước 1-6 có thể được thực hiện bằng các lệnh sau:

  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 (và xóa mọi ib_logfile khác có thể được đặt tên ib_logfile0, ib_logfile1v.v ...)
  5. sudo /etc/init.d/mysqld start
  6. create database [database_name];
  7. mysql -u [username]-p[root_password] [database_name] < dumpfilename.sql

Cảnh báo: các hướng dẫn này sẽ khiến bạn mất các cơ sở dữ liệu khác nếu bạn có các cơ sở dữ liệu khác trên phiên bản mysql này. Đảm bảo rằng các bước 1,2 và 6,7 được sửa đổi để bao gồm tất cả các cơ sở dữ liệu bạn muốn giữ.


6
Bạn cần lặp lại 1,2 và 6 cho mọi cơ sở dữ liệu có bảng InnoDB.
Hầu tước Lorne

4
Bạn cần thêm một vài bước ở giữa # 5 và # 6. Bạn phải tạo lại cơ sở dữ liệu và gán lại quyền. Vì vậy, từ lệnh mysql khách hàng nhanh chóng create database database_name;và sau đógrant all privileges on database_name.* to 'username'@'localhost' identified by 'password';
fred

1
@fred Tôi không cần cấp đặc quyền khi làm việc này. Có thể bởi vì tôi đã tạo lại cơ sở dữ liệu có cùng tên?
crmpicco

2
Để nhập mật khẩu tại một Password:dấu nhắc (đây là cách thực hành an toàn hơn), chỉ cần đặt -pmà không có bất kỳ mật khẩu thực tế nào.
ADTC

Kích hoạt, sự kiện và thói quen / chức năng không bị đổ trừ khi bạn yêu cầu mysqldump làm như vậy. Thêm --triggers, --events và --routines nếu có bất kỳ cơ sở dữ liệu nào của bạn chứa chúng. Ngoài ra, chỉ cần kết xuất với --all-cơ sở dữ liệu để kết xuất tất cả các cơ sở dữ liệu cùng một lúc thay vì từng cái một.
Friek

34

Khi bạn xóa các bảng innodb, MySQL không giải phóng không gian bên trong tệp ibdata, đó là lý do tại sao nó tiếp tục phát triển. Những tập tin này hầu như không bao giờ thu nhỏ.

Cách thu nhỏ tệp ibdata hiện có:

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

Bạn có thể viết kịch bản này và lên lịch cho tập lệnh chạy sau một khoảng thời gian cố định, nhưng đối với thiết lập được mô tả ở trên, có vẻ như nhiều không gian bảng là một giải pháp dễ dàng hơn.

Nếu bạn sử dụng tùy chọn cấu hình innodb_file_per_table, bạn tạo nhiều không gian bảng. Đó là, MySQL tạo các tệp riêng biệt cho mỗi bảng thay vì một tệp được chia sẻ. Những tệp riêng biệt này được lưu trữ trong thư mục của cơ sở dữ liệu và chúng sẽ bị xóa khi bạn xóa cơ sở dữ liệu này. Điều này sẽ loại bỏ sự cần thiết phải thu nhỏ / thanh lọc các tệp ibdata trong trường hợp của bạn.

Thông tin thêm về nhiều không gian bảng:

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


Liên kết đầu tiên bị hỏng, trận đấu gần nhất tôi có thể tìm thấy: dev.mysql.com/doc/refman/5.5/en/iêu
BlackICE

14

Nếu bạn sử dụng công cụ lưu trữ InnoDB cho (một số) bảng MySQL của mình, có lẽ bạn đã gặp phải sự cố với cấu hình mặc định của nó. Như bạn có thể nhận thấy trong thư mục dữ liệu của MySQL (trong Debian / Ubuntu - / var / lib / mysql) nằm trong một tệp có tên 'ibdata1. Nó chứa gần như tất cả dữ liệu InnoDB (nó không phải là nhật ký giao dịch) của phiên bản MySQL và có thể trở nên khá lớn. Theo mặc định, tệp này có kích thước ban đầu là 10Mb và nó tự động mở rộng. Thật không may, theo thiết kế, tệp dữ liệu InnoDB không thể được thu nhỏ. Đó là lý do tại sao XÓA, TRUNCATE, DROP, v.v. sẽ không lấy lại không gian được sử dụng bởi tệp.

Tôi nghĩ bạn có thể tìm thấy lời giải thích và giải pháp tốt ở đó:

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


11

Nhanh chóng viết kịch bản thủ tục câu trả lời được chấp nhận trong 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

Lưu dưới dạng purge_binlogs.shvà chạy nhưroot .

Không bao gồm mysql, information_schema, performance_schema(vàbinlog thư mục).

Giả sử bạn có tín dụng quản trị viên /root/.my.cnfvà cơ sở dữ liệu của bạn mặc định/var/lib/mysql thư mục .

Bạn cũng có thể lọc các bản ghi nhị phân sau khi chạy tập lệnh này để lấy lại nhiều dung lượng đĩa hơn với:

PURGE BINARY LOGS BEFORE CURRENT_TIMESTAMP;

Vẫn không chắc chắn tại sao, nhưng hôm nay một số bảng InnoDB của tôi đã bị hỏng trong quá trình tương tự, vì vậy tôi sẽ không xóa alldatabases.sqltrước khi kiểm tra lại nếu tất cả các bảng đều khỏe. Đối với một số cải tiến: được đặt innodb_fast_shutdown=0trước khi tắt máy, hãy đặt autocommit=0trước khi nhập tệp SQL, thực thi COMMITvà đặt autocommit=1sau khi nhập tệp SQL, sử dụng mysqlcheck --all-databasestrước khi xóa bản sao lưu.
Victor

6

Nếu mục tiêu của bạn là giám sát không gian trống của MySQL và bạn không thể ngăn MySQL thu nhỏ tệp ibdata của mình, thì hãy lấy nó thông qua các lệnh trạng thái bảng. Thí dụ:

MySQL> 5.1.24:

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

MySQL <5.1.24:

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

Sau đó so sánh giá trị này với tệp ibdata của bạn:

du -b ibdata1

Nguồn: http://dev.mysql.com/doc/refman/5.1/en/show-table-status.html


4

Trong một phiên bản mới của công thức nấu ăn máy chủ mysql ở trên sẽ nghiền nát cơ sở dữ liệu "mysql". Trong phiên bản cũ, nó hoạt động. Trong một số bảng mới, chuyển sang loại bảng INNODB và bằng cách đó, bạn sẽ làm hỏng chúng. Cách dễ nhất là:

  • kết xuất tất cả các cơ sở dữ liệu của bạn
  • gỡ cài đặt máy chủ mysql,
  • thêm vào vẫn là my.cnf:
    [mysqld]
    innodb_file_per_table=1
  • xóa tất cả trong / var / lib / mysql
  • cài đặt máy chủ mysql
  • khôi phục người dùng và cơ sở dữ liệu

1

Như đã lưu ý, bạn không thể thu nhỏ ibdata1 (để làm như vậy bạn cần phải đổ và xây dựng lại), nhưng thường cũng không có nhu cầu thực sự.

Sử dụng autoextend (có thể là cài đặt kích thước phổ biến nhất) ibdata1 lưu trữ lưu trữ, phát triển mỗi khi nó gần đầy. Điều đó làm cho ghi nhanh hơn khi không gian đã được phân bổ.

Khi bạn xóa dữ liệu, nó không co lại nhưng không gian bên trong tệp được đánh dấu là không sử dụng. Bây giờ khi bạn chèn dữ liệu mới, nó sẽ sử dụng lại không gian trống trong tệp trước khi phát triển tệp thêm nữa.

Vì vậy, nó sẽ chỉ tiếp tục phát triển nếu bạn thực sự cần dữ liệu đó. Trừ khi bạn thực sự cần dung lượng cho ứng dụng khác, có lẽ không có lý do gì để thu nhỏ nó.


66
Tôi nghĩ bạn hơi quá coi thường nhu cầu giải phóng không gian.
thu hút

2
Tôi có một phân vùng trạng thái rắn 60Gig. Tôi hết dung lượng nhanh, vì tôi làm việc với cơ sở dữ liệu 4 + gig. Tôi đang tìm cách sớm chuyển mysql sang một phân vùng khác, nhưng câu hỏi này và câu trả lời sẽ giúp tôi trong lúc này
NullVoxPopuli

3
Cảm ơn bạn vì câu trả lời này, nó rất hữu ích. Tôi đã xóa một số bảng khỏi dữ liệu cũ ... thật tốt khi biết rằng kích thước trên đĩa sẽ không tăng trở lại bất cứ lúc nào.
Brad

1
Tôi có tệp ibdata1 500G - nhưng hầu như tất cả dữ liệu được lưu trữ trong đó hiện được lưu trữ trong các tệp trên cơ sở dữ liệu. Tôi rất cần thu nhỏ chất thải không gian khổng lồ này!
frankster

2
Hoàn toàn vô nghĩa! Một tập tin tiếp tục đầy hơi cần phải được cắt bớt cho dù bạn có hết dung lượng hay không . Tôi sẽ gọi nó là a storage leak.
ADTC
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.