Cube virtuel - au lieu d'OLAP

Lorsque vous faites le contraire et obtenez la mĂȘme chose ...

Ayant pour tùche de traitement analytique (calcul / agrégation) des données, vous devez trouver un compromis entre réactivité, rapidité et commodité.


Certains systĂšmes sont bien indexĂ©s et trouvĂ©s, d'autres peuvent rapidement calculer et agrĂ©ger des donnĂ©es, tandis que d'autres sont simples. Quelque part, il est nĂ©cessaire d'organiser le prĂ©chargement et l'indexation des donnĂ©es avec toutes les difficultĂ©s associĂ©es, et quelque part l'utilisateur dispose d'une abstraction de son modĂšle de source et de donnĂ©es agrĂ©gĂ©es au-dessus des stockages physiques et des bases de donnĂ©es externes ou intĂ©grĂ©s utilisĂ©s directement pendant les calculs. Dans tous les cas, l'utilisateur, du programmeur Ă  l'analyste, doit faire un travail relativement important, en commençant par prĂ©parer des donnĂ©es brutes et compiler des requĂȘtes, des modĂšles informatiques, pour finir par visualiser le rĂ©sultat sur des widgets, bien sĂ»r, «Sexy» - beau, rĂ©actif et comprĂ©hensible - sinon tout le travail accompli ira Ă  l'Ă©gout. Et souvent, comme par hasard, en passant par les affres du choix d'une solution, nous remarquons comment une tĂąche simple et comprĂ©hensible Ă  premiĂšre vue se transforme en un monstre effrayant, inutile de se battre avec les moyens disponibles, et nous devons inventer d'urgence quelque chose - un vĂ©lo "avec blackjack et putes" ©. Notre vĂ©lo est parti, il fait mĂȘme un bon tour de bosses et fait face Ă  des obstacles que l'on ne pouvait que deviner auparavant.


Ci-dessous sera décrit un cÎté du dispositif interne d'origine du "Rubik's Cube" fictif - traitement informatique pour la visualisation interactive des données.


Une tĂąche simple doit ĂȘtre rĂ©solue simplement, et une tĂąche difficile doit Ă©galement ĂȘtre simple, mais plus longue ...

Commençant Ă  crĂ©er un systĂšme avec de petites forces, nous sommes passĂ©s du simple au complexe. En crĂ©ant un constructeur, nous Ă©tions convaincus en interne que nous comprenons bien la finalitĂ© du systĂšme, en luttant simultanĂ©ment avec le dĂ©sir de ne pas en faire trop et le dĂ©sir opposĂ© d'automatiser tout et tout, crĂ©ant un cadre pour tout. De plus, l'un de nos merveilleux frameworks Ă©tait prĂȘt et mĂȘme testĂ© en production - jsBeans. Nous avons donc commencĂ© Ă  dĂ©velopper le prochain systĂšme de traitement de donnĂ©es, qui a grandi et qui est maintenant en mĂȘme temps un produit auto-suffisant - un concepteur et une plate-forme pour dĂ©velopper toute une classe de systĂšmes de traitement de donnĂ©es. Conditionnellement, dans l'article, nous l'appellerons «Rubik's Cube» afin de se passer de publicitĂ©, mais pour dĂ©crire des solutions intĂ©ressantes, Ă  notre avis.


Cube, tranche, mesure


La tùche principale - disposer d'ensembles de données non liées, y compris des bases de données et des fichiers externes hétérogÚnes, pour former un modÚle multidimensionnel à partir d'éléments interconnectés des données sources et des résultats de leur traitement analytique pour la visualisation sur des tableaux de bord dynamiques et des widgets interconnectés.


Mettez simplement, par exemple, comme un tableau de bord cliquable:


Exemple de tableau de bord évalué par l'école


Un tel modÚle de données multidimensionnel dans notre systÚme est appelé «Cube» et représente littéralement une collection abstraite d'ensembles de données mutables appelés «Slice», interconnectés par des champs / colonnes de sortie communs (affichés) ou des champs internes appelés «Dimensions» et utilisés pour filtrer et reliant les tranches les unes aux autres.


Une tranche peut ĂȘtre reprĂ©sentĂ©e comme une table ou vue virtuelle ( CTE ) avec des paramĂštres et un corps de requĂȘte variable, selon les conditions de filtrage. L'essentiel est que les donnĂ©es de sortie changent, en fonction des conditions de recherche de contexte (Ă  l'intĂ©rieur du widget) et du filtre global, qui est construit en sĂ©lectionnant des valeurs sur les widgets et en utilisant des fonctions logiques de base (AND / OR / NOT) et des combinaisons.


Le filtre global vous permet de "faire tourner le Rubik's Cube", comme dans la vidéo :



Si le champ de sortie d'une tranche est en mĂȘme temps une mesure dans une autre tranche, a le mĂȘme nom, alors les valeurs de ce champ sont perçues par le systĂšme comme des «faits» (si nous parlions d' OLAP ), dĂ©finies sous la forme d'un filtre global qui modifie les ensembles de donnĂ©es d'origine lors des calculs et de l'agrĂ©gation . En consĂ©quence, il existe une interaction dynamique de widgets, dans laquelle les valeurs des indicateurs affichĂ©s dĂ©pendent des Ă©lĂ©ments et des filtres sĂ©lectionnĂ©s.


Une tranche est un ensemble de donnĂ©es qui peuvent ĂȘtre modifiĂ©es "par des mesures" - initiales ou les rĂ©sultats de calculs analytiques; caractĂ©risĂ© par des champs / colonnes de sortie, une liste de mesures prises en charge et un ensemble de paramĂštres avec des valeurs par dĂ©faut; dĂ©crit par une requĂȘte relativement Ă©lĂ©gante dans un Ă©diteur visuel qui prend en charge le filtrage, le tri, le regroupement / l'agrĂ©gation, les intersections (JOIN), les unions (UNION), la rĂ©cursivitĂ© et d'autres manipulations.


Les tranches qui s'utilisent comme sources décrivent la structure interne d'un cube, par exemple:


Exemple de structure de cube


Exemple de slicer dans l'éditeur:


Exemple d'éditeur de demande de tranche


Une tranche prend en charge Ă  la fois les mesures spĂ©cifiĂ©es explicitement dans les champs de sortie et hĂ©rite des mesures des sources de requĂȘte - cela signifie que la sortie de la tranche peut ĂȘtre modifiĂ©e mĂȘme en raison de changements dans d'autres sources de tranches. En d'autres termes, les rĂ©sultats de la tranche peuvent ĂȘtre filtrĂ©s non seulement par les champs de sortie, mais Ă©galement par les mesures de champs internes des sources, quelque part dans la profondeur de la requĂȘte, jusqu'aux tables de base de donnĂ©es primaires.


La structure de la requĂȘte est dĂ©veloppĂ©e et modifiĂ©e automatiquement par le systĂšme au moment de l'exĂ©cution, en fonction du filtre global actuel et des paramĂštres d'entrĂ©e, en les faisant glisser plus profondĂ©ment dans la requĂȘte en fonction du modĂšle de cube, des mesures dĂ©clarĂ©es et des tranches.


Un exemple de filtre global simple, littéralement, lorsqu'un utilisateur a validé ou sélectionné des valeurs sur plusieurs widgets:


Un exemple de filtre global sur un tableau de bord


Le filtre global est stockĂ© dans une requĂȘte JSON:


Exemple JSON d'un filtre global dans le corps de la demande


La demande parvient à la source primaire (à la base de données) déjà sous forme préparée, aprÚs avoir traversé plusieurs étapes principales:


  • Demander l'assemblage, y compris la sĂ©lection et l'incorporation de tranches optimales, en tenant compte du filtre global actuel (lorsque le filtre est absent ou simple, vous pouvez sĂ©lectionner des tranches simples / rapides; lorsque le filtre est complexe - tranches avec une structure complexe et des mesures supplĂ©mentaires);
  • IntĂ©grer un filtre global et ajouter des filtres aux corps des requĂȘtes et des sous-requĂȘtes;
  • IntĂ©grer des macros et des expressions de requĂȘte de modĂšle;
  • Optimisation des requĂȘtes, y compris la suppression des champs et expressions inutilisĂ©s;
  • OpĂ©rations supplĂ©mentaires avec la requĂȘte pour les spĂ©cificitĂ©s des bases de donnĂ©es primaires (par exemple, si nous parlons de SQL et que la base de donnĂ©es ne contient pas WITH, les requĂȘtes nommĂ©es sont intĂ©grĂ©es).

Et la derniÚre étape est la traduction de la demande au format de la source principale, par exemple, en SQL:


Exemple de demande finale avec filtre intégré


Quand les sources sont différentes


En rĂšgle gĂ©nĂ©rale, tout est simple et clair lorsque vous devez travailler avec un seul entrepĂŽt de donnĂ©es. Mais, lorsqu'il y en a plusieurs et qu'ils sont fondamentalement diffĂ©rents - vous devez appliquer des astuces diffĂ©rentes pour chaque tĂąche spĂ©cifique. Et vous voulez toujours avoir une solution universelle qui soit toujours adaptĂ©e, de prĂ©fĂ©rence prĂȘte Ă  l'emploi, avec un maximum de modifications mineures. Pour ce faire, une autre abstraction le supplie: sur les entrepĂŽts de donnĂ©es, d'une part, la mise en Ɠuvre de l'harmonisation des formats et des langages de requĂȘte, et d'autre part, la garantie de l'interdĂ©pendance des donnĂ©es, au moins au niveau des conditions de filtrage supplĂ©mentaires dans les requĂȘtes Ă  une source par des valeurs provenant d'une autre.


Pour ce faire, nous avons dĂ©veloppĂ© un langage de requĂȘte universel, adaptĂ© Ă  la fois pour reprĂ©senter un modĂšle virtuel de donnĂ©es de cube et pour travailler avec des stockages arbitrairement conditionnels en traduisant la requĂȘte dans le format et la langue souhaitĂ©s. Par une heureuse coĂŻncidence, le langage de requĂȘte, initialement destinĂ© Ă  la simple cartographie et au filtrage des donnĂ©es de diverses sources, est facilement devenu un langage de recherche et de traitement de donnĂ©es Ă  part entiĂšre qui permet de construire des constructions de calcul du plus simple au plus complexe en plusieurs pages et avec de nombreuses sous-requĂȘtes.


Les sources peuvent ĂȘtre divisĂ©es en trois types:


  1. fichiers de données à télécharger sur le systÚme;
  2. Bases de données prenant en charge le traitement complet des données et d'autres opérations;
  3. les stockages qui prennent en charge uniquement l'extraction de données avec ou sans filtrage, y compris divers types de services externes.

Tout est sans ambiguĂŻtĂ© avec le premier type - le module d'importation est intĂ©grĂ© dans le systĂšme, qui analyse diffĂ©rents formats d'entrĂ©e et plonge les rĂ©sultats dans le rĂ©fĂ©rentiel. Pour l'importation, un constructeur spĂ©cial a Ă©galement Ă©tĂ© dĂ©veloppĂ©, qui devrait ĂȘtre discutĂ© sĂ©parĂ©ment.


Le deuxiĂšme type est celui des bases de donnĂ©es autonomes, pour lesquelles il vous suffit de traduire la requĂȘte d'origine dans le format et la langue souhaitĂ©s de la requĂȘte, le dialecte.


Le troisiĂšme type nĂ©cessite au moins des donnĂ©es de post-traitement. Et tous les types en mĂȘme temps peuvent Ă©galement nĂ©cessiter un post-traitement - intersections, unions, agrĂ©gation et calculs finaux. Cela se produit lorsque le traitement des donnĂ©es dans une base de donnĂ©es doit ĂȘtre effectuĂ© en tenant compte des rĂ©sultats du filtrage dans un autre externe.


L'exemple le plus simple est lorsqu'une recherche floue est effectuée dans une base de données et qu'en sortie, il est nécessaire d'obtenir une agrégation d'indicateurs stockés dans une autre base de données sur un autre serveur, en tenant compte des résultats de la recherche.


Pour implĂ©menter le travail d'un tel schĂ©ma, un algorithme simple est implĂ©mentĂ© dans notre systĂšme - la requĂȘte initiale est prĂ©parĂ©e simultanĂ©ment par plusieurs interprĂštes, chacun pouvant soit refuser d'exĂ©cuter la requĂȘte lorsqu'elle est incompatible, soit renvoyer un itĂ©rateur avec des donnĂ©es, soit transformer la requĂȘte et lancer le travail de la chaĂźne de prĂ©paration de la requĂȘte suivante par un autre interprĂšte . Par consĂ©quent, pour une seule demande, nous obtenons d'un Ă  plusieurs itĂ©rateurs paresseux qui forment le mĂȘme rĂ©sultat, mais de diffĂ©rentes maniĂšres, parmi lesquelles le meilleur est sĂ©lectionnĂ© (selon divers critĂšres dĂ©finis par le dĂ©veloppeur dans la configuration).


La stratĂ©gie de sĂ©lection de l'itĂ©rateur est spĂ©cifiĂ©e dans les paramĂštres de configuration ou de requĂȘte. Actuellement, plusieurs stratĂ©gies majeures sont prises en charge:


  • premier, tout, dernier;
  • par type de base de donnĂ©es prioritaire;
  • par prioritĂ© des chaĂźnes qui formaient les itĂ©rateurs;
  • par la fonction de pondĂ©ration de la "pondĂ©ration de la demande";
  • selon le premier rĂ©sultat - tous les itĂ©rateurs sont lancĂ©s en parallĂšle et le premier rĂ©sultat est attendu, par consĂ©quent, l'itĂ©rateur le plus rapide est utilisĂ©, les autres sont fermĂ©s.

À la suite d'une telle combinaison pour une demande d'entrĂ©e, nous obtenons plusieurs options pour son exĂ©cution, Ă  la fois en utilisant diffĂ©rentes sources et avec diffĂ©rentes stratĂ©gies d'exĂ©cution - en choisissant la base de donnĂ©es principale / cible dans laquelle la partie principale de la demande sera exĂ©cutĂ©e et l'assemblage final des rĂ©sultats.


Si le SGBD cible prend en charge la connexion de sources externes, il devient possible de créer un circuit inverse dans lequel le SGBD est connecté à l'API systÚme pour recevoir de petites quantités de données du systÚme, par exemple, pour filtrer de gros volumes "en place". Cette intégration est transparente pour l'utilisateur final et l'analyste - le modÚle de cube ne change pas et toutes les opérations sont effectuées automatiquement par le systÚme.


Diagramme de sĂ©quence simplifiĂ© lors de l'interrogation de plusieurs bases de donnĂ©es intĂ©grĂ©es dans une seule requĂȘte


Pour les cas plus complexes, le systĂšme implĂ©mente un interprĂ©teur de requĂȘtes en mĂ©moire interne sur le remarquable moteur de base de donnĂ©es H2 Embedded, qui permet d'intĂ©grer n'importe quelle base de donnĂ©es prise en charge prĂȘte Ă  l'emploi. LittĂ©ralement, cela fonctionne comme ceci - la demande est divisĂ©e en morceaux par groupes de sources, envoyĂ©s pour exĂ©cution, aprĂšs quoi l'assemblage et le traitement final des rĂ©sultats sont effectuĂ©s en mĂ©moire, dans H2.


À premiĂšre vue, un tel schĂ©ma d'intĂ©gration de donnĂ©es au niveau de l'interprĂ©teur interne semble «difficile», et cela est vrai si vous devez travailler avec de grands volumes de donnĂ©es d'entrĂ©e et la nĂ©cessitĂ© d'effectuer des calculs aprĂšs intersections et associations d'ensembles de sources externes. En fait, cette circonstance est partiellement nivelĂ©e - en mĂȘme temps, la demande est exĂ©cutĂ©e par plusieurs gestionnaires dans diffĂ©rentes versions, par consĂ©quent, l'interprĂ©teur n'est utilisĂ© que dans les cas les plus extrĂȘmes, comme solution universelle prĂȘte Ă  l'emploi. En fin de compte, toute intĂ©gration est limitĂ©e par les coĂ»ts de transport typiques de prĂ©paration, de transmission sur le rĂ©seau et de rĂ©ception de donnĂ©es, et c'est une tĂąche complĂštement diffĂ©rente.


CÎté technique


Du point de vue technique, dont vous ne pouvez probablement pas vous passer, en abordant ce sujet, le systÚme est également conçu selon le principe - pour faire plus, mais tout simplifier autant que possible.


Le systÚme de traitement des données est implémenté au-dessus du cadre client-serveur jsBeans sous la forme d'un ensemble de modules supplémentaires et de projets d'assemblage spécifiques. jsBeans, à son tour, est implémenté en Java, fonctionne comme un serveur d'applications, en gros est un groupe de Rhino, Jetty et Akka, et comprend également la technologie de bean client-serveur développée par notre équipe, et une riche bibliothÚque de composants assemblés sur plusieurs années d'application réussie.


Le Rubik's Cube est complÚtement et complÚtement implémenté en JavaScript sous la forme de nombreux js-bins (fichiers * .jsb), dont certains ne fonctionnent que sur le serveur. L'autre partie est sur le client, et le reste est un composant composant, fonctionnant comme un tout distribué, dont les parties interagissent les unes avec les autres, transparentes pour le développeur, mais sous son contrÎle. Les bacs Js peuvent avoir différentes stratégies de vie, par exemple, avec ou sans session utilisateur, et bien plus encore. Le bean est isomorphe; il permet au client et au serveur de travailler avec lui comme une instance virtuelle d'une classe réguliÚre. Le bac est décrit par un seul fichier et comprend trois sections - pour les champs et les méthodes s'exécutant sur le client, pour les champs du serveur, ainsi qu'une section de champs synchronisés communs.


Étant donnĂ© que l'article s'est dĂ©jĂ  rĂ©vĂ©lĂ© verbeux pour ne pas ennuyer les lecteurs, il est temps de procĂ©der Ă  l'achĂšvement, avec l'intention de dĂ©crire bientĂŽt les dĂ©tails et les solutions architecturales les plus intĂ©ressantes dans la mise en Ɠuvre de JsBeans et de nos projets basĂ©s sur celui-ci - le sous-systĂšme de visualisation construit, les processus analytiques, le concepteur ontologique des sujets, la langue requĂȘtes, importation de donnĂ©es et autre chose ...


Pourquoi


Cela ne s'est jamais produit auparavant, et lĂ  encore ...

Au départ, il y avait peu d'ensembles de données primaires. Les sujets et les tùches ont été complÚtement spécifiés. Il semblerait, pourquoi un tel tourment? La tùche semblait simple, tout le monde voulait obtenir le résultat immédiatement - en particulier lorsque la solution rapide reposait à la surface, et la bonne exigeait de la persévérance et des décisions équilibrées, en respectant la configuration d'origine. Nous sommes allés dans la direction opposée, des solutions complexes et longues aux solutions simples et rapides, sur la voie de la généralisation de problÚmes particuliers.


La condition principale est que de nouveaux tableaux de bord doivent ĂȘtre construits rapidement, mĂȘme si le nouveau domaine et les besoins analytiques sont trĂšs diffĂ©rents des prĂ©cĂ©dents. Évidemment, vous ne devinerez mĂȘme pas la moitiĂ© des exigences futures, le systĂšme doit ĂȘtre flexible en premier lieu. Affiner la bibliothĂšque de composants, les algorithmes analytiques, connecter de nouveaux types de sources fait partie intĂ©grante de l'adaptation du systĂšme. En d'autres termes, le groupe a fonctionnĂ© - les analystes crĂ©ent des requĂȘtes et des tableaux de bord, et les programmeurs rĂ©alisent rapidement de nouveaux besoins pour eux. Et nous, en tant que programmeurs, avons d'abord cherchĂ© Ă  simplifier notre travail Ă  l'avenir, en essayant de ne pas nuire Ă  la convivialitĂ©.


Et le systÚme a été immédiatement créé universel et adaptatif - nous avons construit un «constructeur par constructeur», développant un cadre au-dessus d'un cadre précédemment créé avec un objectif similaire, mais encore plus général.


La notation des Ă©coles de Moscou sur la base des rĂ©sultats de l'examen d'État unifiĂ© et des olympiades est un exemple de tableau de bord construit de la maniĂšre dĂ©crite ci-dessus Ă  partir du dĂ©chargement du portail de donnĂ©es ouvertes du gouvernement de Moscou.


Cube-Rubik est une plate-forme de base pour le développement de systÚmes d'information et d'analyse. Conçu comme une branche et une suite logique de jsBeans. Il comprend tous les modules nécessaires pour résoudre les problÚmes de collecte, de traitement, d'analyse (informatique et orientée processus) et de visualisation.


jsBeans est un framework web isomorphe à pile complÚte qui implémente la technologie JavaScript bean client-serveur, développée avec une licence ouverte comme outil universel. Lors de son utilisation, il a bien fait ses preuves, dans la plupart des cas, s'intégrant idéalement aux tùches qui nous attendent.

Source: https://habr.com/ru/post/fr452154/


All Articles