Requêtes lentes sur la table de milliards de lignes // index utilisé

10

Étant donné que je suis un jeune développeur et que je ne suis pas vraiment compétent dans l'utilisation des bases de données (PostgreSQL 9.3), j'ai rencontré des problèmes avec un projet, pour lequel j'ai vraiment besoin d'aide.

Mon projet consiste à collecter des données à partir d'appareils (jusqu'à 1000 appareils ou plus), où chaque appareil envoie un bloc de données chaque seconde, ce qui fait environ 3 millions de lignes par heure.

Actuellement, j'ai une grande table où je stocke les données entrantes de chaque appareil:

CREATE TABLE data_block(
    id bigserial
    timestamp timestamp
    mac bigint
)

Étant donné qu'il existe plusieurs types de données qu'un bloc de données peut (ou ne peut pas) inclure, il existe d'autres tables qui font référence à la data_blocktable.

CREATE TABLE dataA(
    data_block_id bigserial
    data

    CONSTRAINT fkey FOREIGN KEY (data_block_id) REFERENCES data_block(id);
);
CREATE TABLE dataB(...);
CREATE TABLE dataC(...);
CREATE INDEX index_dataA_block_id ON dataA (data_block_id DESC);
...

Il est possible que dans un bloc de données il y ait 3x donnéesA, 1x donnéesB, mais pas de donnéesC.

Les données seront conservées pendant quelques semaines, donc je vais avoir environ 5 milliards de lignes dans ce tableau. Pour le moment, j'ai environ 600 millions de lignes dans le tableau et mes requêtes prennent très longtemps. J'ai donc décidé de faire un index sur timestampet mac, parce que mes instructions select interrogent toujours dans le temps et souvent aussi dans le temps + mac.

CREATE INDEX index_ts_mac ON data_block (timestamp DESC, mac);

... mais mes requêtes prennent toujours des âges. Par exemple, j'ai interrogé des données pour un jour et un mac:

SELECT * FROM data_block 
WHERE timestamp>'2014-09-15' 
AND timestamp<'2014-09-17' 
AND mac=123456789
Index Scan using index_ts_mac on data_block  (cost=0.57..957307.24 rows=315409 width=32) (actual time=39.849..334534.972 rows=285857 loops=1)
  Index Cond: ((timestamp > '2014-09-14 00:00:00'::timestamp without time zone) AND (timestamp < '2014-09-16 00:00:00'::timestamp without time zone) AND (mac = 123456789))
Total runtime: 334642.078 ms

J'ai fait un vide complet avant d'exécuter la requête. Existe-t-il un moyen élégant de résoudre un tel problème avec de grandes tables pour faire une requête <10sec?

J'ai lu sur le partitionnement, mais cela ne fonctionnera pas avec mes références dataA, dataB, dataC à data_block_id, n'est-ce pas? Si cela fonctionnait, devrais-je créer des partitions au fil du temps ou sur Mac?

J'ai changé mon index dans l'autre sens. D'abord MAC, puis horodatage, et il gagne beaucoup de performances.

CREATE INDEX index_mac_ts ON data_block (mac, timestamp DESC);

Mais encore, les requêtes prennent> 30sec. Surtout quand je fais un LEFT JOINavec mes tableaux de données. Voici une EXPLAIN ANALYZEde la requête avec le nouvel index:

EXPLAIN ANALYZE SELECT * FROM data_block WHERE mac = 123456789 AND timestamp < '2014-10-05 00:00:00' AND timestamp > '2014-10-04 00:00:00'
Bitmap Heap Scan on data_block  (cost=1514.57..89137.07 rows=58667 width=28) (actual time=2420.842..32353.678 rows=51342 loops=1)
  Recheck Cond: ((mac = 123456789) AND (timestamp < '2014-10-05 00:00:00'::timestamp without time zone) AND (timestamp > '2014-10-04 00:00:00'::timestamp without time zone))
  ->  Bitmap Index Scan on index_mac_ts  (cost=0.00..1499.90 rows=58667 width=0) (actual time=2399.291..2399.291 rows=51342 loops=1)
        Index Cond: ((mac = 123456789) AND (timestamp < '2014-10-05 00:00:00'::timestamp without time zone) AND (timestamp > '2014-10-04 00:00:00'::timestamp without time zone))
Total runtime: 32360.620 ms 

Malheureusement, mon matériel est strictement limité. J'utilise un Intel i3-2100 à 3,10 GHz, 4 Go de RAM. Mes paramètres actuels sont les suivants:

default_statistics_target = 100
maintenance_work_mem = 512MB
constraint_exclusion = on
checkpoint_completion_target = 0.9
effective_cache_size = 4GB
work_mem = 512MB
wal_buffers = 16MB
checkpoint_segments = 32
shared_buffers = 2GB
max_connections = 20
random_page_cost = 2
homme
la source

Réponses:

1

Cela peut refléter mon biais MS SQL, mais j'essaierais de regrouper la table par timestamp. Si vous extrayez fréquemment des données pour une période de temps spécifique, cela vous aidera car les données seront stockées physiquement de manière contiguë. Le système peut rechercher le point de départ, numériser jusqu'à la fin de la plage et terminer. Si vous interrogez pour une heure spécifique, cela ne représente que 3 600 000 enregistrements.

Si votre requête (qui est ...?) Concerne une machine spécifique, Postgres devra alors filtrer 99,9% de ces 3,6 millions d'enregistrements. Si ce filtre un sur mille est plus sélectif qu'un ajustement de plage de dates typique, vous devez utiliser le macchamp plus sélectif comme premier composant de votre index. Cela vaut peut-être encore la peine d'être regroupé.

Si cela ne fonctionne toujours pas, je partitionnerais par le même champ que vous indexez, soit timestampou mac.

Vous n'avez pas donné les types de données. Sont-ils appropriés aux données? Le stockage de dates sous forme de texte alourdira inutilement votre tableau, par exemple.

Jon de tous les métiers
la source
2
Postgres n'a pas d'index clusterisés (bien qu'il puisse regrouper une table le long d'un index - mais cela doit être fait manuellement et ne "restera" pas)
a_horse_with_no_name
Merci pour le conseil. maintenant, il s'exécute plus rapidement qu'auparavant, mais toujours à des performances très faibles> 30 secondes par requête. J'ai également fait du clustering, mais comme l'a dit @a_horse_with_no_name: en postgres, c'est un coup. mes types de données sont corrects je pense. je les ai ajoutés à la question
manman
Sans tables en cluster, ma prochaine recommandation pour les requêtes de plage serait le partitionnement.
Jon of All Trades du
-2

J'ai travaillé sur une application qui affichait des milliards de relevés de compteurs électriques et exécutais la plupart des requêtes en moins de 10 secondes.

Notre environnement était différent. Microsoft SQL Server sur une machine de classe serveur (4 cœurs, 24 Go de mémoire). Une chance de passer à un serveur?

Un gros problème est que l'ingestion des lectures une par une a eu un impact important sur les performances de la base de données. L'écriture des données nécessaires aux verrous et aux requêtes attendrait. Pouvez-vous faire des insertions par lots?

Avec votre schéma, vous aurez 4 très grandes tables. Il sera important que toutes vos jointures utilisent des index sur les deux tables. Une analyse de table prendra une éternité. Est-il possible de les fusionner en 1 table avec des champs valides nuls?

KC-NH
la source
insertions par lots: je pourrais faire des insertions en bloc mais pour le moment je travaille sur une base de données de test, où aucune insertion n'est effectuée pendant qu'une requête est en cours d'exécution. mais merci j'y penserai plus tard :) les index: j'ai des index sur toutes les tables. sur les tables de données un index sur l'id, sur la table data_block sur (mac, horodatage). le problème est également présent lors de la recherche de donnéesA par jointure gauche, mais il n'y en a pas. même avec index, il recherche les tables de données. champs nullables: ne sont pas possibles car un bloc de données peut avoir plusieurs données d'un même type. 1xdata_block -> 4xdataA eg
manman
Votre outil DB vous fournit-il un analyseur de requêtes? Vous pourriez avoir besoin d'un index sur data_block basé sur id.
KC-NH
je vais essayer, mais je ne comprends pas pourquoi cela peut aider!?
manman
-2

Vous atteignez les limites d'évolutivité inhérentes de Postgres (ou de tout autre SGBDR).

N'oubliez pas qu'un index RDBMS est un B-Tree. Un arbre B est O (log n) pour la moyenne et le pire des cas. Cela en fait un choix agréable, sûr et prévisible pour des valeurs raisonnables de N. Il tombe en panne lorsque N devient trop grand.

Les bases de données NoSQL sont (pour la plupart) des tables de hachage. Une table de hachage est O (1) dans le cas moyen et O (n) dans le pire des cas. En supposant que vous pouvez éviter le pire des cas, il fonctionne très bien pour de très grandes valeurs de N.

De plus, une table de hachage est facile à paralléliser et un b-tree ne l'est pas. Cela rend les tables de hachage plus adaptées à une architecture informatique distribuée.

Lorsque vous commencez à atteindre des milliards de tables de lignes, il est temps d'envisager de passer du RDBMS à NoSQL. Cassandra serait probablement un bon choix pour votre cas d'utilisation.

Professeur Photon
la source
2
Beaucoup de SGBDR ont beaucoup plus d'options que les index B-tree (hachage, bitmap et autres). Certains SGBD stockent des lignes et certains stockent des colonnes. Et O (logn) n'est pas mauvais, même pour des milliards de lignes. Et ils ne peuvent probablement pas atteindre de limite lorsqu'ils utilisent une machine à mémoire de 4 Go.
ypercubeᵀᴹ