Обновление БД 6000 раз займет несколько минут? - PullRequest
1 голос
/ 09 мая 2009

Я пишу тестовую программу с Ruby и ActiveRecord, и она читает документ который как 6000 слов в длину. А потом я просто подсчитал слова на

recordWord = Word.find_by_s(word);
if (recordWord.nil?)
  recordWord = Word.new
  recordWord.s = word
end
if recordWord.count.nil?
  recordWord.count = 1
else
  recordWord.count += 1
end
recordWord.save

и поэтому эта часть зацикливается 6000 раз ... и это занимает несколько минут запустить хотя бы с помощью sqlite3. Это нормально? Я ожидал, что это может бежать через пару секунд ... может ли MySQL ускорить его?

Ответы [ 7 ]

13 голосов
/ 09 мая 2009

С 6000 вызовов для записи в базу данных вы увидите проблемы со скоростью. Я бы сохранял различные результаты в памяти и сохранял в базе данных один раз в конце, а не 6000 раз по пути.

5 голосов
/ 10 мая 2009

Взгляните на AR: Расширения, а также для обработки массовых вставок.

http://rubypond.com/articles/2008/06/18/bulk-insertion-of-data-with-activerecord/

2 голосов
/ 09 мая 2009

Я написал небольшой код на Perl, который просто делает:

  1. Создать базу данных
  2. Вставить запись, которая содержит только одно целое число
  3. Получить самую последнюю запись и убедиться, что она возвращает то, что вставила

И он делает шаги # 2 и # 3 6000 раз. Это, очевидно, значительно более легкая рабочая нагрузка, чем наличие целого объекта / реляционного моста. В этом тривиальном случае с SQLite все еще потребовалось 17 секунд для выполнения, поэтому ваше желание, чтобы это заняло «пару секунд», нереально на «традиционном оборудовании».

Используя монитор, я убедился, что в первую очередь его работа замедлялась. Исходя из этого, если по какой-то причине вам действительно нужна база данных для быстрого поведения, я предлагаю один из двух вариантов:

  1. Делайте то, что предлагали люди, и отыщите вокруг требования
  2. Попробуйте купить твердотельные диски.

Я думаю, # 1 - хороший способ начать :) 1021 *

Код:

#!/usr/bin/perl

use warnings;
use strict;

use DBI;

my $dbh = DBI->connect('dbi:SQLite:dbname=/tmp/dbfile', '', '');

create_database($dbh);
insert_data($dbh);

sub insert_data {
  my ($dbh) = @_;

  my $insert_sql = "INSERT INTO test_table (test_data) values (?)";
  my $retrieve_sql = "SELECT test_data FROM test_table WHERE test_data = ?";

  my $insert_sth = $dbh->prepare($insert_sql);
  my $retrieve_sth = $dbh->prepare($retrieve_sql);

  my $i = 0;
  while (++$i < 6000) {
     $insert_sth->execute(($i));
     $retrieve_sth->execute(($i));

     my $hash_ref = $retrieve_sth->fetchrow_hashref;

     die "bad data!" unless $hash_ref->{'test_data'} == $i;
  }
}

sub create_database {
   my ($dbh) = @_;

   my $status = $dbh->do("DROP TABLE test_table");
   # return error status if CREATE resulted in error
   if (!defined $status) {
     print "DROP TABLE failed";
   }

   my $create_statement = "CREATE TABLE test_table (id INTEGER PRIMARY KEY AUTOINCREMENT, \n";
   $create_statement .= "test_data varchar(255)\n";
   $create_statement .= ");";

   $status = $dbh->do($create_statement);

   # return error status if CREATE resulted in error
   if (!defined $status) {
     die "CREATE failed";
   }
}
1 голос
/ 11 мая 2009

Используйте BEGIN TRANSACTION перед вашими обновлениями, затем COMMIT в конце.

1 голос
/ 10 мая 2009

Не зная о Ruby и Sqlite, некоторые общие подсказки:

создать уникальный индекс в Word.s (вы не указали, есть ли он у вас)

определить значение по умолчанию для Word.count в базе данных (ПО УМОЛЧАНИЮ 1)

оптимизировать назначение количества:

recordWord = Word.find_by_s(word);
if (recordWord.nil?)
    recordWord = Word.new
    recordWord.s = word
    recordWord.count = 1
else
    recordWord.count += 1
end
recordWord.save
1 голос
/ 09 мая 2009

Какой тип подключения к базе данных вы используете? Некоторые базы данных позволяют вам подключаться «напрямую», а не через сетевое соединение TCP, которое проходит через сетевой стек. Другими словами, если вы подключаетесь к Интернету и отправляете данные таким способом, это может замедлить работу.

Еще один способ повысить производительность соединения с базой данных - объединить операторы SQL в одну команду.

Например, создание единственного оператора SQL из 6000 строк, который выглядит следующим образом

"update words set count = count + 1 where word = 'the'
update words set count = count + 1 where word = 'in'
...
update words set count = count + 1 where word = 'copacetic'" 

и запустите его одной командой, производительность будет намного лучше. По умолчанию MySQL имеет ограничение «размер пакета» в 1 мегабайт, но вы можете изменить его в файле my.ini, чтобы увеличить его, если хотите.

Поскольку вы абстрагируете вызовы вашей базы данных через ActiveRecord, вы не имеете большого контроля над тем, как выполняются команды, поэтому может быть сложно оптимизировать ваш код.

Еще один тонкий момент, который вы могли бы сделать, состоял бы в том, чтобы вести подсчет слов в памяти, а затем вставлять только итоговую сумму в базу данных, а не делать обновление каждый раз, когда вы встречаете слово. Это, вероятно, значительно сократит количество вставок, потому что, если вы делаете обновление каждый раз, когда сталкиваетесь со словом «,», это огромная, огромная трата. Слова имеют «длинный хвост», и наиболее распространенные слова встречаются гораздо чаще, чем более неясные слова. Тогда базовый SQL будет выглядеть примерно так:

"update words set count = 300 where word = 'the'
update words set count = 250 where word = 'in'
...
update words set count = 1 where word = 'copacetic'" 

Если вы беспокоитесь о том, чтобы занимать слишком много памяти, вы можете считать слова и периодически «сбрасывать» их. Так что прочитайте пару мегабайт текста, затем потратьте несколько секунд на обновление итогов, а не обновляйте каждое слово каждый раз, когда вы сталкиваетесь с ним. Если вы хотите еще больше повысить производительность, вам следует подумать о том, чтобы напрямую вводить команды SQL

0 голосов
/ 13 мая 2009

хорошо, я нашел общее правило:

1) сначала используйте счет, чтобы сохранить счет, а не db
2) в конце оберните все вставки или обновления в одну транзакцию, чтобы она не достигла БД 6000 раз.

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