Diseño de una estrategia de creación de particiones escalable para Azure Table Storage

En este artículo se describe la creación de particiones de una tabla en Azure Table Storage y las estrategias que puede usar para garantizar una escalabilidad eficaz.

Azure proporciona almacenamiento en la nube que es de alta disponibilidad y altamente escalable. El sistema de almacenamiento subyacente para Azure se proporciona a través de un conjunto de servicios, como Azure Blob Storage, Azure Table Storage, Azure Queue Storage y Azure Files.

Azure Table Storage está diseñado para almacenar datos estructurados. El servicio Azure Storage admite un número ilimitado de tablas. Cada tabla se puede escalar a niveles masivos y proporcionar terabytes de almacenamiento físico. Para aprovechar mejor las tablas, debe particionar los datos de forma óptima. En este artículo se exploran las estrategias que puede usar para crear particiones eficaces de los datos de Azure Table Storage.

Entidades de tabla

Las entidades de tabla representan las unidades de datos que se almacenan en una tabla. Las entidades de tabla son similares a las filas de una tabla de base de datos relacional típica. Cada entidad define una colección de propiedades. Cada propiedad se define como un par clave-valor por su nombre, valor y el tipo de datos del valor. Las entidades deben definir las tres siguientes propiedades del sistema como parte de la colección de propiedades:

  • PartitionKey: la propiedad PartitionKey almacena valores de cadena que identifican la partición a la que pertenece una entidad. Las particiones, como se describe más adelante, son integrales a la escalabilidad de la tabla. Las entidades que tienen el mismo valor PartitionKey se almacenan en la misma partición.

  • RowKey: la propiedad RowKey almacena valores de cadena que identifican de forma única las entidades dentro de cada partición. PartitionKey y RowKey forman juntos la clave principal de la entidad.

  • Marca de tiempo: la propiedad Timestamp proporciona rastreabilidad para una entidad. Una marca de tiempo es un valor de fecha y hora que indica la última vez que se modificó la entidad. A veces, una marca de tiempo se conoce como la versión de la entidad. Las modificaciones en las marcas de tiempo se omiten porque table service mantiene el valor de esta propiedad durante todas las operaciones de inserción y actualización.

Clave principal de tabla

La clave principal de una entidad de Azure consta de las propiedades PartitionKey y RowKey combinadas. Las dos propiedades forman un único índice agrupado dentro de la tabla. Los valores PartitionKey y RowKey pueden tener un tamaño de hasta 1024 caracteres. También se permiten cadenas vacías; sin embargo, no se permiten valores NULL.

El índice agrupado ordena por PartitionKey en orden ascendente y, a continuación, por RowKey en orden ascendente. El criterio de ordenación se observa en todas las respuestas de consulta. Se usan comparaciones léxicas durante la operación de ordenación. Aparece un valor de cadena de "111" antes de un valor de cadena de "2". En algunos casos, es posible que desee que el criterio de ordenación sea numérico. Para ordenar en orden numérico y ascendente, debe usar cadenas de longitud fija y cero rellenadas. En el ejemplo anterior, "002" aparece antes de "111".

Particiones de tabla

Las particiones representan una colección de entidades con los mismos valores PartitionKey . Las particiones siempre se sirven desde un servidor de partición. Cada servidor de particiones puede servir una o varias particiones. Un servidor de particiones tiene como límite de velocidad el número de entidades que puede atender desde una partición a lo largo del tiempo. En concreto, una partición tiene un objetivo de escalabilidad de 2000 entidades por segundo. Este rendimiento puede ser mayor durante la carga mínima en el nodo de almacenamiento, pero se limita cuando el nodo se vuelve activo o activo.

Para ilustrar mejor el concepto de creación de particiones, en la ilustración siguiente se muestra una tabla que contiene un pequeño subconjunto de datos para los registros de eventos de carrera de pie. La ilustración presenta una vista conceptual de la creación de particiones en la que PartitionKey contiene tres valores diferentes: el nombre del evento combinado con tres distancias (marathon completo, media maratón y 10 km). En este ejemplo se usan dos servidores de partición. El servidor A contiene registros para la media maratón y las distancias de 10 km. El servidor B contiene solo las distancias de maratón completa. Los valores RowKey se muestran para proporcionar contexto, pero los valores no son significativos para este ejemplo.

Diagrama que muestra una tabla que tiene tres particiones
Tabla con tres particiones

Escalabilidad

Puesto que una partición siempre la atiende un único servidor de particiones y cada servidor de particiones puede atender una o varias particiones, la eficiencia de las entidades que se atienden se correlaciona con el estado del servidor. Es posible que los servidores que encuentren un tráfico elevado para sus particiones no puedan mantener un alto rendimiento. Por ejemplo, en la ilustración anterior, si se reciben muchas solicitudes de "2011 New York City Marathon__Half", el servidor A podría estar demasiado activo. Para aumentar el rendimiento del servidor, el sistema de almacenamiento equilibra la carga de las particiones en otros servidores. El resultado es que el tráfico se distribuye entre otros muchos servidores. Para un equilibrio de carga óptimo del tráfico, debe usar más particiones para que Azure Table Storage pueda distribuir las particiones a más servidores de partición.

Transacciones de grupos de entidades

Una transacción de grupo de entidades es un conjunto de operaciones de almacenamiento que se implementan de forma atómica en entidades que tienen el mismo valor PartitionKey . Si se produce un error en alguna operación de almacenamiento en el grupo de entidades, se revierten todas las operaciones de almacenamiento de la entidad. Una transacción de grupo de entidades consta de no más de 100 operaciones de almacenamiento y puede no tener más de 4 MiB de tamaño. Las transacciones del grupo de entidades proporcionan a Azure Table Storage una forma limitada de la atomicidad, coherencia, aislamiento y durabilidad (ACID) que proporcionan las bases de datos relacionales.

Las transacciones del grupo de entidades mejoran el rendimiento porque reducen el número de operaciones de almacenamiento individuales que se deben enviar a Azure Table Storage. Las transacciones del grupo de entidades también proporcionan una ventaja económica. Una transacción de grupo de entidades se factura como una sola operación de almacenamiento, independientemente de cuántas operaciones de almacenamiento contenga. Dado que todas las operaciones de almacenamiento de una transacción de grupo de entidades afectan a las entidades que tienen el mismo valor PartitionKey , una necesidad de usar transacciones de grupo de entidades puede impulsar la selección del valor PartitionKey .

Particiones de intervalo

Si usa valores PartitionKey únicos para las entidades, cada entidad pertenece a su propia partición. Si los valores únicos que usa aumentan o reducen el valor, es posible que Azure cree particiones de intervalo. Las particiones de intervalo agrupan entidades que tienen valores PartitionKey secuenciales y únicos para mejorar el rendimiento de las consultas de intervalo. Sin particiones de intervalo, una consulta de intervalo debe cruzar los límites de partición o los límites del servidor, lo que puede reducir el rendimiento de las consultas. Considere una aplicación que usa la tabla siguiente, que tiene un valor de secuencia creciente para PartitionKey:

PartitionKey RowKey
"0001" -
"0002" -
"0003" -
"0004" -
"0005" -
"0006" -

Azure podría agrupar las tres primeras entidades en una partición de intervalo. Si aplica una consulta de intervalo a la tabla que usa PartitionKey como criterios y solicita entidades de "0001" a "0003", la consulta puede realizarse de forma eficaz porque las entidades se sirven desde un único servidor de partición. No hay ninguna garantía de cuándo y cómo se creará una partición de intervalo.

La existencia de particiones de intervalo para la tabla puede afectar al rendimiento de las operaciones de inserción si inserta entidades que tienen valores PartitionKey crecientes o decrecientes. La inserción de entidades que tienen valores de PartitionKey crecientes se denomina patrón de solo anexión. La inserción de entidades que tienen valores decrecientes se denomina patrón de solo anteposición. Considere la posibilidad de no usar estos tipos de patrones porque el rendimiento general de las solicitudes de inserción está limitado por un único servidor de partición. Esto se debe a que, si existen particiones de intervalo, las particiones del primer y último (intervalo) contienen los valores PartitionKey mínimos y mayores, respectivamente. Por lo tanto, la inserción de una nueva entidad, una que tiene un valor PartitionKey secuencialmente inferior o superior, tiene como destino una de las particiones finales. En la ilustración siguiente se muestra un posible conjunto de particiones de intervalo que se basan en el ejemplo anterior. Si se insertara un conjunto de entidades "0007", "0008" y "0009", se asignarían a la última partición (naranja).

Diagrama que muestra un conjunto de particiones de intervalo
Un conjunto de particiones de intervalo

Es importante tener en cuenta que no hay ningún efecto negativo en el rendimiento si las operaciones de inserción usan valores PartitionKey que están más dispersos.

Análisis de datos

A diferencia de una tabla de una base de datos relacional que puede usar para administrar índices, las tablas de Azure Table Storage solo pueden tener un índice. Un índice de Azure Table Storage siempre consta de las propiedades PartitionKey y RowKey .

En una tabla de Azure, no tiene el lujo de optimizar el rendimiento de la tabla agregando más índices o modificando una tabla existente después de implementarla. Debe analizar los datos a medida que diseña la tabla. Los aspectos más importantes que se deben tener en cuenta para obtener una escalabilidad óptima y para la eficacia de la consulta e inserción son los valores PartitionKey y RowKey . En este artículo se destaca cómo elegir PartitionKey porque se relaciona directamente con cómo se particionan las tablas.

Tamaño de la partición

El tamaño de la partición se refiere al número de entidades que contiene una partición. Como se describe en Escalabilidad, tener más particiones significa que obtiene un mejor equilibrio de carga. La granularidad del valor PartitionKey afecta al tamaño de las particiones. En el nivel más general, si se usa un único valor como PartitionKey, todas las entidades se encuentran en una sola partición que es muy grande. En el mejor nivel de granularidad, PartitionKey puede contener valores únicos para cada entidad. El resultado es que hay una partición para cada entidad. En la tabla siguiente se muestran las ventajas y desventajas del intervalo de granularidades:

Granularidad de PartitionKey Tamaño de la partición Ventajas Desventajas
Un solo valor Pequeño número de entidades Las transacciones por lotes son posibles con cualquier entidad.

Todas las entidades son locales y se sirven desde el mismo nodo de almacenamiento.
Un solo valor Gran número de entidades Las transacciones del grupo de entidades pueden ser posibles con cualquier entidad. Para obtener más información sobre los límites de las transacciones del grupo de entidades, consulte Realización de transacciones de grupo de entidades. El escalado está limitado.

El rendimiento general se limita al rendimiento de un solo servidor.
Varios valores Varias particiones

Los tamaños de partición dependen de la distribución de entidades.
Las transacciones por lotes son posibles en algunas entidades.

Es posible crear particiones dinámicas.

Las consultas de solicitud única son posibles (sin tokens de continuación).

El equilibrio de carga entre más servidores de partición es posible.
Una distribución muy desigual de las entidades entre particiones podría limitar el rendimiento de las particiones más grandes y activas.
Valores únicos Muchas particiones pequeñas La tabla es altamente escalable.

Las particiones de intervalo pueden mejorar el rendimiento de las consultas de intervalo entre particiones.
Las consultas que implican intervalos pueden requerir visitas a más de un servidor.

No es posible realizar transacciones por lotes.

Los patrones de solo anexión o solo anteponer pueden afectar al rendimiento de inserción.

En la tabla se muestra cómo afecta el escalado a los valores partitionKey . Se recomienda favorecer las particiones más pequeñas porque ofrecen un mejor equilibrio de carga. Las particiones más grandes pueden ser adecuadas en algunos escenarios y no son necesariamente inconvenientes. Por ejemplo, si la aplicación no requiere escalabilidad, una sola partición grande podría ser adecuada.

Determinación de consultas

Las consultas recuperan datos de las tablas. Al analizar los datos de una tabla en Azure Table Storage, es importante tener en cuenta qué consultas usará la aplicación. Si una aplicación tiene varias consultas, es posible que tenga que priorizarlas, aunque sus decisiones podrían ser subjetivas. En muchos casos, las consultas dominantes son reconocibles de otras consultas. Desde el punto de vista del rendimiento, las consultas se clasifican en distintas categorías. Dado que una tabla tiene solo un índice, el rendimiento de las consultas normalmente está relacionado con las propiedades PartitionKey y RowKey . En la tabla siguiente se muestran los diferentes tipos de consultas y sus clasificaciones de rendimiento:

Tipo de consulta Coincidencia de PartitionKey Coincidencia de RowKey Clasificación del rendimiento
Análisis de rangos de filas Exact Parcial Mejor con particiones de menor tamaño.

Incorrecto con particiones que son muy grandes.
Análisis de rangos de particiones Parcial Parcial Bueno con un pequeño número de servidores de partición que se están tocándose.

Peor con más servidores que se tocan.
Análisis de tabla completa Parcial, ninguna Parcial, ninguna Peor con un subconjunto de particiones que se examinan.

Lo peor es que se examinan todas las particiones.

Nota

En la tabla las evaluaciones de rendimiento se definen con respecto a las demás. El número y el tamaño de las particiones pueden determinar en última instancia cómo se realiza la consulta. Por ejemplo, un examen de intervalo de particiones para una tabla que tiene muchas particiones grandes podría tener un rendimiento deficiente en comparación con un examen de tabla completo para una tabla que tiene algunas particiones pequeñas.

Los tipos de consulta enumerados en la tabla anterior muestran una progresión de los mejores tipos de consultas que se usarán en los peores tipos, en función de sus clasificaciones de rendimiento. El uso del tipo de consultas de punto resulta ser el mejor porque estas usan por completo el índice clúster de la tabla. En la consulta de punto siguiente se usan los datos de la tabla de registro foot race:

http://<account>.windows.core.net/registrations(PartitionKey=”2011 New York City Marathon__Full”,RowKey=”1234__John__M__55”)  
  

Si la aplicación usa varias consultas, no todas pueden ser consultas de punto. Desde el punto de vista del rendimiento, las consultas por rango siguen a las consultas de punto. Hay dos tipos de consultas de intervalo: el examen del intervalo de filas y el examen del intervalo de particiones. El análisis de rangos de filas especifica una sola partición. Dado que la operación se produce en un único servidor de partición, los exámenes de intervalo de filas suelen ser más eficaces que los exámenes de intervalo de particiones. Sin embargo, un factor clave en el rendimiento de los exámenes de intervalo de filas es la manera en que se realiza una consulta selectiva. La selectividad de la consulta determina cuántas filas deben iterarse para encontrar las filas coincidentes. Cuanto más selectivas sean las consultas, más eficientes serán en los análisis de rangos de filas.

Para evaluar las prioridades de las consultas, tenga en cuenta los requisitos de frecuencia y tiempo de respuesta para cada consulta. Las consultas que se ejecutan con frecuencia pueden ser prioritarias más altas. Sin embargo, una consulta importante pero rara vez usada podría tener requisitos de baja latencia que podrían clasificarla más alto en la lista de prioridades.

Elija el valor PartitionKey.

El núcleo del diseño de cualquier tabla es su escalabilidad, las consultas usadas para acceder a ella y los requisitos de operación de almacenamiento. Los valores partitionKey que elija dictan cómo se particiona una tabla y el tipo de consultas que puede usar. Las operaciones de almacenamiento, y especialmente las inserciones, también pueden afectar a la elección de valores PartitionKey . Los valores PartitionKey pueden oscilar entre valores únicos y únicos. También se pueden crear mediante varios valores. Puede usar propiedades de entidad para formar el valor PartitionKey . O bien, la aplicación puede calcular el valor. En las secciones siguientes se describen consideraciones importantes.

Transacciones de grupos de entidades

Los desarrolladores deben considerar primero si la aplicación usará transacciones de grupo de entidades (actualizaciones por lotes). Las transacciones del grupo de entidades requieren que las entidades tengan el mismo valor PartitionKey . Además, dado que las actualizaciones por lotes son para un grupo completo, las opciones de valores PartitionKey pueden estar limitadas. Por ejemplo, una aplicación bancaria que mantenga transacciones en efectivo debe insertar estas transacciones de forma atómica en la tabla. Las transacciones en efectivo representan el débito y los lados del crédito y deben ser netos a cero. Este requisito significa que el número de cuenta no se puede usar como parte del valor PartitionKey porque cada lado de la transacción usa números de cuenta diferentes. En su lugar, un identificador de transacción podría ser una mejor opción.

Particiones

Los números de partición y los tamaños afectan a la escalabilidad de una tabla que está bajo carga. También se controlan mediante la granularidad de los valores partitionKey . Puede resultar difícil determinar PartitionKey en función del tamaño de la partición, especialmente si la distribución de valores es difícil de predecir. Una buena regla práctica consiste en usar varias particiones más pequeñas. Muchas particiones de tabla facilitan a Azure Table Storage administrar los nodos de almacenamiento desde los que se sirven las particiones.

Elegir valores únicos o más finos para PartitionKey da como resultado particiones más pequeñas pero más. Esto generalmente es favorable porque el sistema puede equilibrar la carga de las muchas particiones para distribuir la carga entre muchas particiones. Pero se debe tener en cuenta el efecto de tener muchas particiones en las consultas por rango entre particiones. Estos tipos de consultas deben visitar varias particiones para satisfacer una consulta. Es posible que las particiones se distribuyan entre muchos servidores de partición. Si una consulta cruza el límite de un servidor, deben devolverse tokens de continuación. Los tokens de continuación especifican los siguientes valores PartitionKey o RowKey para recuperar el siguiente conjunto de datos de la consulta. En otras palabras, los tokens de continuación representan al menos una solicitud más al servicio, lo que puede degradar el rendimiento general de la consulta.

La selectividad de la consulta es otro factor que puede afectar al rendimiento de la consulta. La selectividad de la consulta es una medida del número de filas que deben iterarse en cada partición. Cuanto más selectivo sea una consulta, más eficaz será que la consulta devuelva las filas que desee. El rendimiento general de las consultas de intervalo puede depender del número de servidores de particiones que se deben tocar o de cómo es selectiva la consulta. También debe evitar usar los patrones de solo anexión o solo anteponer al insertar datos en la tabla. Si usa estos patrones, a pesar de crear particiones pequeñas y muchas, puede limitar el rendimiento de las operaciones de inserción. Los patrones de solo anexión y solo anteponer se describen en Particiones de intervalo.

Consultas

Conocer las consultas que va a usar puede ayudarle a determinar qué propiedades son importantes tener en cuenta para el valor PartitionKey . Las propiedades que se usan en las consultas son candidatas para el valor PartitionKey . En la tabla siguiente se proporciona una guía general sobre cómo determinar el valor PartitionKey :

Si la entidad… Acción
Tiene una sola propiedad de clave Úselo como PartitionKey.
Tiene dos propiedades de clave Use uno como PartitionKey y el otro como RowKey.
Tiene más de dos propiedades de clave Use una clave compuesta de valores concatenados.

Si hay más de una consulta igual de dominante, puede insertar la información varias veces mediante valores RowKey diferentes que necesite. La aplicación administrará filas secundarias (o terciarias, etc.). Puede usar este tipo de patrón para satisfacer los requisitos de rendimiento de las consultas. En el ejemplo siguiente se usan los datos del ejemplo de registro de carrera de pie. Tiene dos consultas dominantes:

  • Consulta por dorsal
  • Consulta por edad

Para atender los dos consultas dominantes, inserte dos filas como transacción de grupo de entidades. En la tabla siguiente se muestran las propiedades PartitionKey y RowKey de este escenario. Los valores RowKey proporcionan un prefijo para elbib y la antigüedad para que la aplicación pueda distinguir entre los dos valores.

PartitionKey RowKey
Maratón de la Ciudad de Nueva York 2011__Completa BIB:01234__John__M__55
Maratón de la Ciudad de Nueva York 2011__Completa AGE:055__1234__John__M

En este ejemplo, se puede realizar una transacción de grupo de entidades porque los valores PartitionKey son los mismos. La transacción de grupo proporciona atomicidad de la operación de inserción. Aunque es posible usar este patrón con diferentes valores PartitionKey , se recomienda usar los mismos valores para obtener esta ventaja. De lo contrario, es posible que tenga que escribir lógica adicional para asegurarse de que las transacciones atómicas que usan valores PartitionKey diferentes.

Operaciones de almacenamiento

Las tablas de Azure Table Storage pueden encontrar carga no solo desde consultas. También pueden encontrarse con la carga de operaciones de almacenamiento como inserciones, actualizaciones y eliminaciones. Tenga en cuenta qué tipo de operaciones de almacenamiento realizará en la tabla y a qué velocidad. Si realiza estas operaciones con poca frecuencia, es posible que no tenga que preocuparse por ellas. Sin embargo, para las operaciones frecuentes como realizar muchas inserciones en poco tiempo, debe tener en cuenta cómo se sirven esas operaciones como resultado de los valores partitionKey que elija. Los ejemplos importantes son los patrones de solo anexión y solo anteponer. Los patrones de solo anexión y solo anteponer se describen en Particiones de intervalo.

Cuando se usa un patrón de solo anexión o de solo anexión, se usan valores únicos ascendentes o descendentes para PartitionKey en inserciones posteriores. Si combina este patrón con operaciones de inserción frecuentes, la tabla no podrá atender las operaciones de inserción con una gran escalabilidad. La escalabilidad de la tabla se ve afectada porque Azure no puede equilibrar la carga de las solicitudes de operación a otros servidores de partición. En ese caso, puede considerar la posibilidad de usar valores aleatorios, como los valores GUID. A continuación, los tamaños de partición pueden permanecer pequeños y mantener el equilibrio de carga durante las operaciones de almacenamiento.

Pruebas de esfuerzo de partición de tabla

Cuando el valor PartitionKey es complejo o requiere comparaciones con otras asignaciones de PartitionKey , es posible que tenga que probar el rendimiento de la tabla. La prueba debe examinar lo bien que se ejecuta una partición sometida a picos de carga.

Para realizar una prueba de esfuerzo

  1. Cree una tabla de prueba.
  2. Cargue la tabla de prueba con datos para que contenga entidades que tengan el valor PartitionKey de destino.
  3. Use la aplicación para simular la carga máxima en la tabla. Tener como destino una sola partición mediante el valor PartitionKey del paso 2. Este paso es diferente para cada aplicación, pero la simulación debe incluir todas las consultas necesarias y las operaciones de almacenamiento. Es posible que tenga que ajustar la aplicación para que tenga como destino una sola partición.
  4. Examine el rendimiento de las operaciones GET o PUT en la tabla.

Para examinar el rendimiento, compare los valores reales con el límite especificado de una sola partición en un solo servidor. Las particiones están limitadas a 2000 entidades por segundo. Si el rendimiento supera las 2000 entidades por segundo para una partición, el servidor podría ejecutarse demasiado activo en una configuración de producción. En este caso, los valores partitionKey pueden ser demasiado gruesos, por lo que no hay suficientes particiones o las particiones son demasiado grandes. Es posible que tenga que modificar el valor partitionKey para que las particiones se distribuyan entre más servidores.

Equilibrio de carga

El equilibrio de carga en la capa de partición se produce cuando una partición es demasiado activa. Cuando una partición es demasiado activa, la partición, específicamente el servidor de particiones, funciona más allá de su escalabilidad de destino. En el caso del almacenamiento de Azure, cada partición tiene un destino de escalabilidad de 2000 entidades por segundo. El equilibrio de carga también se produce en la capa sistema de archivos distribuido (DFS).

El equilibrio de carga en la capa DFS se ocupa de la carga de E/S y está fuera del ámbito de este artículo. El equilibrio de carga en la capa de partición no se produce inmediatamente después de superar el destino de escalabilidad. En su lugar, el sistema espera unos minutos antes de comenzar el proceso de equilibrio de carga. Así se asegura de que una partición realmente ha pasado a estar activa. No es necesario primor particiones con carga generada que desencadena el equilibrio de carga porque el sistema realiza automáticamente la tarea.

Si una tabla estaba preparada con una carga determinada, el sistema podría equilibrar las particiones en función de la carga real, lo que da lugar a una distribución significativamente diferente de las particiones. En lugar de crear particiones, considere la posibilidad de escribir código que controle el tiempo de espera y los errores de disponibilidad del servidor. Los errores se devuelven cuando el sistema está equilibrando la carga. Al controlar esos errores mediante una estrategia de reintento, la aplicación puede controlar mejor las cargas máximas. Las estrategias de reintento se tratan de forma más detallada en la siguiente sección.

Cuando se produce el equilibrio de carga, la partición se queda sin conexión durante unos segundos. Durante el período sin conexión, el sistema reasigna la partición a otro servidor de particiones. Es importante tener en cuenta que los datos no se almacenan en los servidores de partición. sino que atienden las entidades de la capa del DFS. Dado que los datos no se almacenan en la capa de partición, mover particiones a distintos servidores es un proceso rápido. Esta flexibilidad limita considerablemente el tiempo de inactividad, si existe, que la aplicación podría encontrar.

Estrategia de reintento

Es importante que la aplicación controle los errores de operación de almacenamiento para ayudar a garantizar que no pierda ninguna actualización de datos. Algunos errores no requieren una estrategia de reintento. Por ejemplo, las actualizaciones que devuelven un error 401 No autorizado no se benefician de volver a intentar la operación porque es probable que el estado de la aplicación no cambie entre reintentos que resuelvan el error 401. Sin embargo, los errores como Server Busy o Timeout están relacionados con las características de equilibrio de carga de Azure que proporcionan escalabilidad de tablas. Cuando los nodos de almacenamiento que sirven a las entidades se vuelven activos, Azure equilibra la carga moviendo particiones a otros nodos. Durante este tiempo, es posible que la partición no sea accesible, lo que produce errores de tiempo de espera o ocupado del servidor. Finalmente, la partición se vuelve a habilitar y se reanuda la actualización.

Una estrategia de reintento es adecuada para los errores de tiempo de espera o ocupado del servidor. En la mayoría de los casos, puede excluir errores de 400 niveles y algunos errores de 500 niveles de la lógica de reintento. Los errores que se pueden excluir incluyen la versión 501 no implementada y la versión HTTP 505 no admitida. A continuación, puede implementar una estrategia de reintento para un máximo de 500 errores de nivel, como Servidor ocupado (503) y Tiempo de espera (504).

Puede elegir entre tres estrategias de reintento comunes para la aplicación:

  • Sin reintento: no se realiza ningún intento de reintento.
  • Retroceso fijo: la operación se reintenta N veces con un valor de retroceso constante.
  • Retroceso exponencial: la operación se reintenta N veces con un valor de retroceso exponencial.

La estrategia Sin reintento es una forma sencilla (y evasiva) de administrar errores de operación. Sin embargo, una estrategia sin reintento no es muy útil. Al no imponer ningún intento de reintento, supone un riesgo evidente de que los datos no se almacenen correctamente después de una operación con error. Una mejor estrategia es usar la estrategia de retroceso fijo. que proporciona la capacidad de reintentar las operaciones con la misma duración de retroceso.

Sin embargo, la estrategia no está optimizada para controlar tablas altamente escalables. Si muchos subprocesos o procesos están esperando la misma duración, pueden producirse colisiones. Una estrategia de reintento recomendada es una que usa un retroceso exponencial en el que cada intento de reintento es mayor que el último intento. Es similar al algoritmo de prevención de colisiones usado en redes informáticas, como Ethernet. El retroceso exponencial usa un factor aleatorio para proporcionar una desviación adicional al intervalo resultante. Después, el valor de retroceso se restringe a los límites mínimo y máximo. Se puede usar la siguiente fórmula para calcular el siguiente valor de retroceso con un algoritmo exponencial:

y = Rand(0,8z, 1,2z)(2x-1

y = Min(zmin + y, zmax

Donde:

z = retroceso predeterminado en milisegundos

zmin = retroceso mínimo predeterminado en milisegundos

zmax = retroceso máximo predeterminado en milisegundos

x = número de reintentos

y = valor de reintento en milisegundos

Los multiplicadores 0,8 y 1,2 usados en la función Rand (aleatorio) generan una variación aleatoria del retroceso predeterminado dentro de ±20% del valor original. El intervalo de ±20 % es aceptable para la mayoría de las estrategias de reintento y evita más colisiones. La fórmula se puede implementar mediante el código siguiente:

int retries = 1;  
  
// Initialize variables with default values  
var defaultBackoff = TimeSpan.FromSeconds(30);  
var backoffMin = TimeSpan.FromSeconds(3);  
var backoffMax = TimeSpan.FromSeconds(90);  
  
var random = new Random();  
  
double backoff = random.Next(  
    (int)(0.8D * defaultBackoff.TotalMilliseconds),   
    (int)(1.2D * defaultBackoff.TotalMilliseconds));  
backoff *= (Math.Pow(2, retries) - 1);  
backoff = Math.Min(  
    backoffMin.TotalMilliseconds + backoff,   
    backoffMax.TotalMilliseconds);  
  

Resumen

Una aplicación de Azure Table Storage puede almacenar una gran cantidad de datos porque Table Storage administra y reasigna particiones en muchos nodos de almacenamiento. Se puede usar el particionamiento de datos para controlar la escalabilidad de la tabla. Planee con antelación al definir un esquema de tabla para asegurarse de implementar estrategias de creación de particiones eficaces. En concreto, analice los requisitos, los datos y las consultas de la aplicación antes de seleccionar valores PartitionKey . Cada partición se puede reasignar a distintos nodos de almacenamiento a medida que el sistema responde al tráfico. Use una prueba de esfuerzo de partición para asegurarse de que la tabla tiene los valores partitionKey correctos. Esta prueba le ayuda a determinar cuándo las particiones son demasiado activas y le ayuda a realizar los ajustes de partición necesarios.

Para asegurarse de que la aplicación controla errores intermitentes y que los datos se conservan, use una estrategia de reintento con retroceso. La directiva de reintento predeterminada que usa la biblioteca cliente de Azure Storage tiene un retroceso exponencial que evita colisiones y maximiza el rendimiento de la aplicación.