Particiones de rango de claves


A menudo, puede ser difícil saber cuáles son los puntos de división adecuados por adelantado. En estos casos, podemos implementar la división automática.

Aquí, el coordinador creará solo una partición con un rango de claves que incluye todo el espacio de claves.

Cada partición se puede configurar con un tamaño máximo fijo. A continuación, se ejecuta una tarea en segundo plano en cada nodo del clúster para realizar un seguimiento del tamaño de las particiones. Cuando una partición alcanza su tamaño máximo, se divide en dos particiones, cada una de las cuales tiene aproximadamente la mitad del tamaño de la unique.

Cálculo del tamaño de la partición y búsqueda de la clave central

Obtener el tamaño de la partición y encontrar la clave intermedia depende de los motores de almacenamiento que se utilicen. Una forma easy de hacerlo puede ser escanear toda la partición para calcular su tamaño.
TiKV utilizó inicialmente este enfoque. Para poder dividir la tableta, también se debe encontrar la tecla que se encuentra en el punto medio. Para evitar escanear la partición dos veces, una implementación easy puede obtener la clave central si el tamaño es mayor que el máximo configurado.

Partición de clase…

  public String getMiddleKeyIfSizeCrossed(int partitionMaxSize) {
      int kvSize = 0;
      for (String key : kv.keySet()) {
          kvSize += key.size() + kv.get(key).size();
          if (kvSize >= partitionMaxSize / 2) {
              return key;
          }
      }
      return "";
  }

El coordinador, que maneja el mensaje de activación de división, actualiza los metadatos del rango de claves para la partición unique y crea una nueva partición de metadatos para el rango dividido.

clase ClusterCoordinator…

  personal void handleSplitTriggerMessage(SplitTriggerMessage message) {
      logger.information("Dealing with SplitTriggerMessage " + message.getPartitionId() + " break up key " + message.getSplitKey());
      splitPartition(message.getPartitionId(), message.getSplitKey());
  }

  public CompletableFuture splitPartition(int partitionId, String splitKey) {
      logger.information("Splitting partition " + partitionId + " at key " + splitKey);
      PartitionInfo parentPartition = partitionTable.getPartition(partitionId);
      Vary originalRange = parentPartition.getRange();
      Listing<Vary> splits = originalRange.break up(splitKey);
      Vary shrunkOriginalRange = splits.get(0);
      Vary newRange = splits.get(1);
      return replicatedLog.suggest(new SplitPartitionCommand(partitionId, splitKey, shrunkOriginalRange, newRange));
  }

Una vez que los metadatos de las particiones se almacenan correctamente, envía un mensaje al nodo del clúster que aloja la partición principal para dividir los datos de la partición principal.

clase ClusterCoordinator…

  personal void applySplitPartitionCommand(SplitPartitionCommand command) {
      PartitionInfo originalPartition = partitionTable.getPartition(command.getOriginalPartitionId());
      Vary originalRange = originalPartition.getRange();
      if (!originalRange.coveredBy(command.getUpdatedRange().getStartKey(), command.getNewRange().getEndKey())) {
          logger.error("The unique vary begin and finish keys "+ originalRange + " don't match break up ranges");
          return;
      }

      originalPartition.setRange(command.getUpdatedRange());
      PartitionInfo newPartitionInfo = new PartitionInfo(newPartitionId(), originalPartition.getAddress(), PartitionStatus.ASSIGNED, command.getNewRange());
      partitionTable.addPartition(newPartitionInfo.getPartitionId(), newPartitionInfo);

      //ship requests to cluster nodes if that is the chief node.
      if (isLeader()) {
          var message = new SplitPartitionMessage(command.getOriginalPartitionId(), command.getSplitKey(), newPartitionInfo, requestNumber++, listenAddress);
          scheduler.execute(new RetryableTask(originalPartition.getAddress(), community, this, originalPartition.getPartitionId(), message));
      }
  }

rango de clase…

  public boolean coveredBy(String startKey, String endKey) {
      return getStartKey().equals(startKey)
              && getEndKey().equals(endKey);
  }

El nodo del clúster divide la partición unique y crea una nueva partición. A continuación, los datos de la partición unique se copian en la nueva partición. Luego responde al coordinador diciéndole que la división está completa.

clase KVStore…

  personal void handleSplitPartitionMessage(SplitPartitionMessage splitPartitionMessage) {
      splitPartition(splitPartitionMessage.getPartitionId(),
                                  splitPartitionMessage.getSplitKey(),
                                  splitPartitionMessage.getSplitPartitionId());
      community.ship(coordLeader,
              new SplitPartitionResponseMessage(splitPartitionMessage.getPartitionId(),
                      splitPartitionMessage.getPartitionId(),
                      splitPartitionMessage.getSplitPartitionId(),
                      splitPartitionMessage.messageId, listenAddress));
  }

  personal void splitPartition(int parentPartitionId, String splitKey, int newPartitionId) {
      Partition partition = allPartitions.get(parentPartitionId);
      Partition splitPartition = partition.splitAt(splitKey, newPartitionId);
      logger.information("Including new partition " + splitPartition.getId() + " for vary " + splitPartition.getRange());
      allPartitions.put(splitPartition.getId(), splitPartition);
  }

Partición de clase…

  public Partition splitAt(String splitKey, int newPartitionId) {
      Listing<Vary> splits = this.vary.break up(splitKey);
      Vary shrunkOriginalRange = splits.get(0);
      Vary splitRange = splits.get(1);

      SortedMap<String, String> partition1Kv =
              (vary.getStartKey().equals(Vary.MIN_KEY))
                      ? kv.headMap(splitKey)
                      : kv.subMap(vary.getStartKey(), splitKey);

      SortedMap<String, String> partition2Kv =
              (vary.getEndKey().equals(Vary.MAX_KEY))
                      ? kv.tailMap(splitKey)
                      : kv.subMap(splitKey, vary.getEndKey());

      this.kv = partition1Kv;
      this.vary = shrunkOriginalRange;

      return new Partition(newPartitionId, partition2Kv, splitRange);
  }

rango de clase…

  public Listing<Vary> break up(String splitKey) {
      return Arrays.asList(new Vary(startKey, splitKey), new Vary(splitKey, endKey));
  }

Una vez que el coordinador recibe el mensaje, marca las particiones como en línea

clase ClusterCoordinator…

  personal void handleSplitPartitionResponse(SplitPartitionResponseMessage message) {
      replicatedLog.suggest(new UpdatePartitionStatusCommand(message.getPartitionId(), PartitionStatus.ONLINE));
  }

Uno de los posibles problemas que pueden surgir al intentar modificar la partición existente es que el cliente no puede almacenar en caché y siempre necesita obtener los últimos metadatos de la partición antes de poder enviar solicitudes al nodo del clúster. Uso de almacenes de datos Reloj de generación para tabiques; esto se actualiza cada vez que se divide una partición. Cualquier solicitud de cliente con un número de generación anterior será rechazada. Luego, los clientes pueden volver a cargar la tabla de particiones desde el coordinador y volver a intentar la solicitud. Esto garantiza que los clientes que poseen metadatos más antiguos no obtengan resultados incorrectos.
Yugabyte DB
elige crear dos nuevas particiones separadas y marca el unique como se explica en su
Diseño de división de mesa automática..

Escenario de ejemplo

Considere un ejemplo en el que el nodo del clúster Atenas contiene la partición P1 que cubre todo el rango de claves. El tamaño máximo de partición está configurado para ser de 10 bytes. SplitCheck detecta que el tamaño ha crecido más allá de 10 y encuentra que la clave media aproximada es bob. Luego envía un mensaje al coordinador del clúster, pidiéndole que cree metadatos para la partición dividida. Una vez que el coordinador ha creado correctamente estos metadatos, el coordinador le pide a Atenas que divida la partición P1 y le pasa el ID de partición de los metadatos. Luego, Athens puede reducir P1 y crear una nueva partición, copiando los datos de P1 a la nueva partición. Una vez que la partición se ha creado con éxito, envía una confirmación al coordinador. Luego, el coordinador marca la nueva partición como en línea.

División basada en la carga

Con la división automática, solo comenzamos con un rango. Esto significa que todas las solicitudes de los clientes van a un solo servidor, incluso si hay otros nodos en el clúster. Todas las solicitudes seguirán yendo al único servidor que hospeda el rango único hasta que el rango se divida y se traslade a otros servidores. Esta es la razón por la cual, a veces, la división en parámetros como el número whole de solicitudes o la CPU y el uso de la memoria también se usan para activar una división de partición. Bases de datos modernas como CucarachaDB y Yugabyte DB
soportar la división basada en la carga. Se pueden encontrar más detalles en su documentación en (cucaracha-carga-división)
y (yb-carga-división)

Related Articles

Versión 8.10 de Clarifai

Comunidad Clarifai Panel de uso...

Comments

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Same Category

spot_img

Stay in touch!

Follow our Instagram