I Benchmark DBS Pour connaître le meilleur pour mon projet et j'ai trouvé que count(*)
est extrêmement lent dans Posgeql. Et je ne comprends pas est-ce un comportement normal de postesql ou de faire quelque chose de mal.
J'ai une table avec ~ 200 m records. Définition de la table MySQL:
CREATE TABLE t1 (
id int(11) NOT NULL AUTO_INCREMENT,
t2_id int(11) NOT NULL,
....
PRIMARY KEY (id),
KEY index_t2 (t2_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Demande (retour ~ 30m):
SELECT COUNT(*) FROM t1 WHERE t2_id = 7;
runs:
25,797ms
Mysql (v5.7.11)
1,222,168ms
Postesql (v9.5)
expliquer :
Mysql:
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t1
partitions: NULL
type: ref
possible_keys: index_t2
key: index_t2
key_len: 4
ref: const
rows: 59438630
filtered: 100.00
Extra: Using index
1 row in set, 1 warning (0.00 sec)
PostgreSQL
Aggregate (cost=4469365.02..4469365.03 rows=1 width=0)
-> Bitmap Heap Scan on t1 (cost=715817.34..4382635.74 rows=34691712 width=0)
Recheck Cond: (t2_id = 7)
-> Bitmap Index Scan on index_t2 (cost=0.00..707144.41 rows=34691712 width=0)
Index Cond: (t2_id = 7)
Serveur: AWS RDS (db.r3.xlarge) VCPU: 4 Mémoire: 30 Go
Mise à jour (2016-09-20) :
> explain (analyze, buffers) SELECT COUNT(*) FROM t1 WHERE t2_id = 7;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Aggregate (cost=4469365.02..4469365.03 rows=1 width=4) (actual time=1213456.539..1213456.539 rows=1 loops=1)
Buffers: shared read=2734808
-> Bitmap Heap Scan on t1 (cost=715817.34..4382635.74 rows=34691712 width=4) (actual time=64015.828..1205542.421 rows=31383566 loops=1)
Recheck Cond: (t2_id = 7)
Rows Removed by Index Recheck: 108582028
Heap Blocks: exact=19929 lossy=2606242
Buffers: shared read=2734808
-> Bitmap Index Scan on index_t2 (cost=0.00..707144.41 rows=34691712 width=0) (actual time=64009.598..64009.598 rows=31383566 loops=1)
Index Cond: (t2_id = 7)
Buffers: shared read=108637
Planning time: 0.080 ms
Execution time: 1213456.891 ms
(12 rows)
Time: 1213484.579 ms
Mise à jour (2016-09-21) :
> explain (analyze, buffers) SELECT t2_id FROM t1 WHERE t2_id = 7;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on t1 (cost=715817.34..4382635.74 rows=34691712 width=114) (actual time=59954.834..1234070.436 rows=31383566 loops=1)
Recheck Cond: (t2_id = 7)
Rows Removed by Index Recheck: 108582028
Heap Blocks: exact=19929 lossy=2606242
Buffers: shared hit=4824 read=2729984
-> Bitmap Index Scan on index_t2 (cost=0.00..707144.41 rows=34691712 width=0) (actual time=59948.598..59948.598 rows=31383566 loops=1)
Index Cond: (t2_id = 7)
Buffers: shared hit=4824 read=103813
Planning time: 0.086 ms
Execution time: 1239826.408 ms
(10 rows)
Time: 1239827.053 ms
La façon dont les deux RDBM font le compte diffère. Dans Innodb Nous avons le comportement suivant par défaut :
Pour traiter un décompte SELECT (*) de la déclaration T, INNODB analyse un index du tableau, qui prend un certain temps si l'index n'est pas entièrement dans le pool tampon.
Pour Postgres, vous voudrez peut-être essayer de voir si un scan d'index unique (qui est plus proche du comportement InnoDB) peut vous aider à ce sujet. Plus d'infos ICI . En raison de la quantité de lignes et de la mauvaise cardinalité de cette valeur (près de 15% de la table Selon des statistiques), je ne peux pas garantir que cela fonctionnera, mais vous pouvez essayer:
SELECT COUNT(t2_id) FROM t1 WHERE t2_id = 7;
Adresse Postgres .
Peut-être un estimation suffit. Tout Compte est une estimation après tout et peut être obsolète au moment où vous le voyez. Un décompte frais minimise la fenêtre de temps. Pour les tables en lecture seule, les statistiques dans le catalogue système sont tout aussi bonnes. De toute façon, beaucoup plus rapide.
Il y a des statistiques de base en pg_class
et beaucoup plus dans pg_statistic
(ou la vue lisible par l'homme pg_stats
Basé sur elle).
Pour obtenir le nombre de lignes pour votre sélection, créez un indice partiel :
CREATE INDEX t1_t2_id_7_idx ON t1((1)) WHERE t2_id = 7;
Une rangée pour l'indice partiel incl. Un nombre de lignes est entré dans pg_class
Immédiatement, y compris un nombre de lignes (ou estimation pour vous, n'aurez même pas à courir ANALYZE
sur la table (ou attendez que l'Autovacuum soit lancé). Maintenant, vous pouvez obtenir une estimation de compte sur Close sans frais:
SELECT reltuples::bigint AS count_estimate
FROM pg_class
WHERE oid = 't1_t2_id_7_idx'::regclass;
le manuel sur pg_class.reltuples
:
Nombre de lignes dans la table. Ce n'est qu'une estimation utilisée par le planificateur. Il est mis à jour par
VACUUM
,ANALYZE
et quelques commandes DDL telles queCREATE INDEX
.
Plus dans ces réponses connexes:
Certains d'entre eux peuvent être dus au " Version multi-version Control Concurrency " (MVCC) Postgres utilise pour gérer les modifications dans les transactions actives. Il doit vérifier chaque ligne de la table pour s'assurer qu'elle devrait être visible à la transaction en cours. Cela peut sembler un peu en arrière, mais dans des circonstances où cela ferait une différence, MySQL peut plutôt verser simplement la table entière qui pourrait avoir un impact significatif sur la concurrence et la performance de nombreuses charges de travail.
Il existe des méthodes de cours qui pourraient être utilisées pour minimiser l'impact que cela a sur SELECT COUNT(*)
ou des requêtes similaires, mais ces méthodes augmentent le travail à effectuer dans d'autres processus afin d'être considérés comme des mauvaises optimisations ( Optimisation de SELECT COUNT(*)
au détriment de la charge supplémentaire du processeur, et dans certains cas IO, pour TOUS Insertion de ligne simple ou mise à jour).