MySQL - cách nhanh nhất để THAY ĐỔI BẢNG cho InnoDB


12

Tôi có một bảng InnoDB mà tôi muốn thay đổi. Bảng có ~ 80M hàng và thoát một vài chỉ số.

Tôi muốn thay đổi tên của một trong các cột và thêm một vài chỉ số.

  • Cách nhanh nhất để làm điều đó là gì (giả sử tôi có thể chịu đựng cả thời gian chết - máy chủ là một nô lệ không được sử dụng)?
  • Là một "đơn giản" alter table, giải pháp nhanh nhất?

Tại thời điểm này, tất cả những gì tôi quan tâm là tốc độ :)


Xin vui lòng SHOW CREATE TABLE tblname\G, hiển thị cột cần thay đổi, kiểu dữ liệu của cột và tên mới cho cột.
RolandoMySQLDBA

đây là: pastie.org/3078349 cột cần đổi tên là sent_atvà thêm một vài chỉ số nữa
Ran

sent_at cần được đổi tên thành gì?
RolandoMySQLDBA

giả sử: new_sent_at
Ran

Câu trả lời:


14

Một cách chắc chắn để tăng tốc ALTER TABLE là xóa các chỉ mục không cần thiết

Dưới đây là các bước ban đầu để tải phiên bản mới của bảng

CREATE TABLE s_relations_new LIKE s_relations;
#
# Drop Duplicate Indexes
#
ALTER TABLE s_relations_new
    DROP INDEX source_persona_index,
    DROP INDEX target_persona_index,
    DROP INDEX target_persona_relation_type_index
;

Xin lưu ý những điều sau:

  • Tôi đã bỏ source_persona_index vì đây là cột đầu tiên trong 4 chỉ mục khác

    • unique_target_persona
    • unique_target_object
    • nguồn_and_target_object_index
    • nguồn_target_persona_index
  • Tôi đã bỏ target_persona_index vì đây là cột đầu tiên trong 2 chỉ mục khác

    • đích_persona_relation_type_index
    • đích_persona_relation_type_message_id_index
  • Tôi đã bỏ đích_persona_relation_type_index vì 2 cột đầu tiên cũng nằm trong target_persona_relation_type_message_id_index

OK Điều đó quan tâm đến các chỉ số không cần thiết. Có chỉ số nào có cardinality thấp? Đây là cách để xác định rằng:

Chạy các truy vấn sau:

SELECT COUNT(DISTINCT sent_at)               FROM s_relations;
SELECT COUNT(DISTINCT message_id)            FROM s_relations;
SELECT COUNT(DISTINCT target_object_id)      FROM s_relations;

Theo câu hỏi của bạn, có khoảng 80.000.000 hàng. Theo nguyên tắc thông thường, Trình tối ưu hóa truy vấn MySQL sẽ không sử dụng một chỉ mục nếu tính chính xác của các cột được chọn lớn hơn 5% tổng số hàng của bảng. Trong trường hợp này, đó sẽ là 4.000.000.

  • Nếu COUNT(DISTINCT sent_at)> 4.000.000
    • sau đó ALTER TABLE s_relations_new DROP INDEX sent_at_index;
  • Nếu COUNT(DISTINCT message_id)> 4.000.000
    • sau đó ALTER TABLE s_relations_new DROP INDEX message_id_index;
  • Nếu COUNT(DISTINCT target_object_id)> 4.000.000
    • sau đó ALTER TABLE s_relations_new DROP INDEX target_object_index;

Khi tính hữu dụng hoặc vô dụng của các chỉ mục đó đã được xác định, bạn có thể tải lại dữ liệu

#
# Change the Column Name
# Load the Table
#
ALTER TABLE s_relations_new CHANGE sent_at sent_at_new int(11) DEFAULT NULL;
INSERT INTO s_relations_new SELECT * FROM s_relations;

Đó là nó, phải không? ĐẠO !!!

Nếu trang web của bạn đã hoạt động suốt thời gian này, có thể có INSERT chạy chống lại s_relations trong quá trình tải s_relations_new. Làm thế nào bạn có thể lấy lại những hàng còn thiếu?

Đi tìm id tối đa trong s_relations_new và nối thêm mọi thứ sau ID đó từ s_relations. Để đảm bảo rằng bảng được đóng băng và chỉ được sử dụng cho bản cập nhật này, bạn phải có một chút thời gian chết vì mục đích nhận các hàng cuối cùng được chèn vào s_relation_new. Dưới đây là những gì bạn làm:

Trong HĐH, khởi động lại mysql để không ai khác có thể đăng nhập nhưng root @ localhost (vô hiệu hóa TCP / IP):

$ service mysql restart --skip-networking

Tiếp theo, đăng nhập vào mysql và tải các hàng cuối cùng:

mysql> SELECT MAX(id) INTO @maxidnew FROM s_relations_new;
mysql> INSERT INTO s_relations_new SELECT * FROM s_relations WHERE id > @maxidnew;
mysql> ALTER TABLE s_relations RENAME s_relations_old;
mysql> ALTER TABLE s_relations_new RENAME s_relations;

Sau đó, khởi động lại mysql bình thường

$ service mysql restart

Bây giờ, nếu bạn không thể gỡ bỏ mysql, bạn sẽ phải thực hiện chuyển đổi mồi trên s_relations. Chỉ cần đăng nhập vào mysql và làm như sau:

mysql> ALTER TABLE s_relations RENAME s_relations_old;
mysql> SELECT MAX(id) INTO @maxidnew FROM s_relations_new;
mysql> INSERT INTO s_relations_new SELECT * FROM s_relations_old WHERE id > @maxidnew;
mysql> ALTER TABLE s_relations_new RENAME s_relations;

Hãy thử một lần !!!

CAVEAT: Một khi bạn hài lòng với thao tác này, bạn có thể bỏ bảng cũ một cách thuận tiện sớm nhất:

mysql> DROP TABLE s_relations_old;

12

Câu trả lời đúng phụ thuộc vào phiên bản của công cụ MySQL mà bạn đang sử dụng.

Nếu sử dụng 5.6+, việc đổi tên và thêm / xóa chỉ mục được thực hiện trực tuyến , tức là không sao chép tất cả dữ liệu của bảng.

Chỉ cần sử dụng ALTER TABLEnhư bình thường, nó sẽ chủ yếu là ngay lập tức để đổi tên và giảm chỉ số, và nhanh chóng hợp lý để thêm chỉ mục (nhanh như đọc tất cả các bảng một lần).

Nếu sử dụng 5.1+ và plugin InnoDB được bật, việc thêm / xóa các chỉ mục cũng sẽ trực tuyến. Không chắc chắn về việc đổi tên.

Nếu sử dụng phiên bản cũ hơn, ALTER TABLEvẫn là phiên bản nhanh nhất nhưng có thể sẽ chậm khủng khiếp vì tất cả dữ liệu của bạn sẽ được chèn lại vào một bảng tạm thời dưới mui xe.

Cuối cùng, thời gian để gỡ lỗi huyền thoại. Thật không may, tôi không có đủ nghiệp lực ở đây để bình luận về câu trả lời, nhưng tôi cảm thấy điều quan trọng là phải sửa câu trả lời được bình chọn nhiều nhất. Điều này là sai :

Theo nguyên tắc thông thường, Trình tối ưu hóa truy vấn MySQL sẽ không sử dụng chỉ mục nếu tính chính xác của các cột được chọn lớn hơn 5% tổng số hàng của bảng

Đó thực sự là cách khác .

Các chỉ số rất hữu ích để chọn một vài hàng, vì vậy điều quan trọng là chúng có số lượng thẻ cao , có nghĩa là nhiều giá trị riêng biệt và một vài hàng thống kê có cùng giá trị.


Liên kết đến tài liệu plugin InnoDB (không thể dán do giới hạn đại diện).
mezis

2
Trên MySQL 5.5 tôi đã tìm thấy RENAME TABLEngay lập tức (như mong đợi) nhưng CHANGE COLUMNđể đổi tên khóa chính đã sao chép đầy đủ ... 7 giờ! Có thể chỉ vì nó là khóa chính? Không tốt.
KCD

2

Tôi gặp vấn đề tương tự với Maria DB 10.1.12, sau đó sau khi đọc tài liệu tôi thấy rằng có một tùy chọn để thực hiện thao tác "tại chỗ" để loại bỏ bản sao của bảng. Với tùy chọn này, bảng thay đổi rất nhanh. Trong trường hợp của tôi, đó là:

alter table user add column (resettoken varchar(256),
  resettoken_date date, resettoken_count int), algorithm=inplace;

cái này rất nhanh Nếu không có tùy chọn thuật toán, nó sẽ không bao giờ chấm dứt.

https://mariadb.com/kb/en/mariadb/alter-table/


0

Đối với đổi tên cột,

ALTER TABLE tablename CHANGE columnname newcolumnname datatype;

nên ổn và không mang theo bất kỳ thời gian chết.

Đối với các chỉ mục, câu lệnh CREATE INDEX sẽ khóa bảng. Nếu đó là một nô lệ không được sử dụng như bạn đã đề cập, đó không phải là vấn đề.

Một lựa chọn khác là tạo một bảng hoàn toàn mới có tên và chỉ mục cột thích hợp. Sau đó, bạn có thể sao chép tất cả dữ liệu vào đó, sau đó thực hiện một loạt

BEGIN TRAN;
ALTER TABLE RENAME tablename tablenameold;
ALTER TABLE RENAME newtablename tablename;
DROP TABLE tablenameold;
COMMIT TRAN;

Điều này sẽ giảm thiểu thời gian chết với chi phí sử dụng tạm thời gấp đôi không gian.


1
DDL trong MySQL không phải là giao dịch. Mỗi câu lệnh DDL kích hoạt một CAM KẾT. Tôi đã viết về điều này: dba.stackexchange.com/a/36799/877
RolandoMyQueryDBA

0

Tôi cũng gặp vấn đề này và tôi đã sử dụng SQL này:

/*on créé la table COPY SANS les nouveaux champs et SANS les FKs */
CREATE TABLE IF NOT EXISTS prestations_copy LIKE prestations;

/* on supprime les FKs de la table actuelle */
ALTER TABLE `prestations`
DROP FOREIGN KEY `fk_prestations_pres_promos`,
DROP FOREIGN KEY `fk_prestations_activites`;

/* on remet les FKs sur la table copy */
ALTER TABLE prestations_copy 
    ADD CONSTRAINT `fk_prestations_activites` FOREIGN KEY (`act_id`) REFERENCES `activites` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION,
    ADD CONSTRAINT `fk_prestations_pres_promos` FOREIGN KEY (`presp_id`) REFERENCES `pres_promos` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION;

/* On fait le transfert des données de la table actuelle vers la copy, ATTENTION: il faut le même nombre de colonnes */
INSERT INTO prestations_copy
SELECT * FROM prestations;

/* On modifie notre table copy de la façon que l'on souhaite */
ALTER TABLE `prestations_copy`
    ADD COLUMN `seo_mot_clef` VARCHAR(50) NULL;

/* on supprime la table actuelle et renome la copy avec le bon nom de table */
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE prestations;
RENAME TABLE prestations_copy TO prestations;
SET FOREIGN_KEY_CHECKS=1;   

Tôi hy vọng nó có thể giúp ai đó

Trân trọng,

Sẽ

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.