Quinteto em vez de Byte - abordagem de armazenamento e recuperação de dados

O Quintet é uma maneira de apresentar dados atômicos, indicando seu papel na área de negócios. Os quintetos podem descrever qualquer item, enquanto cada um deles contém informações completas sobre si e suas relações com outros quintetos. Essa descrição não depende da plataforma usada. Seu objetivo é simplificar o armazenamento de dados e melhorar a visibilidade de sua apresentação.



Discutiremos uma abordagem para armazenar e processar informações e compartilharemos algumas ideias sobre a criação de uma plataforma de desenvolvimento nesse novo paradigma. Para que? Para desenvolver mais rapidamente e em iterações mais curtas: esboce seu projeto, verifique se é o que você pensou, refine-o e continue refinando o resultado.

O quinteto tem propriedades: tipo, valor, pai e ordem entre os pares. Assim, existem 5 componentes, incluindo o identificador. Essa é a forma universal mais simples de registrar informações, um novo padrão que poderia atender a qualquer demanda de programação. Os quintetos são armazenados no sistema de arquivos da estrutura unificada, em uma massa indexada homogênea contínua de dados. O modelo de dados do quinteto - um modelo de dados que descreve qualquer estrutura de dados como uma única lista interconectada de tipos e termos básicos baseados neles (metadados), bem como instâncias de objetos armazenados de acordo com esses metadados (dados).

Meio minuto de letra
Atualmente, há um número infinito de padrões para registrar dados, inúmeras abordagens e regras, cujo conhecimento é necessário para trabalhar com esses registros. Os padrões são descritos separadamente e não estão diretamente relacionados aos dados correspondentes. No caso de quintetos, com qualquer um deles, você pode obter informações relevantes sobre sua natureza, propriedades e regras de processamento na área de negócios do usuário. Seu padrão é unificado e fixo para todas as áreas. O quinteto está oculto do usuário - os metadados e os dados estão disponíveis para o último de uma maneira compreensível comum.

Quinteto não é apenas informação, mas também pode representar código executável. Mas, acima de tudo, são os dados que você deseja gravar, armazenar e recuperar. Como no nosso caso, os quintetos são diretamente endereçáveis, interconectados e indexados, nós os armazenaremos em um tipo de banco de dados.


Por que Quinteto em vez de Byte?


Não é um bit ou impulso eletrônico que orienta o giro magnético.

Estamos acostumados a medir os dados em bytes, seja um tamanho de documento ou foto, limite de tráfego na Internet ou espaço disponível no seu dispositivo móvel. Propomos outra medida - Quintet - que não possui um tamanho fixo, como o Byte, mas representa uma quantidade atômica de dados, que tem algum valor para o usuário.

Por exemplo, você pode dizer que seu banco de dados ocupa 119 megabytes de armazenamento ou pode afirmar que esse banco de dados armazena 1,37 mega quintetos. Você não se importa muito com o que é um byte nesse contexto, mas entende que esse banco de dados contém 1,37 milhão de descrições de termos, objetos, atributos, links, eventos, consultas com detalhes, etc. Possuir 1,37 milhão de dados valiosos parece mais sexy do que ter 119 megabytes de coisas em você.

Portanto, isso não é para substituir a maneira como as informações são armazenadas no suporte de dados, mas para mudar para outro nível de abstração.

Estrutura quinteto


A idéia principal deste artigo é substituir tipos de máquinas por termos humanos e substituir variáveis ​​por objetos. Não pelos objetos que precisam de construtor, destruidor, interfaces e coletor de lixo, mas pelas unidades de informações claras que um cliente manipula. Ou seja, se o cliente disser "Cliente", salvar a essência dessa declaração no meio não exigiria a experiência de um programador.



Faz sentido concentrar a atenção do usuário apenas no valor do objeto, enquanto seu tipo, pai, ordem (entre iguais na subordinação) e identificador devem ser óbvios no contexto ou simplesmente ocultos. Isso significa que o usuário não sabe nada sobre quintetos, ele simplesmente distribui uma tarefa, certifica-se de que seja aceita corretamente e inicia sua execução.

Conceitos básicos


Há um conjunto de tipos de dados que todos entendem: sequência, número, arquivo, texto, data e assim por diante. Um conjunto tão simples é suficiente para esboçar a solução e "programá-la" juntamente com os termos necessários para sua implementação. Os tipos básicos representados por quintetos podem ser assim:



Nesse caso, alguns dos componentes do quinteto não são usados, enquanto o quinteto em si é usado como o tipo básico. Isso facilita a navegação do kernel do sistema ao coletar metadados.

O fundo


Devido à lacuna analítica entre o usuário e o programador, ocorre uma deformação significativa dos conceitos no estágio de descrição de um projeto. O eufemismo, incompreensibilidade e iniciativa não solicitada muitas vezes transforma uma idéia simples e razoável do cliente em uma bagunça logicamente impossível, se for avaliada do ponto de vista do usuário.



A transferência de conhecimento deve ocorrer sem perda e distorção. Além disso, ao organizar o armazenamento desse conhecimento, você deve se livrar melhor das restrições impostas pelo sistema de gerenciamento de dados escolhido.

Como armazenamos os dados agora


Normalmente, existem muitos bancos de dados no servidor; cada um deles contém uma descrição do esquema de dados com um conjunto específico de detalhes - dados interconectados logicamente. Eles são armazenados no suporte de dados em uma ordem específica, idealmente - ideal para reduzir os esforços de recuperação.
O sistema de armazenamento de informações proposto é um compromisso entre vários métodos conhecidos: orientado a colunas, relacionais e NoSQL. Ele foi projetado para resolver as tarefas normalmente executadas por uma dessas abordagens.

Por exemplo, a teoria do DBMS orientado a colunas é bonita: lemos apenas a coluna desejada, mas não todas as linhas de registros como um todo. No entanto, na prática, é improvável que os dados sejam colocados na mídia para que seja conveniente recuperar dezenas de diferentes dimensões analíticas. Observe que atributos e métricas analíticas podem ser adicionados e removidos, às vezes mais rápido do que podemos reconstruir nosso armazenamento colunar. Sem mencionar que os dados no banco de dados podem ser alterados, o que também violará a beleza do esquema de armazenamento devido à fragmentação inevitável.

Metadados


Introduzimos um conceito - um termo - para descrever quaisquer objetos com os quais operamos: entidade, propriedade, solicitação, arquivo etc. Definiremos todos os termos que usamos em nossa área de negócios. E com a ajuda deles, descreveremos todas as entidades que possuem detalhes, incluindo a forma de relacionamento entre entidades. Por exemplo, um atributo - um link para uma entrada do dicionário de status. O termo é escrito como um quinteto de dados.

Um conjunto de descrições de termos é metadado como o mesmo representado pela estrutura de tabelas e campos em um banco de dados regular. Por exemplo, existe a seguinte estrutura de dados: uma solicitação de serviço em alguma data que possui o conteúdo (descrição da solicitação) e um status, ao qual os participantes de um processo de produção adicionam comentários indicando a data. Em um construtor de banco de dados tradicional, será algo parecido com isto:



Como decidimos ocultar do usuário todos os detalhes não essenciais, como IDs de ligação, por exemplo, o esquema será um pouco simplificado: as menções aos IDs são removidas e os nomes das entidades e seus valores principais são combinados.

O usuário "desenha" a tarefa: uma solicitação da data de hoje que possui um estado (valor de referência) e ao qual você pode adicionar comentários indicando a data:



Agora, vemos 6 campos de dados diferentes em vez de 9, e todo o esquema nos oferece a leitura e compreensão de 7 palavras em vez de 13. Embora essa não seja a principal, é claro.

A seguir, são apresentados os quintetos gerados pelo kernel de processamento de quintetos para descrever essa estrutura:



São fornecidas explicações no lugar dos valores do quinteto destacados em cinza para maior clareza. Esses campos não são preenchidos porque todas as informações necessárias são determinadas sem ambiguidade pelos componentes restantes.

Veja como os quintetos estão relacionados


O que temos aqui:

  • os atributos com os IDs 80, 81, 83 têm o mesmo pai - Solicitação
  • quinteto # 82 é o atributo de Comment, que por sua vez é um atributo de Request
  • o atributo nº 74 é uma referência ao tipo descrito pelo quinteto nº 73 e é usado como atributo nº 81 da solicitação

Isso pode parecer um pouco complicado para os humanos, mas a boa notícia é: um humano nunca verá isso. O kernel representará os metadados como diagramas compreensíveis e os dados como tabelas simples simples.

Dados do usuário


Deixe-me mostrar como armazenamos esse conjunto de dados para a tarefa acima:



Os dados em si são armazenados em quintetos de acordo com os metadados. Podemos visualizá-los da mesma maneira que fizemos acima:



Vemos uma estrutura hierárquica familiar escrita usando algo como o método Adjacency List.

Armazenamento físico


Os dados são gravados na memória como uma sequência de itens do quinteto em bytes de dados. Para pesquisar por índice, o kernel trata esses bytes de dados de acordo com o tipo de dados definido para eles por tipos básicos.
É isso: uma enorme lista de cinco itens de dados.

Os princípios de armazenamento não são muito diferentes dos mesmos no RDBMS, o que nos permite criar consultas SQL aos dados para fazer recuperação de dados, JOINs, funções agregadas e outras coisas que gostamos em bancos de dados relacionais.
Para testar o protótipo de uma plataforma de desenvolvimento baseada no sistema de armazenamento em quinteto, usamos um banco de dados relacional.

Desempenho


O exemplo acima é muito simples, mas o que será quando a estrutura for mil vezes mais complexa e houver gigabytes de dados?

Do que precisamos:

  1. A estrutura hierárquica discutida - 1 pc.
  2. Árvore B para pesquisa por ID, pai e tipo - 3 peças.

Assim, todos os registros em nosso banco de dados serão indexados, incluindo dados e metadados. Essa indexação é necessária para obter os benefícios de um banco de dados relacional - a ferramenta mais simples e popular. O índice pai é realmente composto (ID pai + tipo). O índice por tipo também é composto (tipo + valor) para pesquisa rápida de objetos de um determinado tipo.

Os metadados nos permitem eliminar a recursão: por exemplo, para encontrar todos os detalhes de um determinado objeto, usamos o índice pelo ID pai. Se você precisar procurar objetos de um determinado tipo, usaremos o índice por ID do tipo. Tipo é um análogo de um nome de tabela e um campo em um DBMS relacional.



De qualquer forma, não analisamos todo o conjunto de dados e, mesmo com um grande número de valores de qualquer tipo, o valor desejado pode ser encontrado em um pequeno número de etapas.

A base para a plataforma de desenvolvimento


Por si só, esse banco de dados não é auto-suficiente para a programação de aplicativos e não é completo, como dizem, de acordo com Turing. No entanto, estamos falando aqui não apenas sobre o banco de dados, mas tentando cobrir todos os aspectos: os objetos são, entre outras coisas, algoritmos de controle arbitrário que podem ser iniciados e eles funcionarão.

Como resultado, em vez de estruturas complexas de banco de dados e algoritmos de código fonte de controle armazenados separadamente, obtemos um campo de informações uniforme, limitado pelo volume do espaço de armazenamento e controlado por metadados. Os dados em si são apresentados ao usuário de uma forma compreensível para ele - a estrutura da área de assunto e as entradas correspondentes. O usuário altera arbitrariamente a estrutura e os dados, inclusive fazendo operações em massa com eles.

Não inventamos nada de novo: todos os dados já estão armazenados no sistema de arquivos e a pesquisa neles é realizada usando árvores B, no sistema de arquivos ou no banco de dados. Apenas reorganizamos a apresentação dos dados para que seja mais fácil e claro trabalhar com eles.



Para trabalhar com essa representação de dados, você precisará de um software de kernel muito compacto - nosso mecanismo de banco de dados é menor que o BIOS de um computador e, portanto, pode ser feito se não estiver em hardware, pelo menos com a mesma rapidez e segurança. livre quanto possível. Por razões de segurança, também pode ser somente leitura.

Adicionando uma nova classe a uma montagem em meu .net favorito, podemos observar a perda de 200 a 300 MB de RAM apenas na definição dessa classe. Esses megabytes não cabem no cache do nível adequado, fazendo com que o sistema troque no disco com toda a sobrecarga resultante. Uma situação semelhante é com Java. A descrição da mesma classe com quintetos levará dezenas ou centenas de bytes, pois a classe usa apenas operações primitivas para trabalhar com dados que o kernel já conhece.

Você pode pensar que essa abordagem já foi implementada várias vezes em vários aplicativos, mas isso não é verdade.


Fizemos uma pesquisa profunda nas bases da Internet e da propriedade intelectual (patentes), e ninguém afirma fazer exatamente a mesma solução para quebrar o limite de desempenho de construtores, soluções de tabela única e outros sistemas baseados em EAV. No entanto, colocamos centenas de gigabytes nesse aplicativo de quinteto e o achamos funcionando muito bem. Caso você queira ver evidências, crie e teste sua própria instância, sinta-se à vontade para visitar nossa conta do github.

O protótipo da plataforma que construímos tem quatro componentes:

  1. Editor de tipo visual para definir os metadados
  2. Ferramenta de navegação de dados como um simples navegador SQL
  3. Designer de relatório visual para criar consultas SQL para os dados
  4. Um processador de modelos para combinar modelos com dados recuperados por consultas



Como foi planejado, trabalhando com o protótipo, nenhum usuário pensaria que existem quintetos no interior - isso parece um construtor comum.

Como lidar com diferentes formatos: RDBMS, NoSQL, bases de colunas
A abordagem discutida abrange duas áreas principais: RDBMS e NoSQL. Ao resolver problemas que tiram proveito dos bancos de dados colunares, precisamos informar ao kernel que determinados objetos devem ser armazenados, levando em consideração a otimização da amostragem em massa dos valores de um determinado tipo de dados (nosso termo). Portanto, o kernel poderá colocar dados no disco da maneira mais lucrativa.

Assim, para um banco de dados colunar, podemos economizar significativamente o espaço ocupado por quintetos: use apenas um ou dois de seus componentes para armazenar dados úteis em vez de cinco e também use o índice apenas para indicar o início das cadeias de dados. Em muitos casos, apenas o índice será usado para amostragem do nosso análogo de uma base colunar, sem a necessidade de acessar os dados da própria lista de quintetos.

Note-se que a idéia não se destina a coletar todos os desenvolvimentos avançados desses três tipos de bancos de dados. Pelo contrário, o mecanismo do novo sistema será reduzido o máximo possível, incorporando apenas o mínimo necessário de funções - tudo o que cobre solicitações de DDL e DML no conceito descrito aqui.


Paradigma de programação


A abordagem descrita não se limita apenas ao uso de quintetos, mas promove um paradigma diferente daquele ao qual os programadores estão acostumados. Em vez de uma linguagem imperativa, declarativa ou de objeto, propomos a linguagem de consulta como mais familiar aos seres humanos e nos permite definir a tarefa diretamente no computador, ignorando os programadores e a camada impenetrável dos ambientes de desenvolvimento existentes.

Obviamente, na maioria dos casos, um tradutor de um idioma de usuário leigo para um idioma de requisitos claros ainda será necessário.

Este tópico será descrito em mais detalhes em artigos separados, com exemplos e desenvolvimentos existentes.

Então, em breve, funciona da seguinte maneira:

  1. Certa vez, descrevemos tipos de dados primitivos usando quintetos: string, número, arquivo, texto e outros, e também treinamos o kernel para trabalhar com eles. Treinamento significa a apresentação correta dos dados e a implementação de operações simples com eles.
  2. Agora, descrevemos os termos do usuário (tipos de dados) - na forma de metadados. A descrição está apenas especificando um tipo de dados primitivo para cada tipo de usuário e determinando as relações.
  3. Entramos nos quintetos de dados de acordo com a estrutura especificada pelos metadados. Cada quinteto de dados contém um link para seu tipo e pai, o que permite encontrá-lo rapidamente no armazenamento de dados.
  4. As tarefas do kernel se resumem a buscar dados e executar operações simples com eles para implementar algoritmos arbitrariamente complexos definidos pelo usuário.
  5. O usuário gerencia dados e algoritmos usando uma interface visual que apresenta os dois.


A integridade de Turing de todo o sistema é garantida pela incorporação dos requisitos básicos: o kernel pode executar operações seqüenciais, ramificar condicionalmente, processar os dados e interromper o trabalho quando um determinado resultado é alcançado.

Para uma pessoa, o benefício é a simplicidade da percepção, por exemplo, em vez de declarar um ciclo envolvendo variáveis

for (i = 0; i <length (A); i ++) if A [i] meets a condition do something with A [i] 


uma forma mais compreensível é usada, como

 with every A, that match a condition, do something 


Sonhamos em abstrair das sutilezas de baixo nível dos sistemas de informação: loops, construtores, funções, manifestos, bibliotecas - tudo isso ocupa muito espaço no cérebro de um programador, deixando pouco espaço para trabalho e desenvolvimento criativos.

Escalabilidade


Um aplicativo geralmente é inútil sem meios de dimensionamento: é necessária uma capacidade ilimitada de expandir a capacidade de carga de um sistema de informações. Na abordagem descrita, levando em consideração a extrema simplicidade da organização dos dados, o dimensionamento acaba sendo organizado não mais complicado do que nas arquiteturas existentes.

No exemplo acima, com as solicitações de serviço, você pode separá-las, por exemplo, por seu ID, criando a ID com bytes HIGH fixos para diferentes servidores. Ou seja, ao usar 32 bits para armazenar ID, os dois, três ou quatro bits à esquerda, conforme necessário, indicarão o servidor no qual esses aplicativos estão armazenados. Assim, cada servidor terá seu próprio pool de IDs.

O kernel de um único servidor pode funcionar independentemente de outros servidores, sem saber nada sobre eles. Ao criar um objeto, será dada alta prioridade ao servidor com o número mínimo de IDs usados, para garantir a distribuição uniforme da carga.

Dado um conjunto limitado de possíveis variações de solicitações e respostas nessa organização de dados, você precisará de um expedidor compacto que distribua solicitações entre servidores e agregue seus resultados.

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


All Articles