我正在使用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可以加速它吗?
答案 0 :(得分:13)
通过6000次调用来写入数据库,您将看到速度问题。我会将各种标签保存在内存中并在结束时保存到数据库中,而不是一直保存6000次。
答案 1 :(得分:5)
看看AR:Extensions以及处理批量插入。
http://rubypond.com/articles/2008/06/18/bulk-insertion-of-data-with-activerecord/
答案 2 :(得分:2)
我在perl中写了一些简单的代码:
它执行步骤#2和#3 6000次。与使用整个对象/关系桥相比,这显然是一个相当轻的工作量。对于SQLite这个简单的案例,它仍然需要17秒才能执行,所以你想要“花几秒钟”的愿望在“传统硬件”上是不现实的。
使用显示器我确认主要是磁盘活动正在减慢速度。基于此,如果由于某种原因你真的需要数据库快速表现我建议两个选项之一:
我认为#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次。