Armazenamento de Valor Chave com Mapa de Crônica

Armazenamento de Valor Chave com Mapa de Crônica

 1. Visão geral

Neste tutorial, veremos como podemos usarChronicle Map para armazenar pares de valores-chave. Também criaremos pequenos exemplos para demonstrar seu comportamento e uso.

2. O que é um mapa do Chronicle?

Seguindo a documentação,“Chronicle Map is a super-fast, in-memory, non-blocking, key-value store, designed for low-latency, and/or multi-process applications”.

Em suma, é um armazenamento de valor-chave fora do heap. O mapa não requer uma grande quantidade de RAM para funcionar corretamente. It can grow based on the available disk capacity. Além disso, ele suporta a replicação dos dados em uma configuração de servidor multimestre.

Vamos agora ver como podemos configurar e trabalhar com isso.

3. Dependência do Maven

Para começar, precisaremos adicionarchronicle-map dependency ao nosso projeto:


    net.openhft
    chronicle-map
    3.17.2

4. Tipos de mapa de crônicas

Podemos criar um mapa de duas maneiras: como um mapa na memória ou como um mapa persistente.

Vamos ver ambos em detalhes.

4.1. Mapa na memória

Um Chronicle Map na memória é um repositório de mapas criado na memória física do servidor. Isso significait’s accessible only within the JVM process in which the map store is created.

Vamos ver um exemplo rápido:

ChronicleMap inMemoryCountryMap = ChronicleMap
  .of(LongValue.class, CharSequence.class)
  .name("country-map")
  .entries(50)
  .averageValue("America")
  .create();

Para simplificar, estamos criando um mapa que armazena 50 ids de países e seus nomes. Como podemos ver no trecho de código, a criação é bastante simples, exceto para a configuraçãoaverageValue(). Isso indica ao mapa para configurar o número médio de bytes obtidos pelos valores de entrada do mapa.

Em outras palavras,when creating the map, the Chronicle Map determines the average number of bytes taken by the serialized form of values. It does this by serializing the given average value using the configured value marshallers. It will then allocate the determined number of bytes for the value of each map entry.

Uma coisa que devemos observar quando se trata do mapa na memória é que os dados são acessíveis apenas quando o processo da JVM está ativo. A biblioteca limpará os dados quando o processo terminar.

4.2. Mapa Persistido

Ao contrário de um mapa na memória,the implementation will save a persisted map to disk. Vamos agora ver como podemos criar um mapa persistente:

ChronicleMap persistedCountryMap = ChronicleMap
  .of(LongValue.class, CharSequence.class)
  .name("country-map")
  .entries(50)
  .averageValue("America")
  .createPersistedTo(new File(System.getProperty("user.home") + "/country-details.dat"));

Isso criará um arquivo chamadocountry-details.dat na pasta especificada. Se esse arquivo já estiver disponível no caminho especificado, a implementação do construtor abrirá um link para o armazenamento de dados existente desse processo da JVM.

Podemos fazer uso do mapa persistente nos casos em que queremos:

  • sobreviver além do processo do criador; por exemplo, para oferecer suporte à reimplementação de aplicativos quentes

  • torná-lo global em um servidor; por exemplo, para oferecer suporte a vários acessos simultâneos ao processo

  • atua como um armazenamento de dados que salvaremos no disco

5. Configuração de tamanho

É obrigatório configurar o valor médio e a chave média ao criar um mapa do Chronicle, exceto no caso em que nosso tipo de chave / valor é uma primitiva em caixa ou uma interface de valor. Em nosso exemplo, não estamos configurando a chave média, pois o tipo de chaveLongValue é avalue interface.

Agora, vamos ver quais são as opções para configurar o número médio de bytes de chave / valor:

  • averageValue() - O valor a partir do qual o número médio de bytes a serem alocados para o valor de uma entrada do mapa é determinado

  • averageValueSize() - O número médio de bytes a serem alocados para o valor de uma entrada do mapa

  • constantValueSizeBySample() - O número de bytes a serem alocados para o valor de uma entrada do mapa quando o tamanho do valor é sempre o mesmo

  • averageKey() - A chave a partir da qual o número médio de bytes a serem alocados para a chave de uma entrada do mapa é determinado

  • averageKeySize() - O número médio de bytes a serem alocados para a chave de uma entrada do mapa

  • constantKeySizeBySample() - O número de bytes a serem alocados para a chave de uma entrada do mapa quando o tamanho da chave é sempre o mesmo

6. Tipos de chave e valor

Existem certos padrões que precisamos seguir ao criar um Mapa de Crônicas, especialmente ao definir a chave e o valor. O mapa funciona melhor quando criamos a chave e o valor usando os tipos recomendados.

Aqui estão alguns dos tipos recomendados:

  • Value interfaces

  • Qualquer classe que implemente a interfaceByteable deChronicle Bytes

  • Qualquer classe que implemente a interfaceBytesMarshallable do Chronicle Bytes; a classe de implementação deve ter um construtor público sem arg

  • byte[] eByteBuffer

  • CharSequence,String eStringBuilder

  • Integer,Long eDouble

  • Qualquer classe implementandojava.io.Externalizable; a classe de implementação deve ter um construtor público sem arg

  • Qualquer tipo que implementejava.io.Serializable, incluindo tipos primitivos em caixa (exceto aqueles listados acima) e tipos de matriz

  • Qualquer outro tipo, se serializadores personalizados forem fornecidos

7. Consultando um mapa do Chronicle

O Chronicle Map suporta consultas de chave única e consultas de chave múltipla.

7.1. Consultas de chave única

As consultas de chave única são as operações que lidam com uma única chave. ChronicleMap suporta todas as operações da interface JavaMap e da interfaceConcurrentMap:

LongValue qatarKey = Values.newHeapInstance(LongValue.class);
qatarKey.setValue(1);
inMemoryCountryMap.put(qatarKey, "Qatar");

//...

CharSequence country = inMemoryCountryMap.get(key);

Além das operações normais de obtenção e colocação,ChronicleMap adds a special operation, getUsing(), that reduces the memory footprint while retrieving and processing an entry. Vamos ver isso em ação:

LongValue key = Values.newHeapInstance(LongValue.class);
StringBuilder country = new StringBuilder();
key.setValue(1);
persistedCountryMap.getUsing(key, country);
assertThat(country.toString(), is(equalTo("Romania")));

key.setValue(2);
persistedCountryMap.getUsing(key, country);
assertThat(country.toString(), is(equalTo("India")));

Aqui, usamos o mesmo objetoStringBuilder para recuperar valores de chaves diferentes, passando-o para o métodogetUsing(). Basicamente, reutiliza o mesmo objeto para recuperar entradas diferentes. Em nosso caso, o métodogetUsing() é equivalente a:

country.setLength(0);
country.append(persistedCountryMap.get(key));

7.2. Consultas multi-chave

Pode haver casos de uso em que precisamos lidar com várias chaves ao mesmo tempo. Para isso, podemos usar a funcionalidadequeryContext(). The queryContext() method will create a context for working with a map entry.

Vamos primeiro criar um multimapa e adicionar alguns valores a ele:

Set averageValue = IntStream.of(1, 2).boxed().collect(Collectors.toSet());
ChronicleMap> multiMap = ChronicleMap
  .of(Integer.class, (Class>) (Class) Set.class)
  .name("multi-map")
  .entries(50)
  .averageValue(averageValue)
  .create();

Set set1 = new HashSet<>();
set1.add(1);
set1.add(2);
multiMap.put(1, set1);

Set set2 = new HashSet<>();
set2.add(3);
multiMap.put(2, set2);

Para trabalhar com várias entradas, temos que bloquear essas entradas para evitar inconsistências que podem ocorrer devido a uma atualização simultânea:

try (ExternalMapQueryContext, ?> fistContext = multiMap.queryContext(1)) {
    try (ExternalMapQueryContext, ?> secondContext = multiMap.queryContext(2)) {
        fistContext.updateLock().lock();
        secondContext.updateLock().lock();

        MapEntry> firstEntry = fistContext.entry();
        Set firstSet = firstEntry.value().get();
        firstSet.remove(2);

        MapEntry> secondEntry = secondContext.entry();
        Set secondSet = secondEntry.value().get();
        secondSet.add(4);

        firstEntry.doReplaceValue(fistContext.wrapValueAsData(firstSet));
        secondEntry.doReplaceValue(secondContext.wrapValueAsData(secondSet));
    }
} finally {
    assertThat(multiMap.get(1).size(), is(equalTo(1)));
    assertThat(multiMap.get(2).size(), is(equalTo(2)));
}

8. Fechando o mapa do Chronicle

Agora que terminamos de trabalhar com nossos mapas, vamos chamar o métodoclose() em nossos objetos de mapa para liberar a memória fora do heap e os recursos associados a ela:

persistedCountryMap.close();
inMemoryCountryMap.close();
multiMap.close();

Uma coisa a ter em mente aqui é que todas as operações do mapa devem ser concluídas antes de fechar o mapa. Caso contrário, a JVM pode travar inesperadamente.

9. Conclusão

Neste tutorial, aprendemos como usar um mapa do Chronicle para armazenar e recuperar pares de valores-chave. Embora a versão da comunidade esteja disponível com a maioria das funcionalidades principais, a versão comercial possui alguns recursos avançados, como replicação de dados em vários servidores e chamadas remotas.

Todos os exemplos que discutimos aqui podem ser encontrados emGithub project.