ALTER TABLE trên một bảng lớn có cột được lập chỉ mục


14

Tôi có một bảng lớn với cột VARCHAR (20) và tôi cần sửa đổi nó để trở thành cột VARCHAR (50). Thông thường, thực hiện ALTER TABLE (thêm TINYINT) trên bảng cụ thể này mất khoảng 90-120 phút để hoàn thành, vì vậy tôi thực sự chỉ có thể làm điều đó vào tối thứ bảy hoặc chủ nhật để tránh ảnh hưởng đến người dùng cơ sở dữ liệu. Nếu có thể tôi muốn thực hiện sửa đổi này trước đó.

Cột cũng được lập chỉ mục, mà tôi đoán sẽ làm cho ALTER TABLE chậm hơn, bởi vì nó phải xây dựng lại chỉ mục sau khi sửa đổi độ dài cột.

Ứng dụng web được thiết lập trong môi trường sao chép MySQL (26 nô lệ và một chủ). Tôi nhớ lại một lần đọc ở đâu đó rằng một phương pháp trước tiên là thực hiện ALTER TABLE trên mỗi nô lệ (giảm thiểu tác động đến người dùng), sau đó thực hiện điều này trên Master, nhưng sau đó có cố gắng sao chép lệnh ALTER TABLE cho nô lệ không?

Vì vậy, câu hỏi của tôi là: cách tốt nhất để tôi sửa đổi bảng này với sự gián đoạn tối thiểu cho người dùng của tôi là gì?

Chỉnh sửa: bảng là InnoDB.


thêm cột tinyint có nghĩa là thực sự thêm một cột có giá trị mặc định? Bởi vì làm điều đó trên một cái bàn lớn có thể mất nhiều thời gian ..
Marian

Câu trả lời:


13

Nếu bạn là một người thích phiêu lưu, bạn có thể đưa vấn đề vào tay bằng cách thực hiện BẢNG THAY ĐỔI trong các giai đoạn bạn có thể thấy. Giả sử bảng bạn muốn thay đổi được gọi là WorkTable. Bạn có thể thực hiện các thay đổi trong các giai đoạn như thế này:

#
#  Script 1
#  Alter table structure of a single column of a large table
#
CREATE TABLE WorkingTableNew LIKE WorkingTable;
ALTER TABLE WorkingTableNew MODIFY BigColumn VARCHAR(50);
INSERT INTO WorkingTableNew SELECT * FROM WorkingTable;
ALTER TABLE WorkingTable RENAME WorkingTableOld;
ALTER TABLE WorkingTableNew RENAME WorkingTable;
DROP TABLE WorkingTableOld;

Bạn có thể thực hiện điều này trên tất cả các nô lệ. Còn chủ thì sao ??? Làm thế nào để bạn ngăn chặn điều này sao chép vào nô lệ. Đơn giản: Đừng gửi SQL vào nhật ký nhị phân của chủ. Đơn giản chỉ cần tắt ghi nhật ký nhị phân trong phiên trước khi thực hiện công cụ ALTER TABLE:

#
#  Script 2
#  Alter table structure of a single column of a large table
#  while preventing it from replicating to slaves
#
SET SQL_LOG_BIN = 0;
CREATE TABLE WorkingTableNew LIKE WorkingTable;
ALTER TABLE WorkingTableNew MODIFY BigColumn VARCHAR(50);
INSERT INTO WorkingTableNew SELECT SQL_NO_CACHE * FROM WorkingTable;
ALTER TABLE WorkingTable RENAME WorkingTableOld;
ALTER TABLE WorkingTableNew RENAME WorkingTable;
DROP TABLE WorkingTableOld;

Nhưng chờ đã !!! Điều gì về bất kỳ dữ liệu mới nào xuất hiện trong khi xử lý các lệnh này ??? Đổi tên bảng khi bắt đầu hoạt động nên thực hiện thủ thuật. Hãy thay đổi mã này một chút để ngăn nhập dữ liệu mới theo khía cạnh đó:

#
#  Script 3
#  Alter table structure of a single column of a large table
#  while preventing it from replicating to slaves
#  and preventing new data from entering into the old table
#
SET SQL_LOG_BIN = 0;
ALTER TABLE WorkingTable RENAME WorkingTableOld;
CREATE TABLE WorkingTableNew LIKE WorkingTableOld;
ALTER TABLE WorkingTableNew MODIFY BigColumn VARCHAR(50);
INSERT INTO WorkingTableNew SELECT SQL_NO_CACHE * FROM WorkingTableOld;
ALTER TABLE WorkingTableNew RENAME WorkingTable;
DROP TABLE WorkingTableOld;
  • Tập lệnh 1 có thể được thực thi trên bất kỳ nô lệ nào không kích hoạt nhật ký nhị phân
  • Tập lệnh 2 có thể được thực thi trên bất kỳ nô lệ nào có bật nhật ký nhị phân
  • Kịch bản 3 có thể được thực thi trên bản gốc hoặc bất kỳ nơi nào khác

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


2
Một vấn đề tôi thấy là nếu bảng chứa trường 'auto_increment'. Tôi đã thực hiện một thử nghiệm cơ bản và rất ngạc nhiên khi thấy rằng TẠO BẢNG .. THÍCH không sao chép giá trị auto_increment vào bảng mới
Derek Downey

1
@DTest: Bắt tốt và bình luận tuyệt vời !!!. Tôi tin rằng bạn có thể truy xuất giá trị auto_increment từ cột information_schema.tables AUTO_INCREMENT. Nếu bảng không có trường AUTO_INCREMENT, thì cột AUTO_INCREMENT trong information_schema.tables sẽ là NULL. Nếu không, nó sẽ chứa giá trị AUTO_INCREMENT cần thiết. Tôi đoán nó có thể được viết kịch bản để trích xuất giá trị không phải NULL đó và thực hiện ALTER TABLE Workset AUTO_INCREMENT = <somenumber>; ngay trước khi đổi tên bảng tạm thời trở lại Workset.
RolandoMySQLDBA

@RolandoMySQLDBA Một điều khác có thể được thực hiện là bạn tạo WorkTableNew bằng cách sao chép các câu lệnh "hiển thị bảng WorkTable" và thay đổi giá trị của trường auto_increment bằng cách thêm nó bằng một số an toàn cho bạn và cũng thay đổi cột thành varchar (50 ) và sau đó thực hiện cả hai đổi tên trong một câu lệnh "đổi tên bảng WorkTable thành WorkTableOld, đổi tên bảng WorkTableNew thành WorkTable" Chạy cả hai lần đổi tên trong một lệnh duy nhất để đảm bảo không có thao tác chèn nào (thử nghiệm đây là Prod cho bảng có 1000 giây chèn / s) bạn có thể thực hiện lệnh "chèn vào ... từ"
Gautam Somani

4

Tôi đoán từ tài liệu sẽ chỉ là việc tăng ràng buộc độ dài trên một varcharsẽ không gây ra rắc rối tương tự như thêm một cột:

Đối với một số thao tác, ALTER TABLE tại chỗ có thể không yêu cầu bảng tạm thời:

Nhưng điều đó dường như bị mâu thuẫn trong các bình luận về câu hỏi SO này .

BIÊN TẬP

Ít nhất là vào ngày 5.0, tôi nghĩ rằng tôi có thể xác nhận rằng việc tăng chiều dài thực sự cần một bảng tạm thời (hoặc một số hoạt động đắt tiền không kém khác):

thử nghiệm:

create table my_table (id int auto_increment primary key, varchar_val varchar(10));
insert into my_table (varchar_val)
select 'HELLO'
from (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s1,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s2,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s3,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s4,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s5,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s6;

kết quả:

alter table my_table modify varchar_val varchar(20);
Query OK, 1000000 rows affected (2.91 sec)

alter table my_table add int_val int;
Query OK, 1000000 rows affected (2.86 sec)

Sửa đổi kích thước trường varchar liên quan đến việc kiểm tra xem bạn không vượt quá kích thước mới. Để tăng kích thước, kiểm tra này có thể được tối ưu hóa đi.
BillThor

3

Tôi nghĩ, tôi đã đề cập đến điều đó kể từ khi ENGINE=INNODB

Nếu bạn có các ràng buộc khóa ngoài, bạn không thể thay đổi và đổi tên với các ràng buộc của bạn trỏ đến bảng cũ (bây giờ được đổi tên). Bạn sẽ phải thay đổi sau đó hoặc bỏ các ràng buộc trong thời gian.


Shabang !!! Điều đó thật đúng. Trong trường hợp đó, người ta chỉ có thể bị mắc kẹt khi thực hiện bảng thay đổi trên bảng gốc. Ít nhất, bạn có thể phải chơi các trò chơi với việc vô hiệu hóa các ràng buộc, trước khi ALTER TABLE diễn ra. +1 cho bắt rất tốt !!!
RolandoMySQLDBA
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.