Schrift
Wiki:Tipp zum Debugging: use Data::Dumper; local $Data::Dumper::Useqq = 1; print Dumper \@var;
[thread]13133[/thread]

Performanzproblem (DBIx::Class + mehrere Tabellen)

Leser: 1


<< >> 7 Einträge, 1 Seite
pktm
 2009-02-13 15:01
#118846 #118846
User since
2003-08-07
2921 Artikel
BenutzerIn
[Homepage]
user image
Hallo!

Ich habe zum Spielen mit DBIx::Class ja mal ein Forum gebastelt. Mit den Testdaten hat das auch prima funktioniert, jetzt habe ich zum testen der Suchfunktion ganz viele Daten eingespielt (ca. 100.000 Posts).

Nun dauerte der Aufbau der Übersichtseite auf einmal 23 Sekunden :-s
Also habe ich angefangen, die Sache zu optimieren. Mein Testskript braucht jetzt "nur noch" 3 Sekunden. Das ist immer noch zu viel. Ich nehme mal an, dass ich den falschen Ansatz habe, eventuell kann mri da ja jemand helfen.

Hier ist mein Test:
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#!/usr/bin/perl

use strict;
use warnings;
use Carp qw/croak/;
use Data::Dumper qw/Dumper/;
use Perl6::Say;
use Benchmark;

use FindBin qw/$Bin/;
use lib $Bin . '/..';
use Bulletinboard::Schema;
use Config::Auto;

my $t0 = Benchmark->new();

my $cfg = Config::Auto::parse($Bin.'/../config/config.pl');

my $schema = Bulletinboard::Schema->connection(
$cfg->{db}->{dsn},
$cfg->{db}->{username},
$cfg->{db}->{password},
$cfg->{db}->{attributes},
);
#$schema->storage->debug(1);

my @topic_loop = ();

my $trs = $schema->resultset('Topic')->search(
undef,
{
order_by => 'me.position ASC',
prefetch => [qw/boards/],
}
);
while( my $topic = $trs->next() ) {

say $topic->topic();

my @board_loop = ();
foreach my $board ( $topic->boards() ) {

say "\t" . $board->title();

# -- get the latest thread
my ($lastthread) = $board->threads(
undef,
{
order_by => 'date_of_creation DESC',
}
)->slice(0,1);

print "\t\t" . $lastthread->subject() . ": ";

my $lastpost = undef;
if( defined $lastthread ) {
($lastpost) = $lastthread->posts(
undef,
{
select => 'timestamp',
order_by => 'timestamp DESC',
}
)->slice(0,1);
say $lastpost->timestamp();
}else{
say "no posts";
}

}

}

my $t1 = Benchmark->new();

my $td = Benchmark::timediff($t1, $t0);
say 'benchmark_timediff: ' . timestr($td);


Der Teil, der den letzten Beitrag errechnet ist derjenige, der die ganze Zeit verbraucht. Kommentiert man ihn aus, bin ich bei 0 wallclock secs ( 0.17 usr + 0.03 sys = 0.20 CPU).

Nur, wie komme ich effizienter an den letzten Beitrag? Sollte ich den besser manuell pflegen? Also die ID des letzten Posts in einem Thread in der Thread-Tabelle mitführen und aktualisieren, wenn ein beitrag verfasst wird?

Grüße, pktm
http://www.intergastro-service.de (mein erstes CMS :) )
nepos
 2009-02-13 16:53
#118852 #118852
User since
2005-08-17
1420 Artikel
BenutzerIn
[Homepage] [default_avatar]
Kenne mich mit DBX::Class nicht aus, aber soweit ich das verstehe, werden hier erstmal alle Threads angezogen und dann alle bis auf den ersten wieder verworfen.
Ähnlich sieht das für mich beim letzten Posting pro Thread aus.
pktm
 2009-02-13 19:14
#118853 #118853
User since
2003-08-07
2921 Artikel
BenutzerIn
[Homepage]
user image
nepos+2009-02-13 15:53:49--
Kenne mich mit DBX::Class nicht aus, aber soweit ich das verstehe, werden hier erstmal alle Threads angezogen und dann alle bis auf den ersten wieder verworfen.
Ähnlich sieht das für mich beim letzten Posting pro Thread aus.


Nein, nicht ganz. slice(0,1); sorgt dafür, dass die LIMIt-Klausel verwendet wird, also genau 1 Beitrag gezogen wird (laut Dokumentation).
Werde das mal überprüfen...
http://www.intergastro-service.de (mein erstes CMS :) )
nepos
 2009-02-15 15:35
#118881 #118881
User since
2005-08-17
1420 Artikel
BenutzerIn
[Homepage] [default_avatar]
Naja, hilft dir aber auch nicht, denn auch fuer LIMIT muss die DB eigentlich erstmal ueber alles drueberlaufen und die Eintraege alle sortieren. Erst dann zieht das LIMIT. Besser waere das wohl mit Hilfe von MAX() oder so die passende Zeile zu finden.
Wie das allerdings mit DBX::Class geht, keine Ahnung...
renee
 2009-02-15 17:11
#118882 #118882
User since
2003-08-04
14371 Artikel
ModeratorIn
[Homepage] [default_avatar]
Wenn es Dir nur um den 1. Eintrag geht, dann nimm besser first statt slice:

Code (perl): (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/perl

use strict;
use warnings;
use Local::DBIC_Schema;
use Benchmark qw(cmpthese);

my $schema = Local::DBIC_Schema->connect( 'DBI:SQLite:pktm' );

cmpthese(
    100_000,
    {
        first => sub {
         
            my ($ding) = $schema->resultset('T')->search(
                undef,
                {
                    order_by => 'testid DESC',
                }
            )->first;
        },
        slicer => sub {
            my ($ding2) = $schema->resultset('T')->search(
                undef,
                {
                    order_by => 'testid DESC',
                }
            )->slice(0,1);
        },
    }
);


Code: (dl )
1
2
3
4
C:\>pktm_test.pl
Rate slicer first
slicer 434/s -- -42%
first 748/s 72% --
OTRS-Erweiterungen (http://feature-addons.de/)
Frankfurt Perlmongers (http://frankfurt.pm/)
--

Unterlagen OTRS-Workshop 2012: http://otrs.perl-services.de/workshop.html
Perl-Entwicklung: http://perl-services.de/
renee
 2009-02-15 17:22
#118883 #118883
User since
2003-08-04
14371 Artikel
ModeratorIn
[Homepage] [default_avatar]
Mit dem slice(0,1) holst Du übrigens zwei Elemente. Also solltest Du wenn schon besser slice(0) verwenden...
OTRS-Erweiterungen (http://feature-addons.de/)
Frankfurt Perlmongers (http://frankfurt.pm/)
--

Unterlagen OTRS-Workshop 2012: http://otrs.perl-services.de/workshop.html
Perl-Entwicklung: http://perl-services.de/
pktm
 2009-02-15 18:22
#118886 #118886
User since
2003-08-07
2921 Artikel
BenutzerIn
[Homepage]
user image
Sooooo! Danke für die Tipps! Die und ein paar Stunden im irc.perl.org#dbix-class habn nun folgendes zustande gebracht:
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
sub overview {
my $self = shift;

return $self->authz->forbidden() unless $self->authz('privileges')->authorize(__PACKAGE__, $self->get_current_runmode());

my $schema = $self->schema();
my @topic_loop = ();

my $trs = $schema->resultset('Topic')->search(
undef,
{
order_by => 'me.position ASC',
prefetch => [qw/boards/],
}
);
while( my $topic = $trs->next() ) {

my @board_loop = ();
my $board_rs = $topic->boards(
undef,
{
'+select' => [{count => 'thread_id'}],
'+as' => ['thread_count'],,
join => [qw/threads/],
group_by => [qw/board_id/],
},
);
while( my $board = $board_rs->next() ) {

# -- get the latest thread
my ($lastthread) = $board->threads(
undef,
{
order_by => 'date_of_creation DESC',
}
)->first();

my $lastpost = undef;
if( defined $lastthread ) {
($lastpost) = $lastthread->posts(
undef,
{
'+select' => [qw/user.username user.user_id/],
'+as' => [qw/username user_id/],
select => 'timestamp',
order_by => 'timestamp DESC',
join => [qw/user/],
}
)->first();
}

my $reply_count = 0;
if( defined $lastthread ) { # if there is a thread
my $rs = $board->threads(
undef,
{
'+select' => {count => 'posts.post_id'},
'+as' => 'reply_count',
join => [qw/posts/],
group_by => [qw/me.thread_id/],
}
);
foreach my $t ( $rs->all() ) {
$reply_count += $t->get_column('reply_count') - 1;
}
}

push @board_loop, {
board_id => $board->board_id(),
title => $board->title(),
thread_count => $board->get_column('thread_count'),
reply_count => $reply_count,

last_post_username => (defined $lastpost ? $lastpost->get_column('username') : undef),
last_post_timestamp => (defined $lastpost ? $lastpost->timestamp() : undef),
last_post_user_id => (defined $lastpost ? $lastpost->get_column('user_id') : undef),

last_thread_subject => (defined $lastthread ? $lastthread->subject() : undef),
last_thread_id => (defined $lastthread ? $lastthread->thread_id() : undef),
};
}

push @topic_loop, {
topic_id => $topic->topic_id(),
topic => $topic->topic(),
boards => \@board_loop,
};
}

$self->param(topics => \@topic_loop);

my $t = $self->load_tmpl('_overview.tmpl', associate => $self);
return $t->output();
} # /overview


Damit bin ich im normalen Betrieb (CGI) bei 1 wallclock secs ( 0.14 usr + 0.03 sys = 0.17 CPU). Sieht gleich viel besser aus :)
Mal schauen, ob ich die Antwortsumme pro Board noch ein Stück schneller berechnen kann.
http://www.intergastro-service.de (mein erstes CMS :) )
<< >> 7 Einträge, 1 Seite



View all threads created 2009-02-13 15:01.