MySQL вставка становится медленной после нескольких быстрых вставок - PullRequest
2 голосов
/ 09 декабря 2011

Я делаю большую вставку, читая файл. Файл выглядит так,

sampletext1
sampletext2
..........
..........
sampletextN

В файле миллионы строк размером около 3 ГБ. Чтение каждой строки в переменную и затем выполнение одной INSERT не сработает, потому что у меня всего около 2 ГБ ОЗУ

Я читаю построчно и создаю строку mysql INSERT. Когда код прочитал 5000 строк, я вставляю их в БД, поэтому в INSERT будет 5000 записей. Запрос MySQL в моем коде (строка INSERT IGNORE INTO $ curr VALUES $) выполняется как обычно до тех пор, пока не будет прочитано и вставлено около 25000 строк, но затем он замедляется и занимает около 5-10 секунд только для одной INSERTion. Я думаю, что она уменьшается линейно с увеличением записей.

Фрагмент кода Perl:

sub StoreToDB {
my $self = shift;;
$self->_doPreliminary();
my $data_struc = $self->_getDATA();
my $file = $data_struc->{DOMAIN_FILE};
my ($count,$cnt,$string,$curr) = (0,0,'',$self->_getTLD() . '_current');
open FH,$file or ( FullLogger($self->_getTLD(),"Cant open $file from StoreToDB : $!\n") and return );
$self->_dbConnect();
while (<FH>) {
   chomp;
   if ( $cnt == MAX ) {
      $self->_dbExecute("INSERT IGNORE INTO $curr VALUES $string");
      $count += $cnt;
      $cnt = 0;
      $string = '';
      Logger("Inside StoreToDB, count is : $count ***\n");
   }
   $string .= "('" . $_ . "')";
   ++$cnt;
   $string = ($cnt != MAX ? $string . ',' : $string . ';');
}#while
close FH;
$self->_dbDisconnect();
return 1;
}#StoreToDB

==============================
DB table details :

mysql> SHOW CREATE TABLE com_current;
+-------------+-------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------------+-------------------------------------------------------------------------------------------------------------------------------+
| com_current | CREATE TABLE `com_current` (
`domain` varchar(60) NOT NULL,
PRIMARY KEY (`domain`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 |
+-------------+-------------------------------------------------------------------------------------------------------------------------------+

1 row in set (16.60 sec)

mysql>

Вывод состояния MySQL:

Uptime: 1057 Threads: 2 Questions: 250 Slow queries: 33 Opens: 38 Flush tables: 1 Open tables: 28 Queries per second avg: 0.236

=============================================== ================ ОБНОВЛЕНИЕ:

До сих пор я пробовал описанные ниже методы, но ни один из них не был лучше:

1) LOCK TABLES my_table WRITE;
   then after inserting, I unlock it,
   UNLOCK TABLES;
2) INSERT DELAYED IGNORE INTO $curr VALUES $string
3) LOAD DATA INFILE '$file' IGNORE INTO TABLE $curr
   this is currently in progress, but seems worse than the original method.

Я не знаю, есть ли у моего my.cnf проблемы. Поэтому я вставил его сюда.

[client]
port        = 3306
socket      = /tmp/mysql.sock

[mysqld]
datadir = /mnt/mysql/data
port        = 3306
socket      = /tmp/mysql.sock
skip-external-locking
key_buffer_size = 16M
max_allowed_packet = 1M
table_open_cache = 64
sort_buffer_size = 512K
net_buffer_length = 8K
read_buffer_size = 256K
read_rnd_buffer_size = 512K
myisam_sort_buffer_size = 8M
log-bin=mysql-bin
binlog_format=mixed
server-id   = 1

[mysqldump]
quick
max_allowed_packet = 16M

[mysql]
no-auto-rehash

[myisamchk]
key_buffer_size = 20M
sort_buffer_size = 20M
read_buffer = 2M
write_buffer = 2M

[mysqlhotcopy]
interactive-timeout

Ответы [ 5 ]

5 голосов
/ 09 декабря 2011

Вы можете использовать синтаксис LOAD DATA INFILE вместо отправки одной строки на оператор вставки.

4 голосов
/ 09 декабря 2011

Как уже говорили другие, использование LOAD DATA INFILE почти наверняка ваш лучший подход.

Но есть одна очевидная проблема с вашим Perl-кодом, которую вы также можете попробовать.Я не знаю, как работает ваше взаимодействие с базой данных (_dbExecute не является методом Perl DBI ), но похоже, что он будет каждый раз готовить оператор SQL.Это будет довольно неэффективно.Вместо этого вы должны подготовить оператор один раз и использовать заполнители в нем для вставки переменных данных.

В терминах DBI вы делаете что-то вроде этого:

foreach (@data) {
  my $sth = $dbh->prepare('INSERT INTO SOME_TABLE (COL1) VALUES ($_)');
  $sth->execute;
}

Когда вам следуетвыполняя что-то вроде этого:

my $sth = $dbh->prepare('INSERT INTO SOME_TABLE (COL1) VALUES (?)');

foreach (@data) {
  $sth->execute($_);
}

Вы почти наверняка найдете это более эффективным.

Подробнее см. документацию по Заполнители и значения привязки .

4 голосов
/ 09 декабря 2011

Если вы начинаете с пустой таблицы или в таблице меньше строк, чем вставляете, то отключение индексов значительно ускорит процесс.

ALTER TABLE tbl_name DISABLE KEYS;
INSERT....
INSERT....
INSERT...
...
ALTER TABLE tbl_name ENABLE KEYS;

OTOH, если вы ужетам много данных, это может на самом деле замедлить процесс.

Пакетирование вставок поможет повысить производительность (особенно если индексы включены), например, из руководства mysql:

INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);

(и, глядя на то, что вы делаете, вы можете подумать об использовании INSERT IGNORE ... и сначала отсортировать список).

Но один из лучших способов повысить производительность для массовых вставок - это загрузитьданных в отдельную выделенную таблицу, затем используйте INSERT .... SELECT ... (использование ORDER BY в операторе SELECT на основе наиболее интенсивно используемого индекса поможет сохранить его сбалансированным).

2 голосов
/ 09 декабря 2011

Использование LOAD DATA INFILE, согласно ответу ypercube, вероятно, лучший путь.В качестве альтернативы, вы также можете начать транзакцию, затем фиксировать ее каждые 500 или около того вставок и начать новую.Это позволяет оптимизировать доступ к диску путем сохранения транзакции в памяти и одновременной записи.

0 голосов
/ 12 декабря 2011

Как уже упоминали несколько человек, LOAD DATA INFILE будет самым быстрым способом получения данных в MySQL.Стоит вставить в свежий стол, если это вообще возможно.Затем вы можете:

  • удалить неуникальные индексы перед вставкой.(или отключить ключи для myisam).
  • вставить в порядке первичного ключа.

Оригинальное исследование, которое я провел некоторое время назад:

http://mysqldump.azundris.com/archives/94-LOAD-DATA-INFILE-and-mysqldump.html

Основная проблема заключается в том, что большие НАГРУЗКИ могут нанести ущерб вашей репликации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...