Depuis que je suis un jeune développeur et pas vraiment habile à utiliser des bases de données (PostgreSQL 9.3), j'ai rencontré des problèmes avec un projet, où j'ai vraiment besoin d'aide.
Mon projet consiste à collecter des données à partir de périphériques (jusqu'à 1000 périphériques ou plus), où chaque appareil envoie un bloc de données chaque seconde, ce qui rend 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
)
Parce qu'il existe plusieurs types de données, un bloc de données peut (ou ne peut pas) inclure, il existe d'autres tables qui font référence à data_block
tableau.
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 fichier data_block, il y ait 3x dataa, 1x Datax, mais pas de données.
Les données seront conservées pendant quelques semaines. Je vais donc avoir environ 5 milliards de lignes dans ce tableau. Pour le moment, j'ai ~ 600 millions de rangées dans la table et mes requêtes prennent très longtemps. J'ai donc décidé de faire un index sur timestamp
et mac
, car mes instructions sélectionnées interrogent toujours dans le temps et souvent aussi au fil du temps + Mac.
CREATE INDEX index_ts_mac ON data_block (timestamp DESC, mac);
... Mais mes requêtes prennent encore des âges. Par exemple, j'ai interrogé les 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 la course de 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 la partition, mais cela ne fonctionnera pas avec mes références DATAA, DANGE, DATAC à Data_Block_ID Droite? Si cela fonctionnerait d'une manière ou d'une autre, devrais-je faire des partitions au fil du temps ou sur Mac?
J'ai changé mon index dans l'autre sens. Premier Mac, puis horodatage, et il gagne beaucoup de performances.
CREATE INDEX index_mac_ts ON data_block (mac, timestamp DESC);
Mais toujours, les requêtes prennent> 30 secondes. Surtout quand je fais un LEFT JOIN
Avec mes tables de données. Voici un EXPLAIN ANALYZE
de 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.10GHz, 4 Go de RAM. Mes paramètres actuels sont comme suit:
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
Cela peut refléter mon biais MS SQL, mais j'essaierais de regrouper la table par timestamp
. Si vous tirez fréquemment des données pour une période d'heure spécifique, cela aidera car les données seront physiquement stockées de manière contiguë. Le système peut chercher au point de départ, numériser vers la fin de la plage et être effectué. Si vous interrogez une heure spécifique, cela ne représente que 3 600 000 enregistrements.
Si votre requête (qui est ...?) Est pour une machine spécifique, Postgres devra ensuite filtrer 99,9% de ces enregistrements de 3,6 M. Si ce filtre à un millier d'un millier est plus sélectif qu'une plage de date typique Fitler, vous devez utiliser le champ plus sélectif mac
comme premier composant de votre index. Cela peut toujours valoir la mise en regroupement.
Si cela ne le fait toujours pas, je partitionnerais par le même champ que vous indexez, soit timestamp
ou mac
.
Vous n'avez pas donné les types de données. Sont-ils appropriés aux données? Stockage des dates sous forme de texte BLOOTEZ inutilement à votre table, par exemple.