É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_block
table.
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 timestamp
et 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 JOIN
avec mes tableaux de données. Voici une 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,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