Cách thêm cột vào bảng lớn trong MySQL


12

Tôi là một nhà phát triển PHP vì vậy đừng nghiêm khắc. Tôi có một bảng lớn ~ bãi chứa 5,5gb. Thủ tướng của chúng tôi đã quyết định tạo cột mới trong đó để thực hiện tính năng mới. Bảng là InnoDB vì vậy những gì tôi đã thử:

  1. Thay đổi bảng trong màn hình với khóa bảng. Mất ~ 30 giờ và không có gì. Vì vậy, tôi chỉ dừng lại nó. Đầu tiên tôi đã phạm sai lầm vì tôi đã không kết thúc tất cả các giao dịch nhưng lần thứ hai không phải là đa cấp. Tình trạng đã copy to tmp table.

  2. Vì tôi cũng cần áp dụng phân vùng cho bảng này, chúng tôi quyết định tạo kết xuất, đổi tên và tạo bảng có cùng tên và cấu trúc mới. Nhưng dump đang tạo ra một bản sao nghiêm ngặt (ít nhất là tôi đã không tìm thấy thứ gì khác). Vì vậy, tôi đã thêm để kết xuất một cột mới với sedvà truy vấn nó. Nhưng một số lỗi lạ bắt đầu. Tôi tin rằng nó được gây ra bởi bộ ký tự. Bảng trong utf-8 và tệp đã trở thành us-ascii sau đó sed. Vì vậy, tôi đã gặp lỗi (lệnh không xác định '\' ') trên 30% dữ liệu. Vì vậy, đây cũng là một cách xấu.

Các tùy chọn khác để thực hiện điều này và tăng tốc hiệu suất (tôi có thể thực hiện với tập lệnh php, nhưng sẽ mất nhiều thời gian). Điều gì sẽ được thực hiện INSERT SELECTtrong trường hợp này.

Cảm ơn cho bất kỳ sự tiến bộ.

Câu trả lời:


11

Sử dụng bàn làm việc của MySQL . Bạn có thể nhấp chuột phải vào bảng và chọn "Gửi tới SQL Editor" -> "Tạo câu lệnh". Bằng cách này, không có "thuộc tính" bảng nào bị quên để thêm (bao gồm CHARSEThoặc COLLATE).
Với lượng dữ liệu khổng lồ này, tôi khuyên bạn nên dọn sạch bảng hoặc cấu trúc dữ liệu bạn sử dụng (một DBA tốt có ích). Nếu không thể:

  • đổi tên bảng ( ALTER) và tạo một bảng mới với CREATEtập lệnh bạn nhận được từ Workbench. Bạn cũng có thể mở rộng truy vấn đó với trường mới mà bạn cần
  • BULK LOAD dữ liệu từ bảng cũ sang bảng mới:
    SET FOREIGN_KEY_CHECKS = 0;
    SET UNIQUE_CHECKS = 0;
    SET AUTOCOMMIT = 0;
    INSERT INTO new_table (fieldA, fieldB, fieldC, ..., fieldN)
       SELECT fieldA, fieldB, fieldC, ..., fieldN
       FROM old_table
    SET UNIQUE_CHECKS = 1;
    SET FOREIGN_KEY_CHECKS = 1;
    COMMIT;

    Bằng cách này, bạn tránh lập chỉ mục / etc để chạy bản ghi bằng bản ghi. Việc "cập nhật" lên bảng vẫn sẽ chậm (vì lượng dữ liệu rất lớn) nhưng đây là cách nhanh nhất tôi có thể nghĩ ra.

    EDIT: đọc này bài viết để có được thông tin chi tiết về các lệnh được sử dụng trong truy vấn mẫu trên đây;)

Lựa chọn của tôi là tốt Và tôi đã nhận SET NAMES utf8COLLATION. Nhưng meh idk tại sao 30% dữ liệu bị hỏng sau đó sed. Tôi nghĩ rằng tải số lượng lớn sẽ là nhanh nhất nhưng có lẽ một cái gì đó tồn tại nhiều hơn mà tôi đang thiếu. Cảm ơn Mark
inragea

1
@ineersa tham nhũng dữ liệu có thể có nhiều lý do: ví dụ: bạn đã mở tệp bằng trình chỉnh sửa không hỗ trợ tất cả các ký tự và lưu nó. Hoặc, cách bạn cố gắng nhập từ kết xuất làm hỏng dữ liệu (đó là lỗi và không thể đọc tệp đúng cách). Hoặc, cùng một người có thể xác định một phần của một số dữ liệu là một biểu thức (ví dụ: "james \ robin" == "\ r" là biểu thức) hoặc lệnh, v.v ... Đây là lý do tại sao tôi không bao giờ khuyên bạn nên sử dụng kết xuất, ngay cả với công cụ kết xuất dữ liệu nhị phân chỉ, ngay cả với dev.mysql.com/doc/refman/5.6/en/mysqldump.html (hoặc BCP cho MS SQL Server). Nó đi sai quá nhiều lần ...

yeap tôi đã thử với hex-blob. nó không giúp Ngoài ra, bạn ngay sau khi sử dụng sed mysql xác định \ 'là lệnh trong một số tên (không phải tất cả). Đó là lạ và lỗi. Sẽ thử tải số lượng lớn tối nay. Hy vọng nó sẽ được thực hiện ít nhất trong 10-15 giờ.
inragea

@ineersa hy vọng nó sẽ. bạn cũng có thể thử chỉ thêm một phần dữ liệu, giả sử 10% dữ liệu để xem mất bao nhiêu thời gian - và có ước tính cho toàn bộ giao dịch. Tuy nhiên, đó sẽ là một ước tính rất sơ bộ, mọi thứ có thể chậm lại nếu bộ nhớ cache / bộ nhớ / bất cứ thứ gì bị lấp đầy / quá tải.

1
Cảm ơn Mark. Làm việc tuyệt vời. Thậm chí nhanh hơn sau đó khôi phục từ bãi chứa. Mất ~ 5 giờ.
inragea

5

Ý tưởng sed của bạn là một phương pháp tốt, nhưng không có lỗi hoặc lệnh bạn đã chạy, chúng tôi không thể giúp bạn.

Tuy nhiên, một phương pháp nổi tiếng để thực hiện các thay đổi trực tuyến cho các bảng lớn là thay đổi lược đồ trực tuyến . Bỏ qua đơn giản những gì công cụ này làm được sao chép từ tài liệu:

pt-online-lược đồ thay đổi hoạt động bằng cách tạo một bản sao trống của bảng để thay đổi, sửa đổi nó theo ý muốn và sau đó sao chép các hàng từ bảng gốc vào bảng mới. Khi bản sao hoàn thành, nó di chuyển đi khỏi bảng gốc và thay thế nó bằng bảng mới. Theo mặc định, nó cũng giảm bảng gốc.

Phương pháp này cũng có thể mất một lúc để hoàn thành, nhưng trong quá trình, bảng gốc sẽ hoàn toàn có thể sử dụng được.


Tôi sẽ thử tải số lượng lớn vào tối nay. Nếu nó không hoạt động sẽ cần công cụ này. Lỗi được gây ra bởi inetifieng một số ký hiệu sau khi sử dụng sed làm lệnh. Ví dụ 'D\'agostini'sẽ gây ra lỗi unknown command '\''. Nhưng không phải lúc nào cũng vậy, như trong 30% trường hợp. Đó là lạ và lỗi. Tương tự đến ngay cả với các bãi chứa hex-blob. Cảm ơn bạn Derek.
inragea

4

alter table add column, algorithm=inplace, lock=none sẽ thay đổi bảng MySQL 5.6 mà không sao chép bảng và không có tác động khóa.

Mới thử nghiệm ngày hôm qua, hàng loạt đã chèn 70K hàng vào bảng phân vùng 280K hàng 7, 10K hàng vào mỗi phân vùng, với 5 giây ngủ ở giữa để cho phép thông lượng khác.

Bắt đầu chèn hàng loạt, sau đó trong phiên riêng biệt bắt đầu altercâu lệnh trực tuyến ở trên trong MySQL Workbench, alterkết thúc trước khi chèn, hai cột mới được thêm vào và không có hàng nào dẫn đến thay đổi có nghĩa là MySQL không sao chép bất kỳ hàng nào.


1
Tại sao câu trả lời này không nhận được nhiều phiếu hơn?, Nó không hoạt động?
fguillen

1

Hiện tại, tùy chọn tốt nhất để thay đổi các bảng lớn có lẽ là https://github.com/github/gh-ost

gh-las là một giải pháp di chuyển lược đồ trực tuyến không kích hoạt cho MySQL. Nó có thể kiểm tra và cung cấp khả năng tạm dừng, kiểm soát / cấu hình lại động, kiểm toán và nhiều đặc quyền hoạt động.

gh-las tạo ra một khối lượng công việc nhẹ trên bản gốc trong suốt quá trình di chuyển, tách rời khỏi khối lượng công việc hiện có trên bảng di chuyển.

Nó đã được thiết kế dựa trên nhiều năm kinh nghiệm với các giải pháp hiện có và thay đổi mô hình di chuyển bảng.


1

Tôi nghĩ Mydumper / Myloader là một công cụ tốt cho các hoạt động như thế này: đang trở nên tốt hơn mỗi ngày. Bạn có thể sử dụng CPU của mình và có thể tải dữ liệu song song: http://www.percona.com/blog/2014/03/10/new-mydumper-0-6-1-release-offers-several-performance-and- tính khả dụng-tính năng /

Tôi đã quản lý để tải hàng trăm gigabyte bảng MySQL trong vài giờ.

Bây giờ, khi nói đến việc thêm một cột mới, thật khó khăn khi MySQL sao chép toàn bộ bảng vào TMPvùng nhớ với ALTER TABLE...Mặc dù MySQL 5.6 nói rằng nó có thể thay đổi lược đồ trực tuyến, tôi đã không quản lý chúng trực tuyến cho các bảng lớn mà không bị khóa ganh đua như chưa.


-2

tôi chỉ có cùng một vấn đề. Một cách giải quyết nhỏ:

TẠO BẢNG new_table CHỌN * TỪ oldtable;

XÓA TỪ new_table

THAY ĐỔI BẢNG new_table ADD COLUMN new_column int (11);

XÁC NHẬN VÀO new_table chọn *, 0 từ old_table

thả bảng old_table; đổi tên bảng new_table thành old_table;


Tại sao không chỉ thêm một mệnh đề where vào câu lệnh tạo bảng để nó không chọn bất kỳ dữ liệu nào? Việc cắt bớt bảng sẽ hiệu quả hơn sau đó xóa dữ liệu
Joe W

Tại sao phải xóa, khi phải chèn sau, lại. Có thể xác định default = 0 tại ADD COLUMN.
dùng195280
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.