Bonjour à tous !
Étant en désarroi j'aimerai avoir vos lumières sur la lenteur d'une requête sur doctrine 2.
L’environnement :
Symfony 2 - Doctrine 2 - Annotations - pgsql 9.0
Phpinfo() :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 PDO PDO support enabled PDO drivers mysql, sqlite, sqlite2, pgsqlSymfony :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 pdo_pgsql PDO Driver for PostgreSQL enabled PostgreSQL(libpq) Version 9.0.3 Module version 1.0.2 Revision $Id: pdo_pgsql.c,v 1.7.2.11 2006/03/14 10:49:18 edink Exp $
Fichier - parameters.ini
Mon Entité AllGeonames :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14 [parameters] database_driver="pdo_pgsql" database_host="localhost" database_port="5432" database_name="tester" database_user="postgres" database_password="je veux du super rapide : )" ;database_driver_class="PgSqlBundle\Doctrine\DBAL\Driver\PDOPgSql\Driver" mailer_transport="gmail" mailer_host="465" mailer_user="tester@gmail.com mailer_password="tester@" locale="en" secret="aaaaaaaaaaaaaaaaaaaaaa"
Mon entité AlternateNames :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 use Doctrine\ORM\Mapping as ORM; /** * Pld\Bundle\LocalisationBundle\Entity\AllGeonames * * @ORM\Table(name="all_geonames", indexes={@ORM\Index(name="allgeo_asciiname_idx", columns={"asciiname"}), * @ORM\Index(name="allgeo_fcodecountryadm_idx", columns={"country","fcode","admin1","admin2","admin3","admin4"}), * @ORM\Index(name="allgeo_adminunid_idx", columns={"admin1id"}), * @ORM\Index(name="allgeo_admindeuxid_idx", columns={"admin2id"}), * @ORM\Index(name="allgeo_admintroisid_idx", columns={"admin3id"}), * @ORM\Index(name="allgeo_adminquatreid_idx", columns={"admin4id"}), * @ORM\Index(name="allgeo_country_idx", columns={"country"}), * @ORM\Index(name="idx_vecteur_ascii", columns={"textevectorise"}), * @ORM\Index(name="allgeo_asciifcode_idx", columns={"asciiname","fclass"})}) * @ORM\Entity(repositoryClass="Pld\Bundle\LocalisationBundle\Repository\AllGeonamesRepository") */ class AllGeonames { /** * @var integer $geonameid * * @ORM\Column(name="geonameid", type="integer", length=11, nullable=false, unique=true) * @ORM\Id */ private $geonameid; /** * @var string $namegeo * * @ORM\Column(name="namegeo", type="string", length=200, nullable=false) */ private $namegeo; /** * @var string $asciiname * * @ORM\Column(name="asciiname", type="string", length=200, nullable=false) */ private $asciiname; ... ... /** * @var VilleDuMondePays * * @ORM\ManyToOne(targetEntity="VilleDuMondePays", cascade={"persist"}) * @ORM\JoinColumns({ * @ORM\JoinColumn(name="country", referencedColumnName="pays", nullable=true) * }) * */ private $country; ... ... /** * @var VilleDuMondeRegion * * @ORM\ManyToOne(targetEntity="VilleDuMondeRegion", cascade={"persist"}) * @ORM\JoinColumns({ * @ORM\JoinColumn(name="admin1id", referencedColumnName="geonameid", nullable=true) * }) * */ private $admin1id; /** * @var VilleDuMondeRegion * * @ORM\ManyToOne(targetEntity="VilleDuMondeRegion", cascade={"persist"}) * @ORM\JoinColumns({ * @ORM\JoinColumn(name="admin2id", referencedColumnName="geonameid", nullable=true) * }) * */ private $admin2id; ... ... /** * @var array|string $textevectorise * * @ORM\Column(name="textevectorise", type="tsvector", nullable=true) */ private $textevectorise;
Voila l'ensemble :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 use Doctrine\ORM\Mapping as ORM; /** * Pld\Bundle\LocalisationBundle\Entity\AlternateNames * * @ORM\Table(name="alternate_names", indexes={ * @ORM\Index(name="alternatename_idx", columns={"alternatename"}), * @ORM\Index(name="alternate_geoname_idx", columns={"geonameid"})}) * @ORM\Entity(repositoryClass="Pld\Bundle\LocalisationBundle\Repository\AlternateNamesRepository") */ class AlternateNames { /** * @var integer $alternatenameid * * @ORM\Column(name="alternatenameid", type="integer", length=11, nullable=false) * @ORM\Id */ private $alternatenameid; /*ORM\GeneratedValue(strategy="SEQUENCE") *ORM\SequenceGenerator(sequenceName="alternate_names_alternatenameid_seq", allocationSize="1", initialValue="1") */ /** * @var integer $geonameid * * @ORM\ManyToOne(targetEntity="AllGeonames", cascade={"persist"}) * @ORM\JoinColumns({ * @ORM\JoinColumn(name="geonameid", referencedColumnName="geonameid", nullable=false) * }) */ private $geonameid; ... ... }
Maintenant la requête dans le repository Allgeonames :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 public function getCity($name) { $rsm = new ResultSetMapping; $rsm->addScalarResult('ngeo', 'geonameid'); $rsm->addScalarResult('nascii', 'asciiname'); $rsm->addScalarResult('adm1id', 'admin1id'); $rsm->addScalarResult('adm2id', 'admin2id'); $rsm->addScalarResult('adm3id', 'admin3id'); $rsm->addScalarResult('adm4id', 'admin4id'); $rsm->addScalarResult('ctry', 'country'); $query = $this->_em->createNativeQuery(" SELECT * from (select n.asciiname as nascii, n.geonameid as ngeo, admin1id.asciiname AS adm1id, admin2id.asciiname as adm2id, admin3id.asciiname as adm3id, admin4id.asciiname as adm4id, country.name_fr as ctry FROM alternate_names a, all_geonames n left join ville_du_monde_region admin1id on admin1id.geonameid = n.admin1id left join ville_du_monde_region admin2id on admin2id.geonameid = n.admin2id left join ville_du_monde_region admin3id on admin3id.geonameid = n.admin3id left join ville_du_monde_region admin4id on admin4id.geonameid = n.admin4id left join ville_du_monde_pays country on country.pays=n.country where n.geonameid=a.geonameid and a.alternatename like :name limit 5) as t union select * from( SELECT n.asciiname as nascii, n.geonameid as ngeo, admin1id.asciiname as adm1id, admin2id.asciiname as adm2id, admin3id.asciiname as adm3id, admin4id.asciiname as adm4id, country.name_fr as ctry FROM all_geonames n left join ville_du_monde_region admin1id on admin1id.geonameid = n.admin1id left join ville_du_monde_region admin2id on admin2id.geonameid = n.admin2id left join ville_du_monde_region admin3id on admin3id.geonameid = n.admin3id left join ville_du_monde_region admin4id on admin4id.geonameid = n.admin4id left join ville_du_monde_pays country on country.pays=n.country where n.asciiname like :name limit 5) as d limit 5;", $rsm); $query->setParameter('name', $name.'%'); $result = $query->getArrayResult(); return $result }
Je fais cette requête sous pgAdmin avec comme nom : 'Rio de', résultat en 340ms
Maintenant dans mon interface par autocomplete : 'Albervi' , résultat en 1m,15s (pour avoir Alberville).
(Et je suis le seul utilisateur).
Pourtant mes jointures sont faits et ma requêtes les utilises.
3M de lignes dans alternatesNames
2,1M dans AllGeonames
et 300 000 dans VilleDuMondeRegion.
Explain sous pgsql :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 "Limit (cost=388.28..388.33 rows=5 width=2232)" " -> HashAggregate (cost=388.28..388.38 rows=10 width=2232)" " -> Append (cost=10.65..388.11 rows=10 width=2232)" " -> Limit (cost=10.65..214.70 rows=5 width=82)" " -> Hash Left Join (cost=10.65..16049.29 rows=393 width=82)" " Hash Cond: ((n.country)::text = (country.pays)::text)" " -> Nested Loop Left Join (cost=0.00..16033.24 rows=393 width=73)" " -> Nested Loop Left Join (cost=0.00..12866.18 rows=393 width=63)" " -> Nested Loop Left Join (cost=0.00..9699.11 rows=393 width=53)" " -> Nested Loop Left Join (cost=0.00..6532.05 rows=393 width=43)" " -> Nested Loop (cost=0.00..3364.99 rows=393 width=33)" " -> Index Scan using alternatename_idx on alternate_names a (cost=0.00..8.89 rows=393 width=4)" " Index Cond: (((alternatename)::text >= 'Rio de'::text) AND ((alternatename)::text < 'Rio df'::text))" " Filter: ((alternatename)::text ~~ 'Rio de%'::text)" " -> Index Scan using all_geonames_pkey on all_geonames n (cost=0.00..8.53 rows=1 width=33)" " Index Cond: (n.geonameid = a.geonameid)" " -> Index Scan using ville_du_monde_region_pkey on ville_du_monde_region admin1id (cost=0.00..8.05 rows=1 width=18)" " Index Cond: (admin1id.geonameid = n.admin1id)" " -> Index Scan using ville_du_monde_region_pkey on ville_du_monde_region admin2id (cost=0.00..8.05 rows=1 width=18)" " Index Cond: (admin2id.geonameid = n.admin2id)" " -> Index Scan using ville_du_monde_region_pkey on ville_du_monde_region admin3id (cost=0.00..8.05 rows=1 width=18)" " Index Cond: (admin3id.geonameid = n.admin3id)" " -> Index Scan using ville_du_monde_region_pkey on ville_du_monde_region admin4id (cost=0.00..8.05 rows=1 width=18)" " Index Cond: (admin4id.geonameid = n.admin4id)" " -> Hash (cost=7.51..7.51 rows=251 width=14)" " -> Seq Scan on ville_du_monde_pays country (cost=0.00..7.51 rows=251 width=14)" " -> Limit (cost=10.65..173.21 rows=5 width=82)" " -> Nested Loop Left Join (cost=10.65..9991.69 rows=307 width=82)" " -> Nested Loop Left Join (cost=10.65..7499.66 rows=307 width=72)" " -> Hash Left Join (cost=10.65..5007.64 rows=307 width=62)" " Hash Cond: ((n.country)::text = (country.pays)::text)" " -> Nested Loop Left Join (cost=0.00..4992.77 rows=307 width=53)" " -> Nested Loop Left Join (cost=0.00..2500.75 rows=307 width=43)" " -> Index Scan using allgeo_asciiname_idx on all_geonames n (cost=0.00..8.72 rows=307 width=33)" " Index Cond: (((asciiname)::text >= 'Rio de'::text) AND ((asciiname)::text < 'Rio df'::text))" " Filter: ((asciiname)::text ~~ 'Rio de%'::text)" " -> Index Scan using ville_du_monde_region_pkey on ville_du_monde_region admin1id (cost=0.00..8.10 rows=1 width=18)" " Index Cond: (admin1id.geonameid = n.admin1id)" " -> Index Scan using ville_du_monde_region_pkey on ville_du_monde_region admin2id (cost=0.00..8.10 rows=1 width=18)" " Index Cond: (admin2id.geonameid = n.admin2id)" " -> Hash (cost=7.51..7.51 rows=251 width=14)" " -> Seq Scan on ville_du_monde_pays country (cost=0.00..7.51 rows=251 width=14)" " -> Index Scan using ville_du_monde_region_pkey on ville_du_monde_region admin3id (cost=0.00..8.10 rows=1 width=18)" " Index Cond: (admin3id.geonameid = n.admin3id)" " -> Index Scan using ville_du_monde_region_pkey on ville_du_monde_region admin4id (cost=0.00..8.10 rows=1 width=18)" " Index Cond: (admin4id.geonameid = n.admin4id)"
Les index sont bien utilisés.
Qu'est ce qui rate chez doctrine ? ou alors qu'est ce que j'ai oublié de faire ?
Ps : tout ça je n'ai pas de cache et autre mis sous doctrine et tout, je ne sais pas encore faire. Peut importe j'ai envie de dire. J'effectue une requête sous pgqdmin qui n'est pas mis en cache ainsi que le résultat, alors donc peut importe qu'il n'y ais encore de cache sous doctrine. Ils sont à "égalité", si j'arrive à bien faire passer l'idée. Je vois juste une trop grande différence et c'est inquiétant.
J'ai fais exprès d'utilisé une requête native, elle n'est pas sencé utilisé le mapping (si je comprends bien), mais cela reste long. Orm bien, juste pour des minis projets... ?
Bien sur qu'un orm est toujours plus lent qu'un accès direct en sql mais comment à ce point ?
Pour l'optique d'une performance d'utilisation comme "LeBonCoin"/"developpez" à fort taux de lecture et d'écriture et utilisateurs, faut lâcher l'Orm ?.
Merci de votre expertise ; ).
Partager