w.k*_*w.k 1 mysql perl benchmarking dbi
在转换数据库的过程中,我尝试使用最佳/最快插入.AFAIK,惯用的大量插入应该准备语句处理程序,然后迭代数据插入它.像这样的东西:
my $sql = q|INSERT INTO test.table ( value ) VALUES ( ? ) |;
my $sth = $dbh->prepare( $sql );
for my $val ( 1 .. 1000000 ) {
$sth->execute( $val );
}
Run Code Online (Sandbox Code Playgroud)
我认为在state-declarator 的帮助下我可以将这个例程重构为函数,如下所示:
sub sql_state {
my ( $val ) = @_;
state $sql = q|INSERT INTO test.table ( value ) VALUES ( ? ) |;
state $sth = $dbh->prepare( $sql );
$sth->execute( $val )
or die "State";
}
Run Code Online (Sandbox Code Playgroud)
所以现在$sql在所有插入过程中初始化一次,并且还$sth准备一次,这是增强的基础.
在迁移我的数据库期间,我觉得这种改进并没有像我希望的那样给我这样的胜利.然后我发现了一篇文章
"国家的敌人",这个问题与我自己提出的问题完全相同:为什么state不给予任何改进my?
在rjbs猜测中,在使用state和my初始化语句处理程序时,差异会很大.我做了一些基准测试并得出了与文章作者相同的结论:即使在某些情况下我得到state一点点(0.5%)更快,在大多数情况下my是相同的速度甚至更快(高达9%).
首先,我尝试使用innodb表,因为我需要完成自己的任务:
Benchmark: timing 100 iterations of callFor, callMy, callState...
callFor: 922 wallclock secs ( 7.31 usr + 3.78 sys = 11.09 CPU) @ 9.02/s (n=100)
callMy: 927 wallclock secs ( 6.09 usr + 4.46 sys = 10.55 CPU) @ 9.48/s (n=100)
callState: 922 wallclock secs ( 6.72 usr + 4.62 sys = 11.34 CPU) @ 8.82/s (n=100)
Run Code Online (Sandbox Code Playgroud)
那些对于更广泛的迭代来说太慢了,所以我也用myisam表做了一些(1000x1000 =百万次插入):
Benchmark: timing 1000 iterations of callfor, callmy, callstate...
callfor: 96 wallclock secs (15.19 usr + 15.50 sys = 30.69 CPU) @ 32.58/s (n=1000)
callmy: 95 wallclock secs (15.18 usr + 14.90 sys = 30.08 CPU) @ 33.24/s (n=1000)
callstate: 104 wallclock secs (18.86 usr + 16.15 sys = 35.01 CPU) @ 28.56/s (n=1000)
Run Code Online (Sandbox Code Playgroud)
另一个运行:
Benchmark: timing 1000 iterations of callfor, callmy, callstate...
callfor: 94 wallclock secs (14.90 usr + 14.47 sys = 29.37 CPU) @ 34.05/s (n=1000)
callmy: 92 wallclock secs (14.77 usr + 14.09 sys = 28.86 CPU) @ 34.65/s (n=1000)
callstate: 99 wallclock secs (17.66 usr + 15.30 sys = 32.96 CPU) @ 30.34/s (n=1000)
Run Code Online (Sandbox Code Playgroud)
这是我的实际测试代码:
use strict; use warnings; use 5.010;
use ...; # something to get $dbh ...
use Benchmark qw{:all} ;
sub prepareTable {
my $dropTable = q|DROP TABLE IF EXISTS test.table|;
$dbh->do( $dropTable )
|| die "droptable";
my $createTable = q|
CREATE TABLE test.table (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`value` varchar(60),
PRIMARY KEY (`id`)
) ENGINE=MYISAM
|;
$dbh->do( $createTable )
|| die "createtable";
}
sub callFor {
prepareTable();
my $sql = q|INSERT INTO test.table ( value ) VALUES ( ? ) |;
my $sth = $dbh->prepare( $sql );
for my $val ( 1 .. 1000 ) {
sql_for( $sth, $val );
}
}
sub callMy {
prepareTable();
for my $val ( 1 .. 1000 ) {
sql_my( $val );
}
}
sub callState {
prepareTable();
for my $val ( 1 .. 1000 ) {
sql_state( $val );
}
}
sub sql_for {
my ( $sth, $val ) = @_;
$sth->execute( $val )
or die "For";
}
sub sql_my {
my ( $val ) = @_;
my $sql = q|INSERT INTO test.table ( value ) VALUES ( ? ) |;
my $sth = $dbh->prepare( $sql );
$sth->execute( $val )
or die "My";
}
sub sql_state {
my ( $val ) = @_;
state $sql = q|INSERT INTO test.table ( value ) VALUES ( ? ) |;
state $sth = $dbh->prepare( $sql );
$sth->execute( $val )
or die "State";
}
timethese(
1000 , {
'callFor' => sub { callFor( ) ; } ,
'callMy' => sub { callFor( ) ; } ,
'callState' => sub { callState( ) ; } ,
}
);
Run Code Online (Sandbox Code Playgroud)
那么,为什么state不在这里赢my?应该很容易.要么?
tjd*_*tjd 10
在奶牛放牧的地方,你正在追逐蚱蜢的蛋白质.
在对数据库进行批量插入或更新时,如果您想要速度,autocommit则不是您的朋友.做单commit时,即可大功告成,或抛出一个在每1000条记录左右.
最后,如果您还在等待更改的磁盘I/O和索引更新完成,那么每次迭代保存几个CPU周期将毫无意义.
如果你想测试状态vs我的,你真正想要的基准是准备的成本.我投入,prepare_cached因为它做同样的事情.
use strict; use warnings; use 5.010;
use DBI;
use Benchmark qw{:all} ;
my $dbh = DBI->connect('DBI:mysql:database=test', '', '',
{RaiseError => 1, AutoCommit => 0}
);
sub prepareTable {
my $dropTable = q|DROP TABLE IF EXISTS test.table|;
$dbh->do( $dropTable );
my $createTable = q{
CREATE TABLE test.table (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`value` varchar(60),
PRIMARY KEY (`id`)
)
};
$dbh->do( $createTable );
}
prepareTable;
my $sql = q|INSERT INTO test.table ( value ) VALUES ( ? ) |;
cmpthese( -3, {
'my' => sub { my $sth = $dbh->prepare($sql); return; },
'state' => sub { state $sth = $dbh->prepare($sql); return; },
'prepare_cached' => sub { my $sth = $dbh->prepare_cached($sql); return; },
});
__END__
Rate my prepare_cached state
my 67966/s -- -73% -99%
prepare_cached 253414/s 273% -- -98%
state 11267589/s 16478% 4346% --
Run Code Online (Sandbox Code Playgroud)
这只是告诉您不运行代码比运行代码更快.如果你想知道这会对实际应用程序产生多大影响,那么你就是在正确的道路上,但是你已经搞砸了你的基准. 这是我改进的基准.这使得它更像是如何在生产中运行代码(AutoCommit off,RaiseError on),消除表脚手架,并使用通常优越的InnoDB.重要的是,它删除了许多额外的代码和子程序,这些只会破坏基准测试.
不出所料,结果是1000次INSERT的成本淹没了准备INSERT的成本.INSERT占主导地位,它的运行时间非常不可靠,很难从中获得一致的基准测试结果.
如果每次准备执行次数减少怎么办?准备应该开始产生更大的影响,这正是我们所看到的.
$EXECUTES_PER_PREPARE = 1;
Rate my prepare_cached state
my 24722/s -- -36% -56%
prepare_cached 38610/s 56% -- -31%
state 56180/s 127% 46% --
$EXECUTES_PER_PREPARE = 2;
Rate my prepare_cached state
my 15949/s -- -22% -41%
prepare_cached 20325/s 27% -- -25%
state 27027/s 69% 33% --
$EXECUTES_PER_PREPARE = 10;
Rate my prepare_cached state
my 4405/s -- -17% -22%
prepare_cached 5305/s 20% -- -6%
state 5618/s 28% 6% --
$EXECUTES_PER_PREPARE = 100;
Rate my prepare_cached state
my 546/s -- -0% -1%
prepare_cached 546/s 0% -- -1%
state 552/s 1% 1% --
Run Code Online (Sandbox Code Playgroud)