更新db 6000次需要几分钟?

时间:2009-05-09 15:50:30

标签: mysql ruby-on-rails ruby sqlite activerecord

我正在使用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 个答案:

答案 0 :(得分:13)

通过6000次调用来写入数据库,您将看到速度问题。我会将各种标签保存在内存中并在结束时保存到数据库中,而不是一直保存6000次。

答案 1 :(得分:5)

答案 2 :(得分:2)

我在perl中写了一些简单的代码:

  1. 创建数据库
  2. 插入仅包含单个整数的记录
  3. 检索最新记录并验证它是否返回插入的内容
  4. 它执行步骤#2和#3 6000次。与使用整个对象/关系桥相比,这显然是一个相当轻的工作量。对于SQLite这个简单的案例,它仍然需要17秒才能执行,所以你想要“花几秒钟”的愿望在“传统硬件”上是不现实的。

    使用显示器我确认主要是磁盘活动正在减慢速度。基于此,如果由于某种原因你真的需要数据库快速表现我建议两个选项之一:

    1. 根据要求做人们的建议并找到答案
    2. 尝试购买一些固态硬盘。
    3. 我认为#1是一个很好的开始方式:)

      代码:

      #!/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";
         }
      }
      

答案 3 :(得分:1)

您使用的是哪种数据库连接?某些数据库允许您“直接”连接,而不是使用通过网络堆栈的TCP网络连接。换句话说,如果您正在建立互联网连接并通过这种方式发送数据,它可能会减慢速度。

提高数据库连接性能的另一种方法是在单个命令中将SQL语句组合在一起。

例如,制作一个看起来像这样的单个6,000行SQL语句

"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抽象出数据库调用,因此您无法控制命令的发布方式,因此优化代码可能很困难。

可以做的另一个细节是在内存中保留单词数,然后只将最终总数插入数据库,而不是每次遇到单词时都进行更新。这可能会减少很多插入数量,因为如果你每次遇到“the”这个词时都做了更新,那就是巨大的浪费。单词具有“长尾”分布,最常见的单词比更加模糊的单词更常见。然后底层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命令

答案 4 :(得分:1)

不知道Ruby和Sqlite,一些一般提示:

在Word.s上创建一个唯一索引(你没有说明你是否有一个)

在数据库中定义Word.count的默认值(DEFAULT 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

答案 5 :(得分:1)

在更新之前使用BEGIN TRANSACTION,然后在结束时使用COMMIT。

答案 6 :(得分:0)

好吧,我找到了一些一般规则:

1)使用哈希来保持计数优先,而不是数据 2)最后,在一个事务中包装所有插入或更新,这样它就不会达到db 6000次。