PostgreSQL bytea vs smallint []


9

Je cherche à importer des données de séries temporelles multicanaux volumineuses (100 Mo - 1 Go) dans une base de données PostgreSQL. Les données proviennent de fichiers au format EDF qui fragmentent les données en «enregistrements» ou «époques» de généralement quelques secondes chacun. L'enregistrement de chaque époque contient les signaux de chaque canal de données sous forme de tableaux séquentiels d'entiers courts.

Je suis mandaté pour stocker les fichiers dans la base de données, dans le pire des cas comme BLOBs. Compte tenu de cela, je voudrais étudier les options qui me permettraient de faire quelque chose de plus avec les données de la base de données, comme faciliter les requêtes basées sur les données de signal.

Mon plan initial est de stocker les données sur une ligne par enregistrement d'époque. Ce que j'essaie de peser, c'est de savoir s'il faut stocker les données de signal réelles sous forme de bytea ou smallint [] (ou même smallint [] []). Quelqu'un pourrait-il recommander l'un sur l'autre? Je m'intéresse aux coûts de stockage et d'accès. L'utilisation est susceptible d'être insérée une fois, lue occasionnellement, jamais mise à jour. Si l'un était plus facilement présenté comme un type personnalisé, de sorte que je pourrais ajouter des fonctions d'analyse de la comparaison des enregistrements, alors tant mieux.

Je ne doute pas de détails, alors n'hésitez pas à ajouter des commentaires sur ce que vous souhaitez que je clarifie.


2
Cela pourrait être l'une des rares utilisations judicieuses de l'utilisation de tableaux dans le modèle de données autoritaire, car vous économisez beaucoup d'espace disque en évitant la surcharge de ligne de 24 à 28 octets. Les tableaux sont également compressés et stockés hors ligne s'ils sont suffisamment longs.
Craig Ringer

beldaz, la façon dont vous devez stocker les données a beaucoup à voir avec la façon dont vous prévoyez d'y accéder et à quelle fréquence. Si les données sont rarement interrogées et que vous souhaitez toujours extraire les données par enregistrement, je pense qu'une ligne par enregistrement dans un tableau est logique. Cependant, si vous souhaitez effectuer des requêtes légèrement plus approfondies, telles que l'extraction de tous les enregistrements pour un patient_id donné, par exemple, nous pouvons peut-être suggérer une légère amélioration de la structure de stockage. Des idées sur vos modèles de requête?
Chris

@Chris Merci. J'ai omis le composant de métadonnées car il est très petit et peut résider dans une relation distincte. Les modèles de requête sont à déterminer, mais je voudrais peut-être comparer deux fichiers différents enregistrés en même temps et extraire des signaux d'époques simultanées.
beldaz

@CraigRinger Je n'ai pas vu beaucoup de preuves de compression de tableau. Doit-il être activé d'une manière ou d'une autre?
beldaz

Réponses:


11

En l’absence de réponses, j’ai moi-même approfondi la question.

Il semble que les fonctions définies par l'utilisateur puissent gérer tous les types de base, y compris bytea et smallint[], donc cela n'affecte pas beaucoup le choix de la représentation.

J'ai essayé plusieurs représentations différentes sur un serveur PostgreSQL 9.4 fonctionnant localement sur un ordinateur portable Windows 7 avec une configuration vanille. Les relations pour stocker ces données de signal réelles étaient les suivantes.

Grand objet pour tout le fichier

CREATE TABLE BlobFile (
    eeg_id INTEGER PRIMARY KEY,
    eeg_oid OID NOT NULL
);

Réseau SMALLINT par canal

CREATE TABLE EpochChannelArray (
    eeg_id INT NOT NULL,
    epoch INT NOT NULL,
    channel INT,
    signal SMALLINT[] NOT NULL,
    PRIMARY KEY (eeg_id, epoch, channel)
);

BYTEA par canal à chaque époque

CREATE TABLE EpochChannelBytea (
    eeg_id INT NOT NULL,
    epoch INT NOT NULL,
    channel INT,
    signal BYTEA NOT NULL,
    PRIMARY KEY (eeg_id, epoch, channel)
);

Tableau 2D SMALLINT par époque

CREATE TABLE EpochArray (
    eeg_id INT NOT NULL,
    epoch INT NOT NULL,
    signals SMALLINT[][] NOT NULL,
    PRIMARY KEY (eeg_id, epoch)
);

Tableau BYTEA par époque

CREATE TABLE EpochBytea (
    eeg_id INT NOT NULL,
    epoch INT NOT NULL,
    signals BYTEA NOT NULL,
    PRIMARY KEY (eeg_id, epoch)
);

J'ai ensuite importé une sélection de fichiers EDF dans chacune de ces relations via Java JDBC et comparé la croissance de la taille de la base de données après chaque téléchargement.

Les fichiers étaient:

  • Fichier A: 2706 époques de 16 canaux, chaque canal 1024 échantillons (16385 échantillons par époque), 85 Mo
  • Fichier B: 11897 époques de 18 canaux, chaque canal 1024 échantillons (18432 échantillons par époque), 418 Mo
  • Fichier C: 11746 époques de 20 canaux, chaque canal 64 à 1024 échantillons (17088 échantillons par époque), 382 Mo

En termes de coût de stockage, voici la taille occupée en Mo pour chaque cas: Coût de stockage en Mo

Par rapport à la taille du fichier d'origine, les gros objets étaient environ 30 à 35% plus grands. En revanche, le stockage de chaque époque en tant que BYTEA ou SMALLINT [] [] était inférieur de 10%. Le stockage de chaque canal en tant que tuple séparé donne une augmentation de 40%, soit BYTEA soit SMALLINT [], donc pas pire que le stockage en tant que grand objet.

Une chose que je n'avais pas appréciée au départ est que "les tableaux multidimensionnels doivent avoir des extensions correspondantes pour chaque dimension" dans PostgreSQL . Cela signifie que la SMALLINT[][]représentation ne fonctionne que lorsque tous les canaux d'une époque ont le même nombre d'échantillons. Par conséquent, le fichier C ne fonctionne pas avec la EpochArrayrelation.

En termes de coûts d'accès, je n'ai pas joué avec cela, mais au moins en termes d'insertion des données, la représentation la plus rapide était au départ EpochByteaet BlobFile, avec EpochChannelArrayla plus lente, cela prenait environ 3 fois plus longtemps que les deux premières.


D'un point de vue académique, je trouve vos résultats très intéressants, mais d'un point de vue pratique, la taille du stockage est-elle très préoccupante? Peut-être que dans votre cas d'utilisation, vous avez de très nombreux enregistrements, et donc le stockage est un problème auquel vous êtes confronté? Cependant, dans ce format de stockage, toute recherche autre que par époque (ou canal, dans le schéma approprié) nécessiterait la lecture d'une partie de chaque enregistrement. Est-ce correct pour votre application?
Chris

Pratiquement oui, c'est certainement important pour moi, car je m'attends à traiter plusieurs To de fichiers bruts. Il s'avère que le courant dans les frais généraux est inférieur à ce que j'attendais, mais s'il avait été de 300% pour une représentation particulière, je l'éviterais certainement. Quant à l'interrogation, je ne m'attendrais pas à accéder par autre chose que l'époque et le canal.
beldaz
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.