azure - microsoft - ¿No se admite la recuperación de muchas filas con TableBatchOperation?
porta azure (6)
Las operaciones "Get" de lotes no son compatibles con Azure Table Storage. Las operaciones admitidas son: Agregar, Eliminar, Actualizar y Fusionar. Debería ejecutar consultas como solicitudes separadas. Para un procesamiento más rápido, es posible que desee ejecutar estas consultas en paralelo.
Aquí hay un fragmento de código que inicializa un TableBatchOperation diseñado para recuperar dos filas en un solo lote:
TableBatchOperation batch = new TableBatchOperation();
batch.Add(TableOperation.Retrieve("somePartition", "rowKey1"));
batch.Add(TableOperation.Retrieve("somePartition", "rowKey2"));
//second call throws an ArgumentException:
//"A batch transaction with a retrieve operation cannot contain
//any other operation"
Como se mencionó, se lanza una excepción, y parece no ser compatible para recuperar N filas en un solo lote . Esto es un gran problema para mí, ya que necesito recuperar aproximadamente 50 filas por solicitud. Este problema tiene tanto que ver con el rendimiento como con el costo. Como sabrá, los precios de Azure Table Storage se basan en la cantidad de transacciones, lo que significa que 50 operaciones de recuperación son 50 veces más costosas que una sola operación por lotes.
¿Me he perdido algo?
Nota al margen Estoy usando el nuevo Azure Storage api 2.0. Me di cuenta de que esta pregunta nunca se ha planteado en la web. ¿Esta restricción podría haberse agregado recientemente?
editar
Encontré una pregunta relacionada aquí: Muy lenta en Azure Table Storage Query en PartitionKey / RowKey List . Parece que usar TableQuery con "o" en rowkeys arrojará resultados con un escaneo completo de la tabla. Realmente hay un problema serio aquí ...
¿Cuántas entidades tienes por partición? Con una operación de recuperación puede recuperar hasta 1000 registros por consulta. Luego puede hacer su filtrado de clave de fila en el conjunto de memoria y solo pagar por 1 operación.
Otra opción es hacer una consulta de rango Row Key para recuperar parte de una partición en una operación. Básicamente, especifique un límite superior e inferior para devolver las claves de fila, en lugar de una partición completa.
Su mejor opción es crear una consulta de selección Linq / OData ... que obtenga lo que está buscando.
Para un mejor rendimiento, debe realizar una consulta por partición y ejecutar esas consultas simultáneamente.
No lo he probado personalmente, pero creo que funcionaría.
Al diseñar su esquema de Partition Key (PK) y Row Key (RK) en Azure Table Storage (ATS), su consideración principal debe ser cómo va a recuperar los datos. Como ha dicho, cada consulta que ejecuta cuesta tanto dinero como, lo que es más importante, tiempo, por lo que debe recuperar todos los datos en una sola consulta eficiente. Las consultas eficientes que puede ejecutar en ATS son de estos tipos:
- Exact PK y RK
- Exact PK, rango RK
- Rango PK
- Rango PK, rango RK
En base a sus comentarios, supongo que tiene algunos datos que son similares a esto:
PK RK Data
Guid1 A {Data:{...}, RelatedRows: [{PK:"Guid2", RK:"B"}, {PK:"Guid3", RK:"C"}]}
Guid2 B {Data:{...}, RelatedRows: [{PK:"Guid1", RK:"A"}]
Guid3 C {Data:{...}, RelatedRows: [{PK:"Guid1", RK:"A"}];}
y ha recuperado los datos en Guid1, y ahora necesita cargar Guid2 y Guid3. También estoy suponiendo que estas filas no tienen un denominador común como que son todas para el mismo usuario. Con esto en mente, crearía una "tabla de índice" adicional que podría verse así:
PK RK Data
Guid1-A Guid2-B {Data:{....}}
Guid1-A Guid3-C {Data:{....}}
Guid2-B Guid1-A {Data:{....}}
Guid2-B Guid1-A {Data:{....}}
Donde el PK es el PK y el RK combinados del padre y el RK son los PK y RK combinados de la fila secundaria. A continuación, puede ejecutar una consulta que dice devolver todas las filas con PK = "Guid1-A" y obtendrá todos los datos relacionados con una sola llamada (o dos llamadas en general). La sobrecarga más grande que esto genera se encuentra en sus escrituras, de modo que ahora, al hacer una fila correcta, también debe escribir filas para cada una de las filas relacionadas y también asegurarse de que los datos estén actualizados (esto puede no ser un problema). para ti si esto es un tipo de escenario de escribir una vez).
Si alguna de mis suposiciones es incorrecta o si tiene algunos datos de ejemplo, puedo actualizar esta respuesta con ejemplos más relevantes.
Pruebe algo como esto:
TableQuery<DynamicTableEntity> query = new TableQuery<DynamicTableEntity>()
.Where(TableQuery.CombineFilters(
TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "partition1"),
TableOperators.And,
TableQuery.CombineFilters(
TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.Equal, "row1"),
TableOperators.Or,
TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.Equal, "row2"))));
Sé que esta es una vieja pregunta, pero como Azure STILL no admite índices secundarios, parece que será relevante por algún tiempo.
Estaba golpeando el mismo tipo de problema. En mi caso, necesitaba buscar cientos de elementos dentro de la misma partición, donde hay millones de filas (imagine GUID como clave de fila). Probé un par de opciones para buscar 10.000 filas
- (PK && RK)
- (PK && RK1) || (PK y RK2) || ...
- PK && (RK1 || RK2 || ...)
Estaba usando la API Async, con un máximo de 10 grados de paralelismo (máximo de 10 solicitudes pendientes). También probé un par de tamaños de lote diferentes (10 filas, 50, 100).
Test Batch Size API calls Elapsed (sec)
(PK && RK) 1 10000 95.76
(PK && RK1) || (PK && RK2) 10 1000 25.94
(PK && RK1) || (PK && RK2) 50 200 18.35
(PK && RK1) || (PK && RK2) 100 100 17.38
PK && (RK1 || RK2 || … ) 10 1000 24.55
PK && (RK1 || RK2 || … ) 50 200 14.90
PK && (RK1 || RK2 || … ) 100 100 13.43
NB: Todos están dentro de la misma partición, solo varias llaves de paso.
Me hubiera encantado reducir el número de llamadas API. Pero como beneficio adicional, el tiempo transcurrido también es significativamente menor, ahorrando en costos de cómputo (¡al menos en mi extremo!).
No es demasiado sorprendente, los lotes de 100 filas entregaron el mejor rendimiento transcurrido. Obviamente, hay otras consideraciones de rendimiento, especialmente el uso de la red (el # 1 casi no usa la red en absoluto, mientras que los demás lo presionan mucho más)
EDITAR Tenga cuidado al consultar muchas llaves de paso. Existe (o por supuesto) una limitación de longitud de URL para la consulta. Si supera la longitud, la consulta seguirá teniendo éxito porque el servicio no puede decir que la URL se truncó. En nuestro caso, hemos limitado la longitud de la consulta combinada a unos 2500 caracteres (¡URL codificada!)