Hiệu suất nhập của InnoDB


10

Tôi đang vật lộn với việc nhập số lượng lớn một Bảng InnoDB khá lớn bao gồm khoảng 10 triệu hàng (hoặc 7 GB) (đối với tôi là bảng lớn nhất tôi từng làm việc cho đến nay).

Tôi đã thực hiện một số nghiên cứu về cách cải thiện tốc độ nhập của Inno và hiện tại thiết lập của tôi trông như thế này:

/etc/mysql/my.cnf/
[...]
innodb_buffer_pool_size = 7446915072 # ~90% of memory
innodb_read_io_threads = 64
innodb_write_io_threads = 64
innodb_io_capacity = 5000
innodb_thread_concurrency=0
innodb_doublewrite = 0
innodb_log_file_size = 1G
log-bin = ""
innodb_autoinc_lock_mode = 2
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit=2
innodb_buffer_pool_instances=8


import is done via bash script, here is the mysql code:
SET GLOBAL sync_binlog = 1;
SET sql_log_bin = 0;
SET FOREIGN_KEY_CHECKS = 0;
SET UNIQUE_CHECKS = 0;
SET AUTOCOMMIT = 0;
SET SESSION tx_isolation='READ-UNCOMMITTED';
LOAD DATA LOCAL INFILE '$filepath' INTO TABLE monster
COMMIT;

Dữ liệu được cung cấp trong một CSVtập tin.
Hiện tại tôi đang kiểm tra cài đặt của mình với các 'bãi thử nghiệm' nhỏ hơn với 2 triệu, 3 triệu, mỗi hàng và sử dụngtime import_script.sh để so sánh hiệu suất.

Hạn chế là tôi chỉ nhận được tổng thời gian chạy vì vậy tôi phải đợi quá trình nhập hoàn tất để có kết quả.

Kết quả của tôi cho đến nay:

  • 10 000 hàng: <1 giây
  • 100 000 hàng: 10 giây
  • 300 000 hàng: 40 giây
  • 2 triệu hàng: 18 phút
  • 3 triệu hàng: 26 phút
  • 4 triệu hàng: (đã hủy sau 2 giờ)

Dường như không có giải pháp 'cookbook' và người ta phải tự mình tìm ra cách pha trộn tối ưu các cài đặt.
Bên cạnh những gợi ý về những gì cần thay đổi trong thiết lập của tôi, tôi cũng thực sự đánh giá cao thông tin nhiều hơn về cách tôi có thể đánh giá tốt hơn quá trình nhập / hiểu rõ hơn những gì đang xảy ra và nơi tắc nghẽn có thể xảy ra.
Tôi đã cố gắng đọc tài liệu cho các cài đặt tôi đang thay đổi nhưng một lần nữa tôi không nhận thấy bất kỳ tác dụng phụ nào và liệu tôi có thể giảm hiệu suất với giá trị được chọn không tốt.

Hiện tại tôi muốn thử một đề xuất từ ​​trò chuyện để sử dụng MyISAMtrong quá trình nhập và thay đổi công cụ bảng sau đó.
Tôi muốn thử điều này nhưng hiện tại DROP TABLEtruy vấn của tôi cũng mất hàng giờ để hoàn thành. (Có vẻ như một chỉ báo khác, cài đặt của tôi ít hơn thì tối ưu).

Thông tin bổ sung:
Máy tôi hiện đang sử dụng có 8GB RAM và ổ cứng Solid State Hybrid w / 5400RPM.
Mặc dù chúng tôi cũng đặt mục tiêu xóa dữ liệu lỗi thời khỏi bảng đang đề cập nhưng tôi vẫn cần nhập nhanh để
kiểm tra) automatic data cleanup featuretrong khi phát triển và
b) trong trường hợp máy chủ của chúng tôi gặp sự cố, chúng tôi muốn sử dụng máy chủ thứ 2 của mình để thay thế dữ liệu đến ngày, lần nhập gần nhất mất hơn 24 giờ)

mysql> SHOW CREATE TABLE monster\G
*************************** 1. row ***************************
       Table: monster
Create Table: CREATE TABLE `monster` (
  `monster_id` int(11) NOT NULL AUTO_INCREMENT,
  `ext_monster_id` int(11) NOT NULL DEFAULT '0',
  `some_id` int(11) NOT NULL DEFAULT '0',
  `email` varchar(250) NOT NULL,
  `name` varchar(100) NOT NULL,
  `address` varchar(100) NOT NULL,
  `postcode` varchar(20) NOT NULL,
  `city` varchar(100) NOT NULL,
  `country` int(11) NOT NULL DEFAULT '0',
  `address_hash` varchar(250) NOT NULL,
  `lon` float(10,6) NOT NULL,
  `lat` float(10,6) NOT NULL,
  `ip_address` varchar(40) NOT NULL,
  `cookie` int(11) NOT NULL DEFAULT '0',
  `party_id` int(11) NOT NULL,
  `status` int(11) NOT NULL DEFAULT '2',
  `creation_date` datetime NOT NULL,
  `someflag` tinyint(1) NOT NULL DEFAULT '0',
  `someflag2` tinyint(4) NOT NULL,
  `upload_id` int(11) NOT NULL DEFAULT '0',
  `news1` tinyint(4) NOT NULL DEFAULT '0',
  `news2` tinyint(4) NOT NULL,
  `someother_id` int(11) NOT NULL DEFAULT '0',
  `note` varchar(2500) NOT NULL,
  `referer` text NOT NULL,
  `subscription` int(11) DEFAULT '0',
  `hash` varchar(32) DEFAULT NULL,
  `thumbs1` int(11) NOT NULL DEFAULT '0',
  `thumbs2` int(11) NOT NULL DEFAULT '0',
  `thumbs3` int(11) NOT NULL DEFAULT '0',
  `neighbours` tinyint(4) NOT NULL DEFAULT '0',
  `relevance` int(11) NOT NULL,
  PRIMARY KEY (`monster_id`),
  KEY `party_id` (`party_id`),
  KEY `creation_date` (`creation_date`),
  KEY `email` (`email`(4)),
  KEY `hash` (`hash`(8)),
  KEY `address_hash` (`address_hash`(8)),
  KEY `thumbs3` (`thumbs3`),
  KEY `ext_monster_id` (`ext_monster_id`),
  KEY `status` (`status`),
  KEY `note` (`note`(4)),
  KEY `postcode` (`postcode`),
  KEY `some_id` (`some_id`),
  KEY `cookie` (`cookie`),
  KEY `party_id_2` (`party_id`,`status`)
) ENGINE=InnoDB AUTO_INCREMENT=13763891 DEFAULT CHARSET=utf8

2
Bạn đã thử với hàng nhập ít hơn, như hàng 10K hoặc 100K?
ypercubeᵀᴹ

1
Hãy chạy SHOW CREATE TABLE yourtable\Gđể chỉ cho chúng tôi cấu trúc bảng của bảng 10 triệu hàng này.
RolandoMySQLDBA

@RolandoMySQLDBA vì vậy tôi đã làm (với tên trường bị che khuất)
nours

Bằng cách vô hiệu hóa bộ đệm ghi kép ( innodb_doublewrite = 0) cài đặt MySQL của bạn không bị sập an toàn: nếu bạn bị mất điện (không phải là sự cố MySQL), dữ liệu của bạn có thể bị hỏng âm thầm.
jfg956

Câu trả lời:


13

Trước tiên, bạn cần biết những gì bạn đang làm với InnoDB khi bạn cày hàng triệu hàng vào một bảng InnoDB. Chúng ta hãy xem Kiến trúc InnoDB.

Kiến trúc InnoDB

Ở góc trên bên trái, có một hình minh họa của Bộ đệm InnoDB. Lưu ý rằng có một phần của nó dành riêng cho bộ đệm chèn. Cái đó làm cái gì ? Nó được chuyển để thay đổi các chỉ mục phụ từ Nhóm bộ đệm sang Bộ đệm chèn bên trong không gian bảng hệ thống (còn gọi là ibdata1). Theo mặc định, innodb_change_buffer_max_size được đặt thành 25. Điều này có nghĩa là có thể sử dụng tới 25% Nhóm bộ đệm để xử lý các chỉ mục phụ.

Trong trường hợp của bạn, bạn có 6,935 GB cho Nhóm đệm InnoDB. Tối đa 1.734 GB sẽ được sử dụng để xử lý các chỉ mục phụ của bạn.

Bây giờ, nhìn vào bảng của bạn. Bạn có 13 chỉ số phụ. Mỗi hàng bạn xử lý phải tạo một mục nhập chỉ mục phụ, ghép nó với khóa chính của hàng và gửi chúng dưới dạng một cặp từ Bộ đệm chèn trong Nhóm bộ đệm vào Bộ đệm chèn trong ibdata1. Điều đó xảy ra 13 lần với mỗi hàng. Nhân số này với 10 triệu và bạn gần như có thể cảm thấy một nút cổ chai sắp tới.

Đừng quên rằng nhập 10 triệu hàng trong một giao dịch sẽ chồng chất mọi thứ vào một phân đoạn rollback và lấp đầy không gian UNDO trong ibdata1.

BỀN VỮNG

BỀN VỮNG # 1

Đề xuất đầu tiên của tôi để nhập bảng khá lớn này sẽ là

  • Bỏ tất cả các chỉ mục không duy nhất
  • Nhập dữ liệu
  • Tạo tất cả các chỉ mục không duy nhất

BỀN VỮNG # 2

Loại bỏ các chỉ số trùng lặp. Trong trường hợp của bạn, bạn có

KEY `party_id` (`party_id`),
KEY `party_id_2` (`party_id`,`status`)

Cả hai chỉ mục đều bắt đầu party_id, bạn có thể tăng xử lý chỉ mục phụ ít nhất là 7,6% để loại bỏ một chỉ số trong số 13. Bạn cần phải chạy cuối cùng

ALTER TABLE monster DROP INDEX party_id;

BỀN VỮNG # 3

Loại bỏ các chỉ mục bạn không sử dụng. Nhìn qua mã ứng dụng của bạn và xem nếu các truy vấn của bạn sử dụng tất cả các chỉ mục. Bạn có thể muốn xem xét việc sử dụng pt-index để cho phép nó gợi ý những chỉ mục nào không được sử dụng.

BỀN VỮNG # 4

Bạn nên tăng innodb_log_buffer_size lên 64M vì mặc định là 8M. Một bộ đệm nhật ký lớn hơn có thể làm tăng hiệu suất ghi I / O của InnoDB.

TIẾNG VIỆT

Đặt hai gợi ý đầu tiên vào vị trí, hãy làm như sau:

  • Bỏ 13 chỉ số không duy nhất
  • Nhập dữ liệu
  • Tạo tất cả các chỉ mục không duy nhất ngoại trừ party_idchỉ mục

Có lẽ những điều sau đây có thể giúp

CREATE TABLE monster_new LIKE monster;
ALTER TABLE monster_new
  DROP INDEX `party_id`,
  DROP INDEX `creation_date`,
  DROP INDEX `email`,
  DROP INDEX `hash`,
  DROP INDEX `address_hash`,
  DROP INDEX `thumbs3`,
  DROP INDEX `ext_monster_id`,
  DROP INDEX `status`,
  DROP INDEX `note`,
  DROP INDEX `postcode`,
  DROP INDEX `some_id`,
  DROP INDEX `cookie`,
  DROP INDEX `party_id_2`;
ALTER TABLE monster RENAME monster_old;
ALTER TABLE monster_new RENAME monster;

Nhập dữ liệu vào monster. Sau đó, chạy nó

ALTER TABLE monster
  ADD INDEX `creation_date`,
  ADD INDEX `email` (`email`(4)),
  ADD INDEX `hash` (`hash`(8)),
  ADD INDEX `address_hash` (`address_hash`(8)),
  ADD INDEX `thumbs3` (`thumbs3`),
  ADD INDEX `ext_monster_id` (`ext_monster_id`),
  ADD INDEX `status` (`status`),
  ADD INDEX `note` (`note`(4)),
  ADD INDEX `postcode` (`postcode`),
  ADD INDEX `some_id` (`some_id`),
  ADD INDEX `cookie` (`cookie`),
  ADD INDEX `party_id_2` (`party_id`,`status`);

HÃY THỬ MỘT LẦN !!!

THAY THẾ

Bạn có thể tạo một bảng được gọi monster_csvlà bảng MyISAM không có chỉ mục và thực hiện điều này:

CREATE TABLE monster_csv ENGINE=MyISAM AS SELECT * FROM monster WHERE 1=2;
ALTER TABLE monster RENAME monster_old;
CREATE TABLE monster LIKE monster_old;
ALTER TABLE monster DROP INDEX `party_id`;

Nhập dữ liệu của bạn vào monster_csv. Sau đó, sử dụng mysqldump để tạo một lần nhập khác

mysqldump -t -uroot -p mydb monster_csv | sed 's/monster_csv/monster/g' > data.sql

Tệp mysqldump data.sqlsẽ mở rộng các lệnh INSERT nhập 10.000-20.000 hàng cùng một lúc.

Bây giờ, chỉ cần tải mysqldump

mysql -uroot -p mydb < data.sql

Cuối cùng, thoát khỏi bảng MyISAM

DROP TABLE monster_csv;

Tôi thậm chí không nhận ra tất cả các phím đó (không phải là thiết kế của tôi) nhưng lời giải thích của bạn có vẻ rất thuyết phục. Cho hôm nay đã muộn để bắt đầu một lần thử khác nhưng tôi thấy một số lời khuyên tuyệt vời nên thử vào ngày mai. Sẽ thông báo cho bạn! <3
nours

1
Tôi đã quản lý để nhập cơ sở dữ liệu đầy đủ (không chỉ monsterbảng) trong vòng chưa đầy 20 phút khi không có khóa trên bảng InnoDB. Thêm khóa mất khoảng. 20 phút nữa. Tôi muốn nói điều này khá nhiều giải quyết vấn đề của tôi trong trường hợp này. Cảm ơn rât nhiều!
Nuala

8

Tôi muốn viết một bình luận (vì đây không phải là một câu trả lời dứt khoát), nhưng nó đã trở nên quá dài:

Tôi sẽ cung cấp cho bạn một số lời khuyên rộng rãi và chúng tôi có thể đi vào chi tiết cho từng người, nếu bạn muốn:

  • Giảm độ bền (bạn đã thực hiện một số trong đó). Phiên bản mới nhất cho phép thậm chí làm điều đó nhiều hơn. Bạn có thể đi xa như vô hiệu hóa bộ đệm ghi đôi, vì tham nhũng không phải là vấn đề đối với hàng nhập khẩu.
  • Tăng bộ đệm bằng cách: Tăng kích thước nhật ký giao dịch và tăng kích thước nhóm bộ đệm có sẵn. Giám sát việc sử dụng tệp nhật ký giao dịch và điểm kiểm tra. Đừng sợ nhật ký lớn cho một nhập khẩu.
  • Tránh các giao dịch lớn - rollback của bạn sẽ trở nên đầy dữ liệu không cần thiết. Đây có lẽ là vấn đề lớn nhất của bạn.
  • SQL sẽ là một nút cổ chai, tránh chi phí SQL (handlersocket, memcached) và / hoặc tải nó đồng thời với một số luồng cùng một lúc. Đồng thời phải đạt đến một điểm ngọt ngào, không quá nhiều, không quá ít.
  • Tải dữ liệu trong phân mảnh khóa chính có thể là một isse
  • Kiểm tra nén InnoDB nếu IO là nút cổ chai của bạn và CPU và bộ nhớ không làm cho nó chậm hơn
  • Hãy thử tạo các khóa phụ của bạn sau đó (nhanh hơn trong một số trường hợp), không tải dữ liệu được lập chỉ mục - Các phím DISABLE không ảnh hưởng đến InnoDB . Nếu không, hãy theo dõi bộ đệm chèn của bạn (có thể vượt qua một nửa nhóm bộ đệm của bạn).
  • Thay đổi hoặc vô hiệu hóa thuật toán tổng kiểm tra - có thể không phải là vấn đề của bạn, nhưng nó trở thành nút cổ chai trên thẻ flash cao cấp.
  • Phương án cuối cùng: Giám sát máy chủ của bạn để tìm ra nút cổ chai hiện tại của bạn và cố gắng giảm thiểu (InnoDB rất linh hoạt về điều đó).

Hãy nhớ rằng một số trong số này không an toàn hoặc được khuyến khích cho việc không nhập khẩu (hoạt động bình thường).


Cảm ơn rât nhiều! Tôi muốn thử ý tưởng của Rolando về các chỉ mục trước tiên nhưng tôi đoán công cụ "giao dịch quay lại" này vẫn sẽ là một vấn đề. Bạn có thể giải thích về điều này? Tôi nghĩ rằng tôi muốn vô hiệu hóa càng nhiều chức năng này càng tốt trong nhập khẩu và chỉ kích hoạt lại khi đi vào sản xuất ~ Tôi nghĩ ...
Nuala

1
Đề nghị của Rolando là quan điểm số 7 của tôi. Tránh sử dụng rollback trên đầu dễ dàng như một sự kết hợp SET SESSION tx_isolation='READ-UNCOMMITTED';(chỉ hữu ích nếu bạn nhập song song với một số luồng) và nhận xét @ypercube về việc chèn theo lô. Bạn có một ví dụ đầy đủ ở đây: mysqlperformanceblog.com/2008/07/03/... Hãy chắc chắn rằng bạn đang nhận được lợi thế của tất cả các tính năng trong phiên bản mới nhất InnoDB: mysqlperformanceblog.com/2011/01/07/...
jynus

1
Tôi có ấn tượng chung là người ta sẽ tránh nhập vào những chiếc mâm nhỏ hơn mà thay vào đó là một hoạt động "bao gồm tất cả" nhưng tôi thấy đa luồng có thể mở ra một số khả năng. Đoán đó là trường hợp cụ thể. Tuy nhiên, tôi đã chấp nhận câu trả lời của Rolando vì chính điều chỉnh này (số 7 của bạn) đã giúp tôi có được nhập khẩu đầy đủ trong <1 giờ nhưng danh sách của bạn chắc chắn không còn giá trị và tôi đoán sẽ sử dụng nó để tham khảo khá sớm vì tốc độ DB của chúng tôi đang tăng lên sợ hãi tôi :)
Nuala

Tôi đồng ý với @yoshi. Câu trả lời của bạn toàn diện hơn về mặt khắc phục sự cố và cải thiện hiệu suất. +1
RolandoMySQLDBA

3

Hầu hết các lời khuyên tốt đã được đưa ra cho đến nay, nhưng không có nhiều lời giải thích cho những người tốt nhất. Tôi sẽ cung cấp thêm chi tiết.

Đầu tiên, trì hoãn việc tạo chỉ mục là một điều tốt, với đủ chi tiết trong các phản hồi khác. Tôi sẽ không trở lại trên đó.

Một tệp nhật ký InnoDB lớn hơn sẽ giúp bạn rất nhiều (nếu bạn đang sử dụng MySQL 5.6 vì không thể tăng nó trong MySQL 5.5). Bạn đang chèn 7 GB dữ liệu, tôi sẽ đề xuất tổng kích thước nhật ký ít nhất là 8 GB (giữ innodb_log_files_in_groupở mức mặc định (2) và tăng innodb_log_file_sizeở mức 4 GB). 8 GB này không chính xác: ít nhất nó phải có kích thước nhập trong nhật ký REDO và có thể tăng gấp đôi hoặc gấp bốn lần kích thước đó. Lý do đằng sau kích thước nhật ký của InnoDB làm tăng rằng khi nhật ký gần đầy, InnoDB sẽ bắt đầu chuyển mạnh vào vùng đệm của nó vào đĩa để tránh nhật ký điền vào (khi nhật ký đã đầy, InnoDB không thể ghi bất kỳ cơ sở dữ liệu nào cho đến khi một số các trang của nhóm bộ đệm được ghi vào đĩa).

Tệp nhật ký InnoDB lớn hơn sẽ giúp bạn, nhưng bạn cũng nên chèn theo thứ tự khóa chính (sắp xếp tệp của bạn trước khi chèn). Nếu bạn chèn theo thứ tự khóa chính, InnoDB sẽ điền vào một trang, rồi một trang khác, v.v. Nếu bạn không chèn theo thứ tự khóa chính, lần chèn tiếp theo của bạn có thể kết thúc trong một trang đã đầy và sẽ phải chịu "chia trang". Việc chia trang này sẽ tốn kém cho InnoDB và sẽ làm chậm quá trình nhập của bạn.

Bạn đã có một vùng đệm lớn như RAM cho phép bạn và nếu bảng của bạn không vừa với nó, bạn không thể làm gì nhiều ngoài việc mua thêm RAM. Nhưng bảng bạn phù hợp với nhóm bộ đệm nhưng lớn hơn 75% của bộ đệm của bạn, bạn có thể thử tăng innodb_max_dirty_pages_pctlên 85 hoặc 95 trong quá trình nhập (giá trị mặc định là 75). Tham số cấu hình này báo cho InnoDB bắt đầu mạnh mẽ xóa vùng đệm khi phần trăm trang bẩn đạt đến giới hạn này. Bằng cách tăng tham số này (và nếu bạn may mắn về kích thước dữ liệu), bạn có thể tránh được IO tích cực trong quá trình nhập và trì hoãn các IO đó sau này.

Có thể (và đây là dự đoán) nhập dữ liệu của bạn trong nhiều giao dịch nhỏ sẽ giúp bạn. Tôi không biết chính xác cách tạo nhật ký REDO, nhưng nếu nó được đệm trong RAM (và đĩa khi cần quá nhiều RAM) trong khi giao dịch đang tiến triển, bạn có thể sẽ bị các IO không cần thiết. Bạn có thể thử điều này: một khi tệp của bạn được sắp xếp, hãy chia nó thành nhiều phần (thử với 16 MB và các kích thước khác) và nhập từng cái một. Điều này cũng sẽ cho phép bạn kiểm soát tiến trình nhập của mình. Nếu bạn không muốn dữ liệu của mình hiển thị một phần cho người đọc khác trong khi bạn nhập, bạn có thể nhập bằng tên bảng khác, tạo các chỉ mục sau đó, sau đó đổi tên bảng.

Về ổ đĩa SSD / 5400RPM lai của bạn, tôi không biết về những thứ đó và cách tối ưu hóa điều này. 5400RPM có vẻ chậm đối với cơ sở dữ liệu, nhưng có lẽ SSD đang tránh điều đó. Có thể bạn đang lấp đầy phần SSD của đĩa bằng cách ghi tuần tự vào nhật ký REDO và SSD đang làm tổn thương hiệu suất. Tôi không biết.

Một mẹo không hay mà bạn không nên thử (hoặc cẩn thận) là: không sử dụng đa luồng: sẽ rất khó để tối ưu hóa để tránh chia trang trong InnoDB. Nếu bạn muốn sử dụng đa luồng, hãy chèn vào các bảng khác nhau (hoặc trong các phân vùng khác nhau của cùng một bảng).

Nếu bạn đang xem xét đa luồng, có thể bạn có máy tính đa ổ cắm (NUMA). Trong trường hợp này, đảm bảo bạn tránh được vấn đề điên rồ hoán đổi MySQL .

Nếu bạn đang sử dụng MySQL 5.5, hãy nâng cấp lên MySQL 5.6: nó có tùy chọn tăng kích thước nhật ký REDO và có thuật toán xóa vùng đệm tốt hơn.

Chúc may mắn với nhập khẩu của bạn.

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.