web-dev-qa-db-fra.com

AWS Athena crée une table et une partition

J'ai stocké mes données de capteur dans S3 (écrire des données toutes les 5 minutes):

farm_iot/sensor_data/farm/farm0001/sensor01/1541252701443

1541252701443 est un fichier json contenant les mesures:

{  "temperature": 14.78,  "pressure": 961.70,  "humidity": 68.32}

Il me manque définitivement des compétences de la ruche. Malheureusement, je n’ai trouvé aucun exemple permettant d’extraire des données JPS de timeseries qui m’a permis de démarrer. Je ne suis pas sûr non plus que météo Hive/Athena prenne en charge ce type de récupération de données.

J'ai du mal à créer une table Athena pour ces données ...

CREATE EXTERNAL TABLE IF NOT EXISTS farm.sensor_data (
  device string,
  sensor string,
  data_point string,
  value double
)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
WITH SERDEPROPERTIES (
  'serialization.format' = '1'
) LOCATION 's3://farm-iot/sensor_data/farm/farm0001/sensor01/'
PARTITIONED BY (timestamp string)
TBLPROPERTIES ('has_encrypted_data'='false')

Une autre voie à laquelle je pense est de stocker les données dans une structure plus facile à traiter/peut-être que je n'ai pas assez partitionné les données ??!

alors peut-être que je devrais ajouter dt à la structure comme ceci:

farm_iot/sensor_data/2018-11-03-02-45-02/farm/farm0001/sensor01/1541252701443

ne me fait toujours pas où je veux être:

+---------------+----------+----------+-------------+--------+
| timestamp     | device   | sensor   | data_point  | value  |
+---------------+----------+----------+-------------+--------+
| 1541252701443 | farm0001 | sensor01 | temperature |  14.78 |
+---------------+----------+----------+-------------+--------+
| 1541252701443 | farm0001 | sensor01 | humidity    |  68.32 |
+---------------+----------+----------+-------------+--------+
| 1541252701443 | farm0001 | sensor01 | pressure    | 961.70 |
+---------------+----------+----------+-------------+--------+

Tout pointeur vers cet objectif serait très apprécié. Je vous remercie!

remarque: je ne veux pas utiliser de colle et j'aime comprendre comment le faire manuellement. en plus de la colle déjà créée ~ environ 16 000 tables hier :)

7
mark

Tout d’abord, merci à @hlagos pour son aide.

AWS Athena n'a pas été en mesure de transformer les données du capteur JSON de la manière dont j'en avais besoin (nous en avons discuté dans les commentaires de @hlagos). Par conséquent, le moyen le plus "simple" de gérer cette situation consistait à modifier le format de données de JSON à CSV afin de le rapprocher du format dont j'avais besoin.

Je stocke maintenant les données du capteur au format CSV au format CSV (écriture des données toutes les 5 minutes) et ajoute les partitions de jour et de périphérique dont nous avons parlé. 

Structure de dossier résultante:

farm_iot/sensor_data/farm/day=20181129/device=farm0001/1543535738493

le contenu des données du fichier CSV:

sensor01,temperature,2.82
sensor01,pressure,952.83
sensor01,humidity,83.64
sensor02,temperature,2.61
sensor02,pressure,952.74
sensor02,humidity,82.41

la définition de la table AWS Athena:

CREATE EXTERNAL TABLE IF NOT EXISTS farm.sensor_data (
  `sensor` string,
  `data_point` string,
  `value` double 
) 
PARTITIONED BY (day string, device string)
ROW FORMAT DELIMITED
    FIELDS TERMINATED BY ','
    ESCAPED BY '\\'
    LINES TERMINATED BY '\n'
LOCATION 's3://farm-iot/sensor_data/farm/'
TBLPROPERTIES ('has_encrypted_data'='false');

Les partitions que j'ajoute aiment ceci (plus tard, j'aurai un script pour créer les partitions à l'avance):

msck repair table farm.sensor_data

maintenant je peux interroger les données:

select regexp_extract("$path", '[^/]+$') as timestamp, device, sensor, 
    data_point, value from farm.sensor_data where day='20181104'

Results
    timestamp       device      sensor      data_point  value
1   1541310040278   farm0001    sensor01    temperature 21.61
2   1541310040278   farm0001    sensor01    pressure    643.65
3   1541310040278   farm0001    sensor01    humidity    74.84
4   1541310040278   farm0001    sensor02    temperature 9.14
5   1541310040278   farm0001    sensor02    pressure    956.04
6   1541310040278   farm0001    sensor02    humidity    88.01
7   1541311840309   farm0001    sensor01    temperature 21.61
8   ...
1
mark

Laissez-moi essayer d’expliquer quelques problèmes que je vois au premier plan. 

  • Il semble que la sortie souhaitée attend des données faisant partie de l'emplacement du fichier de chemin, du périphérique et du capteur. Toutefois, elles ne sont pas définies dans la définition de la table. Seules les colonnes de la définition de la table ou les colonnes virtuelles seront disponibles. . 
  • Plusieurs petits fichiers peuvent affecter les performances de vos requêtes (sans que cela n'affecte le résultat souhaité). 
  • Les partitions Hive sont utilisées pour améliorer les performances des requêtes en évitant d'analyser le fichier Toutes les données. Les partitions pointent vers des dossiers. Dans ce cas, vous essayez d'accéder à des fichiers spécifiques. 
  • Votre sortie souhaitée est en train d'exploser un enregistrement dans plusieurs enregistrements, cela ne devrait pas être traité à la définition de la table, peut être fait via votre instruction select
  • Les partitions Hive ont la convention de nommage partitionname=partitionvalue; ceci n'est pas obligatoire, mais utile si vous souhaitez anticiper les commandes pour ajouter automatiquement des partitions en fonction de la structure de vos dossiers. 

Voici comment résoudre votre problème si vous voulez interroger principalement par capteur ou par appareil

Changer la structure de vos données

Votre dossier devrait idéalement aller de

farm_iot/sensor_data/farm/farm0001/sensor01/1541252701443

to farm_iot/sensor_data/farm/device = farm0001/sensor = sensor01/1541252701443

Changer la définition de votre table

Votre définition de table doit contenir les emplacements de vos partitions pour pouvoir la sélectionner sans regex et tirer parti de l'amélioration de ses performances (j'imagine qu'une requête courante va filtrer par périphérique ou par capteur. De plus, vous devez ajouter toutes vos colonnes json. qui font partie de votre dossier 

CREATE EXTERNAL TABLE IF NOT EXISTS farm.sensor_data (
  temperature double,
  preassure double,
  humidity double
)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
WITH SERDEPROPERTIES (
  'serialization.format' = '1'
) LOCATION 's3://farm-iot/sensor_data/farm/'
PARTITIONED BY (device string, sensor string)
TBLPROPERTIES ('has_encrypted_data'='false')

Interroger vos données

Il nous manque l'horodatage qui fait essentiellement partie de votre nom de fichier avec l'entrée json. Nous pouvons inclure le nom du fichier pendant l'instruction select en utilisant la colonne virtuelle INPUT__FILE__NAME comme suit

select device, sensor, temperature, preassure, humidity, INPUT__FILE__NAME as mytimestamp from farm.sensor_data

Si vous voulez des valeurs précises, la température et l'humidité, ainsi que différentes lignes, je vous recommande de créer un tableau avec ces trois éléments et de l'exploser, il devrait être très efficace d'exécuter 3 requêtes en utilisant UNION ALL pour ajouter les résultats. 

Ajouter une nouvelle partition

Si vous suivez la convention Hive, vous pouvez utiliser la commande msck repair table pour ajouter automatiquement de nouvelles partitions une fois que de nouveaux périphériques/capteurs sont inclus. Dans le pire des cas, si vous souhaitez conserver la structure de vos dossiers, vous pouvez ajouter des partitions comme suit:

ALTER TABLE test ADD PARTITION (device='farm0001', sensor='sensor01') location 's3://farm_iot/sensor_data/farm/farm0001/sensor01'

NOTE: les nouvelles partitions ne seront pas automatiquement ajoutées, vous devez toujours les ajouter. 

J'ai essayé d'ajouter autant de détails que possible. Si quelque chose n'est pas clair, faites le moi savoir. 

EDIT: Si vos requêtes seront principalement basées sur des séries chronologiques (plage de dates par exemple), je vous recommanderais d’ajouter une partition au niveau du jour (pas plus petite que celle-ci) pour améliorer les performances de vos requêtes. Donc, votre définition de table ressemblerait à 

CREATE EXTERNAL TABLE IF NOT EXISTS farm.sensor_data (
  temperature double,
  preassure double,
  humidity double
)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
WITH SERDEPROPERTIES (
  'serialization.format' = '1'
) LOCATION 's3://farm-iot/sensor_data/farm/'
PARTITIONED BY (dt=long, device string, sensor string)
TBLPROPERTIES ('has_encrypted_data'='false')

Et votre structure de dossier ressemblerait à 

farm_iot/sensor_data/farm/dt = 20191204/device = farm0001/sensor = sensor01/1541252701443

Pour plus de clarté, il n'est pas nécessaire de modifier la table pour chaque nouvelle partition; vous devez uniquement ajouter ces partitions à la table. C'est essentiellement ainsi que Hive saura qu'une nouvelle partition a été créée. Si vous décidez d’utiliser des partitions, c’est le seul moyen. Si vous n’en avez pas (cela affectera les performances), il existe d’autres alternatives pour que cela fonctionne. 

EDIT2:

Si vous souhaitez conserver votre structure de données en l'état et ne pas utiliser de partitions, il est possible d'obtenir les résultats attendus comme suit 

CREATE EXTERNAL TABLE IF NOT EXISTS yourdb.sensordata (
  temperature double,
  pressure double,
  humidity double
)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
WITH SERDEPROPERTIES (
  'serialization.format' = '1'
) 
LOCATION 's3://farm-iot/sensor_data/farm/'
TBLPROPERTIES ('has_encrypted_data'='false');

SET Hive.mapred.supports.subdirectories=TRUE;
SET mapred.input.dir.recursive=TRUE;
select * from yourdb.sensordata;

select 
split(input__file__name, "/")[size(split(input__file__name, "/")) - 1] as ts,
split(input__file__name, "/")[size(split(input__file__name, "/")) - 3] as device,
split(input__file__name, "/")[size(split(input__file__name, "/")) - 2] as sensor,
'temperature' as data_point,
temperature as value
from yourdb.sensordata
union all
select 
split(input__file__name, "/")[size(split(input__file__name, "/")) - 1] as ts,
split(input__file__name, "/")[size(split(input__file__name, "/")) - 3] as device,
split(input__file__name, "/")[size(split(input__file__name, "/")) - 2] as sensor,
'pressure' as data_point,
pressure as value
from yourdb.sensordata
union all
select 
split(input__file__name, "/")[size(split(input__file__name, "/")) - 1] as ts,
split(input__file__name, "/")[size(split(input__file__name, "/")) - 3] as device,
split(input__file__name, "/")[size(split(input__file__name, "/")) - 2] as sensor,
'humidity' as data_point,
humidity as value
from yourdb.sensordata;

Comme vous le voyez, je tire le meilleur parti des informations du chemin du fichier, mais il est nécessaire de définir des indicateurs pour indiquer à Hive les dossiers lus de manière récursive 

ts,device,sensor,_data_point,value
1541252701443,farm0001,sensor01,temperature,14.78
1541252701443,farm0001,sensor01,pressure,961.7
1541252701443,farm0001,sensor01,humidity,68.32
4
hlagos