

# Uso de tablas, elementos, consultas, análisis e índices
<a name="WorkingWithDynamo"></a>

Esta sección proporciona detalles sobre el uso de tablas, elementos, consultas y mucho más en Amazon DynamoDB.

**Topics**
+ [Uso de tablas y datos en DynamoDB](WorkingWithTables.md)
+ [Tablas globales: multiactivas, replicación en varias regiones](GlobalTables.md)
+ [Uso de elementos y atributos en DynamoDB](WorkingWithItems.md)
+ [Mejora del acceso con índices secundarios en DynamoDB](SecondaryIndexes.md)
+ [Administración de flujos de trabajo complejos con transacciones de DynamoDB](transactions.md)
+ [Captura de datos de cambio con Amazon DynamoDB](streamsmain.md)

# Uso de tablas y datos en DynamoDB
<a name="WorkingWithTables"></a>

En esta sección se describe cómo usar la AWS Command Line Interface (AWS CLI) y los SDK de AWS para crear, actualizar y eliminar tablas de Amazon DynamoDB.

**nota**  
También puede llevar a cabo las mismas tareas utilizando la Consola de administración de AWS. Para obtener más información, consulte [Uso de la consola](AccessingDynamoDB.md#ConsoleDynamoDB).

En esta sección también se ofrece más información sobre la capacidad de rendimiento, el uso del escalado automático de DynamoDB o la configuración manual del rendimiento aprovisionado.

**Topics**
+ [Operaciones básicas en tablas de DynamoDB](WorkingWithTables.Basics.md)
+ [Aspectos que tener en cuenta a la hora de elegir una clase de tabla en DynamoDB](WorkingWithTables.tableclasses.md)
+ [Cómo agregar etiquetas a los recursos en DynamoDB](Tagging.md)

# Operaciones básicas en tablas de DynamoDB
<a name="WorkingWithTables.Basics"></a>

Al igual que otros sistemas de base de datos, Amazon DynamoDB almacena datos en tablas. Puede administrar las tablas con una pocas operaciones básicas.

**Topics**
+ [Creación de una tabla](#WorkingWithTables.Basics.CreateTable)
+ [Descripción de una tabla](#WorkingWithTables.Basics.DescribeTable)
+ [Actualización de una tabla](#WorkingWithTables.Basics.UpdateTable)
+ [Eliminación de una tabla](#WorkingWithTables.Basics.DeleteTable)
+ [Uso de la protección contra eliminación](#WorkingWithTables.Basics.DeletionProtection)
+ [Enumeración de nombres de tablas](#WorkingWithTables.Basics.ListTables)
+ [Descripción de las cuotas de rendimiento aprovisionado](#WorkingWithTables.Basics.DescribeLimits)

## Creación de una tabla
<a name="WorkingWithTables.Basics.CreateTable"></a>

Use la operación `CreateTable` para crear una tabla en Amazon DynamoDB. Para crear la tabla, debe proporcionar la siguiente información:
+ **Nombre de la tabla.** El nombre debe cumplir las reglas de nomenclatura de DynamoDB y debe ser único para la región y cuenta de AWS actuales. Por ejemplo, puede crear una tabla `People` en EE. UU. Este (Norte de Virginia) y otra tabla `People` en Europa (Irlanda). Sin embargo, estas dos tablas serían totalmente distintas entre sí. Para obtener más información, consulte [Tipos de datos y reglas de nomenclatura admitidos en Amazon DynamoDB](HowItWorks.NamingRulesDataTypes.md).
+ **Primary key (Clave principal.** La clave principal puede constar de un atributo (clave de partición) o dos (clave de partición y clave de ordenación). Debe proporcionar el nombre, el tipo de datos y el rol de cada atributo: `HASH` (para una clave de partición) y `RANGE` (para una clave de ordenación). Para obtener más información, consulte [Clave principal](HowItWorks.CoreComponents.md#HowItWorks.CoreComponents.PrimaryKey).
+ **Ajustes de rendimiento (de las tablas aprovisionadas).** Si utiliza el modo aprovisionado, debe especificar los ajustes iniciales de rendimiento de lectura y escritura de la tabla. Puede modificar esta configuración en otro momento o habilitar la función Auto Scaling de DynamoDB para administrarlos automáticamente. Para obtener más información, consulte [Modo de capacidad aprovisionada de DynamoDB](provisioned-capacity-mode.md) y [Administración automática de la capacidad de rendimiento con la función Auto Scaling de DynamoDB](AutoScaling.md).

### Ejemplo 1: creación de una tabla bajo demanda
<a name="create-payperrequest-example"></a>

Para crear la misma tabla `Music` utilizando el modo bajo demanda:

```
aws dynamodb create-table \
    --table-name Music \
    --attribute-definitions \
        AttributeName=Artist,AttributeType=S \
        AttributeName=SongTitle,AttributeType=S \
    --key-schema \
        AttributeName=Artist,KeyType=HASH \
        AttributeName=SongTitle,KeyType=RANGE \
    --billing-mode=PAY_PER_REQUEST
```

La operación `CreateTable` devuelve metadatos de la tabla, como se muestra a continuación:

```
{
    "TableDescription": {
        "TableArn": "arn:aws:dynamodb:us-east-1:123456789012:table/Music",
        "AttributeDefinitions": [
            {
                "AttributeName": "Artist",
                "AttributeType": "S"
            },
            {
                "AttributeName": "SongTitle",
                "AttributeType": "S"
            }
        ],
        "ProvisionedThroughput": {
            "NumberOfDecreasesToday": 0,
            "WriteCapacityUnits": 0,
            "ReadCapacityUnits": 0
        },
        "TableSizeBytes": 0,
        "TableName": "Music",
        "BillingModeSummary": {
            "BillingMode": "PAY_PER_REQUEST"
        },
        "TableStatus": "CREATING",
        "TableId": "12345678-0123-4567-a123-abcdefghijkl",
        "KeySchema": [
            {
                "KeyType": "HASH",
                "AttributeName": "Artist"
            },
            {
                "KeyType": "RANGE",
                "AttributeName": "SongTitle"
            }
        ],
        "ItemCount": 0,
        "CreationDateTime": 1542397468.348
    }
}
```

**importante**  
 Al llamar a `DescribeTable` en una tabla bajo demanda, las unidades de capacidad de lectura y de escritura se establecen en 0. 

### Ejemplo 2: creación de una tabla aprovisionada
<a name="create-provisioned-example"></a>

En el ejemplo siguiente de la AWS CLI se muestra cómo crear una tabla (`Music`). La clave principal consta de `Artist` (clave de partición) y `SongTitle` (clave de ordenación), ambas de tipo `String`. El desempeño máximo de esta tabla es de 10 unidades de capacidad de lectura y 5 unidades de capacidad de escritura.

```
aws dynamodb create-table \
    --table-name Music \
    --attribute-definitions \
        AttributeName=Artist,AttributeType=S \
        AttributeName=SongTitle,AttributeType=S \
    --key-schema \
        AttributeName=Artist,KeyType=HASH \
        AttributeName=SongTitle,KeyType=RANGE \
    --provisioned-throughput \
        ReadCapacityUnits=10,WriteCapacityUnits=5
```

La operación `CreateTable` devuelve metadatos de la tabla, como se muestra a continuación:

```
{
    "TableDescription": {
        "TableArn": "arn:aws:dynamodb:us-east-1:123456789012:table/Music",
        "AttributeDefinitions": [
            {
                "AttributeName": "Artist",
                "AttributeType": "S"
            },
            {
                "AttributeName": "SongTitle",
                "AttributeType": "S"
            }
        ],
        "ProvisionedThroughput": {
            "NumberOfDecreasesToday": 0,
            "WriteCapacityUnits": 5,
            "ReadCapacityUnits": 10
        },
        "TableSizeBytes": 0,
        "TableName": "Music",
        "TableStatus": "CREATING",
        "TableId": "12345678-0123-4567-a123-abcdefghijkl",
        "KeySchema": [
            {
                "KeyType": "HASH",
                "AttributeName": "Artist"
            },
            {
                "KeyType": "RANGE",
                "AttributeName": "SongTitle"
            }
        ],
        "ItemCount": 0,
        "CreationDateTime": 1542397215.37
    }
}
```

El componente `TableStatus` indica el estado actual de la tabla (`CREATING`). Puede que la tabla tarde un tiempo en crearse, según los valores especificados para `ReadCapacityUnits` y `WriteCapacityUnits`. Cuanto mayores sean estos valores, más recursos tendrá que asignar DynamoDB a la tabla.

### Ejemplo 3: creación de una tabla mediante la clase de tabla de acceso poco frecuente estándar de DynamoDB
<a name="create-infrequent-access-example"></a>

Para crear lo misma tabla `Music` utilizando la clase de tabla de acceso DynamoDB Estándar - Acceso poco frecuente.

```
aws dynamodb create-table \
    --table-name Music \
    --attribute-definitions \
        AttributeName=Artist,AttributeType=S \
        AttributeName=SongTitle,AttributeType=S \
    --key-schema \
        AttributeName=Artist,KeyType=HASH \
        AttributeName=SongTitle,KeyType=RANGE \
    --provisioned-throughput \
        ReadCapacityUnits=10,WriteCapacityUnits=5 \
    --table-class STANDARD_INFREQUENT_ACCESS
```

La operación `CreateTable` devuelve metadatos de la tabla, como se muestra a continuación:

```
{
    "TableDescription": {
        "TableArn": "arn:aws:dynamodb:us-east-1:123456789012:table/Music",
        "AttributeDefinitions": [
            {
                "AttributeName": "Artist",
                "AttributeType": "S"
            },
            {
                "AttributeName": "SongTitle",
                "AttributeType": "S"
            }
        ],
        "ProvisionedThroughput": {
            "NumberOfDecreasesToday": 0,
            "WriteCapacityUnits": 5,
            "ReadCapacityUnits": 10
        },
        "TableClassSummary": {
            "LastUpdateDateTime": 1542397215.37,
            "TableClass": "STANDARD_INFREQUENT_ACCESS"
        },
        "TableSizeBytes": 0,
        "TableName": "Music",
        "TableStatus": "CREATING",
        "TableId": "12345678-0123-4567-a123-abcdefghijkl",
        "KeySchema": [
            {
                "KeyType": "HASH",
                "AttributeName": "Artist"
            },
            {
                "KeyType": "RANGE",
                "AttributeName": "SongTitle"
            }
        ],
        "ItemCount": 0,
        "CreationDateTime": 1542397215.37
    }
}
```

## Descripción de una tabla
<a name="WorkingWithTables.Basics.DescribeTable"></a>

Para ver información detallada sobre una tabla, utilice la operación `DescribeTable`. Debe proporcionar el nombre de la tabla. El resultado de `DescribeTable` tiene el mismo formato que el de `CreateTable`; Incluye la marca temporal del momento de creación de la tabla, su esquema de claves, su configuración de rendimiento aprovisionado, su tamaño aproximado y los índices secundarios (si los hay).

**importante**  
 Al llamar a `DescribeTable` en una tabla bajo demanda, las unidades de capacidad de lectura y de escritura se establecen en 0. 

**Example**  

```
aws dynamodb describe-table --table-name Music
```

La tabla está lista para usarla cuando el valor de `TableStatus` cambia de `CREATING` a `ACTIVE`.

**nota**  
Si emite una solicitud `DescribeTable` inmediatamente después de una solicitud `CreateTable`, DynamoDB podría devolver un error (`ResourceNotFoundException`). El motivo es que `DescribeTable` usa una consulta con consistencia final, aunque los metadatos de la tabla podrían no estar disponibles todavía. Espere unos segundos y repita la solicitud `DescribeTable`.  
A efectos de facturación, los costes de almacenamiento de DynamoDB incluyen un importe por elemento de 100 bytes en concepto de gastos generales. (Para obtener más información, consulte los [precios de DynamoDB](https://aws.amazon.com/dynamodb/pricing/)). Este importe adicional de 100 bytes por elemento no se utiliza al calcular las unidades de capacidad ni en la operación `DescribeTable`. 

## Actualización de una tabla
<a name="WorkingWithTables.Basics.UpdateTable"></a>

La operación `UpdateTable` permite realizar una de las acciones siguientes:
+ Modificar los ajustes de rendimiento aprovisionado de una tabla (para las tablas en modo aprovisionado).
+ Cambiar el modo de capacidad de lectura/escritura de la tabla.
+ Manipular los índices secundarios globales de la tabla (consulte [Uso de índices secundarios globales en DynamoDB](GSI.md)).
+ Habilite o desactive los DynamoDB Streams en la tabla (consulte [Captura de datos de cambios para DynamoDB Streams](Streams.md)).

**Example**  
En el siguiente ejemplo de la AWS CLI se muestra cómo modificar los ajustes de desempeño provisionado de una tabla:  

```
aws dynamodb update-table --table-name Music \
    --provisioned-throughput ReadCapacityUnits=20,WriteCapacityUnits=10
```

**nota**  
Cuando se emite una solicitud `UpdateTable`, el estado de la tabla cambia de `AVAILABLE` a `UPDATING`. La tabla permanece plenamente disponible para su uso mientras se encuentra en el estado `UPDATING`. Cuando este proceso finaliza, el estado de la tabla cambia de `UPDATING` a `AVAILABLE`.

**Example**  
En el siguiente ejemplo de la AWS CLI se muestra cómo modificar el modo de capacidad de lectura/escritura de una tabla al modo bajo demanda:  

```
aws dynamodb update-table --table-name Music \
    --billing-mode PAY_PER_REQUEST
```

## Eliminación de una tabla
<a name="WorkingWithTables.Basics.DeleteTable"></a>

Puede eliminar las tablas que no utilice con la operación `DeleteTable`. La eliminación de una tabla es una operación irrecuperable. Para eliminar una tabla con la Consola de administración de AWS, consulte [Paso 6: (opcional) Eliminar la tabla de DynamoDB para limpiar los recursos](getting-started-step-6.md).

**Example**  
En el ejemplo siguiente de AWS CLI, se muestra cómo se elimina una tabla.  

```
aws dynamodb delete-table --table-name Music
```

Cuando se emite una solicitud `DeleteTable`, el estado de la tabla cambia de `ACTIVE` a `DELETING`. Puede que la tabla tarde un tiempo en eliminarse, según los recursos que utilice; por ejemplo, los datos almacenados en la tabla y las secuencias o índices que contenga.

Cuando la operación `DeleteTable` concluya, la tabla ya no existirá en DynamoDB.

## Uso de la protección contra eliminación
<a name="WorkingWithTables.Basics.DeletionProtection"></a>

Puede proteger una tabla contra la eliminación accidental con la propiedad de protección contra la eliminación. Activar esta propiedad para las tablas contribuye a garantizar que las tablas no se eliminen accidentalmente durante las operaciones habituales de administración de tablas por parte de sus administradores. De este modo evitará que se interrumpan las operaciones de negocio normales.

 El propietario de la tabla o un administrador autorizado controla la propiedad de protección contra la eliminación de cada tabla. La propiedad de protección contra eliminación para cada tabla está desactivada de forma predeterminada. Se incluyen las réplicas globales y las tablas restauradas a partir de copias de seguridad. Cuando la protección contra el borrado está desactivada para una tabla, ésta puede ser eliminada por cualquier usuario autorizado por una política de Identity and Access Management (IAM). Cuando la protección contra la eliminación está activada para una tabla, nadie puede eliminarla. 

Para cambiar esta configuración, vaya a la **configuración adicional** de la tabla, navegue al panel **Protección contra eliminación** y seleccione **Habilitar protección contra eliminaciones**. 

La propiedad de protección contra eliminación se admite en la consola de DynamoDB, API, CLI/SDK y CloudFormation. La API `CreateTable` admite la propiedad de protección contra eliminación en el momento de creación de la tabla y la API `UpdateTable` admite el cambio de la propiedad de protección contra eliminación para las tablas existentes.

**nota**  
Si se elimina una cuenta de AWS, todos los datos de esa cuenta, incluidas las tablas, se eliminan igualmente en un plazo de 90 días.
Si DynamoDB pierde el acceso a una clave administrada por el cliente que se utilizó para cifrar una tabla, seguirá archivando la tabla. Archivar implica hacer una copia de seguridad de la tabla y eliminar el original.

## Enumeración de nombres de tablas
<a name="WorkingWithTables.Basics.ListTables"></a>

La operación `ListTables` devuelve los nombres de las tablas de DynamoDB de la región y cuenta de AWS actuales.

**Example**  
En el siguiente ejemplo de la AWS CLI se muestra cómo enumerar los nombres de las tablas de DynamoDB.  

```
aws dynamodb list-tables
```

## Descripción de las cuotas de rendimiento aprovisionado
<a name="WorkingWithTables.Basics.DescribeLimits"></a>

La operación `DescribeLimits` devuelve las cuotas vigentes de capacidad de lectura y escritura de la región y cuenta de AWS actuales.

**Example**  
En el siguiente ejemplo de la AWS CLI se muestra cómo describir las cuotas de rendimiento aprovisionado actuales.  

```
aws dynamodb describe-limits
```
El resultado muestra las cuotas superiores de unidades de capacidad de lectura y escritura de la región y cuenta de AWS actuales.

Para obtener más información acerca de estas cuotas y cómo solicitar un aumento de cuotas, consulte [Cuotas de rendimiento predeterminadas](ServiceQuotas.md#default-limits-throughput).

# Aspectos que tener en cuenta a la hora de elegir una clase de tabla en DynamoDB
<a name="WorkingWithTables.tableclasses"></a>

DynamoDB ofrece dos clases de tablas diseñadas para ayudarle a optimizar los costos. La clase de tabla DynamoDB Estándar es la predeterminada y se recomienda para la gran mayoría de las cargas de trabajo. La clase de tabla DynamoDB Estándar - Acceso poco frecuente (DynamoDB Standard-IA) está optimizada para tablas en las que el almacenamiento es el costo dominante. Por ejemplo, las tablas que almacenan datos a los que se accede con poca frecuencia, como registros de aplicaciones, publicaciones antiguas en redes sociales, historial de pedidos de comercio electrónico y antiguos logros en juegos, son buenas candidatas para la clase de tabla Standard - IA.

Cada tabla de DynamoDB está asociada a una clase de tabla. Todos los índices secundarios asociados a la tabla utilizan la misma clase de tabla. Puede configurar la clase de tabla al crear la tabla (DynamoDB Estándar de forma predeterminada) y actualizar la clase de tabla de una tabla existente mediante la Consola de administración de AWS, la CLI de AWS o el SDK de AWS. DynamoDB también es compatible con la administración de la clase de tabla mediante AWS CloudFormation para tablas de una región (tablas que no son globales). Cada clase de tabla ofrece diferentes precios para el almacenamiento de datos, así como para las solicitudes de lectura y escritura. A la hora de elegir una clase de tabla para su tabla, tenga en cuenta lo siguiente:
+ La clase de tabla DynamoDB Estándar ofrece menores costos de rendimiento que DynamoDB Standard - IA y es la opción más rentable para tablas en las que el rendimiento es el costo dominante. 
+ La clase de tabla DynamoDB Standard - IA ofrece menores costos de almacenamiento que DynamoDB Estándar y es la opción más rentable para tablas en las que el almacenamiento es el costo dominante. Cuando el almacenamiento supera el 50 % del coste de rendimiento (lecturas y escrituras) de una tabla que utiliza la clase de tabla DynamoDB Estándar, la clase de tabla DynamoDB Standard - IA puede ayudarlo a reducir el costo total de la tabla. 
+ Las tablas DynamoDB Standard-IA ofrecen el mismo rendimiento, durabilidad y disponibilidad que las tablas DynamoDB Standard. 
+ El cambio entre las clases de tabla DynamoDB Standard y DynamoDB Standard-IA no requiere cambiar el código de la aplicación. Se utilizan las mismas API de DynamoDB y punto de conexión de servicio, independientemente de la clase de tabla que utilicen sus tablas. 
+ Las tablas DynamoDB Standard-IA son compatibles con todas las características existentes de DynamoDB, tales como escalado automático, modo bajo demanda, período de vida (TTL), copias de seguridad bajo demanda, recuperación a un momento dado (PITR) e índices secundarios globales.

La clase de tabla más rentable para su tabla depende de los patrones de uso de almacenamiento y rendimiento previstos para su tabla. Puede consultar el historial de costos de almacenamiento y rendimiento de la tabla y su uso con los Informes de costos y uso de AWS y con AWS Cost Explorer. Utilice estos datos históricos para determinar la clase de tabla más rentable para su tabla. Para obtener más información sobre el uso de los Informes de costos y uso de AWS y AWS Cost Explorer, consulte la [Documentación sobre Billing and Cost Management de AWS](https://docs.aws.amazon.com/account-billing/index.html). Consulte [Precio de Amazon DynamoDB](https://aws.amazon.com/dynamodb/pricing/on-demand/) para obtener más información sobre los precios de las clases de tablas.

**nota**  
Una actualización de clase de tabla es un proceso en segundo plano. Puede seguir accediendo a la tabla con normalidad durante la actualización de una clase de tabla. El tiempo de actualización de la clase de tabla depende del tráfico de la tabla, el tamaño del almacenamiento y otras variables relacionadas. No se permiten más de dos actualizaciones de la clase de tabla en un período de 30 días.

# Cómo agregar etiquetas a los recursos en DynamoDB
<a name="Tagging"></a>

Puede asignar *etiquetas* a los recursos de Amazon DynamoDB. Las etiquetas le permiten categorizar los recursos de distintas maneras; por ejemplo, según su finalidad, propietario, entorno u otro criterio. Las etiquetas pueden ayudar a hacer lo siguiente:
+ Identificar rápidamente un recurso según las etiquetas que le haya asignado.
+ Consultar las facturas de AWS desglosadas por etiquetas.
**nota**  
A los índices secundarios locales (LSI) y los índices secundarios globales (GSI) relacionados con las tablas etiquetadas se les asignan automáticamente las mismas etiquetas. En la actualidad, no se puede etiquetar el uso de DynamoDB Streams.

El etiquetado es compatible con servicios de AWS como Amazon EC2, Amazon S3, DynamoDB, entre otros. Un etiquetado eficiente puede ofrecerle información detallada sobre los costos, ya que le permite crear informes sobre los servicios que llevan una etiqueta determinada.

Para empezar a usar etiquetas, haga lo siguiente:

1. Comprender [Restricciones de etiquetado en DynamoDB](#TaggingRestrictions).

1. Crear etiquetas mediante [Recursos de etiquetado en DynamoDB](Tagging.Operations.md).

1. Usar [Uso de etiquetas de DynamoDB para crear informes de asignación de costos](#CostAllocationReports) para realizar el seguimiento de los costes de AWS según su etiqueta activa.

Por último, es conveniente seguir estrategias de etiquetado óptimas. Para obtener más información, consulte [Estrategias de etiquetado de AWS](https://d0.awsstatic.com/aws-answers/AWS_Tagging_Strategies.pdf).

## Restricciones de etiquetado en DynamoDB
<a name="TaggingRestrictions"></a>

 Cada etiqueta consta de una clave y un valor, ambos definidos por el usuario. Se aplican las siguientes restricciones: 
+  Cada tabla puede de DynamoDB solo puede tener una etiqueta con la misma clave. Si intenta agregar una etiqueta existente (con la misma clave), el valor de la etiqueta existente se actualiza con el valor nuevo. 
+  Las claves y los valores de las etiquetas distinguen entre mayúsculas y minúsculas. 
+  La longitud máxima de la clave es de 128 caracteres Unicode. 
+ La longitud máxima del valor es de 256 caracteres Unicode. 
+  Los caracteres permitidos son letras, espacios en blanco y números, además de los caracteres especiales siguientes: `+ - = . _ : /` 
+  El número máximo de etiquetas por recurso es 50.
+ El tamaño máximo admitido para todas las etiquetas de una tabla es de 10 KB.
+ A los nombres y valores de etiquetas asignados por AWS se les asigna automáticamente el prefijo `aws:`, que no puede asignar. Los nombres de etiquetas asignados por AWS no cuentan para el límite de etiquetas de 50 o el límite de tamaño máximo de 10 KB. Los nombres de etiquetas asignados por el usuario presentan el prefijo `user:` en el informe de asignación de costos. 
+  No es posible antedatar la aplicación de una etiqueta. 

# Recursos de etiquetado en DynamoDB
<a name="Tagging.Operations"></a>

Puede usar la consola de Amazon DynamoDB o la AWS Command Line Interface (AWS CLI) para añadir, enumerar, editar o eliminar etiquetas. A continuación, puede activar estas etiquetas definidas por el usuario de modo que aparezcan en la consola de Administración de facturación y costos de AWS y así poder usarlas para el seguimiento de asignación de costos. Para obtener más información, consulte [Uso de etiquetas de DynamoDB para crear informes de asignación de costos](Tagging.md#CostAllocationReports). 

 Para la edición en bloque, también puede usar Tag Editor en la Consola de administración de AWS. Para obtener más información, consulte [Uso de Tag Editor](https://docs.aws.amazon.com/awsconsolehelpdocs/latest/gsg/tag-editor.html).

 Para usar la API de DynamoDB en su lugar, consulte las siguientes operaciones en la [Referencia de la API de Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/):
+ [TagResource](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TagResource.html)
+ [UntagResource](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UntagResource.html)
+ [ListTagsOfResource](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_ListTagsOfResource.html)

**Topics**
+ [Establecer permisos para filtrar por etiquetas](#Tagging.Operations.permissions)
+ [Añadir etiquetas a tablas nuevas o existentes (Consola de administración de AWS)](#Tagging.Operations.using-console)
+ [Añadir etiquetas a tablas nuevas o existentes (AWS CLI)](#Tagging.Operations.using-cli)

## Establecer permisos para filtrar por etiquetas
<a name="Tagging.Operations.permissions"></a>

Para utilizar etiquetas a fin de filtrar la lista de tablas en la consola de DynamoDB, asegúrese de que las políticas del usuario incluyan acceso a las siguientes operaciones:
+ `tag:GetTagKeys`
+ `tag:GetTagValues`

Puede acceder a estas operaciones adjuntando una nueva política de IAM a su usuario siguiendo los pasos que se indican a continuación.

1. Ingrese a la [Consola de IAM](https://console.aws.amazon.com/iam/) con un usuario administrador.

1. Seleccione “Policies” (Políticas) en el menú de navegación izquierdo.

1. Seleccione “Create Policy” (Crear política).

1. Pegue la siguiente política en el editor JSON.

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Effect": "Allow",
               "Action": [
                   "tag:GetTagKeys",
                   "tag:GetTagValues"
               ],
               "Resource": "*"
           }
       ]
   }
   ```

------

1. Complete el asistente y asigne un nombre a la política (por ejemplo, `TagKeysAndValuesReadAccess`).

1. Seleccione “Users” (Usuarios) en el menú de navegación izquierdo.

1. En la lista, seleccione el usuario que utiliza habitualmente para acceder a la consola de DynamoDB.

1. Seleccione “Add permissions” (Añadir permisos).

1. Seleccione “Attach existing policies directly” (Asociar directamente las políticas existentes).

1. En la lista, seleccione la política que ha creado previamente.

1. Complete el asistente.

## Añadir etiquetas a tablas nuevas o existentes (Consola de administración de AWS)
<a name="Tagging.Operations.using-console"></a>

Puede usar la consola de DynamoDB para añadir etiquetas a tablas nuevas cuando las cree, o para añadir, editar o eliminar etiquetas de tablas existentes.

**Para etiquetar recursos al crearlos (consola)**

1. Inicie sesión en la Consola de administración de AWS y abra la consola de DynamoDB en [https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/).

1. En el panel de navegación, elija **Tablas** y, a continuación, seleccione **Crear tabla**.

1. En la página **Create DynamoDB table (Crear tabla de DynamoDB)**, proporcione un nombre y una clave principal. En la sección **Tags** (Etiquetas), elija **Add new tag** (Agregar nueva etiqueta) e ingrese las etiquetas que desee utilizar.

   Para obtener más información sobre la estructura de las etiquetas, consulte [Restricciones de etiquetado en DynamoDB](Tagging.md#TaggingRestrictions). 

   Para obtener más información sobre cómo crear tablas, consulte [Operaciones básicas en tablas de DynamoDB](WorkingWithTables.Basics.md).

**Para etiquetar recursos existentes (consola)**

Abra la consola de DynamoDB en [https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/).

1. En el panel de navegación, elija **Tablas**.

1. Elija una tabla de la lista y, a continuación, elija la pestaña **Additional settings** (Configuración adicional). Puede agregar, editar o eliminar las etiquetas en la sección **Tags** (Etiquetas) en la parte inferior de la página.

## Añadir etiquetas a tablas nuevas o existentes (AWS CLI)
<a name="Tagging.Operations.using-cli"></a>

En los ejemplos siguientes se muestra cómo usar la AWS CLI para especificar etiquetas al crear tablas e índices, así como para etiquetar recursos existentes.

**Para etiquetar recursos al crearlos (AWS CLI)**
+ En el ejemplo siguiente se crea una nueva tabla de `Movies` y se añade la etiqueta `Owner` con un valor de `blueTeam`: 

  ```
  aws dynamodb create-table \
      --table-name Movies \
      --attribute-definitions AttributeName=Title,AttributeType=S \
      --key-schema AttributeName=Title,KeyType=HASH \
      --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
      --tags Key=Owner,Value=blueTeam
  ```

**Para etiquetar recursos existentes (AWS CLI)**
+ En el ejemplo siguiente se añade la etiqueta `Owner` con un valor de `blueTeam` para la tabla `Movies`: 

  ```
  aws dynamodb tag-resource \
      --resource-arn arn:aws:dynamodb:us-east-1:123456789012:table/Movies \
      --tags Key=Owner,Value=blueTeam
  ```

**Para enumerar todas las etiquetas de una tabla (AWS CLI)**
+ En el ejemplo siguiente se muestran todas las etiquetas asociadas con la tabla `Movies`:

  ```
  aws dynamodb list-tags-of-resource \
      --resource-arn arn:aws:dynamodb:us-east-1:123456789012:table/Movies
  ```

## Uso de etiquetas de DynamoDB para crear informes de asignación de costos
<a name="CostAllocationReports"></a>

AWS utiliza etiquetas para organizar los costos de los recursos en el informe de asignación de costos. AWS proporciona dos tipos de etiquetas de asignación de costos:
+ Una etiqueta generada por AWS. AWS define, crea y aplica esta etiqueta por usted.
+ Etiquetas definidas por el usuario. Puede definir, crear y aplicar estas etiquetas.

Debe activar ambos tipos de etiquetas por separado para que puedan aparecer en el Explorador de costos o en un informe de asignación de costos. 

 Para activar las etiquetas generadas por AWS: 

1.  Inicie sesión en la Consola de administración de AWS abra la consola de Billing and Cost Management en [https://console.aws.amazon.com/billing/home\$1/](https://console.aws.amazon.com/billing/home#/.). 

1.  En el panel de navegación, seleccione **Etiquetas de asignación de costos**. 

1.  En **Etiquetas de asignación de costos generada por AWS**, elija **Activar**. 

 Para activar las etiquetas definidas por el usuario: 

1.  Inicie sesión en la Consola de administración de AWS abra la consola de Billing and Cost Management en [https://console.aws.amazon.com/billing/home\$1/](https://console.aws.amazon.com/billing/home#/.). 

1.  En el panel de navegación, seleccione **Etiquetas de asignación de costos**. 

1.  En **Etiquetas de asignación de costos definidas por el usuario**, elija **Activar**. 

 Después de crear y activar las etiquetas, AWS genera un informe de asignación de costos con el uso y los costos agrupados según las etiquetas activas. El informe de asignación de costes incluye todos los costes de AWS para cada periodo de facturación. El informe incluye tanto recursos etiquetados como sin etiquetar, para que pueda organizar con claridad los cargos de los recursos. 

**nota**  
 En la actualidad, los datos transferidos desde DynamoDB no se desglosan según las etiquetas en los informes de asignación de costos. 

 Para obtener más información, consulte [Uso de etiquetas de asignación de costos](https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/cost-alloc-tags.html). 

# Tablas globales: multiactivas, replicación en varias regiones
<a name="GlobalTables"></a>

Las *tablas globales de Amazon DynamoDB* son una característica de base de datos totalmente administrada, de varias regiones y multiactiva que proporciona una replicación de datos fácil de usar y un rendimiento de lectura y escritura rápido y local para aplicaciones escaladas de forma global.

Las tablas globales replican automáticamente los datos de las tablas de DynamoDB en todas las Regiones de AWS y opcionalmente en todas las cuentas de AWS sin necesidad de que cree y mantenga su propia solución de replicación. Las tablas globales son ideales para aplicaciones que requieren continuidad empresarial y alta disponibilidad a través de una implementación en varias regiones. Cualquier réplica de una tabla global puede servir para lecturas y escrituras. Las aplicaciones pueden lograr una alta resiliencia con un objetivo de punto de recuperación (RPO) bajo o nulo al desplazar el tráfico a una región diferente si el procesamiento de la aplicación se interrumpe en una región. Las tablas globales están disponibles en todas las regiones donde DynamoDB está disponible.

## Modos de coherencia
<a name="GlobalTables.consistency-modes"></a>

Al crear una tabla global, puede configurar el modo de consistencia. Las tablas globales admiten dos modos de coherencia: coherencia final de varias regiones (MREC) y coherencia alta de varias regiones (MRSC).

Si no especifica un modo de coherencia al crear una tabla global, esta adoptará de forma predeterminada la coherencia final de varias regiones (MREC). Una tabla global no puede contener réplicas configuradas con diferentes modos de coherencia. No puede cambiar el modo de coherencia de una tabla global después de la creación.

## Configuraciones de cuentas
<a name="GlobalTables.account-configurations"></a>

DynamoDB ahora admite dos modelos de tablas globales, cada uno diseñado para patrones arquitectónicos diferentes:
+ **Tablas globales con la misma cuenta**: todas las réplicas se crean y administran en una sola cuenta de AWS.
+ **Tablas globales de varias cuentas**: las réplicas se implementan en varias cuentas de AWS mientras participan en un grupo de replicación compartido.

Los modelos con la misma cuenta y con múltiples cuentas admiten escrituras en varias regiones, la replicación asíncrona, la resolución de conflictos en la que gana el último autor y el mismo modelo de facturación. Sin embargo, difieren en la forma en que se gestionan las cuentas, los permisos, el cifrado y la gobernanza de las tablas.

Las tablas globales configuradas para MRSC solo admiten configuraciones de la misma cuenta.

Puede configurar una tabla global mediante la consola de administración de AWS. Las tablas globales utilizan las API de DynamoDB existentes para leer y escribir datos en las tablas, por lo que no se requieren cambios en la aplicación. Solo paga por los recursos que aprovisione o use, sin costos iniciales ni compromisos.


| **Propiedades de** | **Tablas globales de la misma cuenta** | **Tablas globales de varias cuentas** | 
| --- | --- | --- | 
| Caso de uso principal | Resiliencia de varias regiones para aplicaciones dentro de una sola cuenta de AWS | Replicación de varias regiones y varias cuentas para aplicaciones que pertenecen a diferentes equipos o unidades de negocio, o bien existen estrictos límites de seguridad entre las cuentas | 
| Modelo de cuenta | Todas las réplicas se crean y administran en una sola cuenta de AWS | Réplicas creadas en varias cuentas de AWS dentro de la misma implementación | 
| Propiedad del recurso | La tabla y todas las réplicas son propiedad de una sola cuenta | Cada cuenta es propietaria de su réplica local; el grupo de replicación abarca las cuentas | 
| Versión admitida | Versión 2019.11.21 (actual) y versión 2017.11.29 (antigua) de tablas globales | Versión 2019.11.21 (actual) de tablas globales | 
| Operaciones del plano de control | Creación, modificación y eliminación de réplicas a través de la cuenta del propietario de la tabla | Operaciones del plano de control distribuido: las cuentas se unen o abandonan el grupo de replicación | 
| Operaciones de plano de datos de | Puntos de conexión de DynamoDB estándar por región | Acceso al plano de datos por cuenta o región; enrutamiento a través del grupo de replicación | 
| Límite de seguridad | Un único límite de IAM y KMS | IAM, KMS, facturación, CloudTrail y gobernanza distintos por cuenta | 
| Mejor ajuste | Organizaciones con propiedad centralizada de las tablas | Organizaciones con equipos federados, límites de gobernanza o configuraciones de varias cuentas | 

**Topics**
+ [Modos de coherencia](#GlobalTables.consistency-modes)
+ [Configuraciones de cuentas](#GlobalTables.account-configurations)
+ [Conceptos clave de tablas globales](globaltables-CoreConcepts.md)
+ [Tabla global de la misma cuenta de DynamoDB](globaltables-SameAccount.md)
+ [Tablas globales de varias cuentas de DynamoDB](globaltables-MultiAccount.md)
+ [Descripción de la facturación de tablas globales en Amazon DynamoDB](global-tables-billing.md)
+ [Versiones de tablas globales de DynamoDB](V2globaltables_versions.md)
+ [Prácticas recomendadas para tablas globales](globaltables-bestpractices.md)

# Conceptos clave de tablas globales
<a name="globaltables-CoreConcepts"></a>

En las siguientes secciones, se describen los conceptos y los comportamientos de las tablas globales en Amazon DynamoDB

## Conceptos
<a name="globaltables-CoreConcepts.KeyConcepts"></a>

Las *tablas globales* son una característica de DynamoDB que replica los datos de las tablas en todas las regiones de AWS.

Una *tabla de réplica* (o réplica) es una tabla de DynamoDB que funciona como parte de una tabla global. Una tabla global consta de dos o más tablas de réplica en diferentes regiones de AWS. Cada tabla global solo puede tener una réplica por región de AWS. Todas las réplicas de una tabla global comparten el mismo nombre de tabla, esquema de clave principal y datos de elementos.

Cuando una aplicación escribe datos en una réplica en una región, DynamoDB replica automáticamente la escritura en el resto de las réplicas en la tabla global. Para obtener más información sobre cómo empezar a usar tablas globales, consulte [Tutoriales: creación de tablas globales](V2globaltables.tutorial.md) o [Tutoriales: Creación de tablas globales de varias cuentas](V2globaltables_MA.tutorial.md).

## Versiones
<a name="globaltables-CoreConcepts.Versions"></a>

Hay disponibles dos versiones de las tablas globales de DynamoDB: [versión 2019.11.21 (actual) de las tablas globales](GlobalTables.md) y [Versión 2017.11.29 (heredada) de las tablas globales](globaltables.V1.md). Debe utilizar la versión 2019.11.21 (actual) de tablas globales siempre que sea posible. La información de esta sección de la documentación corresponde a la versión 2019.11.21 (actual). Para obtener más información, consulte Determinación de la versión de una tabla global [Determinación de la versión de una tabla global](V2globaltables_versions.md#globaltables.DetermineVersion).

## Disponibilidad
<a name="globaltables-CoreConcepts.availability"></a>

Las tablas globales ayudan a mejorar la continuidad empresarial al facilitar la implementación de una arquitectura de alta disponibilidad de varias regiones. Si una carga de trabajo en una única región de AWS se ve afectada, puede cambiar el tráfico de la aplicación a otra región y realizar las operaciones de lectura y escritura en una tabla de réplica diferente en la misma tabla global.

Cada tabla réplica en una tabla global proporciona la misma durabilidad y disponibilidad que una tabla de DynamoDB de una sola región. Las tablas globales ofrecen un [Acuerdo de nivel de servicio (SLA)](https://aws.amazon.com//dynamodb/sla/) con una disponibilidad del 99,999 %, en comparación con el 99,99 % de las tablas de una sola región.

## Pruebas de inyección de errores
<a name="fault-injection-testing"></a>

Las tablas globales de MREC y MRSC se integran con el [Servicio de inyección de errores de AWS](https://docs.aws.amazon.com/resilience-hub/latest/userguide/testing.html) (AWS FIS), un servicio totalmente gestionado para ejecutar experimentos controlados de inyección de errores con el fin de mejorar la resiliencia de una aplicación. Con el AWS FIS, puede hacer lo siguiente:
+ Crear plantillas de experimentos que definen escenarios de error específicos.
+ Inyectar errores para validar la resiliencia de la aplicación simulando el aislamiento de una región (es decir, deteniendo la replicación hacia y desde una réplica seleccionada) para probar la gestión de errores, los mecanismos de recuperación y el comportamiento del cambio de tráfico entre regiones cuando una región de AWS sufre una interrupción.

Por ejemplo, en una tabla global con réplicas en el Este de EE. UU. (Norte de Virginia), el Este de EE. UU. (Ohio) y el Oeste de EE. UU. (Oregón), puede realizar un experimento en el Este de EE. UU. (Ohio) para probar el aislamiento de la región allí, mientras que el Este de EE. UU. (Norte de Virginia) y el Oeste de EE. UU. (Oregón) continúan con sus operaciones normales. Estas pruebas controladas le ayudan a identificar y resolver posibles problemas antes de que afecten a las cargas de trabajo de producción. 

Consulte los [objetivos de acción](https://docs.aws.amazon.com/fis/latest/userguide/action-sequence.html#action-targets) en la *Guía del usuario de AWS FIS* para obtener una lista completa de las acciones respaldadas por AWS FIS y [Conectividad entre regiones](https://docs.aws.amazon.com/fis/latest/userguide/cross-region-scenario.html) para detener la replicación de DynamoDB entre regiones.

Para obtener información sobre las acciones de las tablas globales de Amazon DynamoDB disponibles en AWS FIS, consulte [Referencia de acciones de tablas globales de DynamoDB](https://docs.aws.amazon.com/fis/latest/userguide/fis-actions-reference.html#dynamodb-actions-reference) en la *Guía del usuario de AWS FIS*.

Para empezar a realizar experimentos de inyección de errores, consulte [Planificación de los experimentos de AWS FIS](https://docs.aws.amazon.com/fis/latest/userguide/getting-started-planning.html) en la Guía del usuario de AWS FIS.

**nota**  
Durante los experimentos de AWS FIS en la MRSC, se permiten lecturas coherentes posteriores, pero no se permiten actualizaciones de configuración de las tablas, como cambiar el modo de facturación o configurar el rendimiento de las tablas, de forma similar a lo que ocurre con la MREC. Consulte la métrica de CloudWatch [`FaultInjectionServiceInducedErrors`](metrics-dimensions.md#FaultInjectionServiceInducedErrors) para obtener información adicional sobre el código de errores.

## Tiempo de vida (TTL)
<a name="global-tables-ttl"></a>

Las tablas globales configuradas para MREC admiten la configuración de la eliminación de [Tiempo de vida](TTL.md) (TTL). La configuración de TTL se sincroniza automáticamente para todas las réplicas en una tabla global. Cuando TTL elimina un elemento de una réplica en una región, la eliminación se replica en todas las demás réplicas de la tabla global. El TTL no consume capacidad de escritura, por lo que no se le cobrará por la eliminación de TTL en la región donde se realizó la eliminación. Sin embargo, se le cobrará por la eliminación replicada en cada una de las demás regiones con una réplica en la tabla global.

La replicación de eliminación de TTL consume la capacidad de escritura de las réplicas en las que se está replicando la eliminación. Las réplicas configuradas para la capacidad aprovisionada pueden limitar las solicitudes si la combinación del rendimiento de escritura y el rendimiento de eliminación de TTL es superior a la capacidad de escritura aprovisionada.

Las tablas globales configuradas para la coherencia alta de varias regiones (MRSC) no admiten la configuración de la eliminación de Tiempo de vida (TTL).

## Transmisión
<a name="global-tables-streams"></a>

Las tablas globales configuradas para una coherencia final de varias regiones (MREC) replican los cambios leyendo esos cambios desde un [Flujo de DynamoDB](Streams.md) en una tabla de réplica y aplicándolos a todas las demás tablas de réplica. Por lo tanto, los flujos están habilitados de forma predeterminada en todas las réplicas de una tabla global de MREC y no se pueden desactivar en esas réplicas. El proceso de replicación de MREC puede combinar varios cambios en un corto periodo de tiempo en una sola escritura replicada, lo que hace que el flujo de cada réplica contenga registros ligeramente diferentes. Los registros de Streams en las réplicas de MREC mantienen el orden de todos los cambios realizados en el mismo elemento, pero el orden relativo de los cambios en los distintos elementos puede variar de una réplica a otra.

Si desea escribir una aplicación que procese los registros de flujos para cambios que se produjeron en una región específica pero no en otras regiones de una tabla global, puede agregar un atributo a cada elemento que defina en qué región se produjo el cambio para ese elemento. Puede usar este atributo para filtrar los registros de flujos en busca de cambios que se hayan producido en otras regiones, incluido el uso de filtros de eventos de Lambda para invocar solo las funciones de Lambda para los cambios en una región específica.

Las tablas globales configuradas para una coherencia sólida de varias regiones (MRSC) no utilizan los flujos de DynamoDB para la replicación, por lo que los flujos no están habilitados de forma predeterminada en las réplicas de MRSC. Puede habilitar flujos en una réplica de MRSC. Los registros de flujos en las réplicas de MRSC son idénticos para todas las réplicas, incluido el orden de los registros de flujos.

## Transacciones
<a name="global-tables-transactions"></a>

En una tabla global configurada para MREC, las operaciones de transacción de DynamoDB ([https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactWriteItems.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactWriteItems.html) y [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactGetItems.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactGetItems.html)) son solo atómicas dentro de la región donde se invocó la operación. Las escrituras transaccionales no se replican como una unidad en todas las regiones, lo que significa que solo algunas de las escrituras de una transacción pueden devolverse mediante operaciones de lectura en otras réplicas en un momento dado.

Por ejemplo, si tiene una tabla global con réplicas en las regiones Este de EE. UU. (Ohio) y Oeste de EE. UU. (Oregón) y realiza una operación `TransactWriteItems` en la región Este de EE. UU. (Ohio), puede observar transacciones completadas parcialmente en la región Oeste de EE. UU. (Oregón) a medida que los cambios se replican. Solo se replicarán los cambios en otras regiones cuando se hayan confirmado en la región de origen.

Las tablas globales configuradas para lograr una coherencia alta de varias regiones (MRSC) no admiten operaciones de transacción y devolverán un error si esas operaciones se invocan en una réplica de MRSC.

## Rendimiento de lectura y escritura
<a name="globaltables-CoreConcepts.Throughput"></a>

### Modo aprovisionado
<a name="gt_throughput.provisioned"></a>

La replicación consume capacidad de escritura. Las réplicas configuradas para la capacidad aprovisionada pueden limitar las solicitudes si la combinación del rendimiento de escritura de la aplicación y el rendimiento de escritura de la replicación supera la capacidad de escritura aprovisionada. En el caso de las tablas globales que utilizan el modo aprovisionado, la configuración de escalado automático para las capacidades de lectura y escritura se sincroniza entre las réplicas.

Puede configurar de forma independiente los ajustes de capacidad de lectura para cada réplica en una tabla global usando el parámetro [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_ProvisionedThroughputOverride.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_ProvisionedThroughputOverride.html) a nivel de réplica. De forma predeterminada, los cambios en la capacidad de lectura aprovisionada se aplican a todas las réplicas de la tabla global. Al agregar una nueva réplica a una tabla global, se utiliza la capacidad de lectura de la tabla de origen o de la réplica como valor inicial, a menos que se especifique explícitamente una sustitución a nivel de réplica.

### Modo bajo demanda
<a name="gt_throughput.on-demand"></a>

En las tablas globales configuradas para el modo bajo demanda, la capacidad de escritura se sincroniza automáticamente en todas las réplicas. DynamoDB ajusta automáticamente la capacidad en función del tráfico y no hay que gestionar ninguna configuración de capacidad de lectura o escritura específica para las réplicas.

## Monitoreo de tablas globales
<a name="monitoring-global-tables"></a>

Las tablas globales configuradas para una coherencia final de varias regiones (MREC) publican la métrica [`ReplicationLatency`](metrics-dimensions.md#ReplicationLatency) en CloudWatch. Esta métrica realiza un seguimiento del tiempo transcurrido entre el momento en que un elemento se escribe en una tabla de réplica y el momento en que dicho elemento aparece en otra réplica de la tabla global. `ReplicationLatency` se expresa en milisegundos y se emite para cada par de región de origen y destino en una tabla global. 

Los valores típicos de `ReplicationLatency` dependen de la distancia entre las regiones de AWS elegidas, así como de otras variables, como el tipo de carga de trabajo y el rendimiento. Por ejemplo, una réplica de origen en la región Oeste de EE. UU. (Norte de California) (us-west-1) tiene un nivel más bajo de `ReplicationLatency` hacia la región Oeste de EE. UU. (Oregón) (us-west-2) en comparación con la región África (Ciudad del Cabo) (af-south-1).

Un valor en aumento de `ReplicationLatency` podría indicar que las actualizaciones de una réplica no se están propagando hacia otras tablas de réplica de manera puntual. En este caso, puede redirigir temporalmente la actividad de lectura y escritura de la aplicación a otra región de AWS.

Las tablas globales configuradas para coherencia alta de varias regiones (MRSC) no publican una métrica de `ReplicationLatency`.

## Consideraciones para la administración de tablas globales
<a name="management-considerations"></a>

No puede eliminar una tabla utilizada para agregar una nueva réplica de tabla global hasta que hayan transcurrido 24 horas desde que se creó la nueva réplica.

Si desactiva una región de AWS que contiene réplicas de tablas globales, esas réplicas se convierten permanentemente en tablas de una sola región 20 horas después de desactivar la región.

# Tabla global de la misma cuenta de DynamoDB
<a name="globaltables-SameAccount"></a>

Las tablas globales de la misma cuenta replican automáticamente los datos de las tablas de DynamoDB en todas las regiones de AWS dentro de una sola cuenta de AWS. Las tablas globales de la misma cuenta proporcionan el modelo más sencillo para ejecutar aplicaciones de varias regiones, ya que todas las réplicas comparten el mismo modelo de límites de cuentas, propiedad y permisos. Cuando elige las regiones de AWS para las tablas de réplicas, las tablas globales gestionan toda la replicación automáticamente. Las tablas globales están disponibles en todas las regiones donde DynamoDB está disponible.

Las tablas globales de la misma cuenta proporcionan los siguientes beneficios:
+ Replicación de forma automática de los datos de las tablas de DynamoDB en las regiones de AWS que elija para ubicar los datos más cerca de los usuarios
+ Habilitación de una mayor disponibilidad de las aplicaciones durante el aislamiento o la degradación regional
+ Use la resolución de conflictos integrada para centrarse en la lógica empresarial de la aplicación.
+ Al crear una tabla global de la misma cuenta, puede elegir entre [Consistencia posterior de varias regiones (MREC)](V2globaltables_HowItWorks.md#V2globaltables_HowItWorks.consistency-modes.mrec) o [Coherencia alta de varias regiones (MRSC)](V2globaltables_HowItWorks.md#V2globaltables_HowItWorks.consistency-modes.mrsc).

**Topics**
+ [Funcionamiento de las tablas globales de DynamoDB](V2globaltables_HowItWorks.md)
+ [Tutoriales: creación de tablas globales](V2globaltables.tutorial.md)
+ [Seguridad de las tablas globales de DynamoDB](globaltables-security.md)

# Funcionamiento de las tablas globales de DynamoDB
<a name="V2globaltables_HowItWorks"></a>

En las siguientes secciones, se describen los conceptos y los comportamientos de las tablas globales en Amazon DynamoDB.

## Conceptos
<a name="V2globaltables_HowItWorks.KeyConcepts"></a>

Las *tablas globales* son una característica de DynamoDB que replica los datos de las tablas en todas las regiones de AWS. 

Una *tabla de réplica* (o réplica) es una tabla de DynamoDB que funciona como parte de una tabla global. Una tabla global consta de dos o más tablas de réplica en diferentes regiones de AWS. Cada tabla global solo puede tener una réplica por región de AWS. Todas las réplicas de una tabla global comparten el mismo nombre de tabla, esquema de clave principal y datos de elementos.

Cuando una aplicación escribe datos en una réplica en una región, DynamoDB replica automáticamente la escritura en el resto de las réplicas en la tabla global. Para obtener más información sobre cómo empezar a usar tablas globales, consulte [Tutoriales: creación de tablas globales](V2globaltables.tutorial.md).

## Versiones
<a name="V2globaltables_HowItWorks.versions"></a>

Hay disponibles dos versiones de las tablas globales de DynamoDB: versión 2019.11.21 (actual) y [versión 2017.11.29 (antigua)](globaltables.V1.md). Debe utilizar la versión 2019.11.21 (actual) siempre que sea posible. La información de esta sección de la documentación corresponde a la versión 2019.11.21 (actual). Para obtener más información, consulte [Determinación de la versión de una tabla global](V2globaltables_versions.md#globaltables.DetermineVersion).

## Disponibilidad
<a name="V2globaltables_HowItWorks.availability"></a>

Las tablas globales ayudan a mejorar la continuidad empresarial al facilitar la implementación de una arquitectura de alta disponibilidad de varias regiones. Si una carga de trabajo en una única región de AWS se ve afectada, puede cambiar el tráfico de la aplicación a otra región y realizar las operaciones de lectura y escritura en una tabla de réplica diferente en la misma tabla global.

Cada tabla réplica en una tabla global proporciona la misma durabilidad y disponibilidad que una tabla de DynamoDB de una sola región. Las tablas globales ofrecen un [Acuerdo de nivel de servicio (SLA)](https://aws.amazon.com//dynamodb/sla/) con una disponibilidad del 99,999 %, en comparación con el 99,99 % de las tablas de una sola región.

## Modos de coherencia
<a name="V2globaltables_HowItWorks.consistency-modes"></a>

Al crear una tabla global, puede configurar el modo de consistencia. Las tablas globales admiten dos modos de coherencia: coherencia final de varias regiones (MREC) y coherencia alta de varias regiones (MRSC).

Si no especifica un modo de coherencia al crear una tabla global, esta adoptará de forma predeterminada la coherencia final de varias regiones (MREC). Una tabla global no puede contener réplicas configuradas con diferentes modos de coherencia. No puede cambiar el modo de coherencia de una tabla global después de la creación.

### Consistencia posterior de varias regiones (MREC)
<a name="V2globaltables_HowItWorks.consistency-modes.mrec"></a>

La coherencia final de varias regiones (MREC) es el modo de coherencia predeterminado para las tablas globales. Los cambios de elementos en una réplica de tabla global de MREC se replican de forma asíncrona en todas las demás réplicas, normalmente en un segundo o menos. En el improbable caso de que una réplica de una tabla global de MREC quede aislada o se vea afectada, cualquier dato que aún no se haya replicado en otras regiones se replicará cuando la réplica recupere el estado correcto.

Si el mismo elemento se modifica simultáneamente en varias regiones, DynamoDB resolverá el conflicto mediante la modificación con la última marca temporal interna por elemento, lo que se denomina método de resolución de conflictos “el último escritor gana”. Al final, un elemento convergerá en todas las réplicas a la versión creada por la última escritura.

Las [operaciones de lectura altamente coherente](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_GetItem.html#DDB-GetItem-request-ConsistentRead) devuelven la versión más reciente de un elemento si este se ha actualizado por última vez en la región en la que se ha producido la lectura, pero podrían devolver datos obsoletos si el elemento se ha actualizado por última vez en una región diferente. Las escrituras condicionales evalúan la expresión de la condición comparándola con la versión del elemento en la región.

Para crear una tabla global de MREC, se agrega una réplica a una tabla de DynamoDB existente. La agregación de una réplica no tiene impacto en el rendimiento de las tablas de DynamoDB de una sola región ni de las réplicas de tablas globales existentes. Puede agregar réplicas a una tabla global de MREC para ampliar el número de regiones en las que se replican los datos o eliminar réplicas de una tabla global de MREC si ya no son necesarias. Una tabla global de MREC puede tener una réplica en cualquier región en la que DynamoDB esté disponible y puede tener tantas réplicas como regiones haya en la [partición de AWS.](https://docs.aws.amazon.com/whitepapers/latest/aws-fault-isolation-boundaries/partitions.html)

### Coherencia alta de varias regiones (MRSC)
<a name="V2globaltables_HowItWorks.consistency-modes.mrsc"></a>

Puede configurar el modo de coherencia alta de varias regiones (MRSC) al crear una tabla global. Los cambios de elementos en la réplica de una tabla global de MRSC se replican de forma sincrónica en al menos otra región antes de que la operación de escritura devuelva una respuesta correcta. Las operaciones de lectura altamente coherente en cualquier réplica de MRSC siempre devuelven la versión más reciente de un elemento. Las escrituras condicionales siempre evalúan la expresión de la condición comparándola con la versión más reciente de un elemento.

Una tabla global de MRSC se debe implementar exactamente en tres regiones. Puede configurar una tabla global de MRSC con tres réplicas o con dos réplicas y un testigo. Un testigo es un componente de una tabla global de MRSC que contiene datos escritos en réplicas de tablas globales y proporciona una alternativa opcional a una réplica completa, al tiempo que admite la arquitectura de disponibilidad de MRSC. No puede realizar operaciones de lectura o escritura en un testigo. Un testigo se encuentra en una región diferente a las dos réplicas. Al crear una tabla global de MRSC, debe elegir las regiones para las réplicas y para la implementación de testigos en el momento de crear la tabla de MRSC. Puede determinar si una tabla global de MRSC tiene un testigo configurado y en qué región, a partir de la salida de la API de [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DescribeTable.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DescribeTable.html). El testigo es propiedad y lo administra DynamoDB, y no aparecerá en la cuenta de AWS en la región en la que esté configurado.

Las tablas globales del MRSC están disponibles en los siguientes conjuntos de regiones: conjunto de regiones de EE. UU. (Este de EE. UU. (Norte de Virginia, Este de EE. UU. (Ohio), Oeste de EE. UU. (Oregón)), conjunto de regiones de la UE (Europa Irlanda, Europa Londres, Europa París, Europa Fráncfort) y conjunto de regiones de Asia-Pacífico (Asia Pacífico Tokio, Asia Pacífico Seúl y Asia Pacífico Osaka). Las tablas globales de MRSC no pueden abarcar conjuntos de regiones (por ejemplo, una tabla global de MRSC no puede contener réplicas de conjuntos de regiones de EE. UU. y de la UE).

Se crea una tabla global de MRSC mediante la agregación de una réplica y un testigo o dos réplicas a una tabla de DynamoDB existente que no contenga datos. Al convertir una tabla de una sola región existente en una tabla global de MRSC, debe asegurarse de que la tabla esté vacía. No se admite la conversión de una tabla de una sola región en una tabla global de MRSC con los elementos existentes. Asegúrese de que no se escriba ningún dato en la tabla durante el proceso de conversión. No puede agregar réplicas adicionales a una tabla global de MRSC existente. No puede eliminar una réplica única o un testigo de una tabla global de MRSC. Puede eliminar dos réplicas o eliminar una réplica y un testigo de una tabla global de MRSC, convirtiendo la réplica restante en una tabla de DynamoDB de una sola región.

Una operación de escritura produce un error con una `ReplicatedWriteConflictException` cuando intenta modificar un elemento que ya se está modificando en otra región. Las escrituras que tengan un error con la `ReplicatedWriteConflictException` se pueden reintentar y se realizarán correctamente si el elemento ya no se está modificando en otra región.

Las siguientes consideraciones se aplican a las tablas globales de MRSC:
+ El Tiempo de vida (TTL) no se admite para las tablas globales de MRSC.
+ Los índices secundarios locales (LSI) no se admiten para las tablas globales de MRSC.
+ La información de CloudWatch Contributor Insights solo se comunica para la región en la que se ha producido una operación.

## Elección de un modo de coherencia
<a name="V2globaltables_HowItWorks.choosing-consistency-mode"></a>

El criterio clave para elegir un modo de coherencia de varias regiones es si la aplicación prioriza las escrituras de menor latencia y las lecturas altamente coherentes, o si prioriza la coherencia alta global.

Las tablas globales de MREC tendrán latencias de escritura y lectura altamente coherente inferiores en comparación con las tablas globales de MRSC. Las tablas globales de MREC tienen un objetivo de punto de recuperación (RPO) igual al retraso de replicación entre réplicas, normalmente unos segundos, en función de las regiones de las réplicas.

Debe utilizar el modo MREC cuando:
+ La aplicación puede tolerar que se devuelvan datos obsoletos de operaciones de lectura altamente coherentes si esos datos se han actualizado en otra región.
+ Prioriza las latencias de escritura y de lectura altamente coherente más bajas sobre la consistencia de lectura de varias regiones.
+ La estrategia de alta disponibilidad de varias regiones puede tolerar un RPO superior a cero.

Las tablas globales de MRSC tendrán latencias de escritura y de lectura altamente coherente más elevadas que las tablas globales de MREC. Las tablas globales de MRSC admiten un objetivo de punto de recuperación (RPO) de cero.

Debe utilizar el modo MRSC cuando:
+ Necesita lecturas altamente coherentes en varias regiones.
+ Dé prioridad a la consistencia de lectura global sobre una latencia de escritura menor.
+ La estrategia de alta disponibilidad de varias regiones requiera un RPO de cero.

## Monitoreo de tablas globales
<a name="monitoring-global-tables"></a>

Las tablas globales configuradas para una coherencia final de varias regiones (MREC) publican la métrica [`ReplicationLatency`](metrics-dimensions.md#ReplicationLatency) en CloudWatch. Esta métrica realiza un seguimiento del tiempo transcurrido entre el momento en que un elemento se escribe en una tabla de réplica y el momento en que dicho elemento aparece en otra réplica de la tabla global. `ReplicationLatency` se expresa en milisegundos y se emite para cada par de región de origen y destino en una tabla global. 

Los valores típicos de `ReplicationLatency` dependen de la distancia entre las regiones de AWS elegidas, así como de otras variables, como el tipo de carga de trabajo y el rendimiento. Por ejemplo, una réplica de origen en la región Oeste de EE. UU. (Norte de California) (us-west-1) tiene un nivel más bajo de `ReplicationLatency` hacia la región Oeste de EE. UU. (Oregón) (us-west-2) en comparación con la región África (Ciudad del Cabo) (af-south-1).

Un valor en aumento de `ReplicationLatency` podría indicar que las actualizaciones de una réplica no se están propagando hacia otras tablas de réplica de manera puntual. En este caso, puede redirigir temporalmente la actividad de lectura y escritura de la aplicación a otra región de AWS.

Las tablas globales configuradas para coherencia alta de varias regiones (MRSC) no publican una métrica de `ReplicationLatency`.

## Pruebas de inyección de errores
<a name="fault-injection-testing"></a>

Las tablas globales de MREC y MRSC se integran con el [Servicio de inyección de errores de AWS](https://docs.aws.amazon.com/resilience-hub/latest/userguide/testing.html) (AWS FIS), un servicio totalmente gestionado para ejecutar experimentos controlados de inyección de errores con el fin de mejorar la resiliencia de una aplicación. Con el AWS FIS, puede hacer lo siguiente:
+ Crear plantillas de experimentos que definen escenarios de error específicos.
+ Inyectar errores para validar la resiliencia de la aplicación simulando el aislamiento de una región (es decir, deteniendo la replicación hacia y desde una réplica seleccionada) para probar la gestión de errores, los mecanismos de recuperación y el comportamiento del cambio de tráfico entre regiones cuando una región de AWS sufre una interrupción.

Por ejemplo, en una tabla global con réplicas en el Este de EE. UU. (Norte de Virginia), el Este de EE. UU. (Ohio) y el Oeste de EE. UU. (Oregón), puede realizar un experimento en el Este de EE. UU. (Ohio) para probar el aislamiento de la región allí, mientras que el Este de EE. UU. (Norte de Virginia) y el Oeste de EE. UU. (Oregón) continúan con sus operaciones normales. Estas pruebas controladas le ayudan a identificar y resolver posibles problemas antes de que afecten a las cargas de trabajo de producción. 

Consulte los [objetivos de acción](https://docs.aws.amazon.com/fis/latest/userguide/action-sequence.html#action-targets) en la *Guía del usuario de AWS FIS* para obtener una lista completa de las acciones respaldadas por AWS FIS y [Conectividad entre regiones](https://docs.aws.amazon.com/fis/latest/userguide/cross-region-scenario.html) para detener la replicación de DynamoDB entre regiones.

Para obtener información sobre las acciones de las tablas globales de Amazon DynamoDB disponibles en AWS FIS, consulte [Referencia de acciones de tablas globales de DynamoDB](https://docs.aws.amazon.com/fis/latest/userguide/fis-actions-reference.html#dynamodb-actions-reference) en la *Guía del usuario de AWS FIS*.

Para empezar a realizar experimentos de inyección de errores, consulte [Planificación de los experimentos de AWS FIS](https://docs.aws.amazon.com/fis/latest/userguide/getting-started-planning.html) en la Guía del usuario de AWS FIS.

**nota**  
Durante los experimentos de AWS FIS en la MRSC, se permiten lecturas coherentes posteriores, pero no se permiten actualizaciones de configuración de las tablas, como cambiar el modo de facturación o configurar el rendimiento de las tablas, de forma similar a lo que ocurre con la MREC. Consulte la métrica de CloudWatch [`FaultInjectionServiceInducedErrors`](metrics-dimensions.md#FaultInjectionServiceInducedErrors) para obtener información adicional sobre el código de errores.

## Tiempo de vida (TTL)
<a name="global-tables-ttl"></a>

Las tablas globales configuradas para MREC admiten la configuración de la eliminación de [Tiempo de vida](TTL.md) (TTL). La configuración de TTL se sincroniza automáticamente para todas las réplicas en una tabla global. Cuando TTL elimina un elemento de una réplica en una región, la eliminación se replica en todas las demás réplicas de la tabla global. El TTL no consume capacidad de escritura, por lo que no se le cobrará por la eliminación de TTL en la región donde se realizó la eliminación. Sin embargo, se le cobrará por la eliminación replicada en cada una de las demás regiones con una réplica en la tabla global.

La replicación de eliminación de TTL consume la capacidad de escritura de las réplicas en las que se está replicando la eliminación. Las réplicas configuradas para la capacidad aprovisionada pueden limitar las solicitudes si la combinación del rendimiento de escritura y el rendimiento de eliminación de TTL es superior a la capacidad de escritura aprovisionada.

Las tablas globales configuradas para la coherencia alta de varias regiones (MRSC) no admiten la configuración de la eliminación de Tiempo de vida (TTL).

## Transmisión
<a name="global-tables-streams"></a>

Las tablas globales configuradas para una coherencia final de varias regiones (MREC) replican los cambios leyendo esos cambios desde un [Flujo de DynamoDB](Streams.md) en una tabla de réplica y aplicándolos a todas las demás tablas de réplica. Por lo tanto, los flujos están habilitados de forma predeterminada en todas las réplicas de una tabla global de MREC y no se pueden desactivar en esas réplicas. El proceso de replicación de MREC puede combinar varios cambios en un corto periodo de tiempo en una sola escritura replicada, lo que hace que el flujo de cada réplica contenga registros ligeramente diferentes. Los registros de flujos en las réplicas de MREC siempre se ordenan por elemento, pero el orden entre los elementos puede variar entre las réplicas.

Las tablas globales configuradas para una coherencia sólida de varias regiones (MRSC) no utilizan los flujos de DynamoDB para la replicación, por lo que los flujos no están habilitados de forma predeterminada en las réplicas de MRSC. Puede habilitar flujos en una réplica de MRSC. Los registros de flujos en las réplicas de MRSC son idénticos para todas las réplicas, incluido el orden de los registros de flujos.

Si desea escribir una aplicación que procese los registros de flujos para cambios que se produjeron en una región específica pero no en otras regiones de una tabla global, puede agregar un atributo a cada elemento que defina en qué región se produjo el cambio para ese elemento. Puede usar este atributo para filtrar los registros de flujos en busca de cambios que se hayan producido en otras regiones, incluido el uso de filtros de eventos de Lambda para invocar solo las funciones de Lambda para los cambios en una región específica.

## Transacciones
<a name="global-tables-transactions"></a>

En una tabla global configurada para MREC, las operaciones de transacción de DynamoDB ([https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactWriteItems.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactWriteItems.html) y [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactGetItems.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactGetItems.html)) son solo atómicas dentro de la región donde se invocó la operación. Las escrituras transaccionales no se replican como una unidad en todas las regiones, lo que significa que solo algunas de las escrituras de una transacción pueden devolverse mediante operaciones de lectura en otras réplicas en un momento dado.

Por ejemplo, si tiene una tabla global con réplicas en las regiones Este de EE. UU. (Ohio) y Oeste de EE. UU. (Oregón) y realiza una operación `TransactWriteItems` en la región Este de EE. UU. (Ohio), puede observar transacciones completadas parcialmente en la región Oeste de EE. UU. (Oregón) a medida que los cambios se replican. Solo se replicarán los cambios en otras regiones cuando se hayan confirmado en la región de origen.

Las tablas globales configuradas para lograr una coherencia alta de varias regiones (MRSC) no admiten operaciones de transacción y devolverán un error si esas operaciones se invocan en una réplica de MRSC.

## Rendimiento de lectura y escritura
<a name="V2globaltables_HowItWorks.Throughput"></a>

### Modo aprovisionado
<a name="gt_throughput.provisioned"></a>

La replicación consume capacidad de escritura. Las réplicas configuradas para la capacidad aprovisionada pueden limitar las solicitudes si la combinación del rendimiento de escritura de la aplicación y el rendimiento de escritura de la replicación supera la capacidad de escritura aprovisionada. En el caso de las tablas globales que utilizan el modo aprovisionado, la configuración de escalado automático para las capacidades de lectura y escritura se sincroniza entre las réplicas.

Puede configurar de forma independiente los ajustes de capacidad de lectura para cada réplica en una tabla global usando el parámetro [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_ProvisionedThroughputOverride.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_ProvisionedThroughputOverride.html) a nivel de réplica. De forma predeterminada, los cambios en la capacidad de lectura aprovisionada se aplican a todas las réplicas de la tabla global. Al agregar una nueva réplica a una tabla global, se utiliza la capacidad de lectura de la tabla de origen o de la réplica como valor inicial, a menos que se especifique explícitamente una sustitución a nivel de réplica.

### Modo bajo demanda
<a name="gt_throughput.on-demand"></a>

En las tablas globales configuradas para el modo bajo demanda, la capacidad de escritura se sincroniza automáticamente en todas las réplicas. DynamoDB ajusta automáticamente la capacidad en función del tráfico y no hay que gestionar ninguna configuración de capacidad de lectura o escritura específica para las réplicas.

## Sincronización de ajustes
<a name="V2globaltables_HowItWorks.setting-synchronization"></a>

Los ajustes de las tablas globales de DynamoDB son parámetros de configuración que controlan varios aspectos del comportamiento y la replicación de las tablas. Estos ajustes se administran mediante las API del plano de control de DynamoDB y se pueden configurar al crear o modificar tablas globales. Las tablas globales sincronizan automáticamente determinados ajustes en todas las réplicas para mantener la coherencia, al tiempo que permiten flexibilidad para optimizaciones específicas de cada región. Entender qué ajustes se sincronizan y cómo se comportan le ayuda a configurar la tabla global de manera efectiva. Los ajustes se dividen en tres categorías principales en función de cómo se sincronizan entre las réplicas.

Los siguientes ajustes siempre se sincronizan entre las réplicas de una tabla global:
+ Modo de capacidad (capacidad aprovisionada o bajo demanda)
+ Capacidad de escritura aprovisionada de tabla
+ Escalado automático de la escritura de tablas
+ Definición de atributos del esquema de claves
+ Definición del índice secundario global (GSI)
+ Capacidad de escritura aprovisionada por GSI
+ Escalado automático de escritura de GSI
+ Tipo de cifrado del servidor (SSE)
+ Definición de flujos en modo de MREC
+ Tiempo de vida (TTL)
+ Rendimiento en caliente
+ Rendimiento de escritura máximo bajo demanda

Los siguientes ajustes se sincronizan entre las réplicas, pero se pueden invalidar por réplica:
+ La capacidad de lectura aprovisionada de la tabla
+ Escalado automático de lectura de tablas
+ Capacidad de lectura aprovisionada de GSI
+ Escalado automático de lectura de GSI
+ Clase de tabla
+ Rendimiento de lectura máximo bajo demanda

**nota**  
Los valores de configuración anulables se modifican si la configuración se cambia en cualquier otra réplica. Como ejemplo, tiene una tabla global de MREC con réplicas en Este de EE. UU. (Norte de Virginia) y Oeste de EE. UU. (Oregón). La réplica de Este de EE. UU. (Norte de Virginia) tiene un rendimiento de lectura aprovisionado establecido en 200 RCU. La réplica en Oeste de EE. UU. (Oregón) tiene una invalidación del rendimiento de lectura aprovisionada establecida en 100 RCU. Si actualiza la configuración de rendimiento de lectura aprovisionada en la réplica de Este de EE. UU. (Norte de Virginia) de 200 a 300 RCU, el nuevo valor de rendimiento de lectura aprovisionado también se aplicará a la réplica en Oeste de EE. UU. (Oregón). Esto cambia la configuración de rendimiento de lectura aprovisionada para la réplica de Oeste de EE. UU. (Oregón) del valor invalidado de 100 RCU al nuevo valor de 300 RCU.

Los siguientes ajustes nunca se sincronizan entre réplicas:
+ Protección contra eliminación
+ Recuperación en un momento dado
+ Etiquetas
+ Habilitación de CloudWatch Contributor Insights para tablas
+ Habilitación de CloudWatch Contributor Insights para GSI
+ Definición de Kinesis Data Streams
+ Políticas de recursos
+ Definición de flujos en modo de MRSC

Todos los demás ajustes no están sincronizados entre réplicas.

## DynamoDB Accelerator (DAX)
<a name="V2globaltables_HowItWorks.dax"></a>

Las réplicas de escrituras en tablas globales omiten DynamoDB Accelerator (DAX) y actualizan DynamoDB directamente. Como resultado, las cachés de DAX pueden quedar obsoletas, ya que las escrituras no actualizan la caché de DAX. Las cachés de DAX configuradas para réplicas de tablas globales solo se actualizarán cuando caduque el TTL de la caché.

## Consideraciones para la administración de tablas globales
<a name="management-considerations"></a>

No puede eliminar una tabla utilizada para agregar una nueva réplica de tabla global hasta que hayan transcurrido 24 horas desde que se creó la nueva réplica.

Si desactiva una región de AWS que contiene réplicas de tablas globales, esas réplicas se convierten permanentemente en tablas de una sola región 20 horas después de desactivar la región.

# Tutoriales: creación de tablas globales
<a name="V2globaltables.tutorial"></a>

Esta sección proporciona instrucciones paso a paso para crear tablas globales de DynamoDB configuradas para su modo de coherencia preferido. Elija los modos de coherencia final de varias regiones (MREC) o coherencia alta de varias regiones (MRSC) según los requisitos de la aplicación.

Las tablas globales de MREC proporcionan una latencia de escritura más baja con coherencia final en todas las Regiones de AWS. Las tablas globales de MRSC proporcionan lecturas altamente coherentes en todas las regiones, con latencias de escritura ligeramente superiores a las de MREC. Elija el modo de coherencia que mejor se adapte a las necesidades de la aplicación en cuanto a coherencia de datos, latencia y disponibilidad.

**Topics**
+ [Creación de una tabla global configurada para MREC](#V2creategt_mrec)
+ [Creación de una tabla global configurada para MRSC](#create-gt-mrsc)

## Creación de una tabla global configurada para MREC
<a name="V2creategt_mrec"></a>

En esta sección se muestra cómo crear una tabla global con el modo de coherencia final de varias regiones (MREC). MREC es el modo de coherencia predeterminado para tablas globales y proporciona escrituras de baja latencia con replicación asincrónica en todas las Regiones de AWS. Los cambios realizados en un elemento de una región suelen replicarse en todas las demás regiones en un segundo. Esto hace que MREC sea ideal para aplicaciones que priorizan una baja latencia de escritura y pueden tolerar breves periodos en los que diferentes regiones pueden devolver versiones de datos ligeramente diferentes.

Puede crear tablas globales de MREC con réplicas en cualquier región de AWS donde DynamoDB esté disponible y agregar o eliminar réplicas en cualquier momento. Los ejemplos siguientes muestran cómo crear una tabla global de MREC con réplicas en varias regiones.

### Creación de una tabla global de MREC mediante la consola de DynamoDB
<a name="mrec-console"></a>

Siga estos pasos para crear una tabla global mediante la Consola de administración de AWS. En el ejemplo siguiente, se crea una tabla global con tablas de réplica en Estados Unidos y Europa.

1. Inicie sesión en la Consola de administración de AWS y abra la consola de DynamoDB en [https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/).

1. Para este ejemplo, elija **Este de EE. UU. (Ohio)** en el selector de regiones en la barra de navegación.

1. En el panel de navegación del lado izquierdo de la consola, elija **Tablas**.

1. Seleccione **Create Table (Crear tabla)**.

1. En la página **Crear tabla**:

   1. En **Nombre de la tabla**, introduzca **Music**.

   1. En **Partition key** (Clave de partición), ingrese **Artist**.

   1. Para **Clave de clasificación**, ingrese **SongTitle**.

   1. Deje los demás valores predeterminados y elija **Crear tabla**.

      Esta nueva tabla le servirá de primera tabla de réplica en una nueva tabla global. Será el prototipo para crear otras tablas de réplica que quiera añadir más tarde.

1. Cuando la tabla se active:

   1. Elija la tabla **Música** en la lista de tablas.

   1. Elija la pestaña **Tablas globales**.

   1. Elija **Create replica (Crear réplica)**.

1. En el menú desplegable **Regiones de replicación disponibles**, elija **Oeste de EE. UU. (Oregón) us-west-2**.

   La consola asegura que no haya ninguna tabla con el mismo nombre en la región seleccionada. Si ya existe una tabla con el mismo nombre, debe eliminar la tabla existente antes de crear una nueva tabla de réplica en esa región.

1. Elija **Create replica (Crear réplica)**. Esto comienza el proceso de creación de la tabla en la región Oeste de EE. UU (Oregón) us-west-2.

   La pestaña **Tablas globales** de la tabla **Música** (y de cualquier otra tabla de réplica) indica que la tabla se ha replicado en varias regiones.

1. Agregue otra región repitiendo los pasos anteriores, pero elija **Europa (Fráncfort) eu-central-1** como región.

1. Para probar la replicación:

   1. Asegúrese de que utiliza la Consola de administración de AWS en la región Este de EE. UU. (Ohio).

   1. Elija **Explorar elementos de la tabla**.

   1. Seleccione **Crear elemento**.

   1. Ingrese **item\$11** para **Artista** y **Song Value 1** para **Título de la canción**.

   1. Seleccione **Crear elemento**.

1. Compruebe la replicación cambiando a las otras regiones:

   1. En el selector de regiones en la esquina superior derecha, elija **Europa (Fráncfort)**.

   1. Compruebe que la tabla **Música** contiene el elemento que ha creado.

   1. Repita la verificación para **Oeste de EE. UU. (Oregón)**.

### Creación de una tabla global de MREC mediante AWS CLI o Java
<a name="mrec-cli-java"></a>

------
#### [ CLI ]

En el siguiente ejemplo de código, se muestra cómo administrar las tablas globales de DynamoDB con replicación de varias regiones con coherencia final (MREC).
+ Cree una tabla con replicación de varias regiones (MREC).
+ Coloque y obtenga elementos de las tablas de réplica.
+ Elimine réplicas una por una.
+ Efectúe una limpieza eliminando la tabla.

**AWS CLI con Bash script**  
Cree una tabla con replicación de varias regiones.  

```
# Step 1: Create a new table (MusicTable) in US East (Ohio), with DynamoDB Streams enabled (NEW_AND_OLD_IMAGES)
aws dynamodb create-table \
    --table-name MusicTable \
    --attribute-definitions \
        AttributeName=Artist,AttributeType=S \
        AttributeName=SongTitle,AttributeType=S \
    --key-schema \
        AttributeName=Artist,KeyType=HASH \
        AttributeName=SongTitle,KeyType=RANGE \
    --billing-mode PAY_PER_REQUEST \
    --stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES \
    --region us-east-2

# Step 2: Create an identical MusicTable table in US East (N. Virginia)
aws dynamodb update-table --table-name MusicTable --cli-input-json \
'{
  "ReplicaUpdates":
  [
    {
      "Create": {
        "RegionName": "us-east-1"
      }
    }
  ]
}' \
--region us-east-2

# Step 3: Create a table in Europe (Ireland)
aws dynamodb update-table --table-name MusicTable --cli-input-json \
'{
  "ReplicaUpdates":
  [
    {
      "Create": {
        "RegionName": "eu-west-1"
      }
    }
  ]
}' \
--region us-east-2
```
Describa la tabla de varias regiones.  

```
# Step 4: View the list of replicas created using describe-table
aws dynamodb describe-table \
    --table-name MusicTable \
    --region us-east-2 \
    --query 'Table.{TableName:TableName,TableStatus:TableStatus,MultiRegionConsistency:MultiRegionConsistency,Replicas:Replicas[*].{Region:RegionName,Status:ReplicaStatus}}'
```
Coloque elementos en una tabla de réplica.  

```
# Step 5: To verify that replication is working, add a new item to the Music table in US East (Ohio)
aws dynamodb put-item \
    --table-name MusicTable \
    --item '{"Artist": {"S":"item_1"},"SongTitle": {"S":"Song Value 1"}}' \
    --region us-east-2
```
Obtenga elementos de las tablas de réplica.  

```
# Step 6: Wait for a few seconds, and then check to see whether the item has been 
# successfully replicated to US East (N. Virginia) and Europe (Ireland)
aws dynamodb get-item \
    --table-name MusicTable \
    --key '{"Artist": {"S":"item_1"},"SongTitle": {"S":"Song Value 1"}}' \
    --region us-east-1

aws dynamodb get-item \
    --table-name MusicTable \
    --key '{"Artist": {"S":"item_1"},"SongTitle": {"S":"Song Value 1"}}' \
    --region eu-west-1
```
Elimine réplicas.  

```
# Step 7: Delete the replica table in Europe (Ireland) Region
aws dynamodb update-table --table-name MusicTable --cli-input-json \
'{
  "ReplicaUpdates":
  [
    {
      "Delete": {
        "RegionName": "eu-west-1"
      }
    }
  ]
}' \
--region us-east-2

# Delete the replica table in US East (N. Virginia) Region
aws dynamodb update-table --table-name MusicTable --cli-input-json \
'{
  "ReplicaUpdates":
  [
    {
      "Delete": {
        "RegionName": "us-east-1"
      }
    }
  ]
}' \
--region us-east-2
```
Efectúe una limpieza eliminando la tabla.  

```
# Clean up: Delete the primary table
aws dynamodb delete-table --table-name MusicTable --region us-east-2

echo "Global table demonstration complete."
```
+ Para obtener información sobre la API, consulte los siguientes temas en la *Referencia de comandos de AWS CLI*.
  + [CreateTable](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/CreateTable)
  + [DeleteTable](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/DeleteTable)
  + [DescribeTable](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/DescribeTable)
  + [GetItem](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/GetItem)
  + [PutItem](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/PutItem)
  + [UpdateTable](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/UpdateTable)

------
#### [ Java ]

En el ejemplo de código siguiente, se muestra cómo crear y administrar las tablas globales de DynamoDB con réplicas en varias regiones.
+ Cree una tabla con un índice secundario global y flujos de DynamoDB.
+ Agregue réplicas en diferentes regiones para crear una tabla global.
+ Elimine réplicas de una tabla global.
+ Agregue elementos de prueba para verificar la replicación en todas las regiones.
+ Describa la configuración de la tabla global y el estado de las réplicas.

**SDK para Java 2.x**  
Cree una tabla con un índice secundario global y flujos de DynamoDB mediante AWS SDK for Java 2.x.  

```
    public static CreateTableResponse createTableWithGSI(
        final DynamoDbClient dynamoDbClient, final String tableName, final String indexName) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }
        if (indexName == null || indexName.trim().isEmpty()) {
            throw new IllegalArgumentException("Index name cannot be null or empty");
        }

        try {
            LOGGER.info("Creating table: " + tableName + " with GSI: " + indexName);

            CreateTableRequest createTableRequest = CreateTableRequest.builder()
                .tableName(tableName)
                .attributeDefinitions(
                    AttributeDefinition.builder()
                        .attributeName("Artist")
                        .attributeType(ScalarAttributeType.S)
                        .build(),
                    AttributeDefinition.builder()
                        .attributeName("SongTitle")
                        .attributeType(ScalarAttributeType.S)
                        .build())
                .keySchema(
                    KeySchemaElement.builder()
                        .attributeName("Artist")
                        .keyType(KeyType.HASH)
                        .build(),
                    KeySchemaElement.builder()
                        .attributeName("SongTitle")
                        .keyType(KeyType.RANGE)
                        .build())
                .billingMode(BillingMode.PAY_PER_REQUEST)
                .globalSecondaryIndexes(GlobalSecondaryIndex.builder()
                    .indexName(indexName)
                    .keySchema(KeySchemaElement.builder()
                        .attributeName("SongTitle")
                        .keyType(KeyType.HASH)
                        .build())
                    .projection(
                        Projection.builder().projectionType(ProjectionType.ALL).build())
                    .build())
                .streamSpecification(StreamSpecification.builder()
                    .streamEnabled(true)
                    .streamViewType(StreamViewType.NEW_AND_OLD_IMAGES)
                    .build())
                .build();

            CreateTableResponse response = dynamoDbClient.createTable(createTableRequest);
            LOGGER.info("Table creation initiated. Status: "
                + response.tableDescription().tableStatus());

            return response;

        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to create table: " + tableName + " - " + e.getMessage());
            throw e;
        }
    }
```
Espere a que una tabla se active mediante AWS SDK for Java 2.x.  

```
    public static void waitForTableActive(final DynamoDbClient dynamoDbClient, final String tableName) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }

        try {
            LOGGER.info("Waiting for table to become active: " + tableName);

            try (DynamoDbWaiter waiter =
                DynamoDbWaiter.builder().client(dynamoDbClient).build()) {
                DescribeTableRequest request =
                    DescribeTableRequest.builder().tableName(tableName).build();

                waiter.waitUntilTableExists(request);
                LOGGER.info("Table is now active: " + tableName);
            }

        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to wait for table to become active: " + tableName + " - " + e.getMessage());
            throw e;
        }
    }
```
Agregue una réplica para crear o ampliar una tabla global mediante AWS SDK for Java 2.x.  

```
    public static UpdateTableResponse addReplica(
        final DynamoDbClient dynamoDbClient,
        final String tableName,
        final Region replicaRegion,
        final String indexName,
        final Long readCapacity) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }
        if (replicaRegion == null) {
            throw new IllegalArgumentException("Replica region cannot be null");
        }
        if (indexName == null || indexName.trim().isEmpty()) {
            throw new IllegalArgumentException("Index name cannot be null or empty");
        }
        if (readCapacity == null || readCapacity <= 0) {
            throw new IllegalArgumentException("Read capacity must be a positive number");
        }

        try {
            LOGGER.info("Adding replica in region: " + replicaRegion.id() + " for table: " + tableName);

            // Create a ReplicationGroupUpdate for adding a replica
            ReplicationGroupUpdate replicationGroupUpdate = ReplicationGroupUpdate.builder()
                .create(builder -> builder.regionName(replicaRegion.id())
                    .globalSecondaryIndexes(ReplicaGlobalSecondaryIndex.builder()
                        .indexName(indexName)
                        .provisionedThroughputOverride(ProvisionedThroughputOverride.builder()
                            .readCapacityUnits(readCapacity)
                            .build())
                        .build())
                    .build())
                .build();

            UpdateTableRequest updateTableRequest = UpdateTableRequest.builder()
                .tableName(tableName)
                .replicaUpdates(replicationGroupUpdate)
                .build();

            UpdateTableResponse response = dynamoDbClient.updateTable(updateTableRequest);
            LOGGER.info("Replica addition initiated in region: " + replicaRegion.id());

            return response;

        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to add replica in region: " + replicaRegion.id() + " - " + e.getMessage());
            throw e;
        }
    }
```
Elimine una réplica de una tabla global mediante AWS SDK for Java 2.x.  

```
    public static UpdateTableResponse removeReplica(
        final DynamoDbClient dynamoDbClient, final String tableName, final Region replicaRegion) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }
        if (replicaRegion == null) {
            throw new IllegalArgumentException("Replica region cannot be null");
        }

        try {
            LOGGER.info("Removing replica in region: " + replicaRegion.id() + " for table: " + tableName);

            // Create a ReplicationGroupUpdate for removing a replica
            ReplicationGroupUpdate replicationGroupUpdate = ReplicationGroupUpdate.builder()
                .delete(builder -> builder.regionName(replicaRegion.id()).build())
                .build();

            UpdateTableRequest updateTableRequest = UpdateTableRequest.builder()
                .tableName(tableName)
                .replicaUpdates(replicationGroupUpdate)
                .build();

            UpdateTableResponse response = dynamoDbClient.updateTable(updateTableRequest);
            LOGGER.info("Replica removal initiated in region: " + replicaRegion.id());

            return response;

        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to remove replica in region: " + replicaRegion.id() + " - " + e.getMessage());
            throw e;
        }
    }
```
Agregue elementos de prueba para verificar la replicación mediante AWS SDK for Java 2.x.  

```
    public static PutItemResponse putTestItem(
        final DynamoDbClient dynamoDbClient, final String tableName, final String artist, final String songTitle) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }
        if (artist == null || artist.trim().isEmpty()) {
            throw new IllegalArgumentException("Artist cannot be null or empty");
        }
        if (songTitle == null || songTitle.trim().isEmpty()) {
            throw new IllegalArgumentException("Song title cannot be null or empty");
        }

        try {
            LOGGER.info("Adding test item to table: " + tableName);

            Map<String, software.amazon.awssdk.services.dynamodb.model.AttributeValue> item = new HashMap<>();
            item.put(
                "Artist",
                software.amazon.awssdk.services.dynamodb.model.AttributeValue.builder()
                    .s(artist)
                    .build());
            item.put(
                "SongTitle",
                software.amazon.awssdk.services.dynamodb.model.AttributeValue.builder()
                    .s(songTitle)
                    .build());

            PutItemRequest putItemRequest =
                PutItemRequest.builder().tableName(tableName).item(item).build();

            PutItemResponse response = dynamoDbClient.putItem(putItemRequest);
            LOGGER.info("Test item added successfully");

            return response;

        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to add test item to table: " + tableName + " - " + e.getMessage());
            throw e;
        }
    }
```
Describa la configuración de la tabla global y las réplicas mediante AWS SDK for Java 2.x.  

```
    public static DescribeTableResponse describeTable(final DynamoDbClient dynamoDbClient, final String tableName) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }

        try {
            LOGGER.info("Describing table: " + tableName);

            DescribeTableRequest request =
                DescribeTableRequest.builder().tableName(tableName).build();

            DescribeTableResponse response = dynamoDbClient.describeTable(request);

            LOGGER.info("Table status: " + response.table().tableStatus());
            if (response.table().replicas() != null
                && !response.table().replicas().isEmpty()) {
                LOGGER.info("Number of replicas: " + response.table().replicas().size());
                response.table()
                    .replicas()
                    .forEach(replica -> LOGGER.info(
                        "Replica region: " + replica.regionName() + ", Status: " + replica.replicaStatus()));
            }

            return response;

        } catch (ResourceNotFoundException e) {
            LOGGER.severe("Table not found: " + tableName + " - " + e.getMessage());
            throw e;
        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to describe table: " + tableName + " - " + e.getMessage());
            throw e;
        }
    }
```
Ejemplo completo de operaciones de tablas globales mediante AWS SDK for Java 2.x.  

```
    public static void exampleUsage(final Region sourceRegion, final Region replicaRegion) {

        String tableName = "Music";
        String indexName = "SongTitleIndex";
        Long readCapacity = 15L;

        // Create DynamoDB client for the source region
        try (DynamoDbClient dynamoDbClient =
            DynamoDbClient.builder().region(sourceRegion).build()) {

            try {
                // Step 1: Create the initial table with GSI and streams
                LOGGER.info("Step 1: Creating table in source region: " + sourceRegion.id());
                createTableWithGSI(dynamoDbClient, tableName, indexName);

                // Step 2: Wait for table to become active
                LOGGER.info("Step 2: Waiting for table to become active");
                waitForTableActive(dynamoDbClient, tableName);

                // Step 3: Add replica in destination region
                LOGGER.info("Step 3: Adding replica in region: " + replicaRegion.id());
                addReplica(dynamoDbClient, tableName, replicaRegion, indexName, readCapacity);

                // Step 4: Wait a moment for replica creation to start
                Thread.sleep(5000);

                // Step 5: Describe table to view replica information
                LOGGER.info("Step 5: Describing table to view replicas");
                describeTable(dynamoDbClient, tableName);

                // Step 6: Add a test item to verify replication
                LOGGER.info("Step 6: Adding test item to verify replication");
                putTestItem(dynamoDbClient, tableName, "TestArtist", "TestSong");

                LOGGER.info("Global table setup completed successfully!");
                LOGGER.info("You can verify replication by checking the item in region: " + replicaRegion.id());

                // Step 7: Remove replica and clean up table
                LOGGER.info("Step 7: Removing replica from region: " + replicaRegion.id());
                removeReplica(dynamoDbClient, tableName, replicaRegion);
                DeleteTableResponse deleteTableResponse = dynamoDbClient.deleteTable(
                    DeleteTableRequest.builder().tableName(tableName).build());
                LOGGER.info("MREC global table demonstration completed successfully!");

            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("Thread was interrupted", e);
            } catch (DynamoDbException e) {
                LOGGER.severe("DynamoDB operation failed: " + e.getMessage());
                throw e;
            }
        }
    }
```
+ Para obtener detalles sobre la API, consulte los siguientes temas en la *Referencia de la API de AWS SDK for Java 2.x*.
  + [CreateTable](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/CreateTable)
  + [DescribeTable](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/DescribeTable)
  + [PutItem](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/PutItem)
  + [UpdateTable](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/UpdateTable)

------

## Creación de una tabla global configurada para MRSC
<a name="create-gt-mrsc"></a>

En esta sección se muestra cómo crear una tabla global de coherencia alta de varias regiones (MRSC). Las tablas globales de MRSC replican de forma sincrónica los cambios de los elementos en todas las regiones, lo que garantiza que las operaciones de lectura altamente coherente en cualquier réplica siempre devuelvan la versión más reciente de un elemento. Al convertir una tabla de una sola región en una tabla global de MRSC, debe asegurarse de que la tabla esté vacía. No se admite la conversión de una tabla de una sola región en una tabla global de MRSC con los elementos existentes. Asegúrese de que no se escriba ningún dato en la tabla durante el proceso de conversión.

Puede configurar una tabla global de MRSC con tres réplicas o dos réplicas y un testigo. Al crear una tabla global de MRSC, debe elegir las regiones en las que se despliegan las réplicas y un testigo opcional. En el siguiente ejemplo, se crea una tabla global de MRSC con réplicas en las regiones Este de EE. UU. (Norte de Virginia) y Este de EE. UU. (Ohio), con un testigo en la región Oeste de EE. UU. (Oregón).

**nota**  
Antes de crear una tabla global, compruebe que los límites de rendimiento de la cuota de servicio sean coherentes en todas las regiones de destino, ya que es necesario para crear una tabla global. Para obtener más información sobre los límites de rendimiento de las tablas globales, consulte [Cuotas de tablas globales](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ServiceQuotas.html#gt-limits-throughput).

### Creación de una tabla global de MRSC mediante la consola de DynamoDB
<a name="mrsc_console"></a>

Siga estos pasos para crear una tabla global de MRSC mediante la Consola de administración de AWS.

1. Inicie sesión en la Consola de administración de AWS y abra la consola de DynamoDB en [https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/).

1. En el selector de regiones de la barra de navegación, elija una región en la que [se admiten](V2globaltables_HowItWorks.md#V2globaltables_HowItWorks.consistency-modes) tablas globales con MRSC, como **us-east-2**.

1. En el panel de navegación, elija **Tablas**.

1. Seleccione **Create table (Creación de tabla)**.

1. En la página **Crear tabla**:

   1. En **Nombre de la tabla**, introduzca **Music**.

   1. En **Clave de partición**, ingrese **Artist** y mantenga la **Cadena** predeterminada.

   1. En **Clave de clasificación**, ingrese **SongTitle** y mantenga el tipo de **Cadena** predeterminada.

   1. Deje los demás valores predeterminados y elija **Crear tabla**

      Esta nueva tabla le servirá de primera tabla de réplica en una nueva tabla global. Será el prototipo para crear otras tablas de réplica que quiera añadir más tarde.

1. Espere a que la tabla se active y luego selecciónela en la lista de tablas.

1. Elija la pestaña **Tablas globales** y, a continuación, seleccione **Crear réplica**.

1. En la página **Crear réplica**:

   1. En **Consistencia de varias regiones**, elija **Consistencia alta**.

   1. Para **Región de replicación 1**, elija **US East (N. Virginia) us-east-1**.

   1. Para **Región de replicación 2**, elija **US West (Oregon) us-west-2**.

   1. Compruebe **Configurar como testigo** para la región Oeste de EE. UU. (Oregón).

   1. Elija **Crear réplicas**.

1. Espere a que se complete el proceso de creación de la réplica y el testigo. El estado de la réplica se mostrará como **Activo** cuando la tabla esté lista para usarse.

### Creación de una tabla global de MRSC mediante la AWS CLI o Java
<a name="mrsc-cli-java"></a>

Antes de empezar, asegúrese de que la entidad principal de IAM tenga los permisos necesarios para crear una tabla global de MRSC con una región testigo.

La política de IAM de ejemplo siguiente le permite crear una tabla de DynamoDB (`MusicTable`) en Este de EE. UU. (Ohio) con una réplica en Este de EE. UU. (Norte de Virginia) y una región testigo en Oeste de EE. UU. (Oregón):

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:CreateTable",
                "dynamodb:CreateTableReplica",
                "dynamodb:CreateGlobalTableWitness",
                "dynamodb:DescribeTable",
                "dynamodb:UpdateTable",
                "dynamodb:DeleteTable",
                "dynamodb:DeleteTableReplica",
                "dynamodb:DeleteGlobalTableWitness",
                "dynamodb:Scan",
                "dynamodb:Query",
                "dynamodb:UpdateItem",
                "dynamodb:PutItem",
                "dynamodb:GetItem",
                "dynamodb:DeleteItem",
                "dynamodb:BatchWriteItem"
            ],
            "Resource": [
                "arn:aws:dynamodb:us-east-1:123456789012:table/MusicTable",
                "arn:aws:dynamodb:us-east-2:123456789012:table/MusicTable",
                "arn:aws:dynamodb:us-west-2:123456789012:table/MusicTable"
            ]
        },
        {
            "Effect": "Allow",
            "Action": "iam:CreateServiceLinkedRole",
            "Resource": "arn:aws:iam::*:role/aws-service-role/replication.dynamodb.amazonaws.com/AWSServiceRoleForDynamoDBReplication",
            "Condition": {
                "StringLike": {
                    "iam:AWSServiceName": "replication.dynamodb.amazonaws.com"
                }
            }
        }
    ]
}
```

------

Los ejemplos de código siguientes, muestran cómo crear y administrar las tablas globales de DynamoDB con coherencia alta de varias regiones (MRSC).
+ Cree una tabla con coherencia alta de varias regiones.
+ Verifique la configuración de MRSC y el estado de la réplica.
+ Pruebe la coherencia alta entre regiones con lecturas inmediatas.
+ Realice escrituras condicionales con garantías de MRSC.
+ Limpie los recursos de la tabla global de MRSC.

------
#### [ Bash ]

**AWS CLI con Bash script**  
Cree una tabla con coherencia alta de varias regiones.  

```
# Step 1: Create a new table in us-east-2 (primary region for MRSC)
# Note: Table must be empty when enabling MRSC
aws dynamodb create-table \
    --table-name MusicTable \
    --attribute-definitions \
        AttributeName=Artist,AttributeType=S \
        AttributeName=SongTitle,AttributeType=S \
    --key-schema \
        AttributeName=Artist,KeyType=HASH \
        AttributeName=SongTitle,KeyType=RANGE \
    --billing-mode PAY_PER_REQUEST \
    --region us-east-2

# Wait for table to become active
aws dynamodb wait table-exists --table-name MusicTable --region us-east-2

# Step 2: Add replica and witness with Multi-Region Strong Consistency
# MRSC requires exactly three replicas in supported regions
aws dynamodb update-table \
    --table-name MusicTable \
    --replica-updates '[{"Create": {"RegionName": "us-east-1"}}]' \
    --global-table-witness-updates '[{"Create": {"RegionName": "us-west-2"}}]' \
    --multi-region-consistency STRONG \
    --region us-east-2
```
Verifique la configuración de MRSC y el estado de la réplica.  

```
# Verify the global table configuration and MRSC setting
aws dynamodb describe-table \
    --table-name MusicTable \
    --region us-east-2 \
    --query 'Table.{TableName:TableName,TableStatus:TableStatus,MultiRegionConsistency:MultiRegionConsistency,Replicas:Replicas[*],GlobalTableWitnesses:GlobalTableWitnesses[*].{Region:RegionName,Status:ReplicaStatus}}'
```
Pruebe la coherencia alta con lecturas inmediatas entre regiones.  

```
# Write an item to the primary region
aws dynamodb put-item \
    --table-name MusicTable \
    --item '{"Artist": {"S":"The Beatles"},"SongTitle": {"S":"Hey Jude"},"Album": {"S":"The Beatles 1967-1970"},"Year": {"N":"1968"}}' \
    --region us-east-2

# Read the item from replica region to verify strong consistency (cannot read or write to witness)
# No wait time needed - MRSC provides immediate consistency
echo "Reading from us-east-1 (immediate consistency):"
aws dynamodb get-item \
    --table-name MusicTable \
    --key '{"Artist": {"S":"The Beatles"},"SongTitle": {"S":"Hey Jude"}}' \
    --consistent-read \
    --region us-east-1
```
Realice escrituras condicionales con garantías de MRSC.  

```
# Perform a conditional update from a different region
# This demonstrates that conditions work consistently across all regions
aws dynamodb update-item \
    --table-name MusicTable \
    --key '{"Artist": {"S":"The Beatles"},"SongTitle": {"S":"Hey Jude"}}' \
    --update-expression "SET #rating = :rating" \
    --condition-expression "attribute_exists(Artist)" \
    --expression-attribute-names '{"#rating": "Rating"}' \
    --expression-attribute-values '{":rating": {"N":"5"}}' \
    --region us-east-1
```
Limpie los recursos de la tabla global de MRSC.  

```
# Remove replica tables (must be done before deleting the primary table)
aws dynamodb update-table \
    --table-name MusicTable \
    --replica-updates '[{"Delete": {"RegionName": "us-east-1"}}]' \
    --global-table-witness-updates '[{"Delete": {"RegionName": "us-west-2"}}]' \
    --region us-east-2

# Wait for replicas to be deleted
echo "Waiting for replicas to be deleted..."
sleep 30

# Delete the primary table
aws dynamodb delete-table \
    --table-name MusicTable \
    --region us-east-2
```
+ Para obtener información sobre la API, consulte los siguientes temas en la *Referencia de comandos de AWS CLI*.
  + [CreateTable](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/CreateTable)
  + [DeleteTable](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/DeleteTable)
  + [DescribeTable](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/DescribeTable)
  + [GetItem](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/GetItem)
  + [PutItem](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/PutItem)
  + [UpdateItem](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/UpdateItem)
  + [UpdateTable](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/UpdateTable)

------
#### [ Java ]

**SDK para Java 2.x**  
Cree una tabla regional lista para la conversión de MRSC con AWS SDK for Java 2.x.  

```
    public static CreateTableResponse createRegionalTable(final DynamoDbClient dynamoDbClient, final String tableName) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }

        try {
            LOGGER.info("Creating regional table: " + tableName + " (must be empty for MRSC)");

            CreateTableRequest createTableRequest = CreateTableRequest.builder()
                .tableName(tableName)
                .attributeDefinitions(
                    AttributeDefinition.builder()
                        .attributeName("Artist")
                        .attributeType(ScalarAttributeType.S)
                        .build(),
                    AttributeDefinition.builder()
                        .attributeName("SongTitle")
                        .attributeType(ScalarAttributeType.S)
                        .build())
                .keySchema(
                    KeySchemaElement.builder()
                        .attributeName("Artist")
                        .keyType(KeyType.HASH)
                        .build(),
                    KeySchemaElement.builder()
                        .attributeName("SongTitle")
                        .keyType(KeyType.RANGE)
                        .build())
                .billingMode(BillingMode.PAY_PER_REQUEST)
                .build();

            CreateTableResponse response = dynamoDbClient.createTable(createTableRequest);
            LOGGER.info("Regional table creation initiated. Status: "
                + response.tableDescription().tableStatus());

            return response;

        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to create regional table: " + tableName + " - " + e.getMessage());
            throw DynamoDbException.builder()
                .message("Failed to create regional table: " + tableName)
                .cause(e)
                .build();
        }
    }
```
Convierta una tabla regional en MRSC utilizando réplicas y testigos con AWS SDK for Java 2.x.  

```
    public static UpdateTableResponse convertToMRSCWithWitness(
        final DynamoDbClient dynamoDbClient,
        final String tableName,
        final Region replicaRegion,
        final Region witnessRegion) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }
        if (replicaRegion == null) {
            throw new IllegalArgumentException("Replica region cannot be null");
        }
        if (witnessRegion == null) {
            throw new IllegalArgumentException("Witness region cannot be null");
        }

        try {
            LOGGER.info("Converting table to MRSC with replica in " + replicaRegion.id() + " and witness in "
                + witnessRegion.id());

            // Create replica update using ReplicationGroupUpdate
            ReplicationGroupUpdate replicaUpdate = ReplicationGroupUpdate.builder()
                .create(CreateReplicationGroupMemberAction.builder()
                    .regionName(replicaRegion.id())
                    .build())
                .build();

            // Create witness update
            GlobalTableWitnessGroupUpdate witnessUpdate = GlobalTableWitnessGroupUpdate.builder()
                .create(CreateGlobalTableWitnessGroupMemberAction.builder()
                    .regionName(witnessRegion.id())
                    .build())
                .build();

            UpdateTableRequest updateTableRequest = UpdateTableRequest.builder()
                .tableName(tableName)
                .replicaUpdates(List.of(replicaUpdate))
                .globalTableWitnessUpdates(List.of(witnessUpdate))
                .multiRegionConsistency(MultiRegionConsistency.STRONG)
                .build();

            UpdateTableResponse response = dynamoDbClient.updateTable(updateTableRequest);
            LOGGER.info("MRSC conversion initiated. Status: "
                + response.tableDescription().tableStatus());
            LOGGER.info("UpdateTableResponse full object: " + response);
            return response;

        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to convert table to MRSC: " + tableName + " - " + e.getMessage());
            throw DynamoDbException.builder()
                .message("Failed to convert table to MRSC: " + tableName)
                .cause(e)
                .build();
        }
    }
```
Describa la configuración de una tabla global de MRSC con AWS SDK for Java 2.x.  

```
    public static DescribeTableResponse describeMRSCTable(final DynamoDbClient dynamoDbClient, final String tableName) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }

        try {
            LOGGER.info("Describing MRSC global table: " + tableName);

            DescribeTableRequest request =
                DescribeTableRequest.builder().tableName(tableName).build();

            DescribeTableResponse response = dynamoDbClient.describeTable(request);

            LOGGER.info("Table status: " + response.table().tableStatus());
            LOGGER.info("Multi-region consistency: " + response.table().multiRegionConsistency());

            if (response.table().replicas() != null
                && !response.table().replicas().isEmpty()) {
                LOGGER.info("Number of replicas: " + response.table().replicas().size());
                response.table()
                    .replicas()
                    .forEach(replica -> LOGGER.info(
                        "Replica region: " + replica.regionName() + ", Status: " + replica.replicaStatus()));
            }

            if (response.table().globalTableWitnesses() != null
                && !response.table().globalTableWitnesses().isEmpty()) {
                LOGGER.info("Number of witnesses: "
                    + response.table().globalTableWitnesses().size());
                response.table()
                    .globalTableWitnesses()
                    .forEach(witness -> LOGGER.info(
                        "Witness region: " + witness.regionName() + ", Status: " + witness.witnessStatus()));
            }

            return response;

        } catch (ResourceNotFoundException e) {
            LOGGER.severe("Table not found: " + tableName + " - " + e.getMessage());
            throw DynamoDbException.builder()
                .message("Table not found: " + tableName)
                .cause(e)
                .build();
        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to describe table: " + tableName + " - " + e.getMessage());
            throw DynamoDbException.builder()
                .message("Failed to describe table: " + tableName)
                .cause(e)
                .build();
        }
    }
```
Agregue elementos de prueba para verificar la coherencia alta de MRSC con AWS SDK for Java 2.x.  

```
    public static PutItemResponse putTestItem(
        final DynamoDbClient dynamoDbClient,
        final String tableName,
        final String artist,
        final String songTitle,
        final String album,
        final String year) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }
        if (artist == null || artist.trim().isEmpty()) {
            throw new IllegalArgumentException("Artist cannot be null or empty");
        }
        if (songTitle == null || songTitle.trim().isEmpty()) {
            throw new IllegalArgumentException("Song title cannot be null or empty");
        }

        try {
            LOGGER.info("Adding test item to MRSC global table: " + tableName);

            Map<String, AttributeValue> item = new HashMap<>();
            item.put("Artist", AttributeValue.builder().s(artist).build());
            item.put("SongTitle", AttributeValue.builder().s(songTitle).build());

            if (album != null && !album.trim().isEmpty()) {
                item.put("Album", AttributeValue.builder().s(album).build());
            }
            if (year != null && !year.trim().isEmpty()) {
                item.put("Year", AttributeValue.builder().n(year).build());
            }

            PutItemRequest putItemRequest =
                PutItemRequest.builder().tableName(tableName).item(item).build();

            PutItemResponse response = dynamoDbClient.putItem(putItemRequest);
            LOGGER.info("Test item added successfully with strong consistency");

            return response;

        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to add test item to table: " + tableName + " - " + e.getMessage());
            throw DynamoDbException.builder()
                .message("Failed to add test item to table: " + tableName)
                .cause(e)
                .build();
        }
    }
```
Lea los elementos con lecturas coherentes de las réplicas de MRSC con AWS SDK for Java 2.x.  

```
    public static GetItemResponse getItemWithConsistentRead(
        final DynamoDbClient dynamoDbClient, final String tableName, final String artist, final String songTitle) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }
        if (artist == null || artist.trim().isEmpty()) {
            throw new IllegalArgumentException("Artist cannot be null or empty");
        }
        if (songTitle == null || songTitle.trim().isEmpty()) {
            throw new IllegalArgumentException("Song title cannot be null or empty");
        }

        try {
            LOGGER.info("Reading item from MRSC global table with consistent read: " + tableName);

            Map<String, AttributeValue> key = new HashMap<>();
            key.put("Artist", AttributeValue.builder().s(artist).build());
            key.put("SongTitle", AttributeValue.builder().s(songTitle).build());

            GetItemRequest getItemRequest = GetItemRequest.builder()
                .tableName(tableName)
                .key(key)
                .consistentRead(true)
                .build();

            GetItemResponse response = dynamoDbClient.getItem(getItemRequest);

            if (response.hasItem()) {
                LOGGER.info("Item found with strong consistency - no wait time needed");
            } else {
                LOGGER.info("Item not found");
            }

            return response;

        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to read item from table: " + tableName + " - " + e.getMessage());
            throw DynamoDbException.builder()
                .message("Failed to read item from table: " + tableName)
                .cause(e)
                .build();
        }
    }
```
Realice actualizaciones condicionales con garantías de MRSC mediante AWS SDK for Java 2.x.  

```
    public static UpdateItemResponse performConditionalUpdate(
        final DynamoDbClient dynamoDbClient,
        final String tableName,
        final String artist,
        final String songTitle,
        final String rating) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }
        if (artist == null || artist.trim().isEmpty()) {
            throw new IllegalArgumentException("Artist cannot be null or empty");
        }
        if (songTitle == null || songTitle.trim().isEmpty()) {
            throw new IllegalArgumentException("Song title cannot be null or empty");
        }
        if (rating == null || rating.trim().isEmpty()) {
            throw new IllegalArgumentException("Rating cannot be null or empty");
        }

        try {
            LOGGER.info("Performing conditional update on MRSC global table: " + tableName);

            Map<String, AttributeValue> key = new HashMap<>();
            key.put("Artist", AttributeValue.builder().s(artist).build());
            key.put("SongTitle", AttributeValue.builder().s(songTitle).build());

            Map<String, String> expressionAttributeNames = new HashMap<>();
            expressionAttributeNames.put("#rating", "Rating");

            Map<String, AttributeValue> expressionAttributeValues = new HashMap<>();
            expressionAttributeValues.put(
                ":rating", AttributeValue.builder().n(rating).build());

            UpdateItemRequest updateItemRequest = UpdateItemRequest.builder()
                .tableName(tableName)
                .key(key)
                .updateExpression("SET #rating = :rating")
                .conditionExpression("attribute_exists(Artist)")
                .expressionAttributeNames(expressionAttributeNames)
                .expressionAttributeValues(expressionAttributeValues)
                .build();

            UpdateItemResponse response = dynamoDbClient.updateItem(updateItemRequest);
            LOGGER.info("Conditional update successful - demonstrates strong consistency");

            return response;

        } catch (ConditionalCheckFailedException e) {
            LOGGER.warning("Conditional check failed: " + e.getMessage());
            throw e;
        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to perform conditional update: " + tableName + " - " + e.getMessage());
            throw DynamoDbException.builder()
                .message("Failed to perform conditional update: " + tableName)
                .cause(e)
                .build();
        }
    }
```
Espere a que las réplicas de MRSC y los testigos se activen mediante AWS SDK for Java 2.x.  

```
    public static void waitForMRSCReplicasActive(
        final DynamoDbClient dynamoDbClient, final String tableName, final int maxWaitTimeSeconds)
        throws InterruptedException {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }
        if (maxWaitTimeSeconds <= 0) {
            throw new IllegalArgumentException("Max wait time must be positive");
        }

        try {
            LOGGER.info("Waiting for MRSC replicas and witnesses to become active: " + tableName);

            final long startTime = System.currentTimeMillis();
            final long maxWaitTimeMillis = maxWaitTimeSeconds * 1000L;
            int backoffSeconds = 5; // Start with 5 second intervals
            final int maxBackoffSeconds = 30; // Cap at 30 seconds

            while (System.currentTimeMillis() - startTime < maxWaitTimeMillis) {
                DescribeTableResponse response = describeMRSCTable(dynamoDbClient, tableName);

                boolean allActive = true;
                StringBuilder statusReport = new StringBuilder();

                if (response.table().multiRegionConsistency() == null
                    || !MultiRegionConsistency.STRONG
                        .toString()
                        .equals(response.table().multiRegionConsistency().toString())) {
                    allActive = false;
                    statusReport
                        .append("MultiRegionConsistency: ")
                        .append(response.table().multiRegionConsistency())
                        .append(" ");
                }
                if (response.table().replicas() == null
                    || response.table().replicas().isEmpty()) {
                    allActive = false;
                    statusReport.append("No replicas found. ");
                }
                if (response.table().globalTableWitnesses() == null
                    || response.table().globalTableWitnesses().isEmpty()) {
                    allActive = false;
                    statusReport.append("No witnesses found. ");
                }

                // Check table status
                if (!"ACTIVE".equals(response.table().tableStatus().toString())) {
                    allActive = false;
                    statusReport
                        .append("Table: ")
                        .append(response.table().tableStatus())
                        .append(" ");
                }

                // Check replica status
                if (response.table().replicas() != null) {
                    for (var replica : response.table().replicas()) {
                        if (!"ACTIVE".equals(replica.replicaStatus().toString())) {
                            allActive = false;
                            statusReport
                                .append("Replica(")
                                .append(replica.regionName())
                                .append("): ")
                                .append(replica.replicaStatus())
                                .append(" ");
                        }
                    }
                }

                // Check witness status
                if (response.table().globalTableWitnesses() != null) {
                    for (var witness : response.table().globalTableWitnesses()) {
                        if (!"ACTIVE".equals(witness.witnessStatus().toString())) {
                            allActive = false;
                            statusReport
                                .append("Witness(")
                                .append(witness.regionName())
                                .append("): ")
                                .append(witness.witnessStatus())
                                .append(" ");
                        }
                    }
                }

                if (allActive) {
                    LOGGER.info("All MRSC replicas and witnesses are now active: " + tableName);
                    return;
                }

                LOGGER.info("Waiting for MRSC components to become active. Status: " + statusReport.toString());
                LOGGER.info("Next check in " + backoffSeconds + " seconds...");

                tempWait(backoffSeconds);

                // Exponential backoff with cap
                backoffSeconds = Math.min(backoffSeconds * 2, maxBackoffSeconds);
            }

            throw DynamoDbException.builder()
                .message("Timeout waiting for MRSC replicas to become active after " + maxWaitTimeSeconds + " seconds")
                .build();

        } catch (DynamoDbException | InterruptedException e) {
            LOGGER.severe("Failed to wait for MRSC replicas to become active: " + tableName + " - " + e.getMessage());
            throw e;
        }
    }
```
Limpie las réplicas y los testigos de MRSC mediante AWS SDK for Java 2.x.  

```
    public static UpdateTableResponse cleanupMRSCReplicas(
        final DynamoDbClient dynamoDbClient,
        final String tableName,
        final Region replicaRegion,
        final Region witnessRegion) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }
        if (replicaRegion == null) {
            throw new IllegalArgumentException("Replica region cannot be null");
        }
        if (witnessRegion == null) {
            throw new IllegalArgumentException("Witness region cannot be null");
        }

        try {
            LOGGER.info("Cleaning up MRSC replicas and witnesses for table: " + tableName);

            // Remove replica using ReplicationGroupUpdate
            ReplicationGroupUpdate replicaUpdate = ReplicationGroupUpdate.builder()
                .delete(DeleteReplicationGroupMemberAction.builder()
                    .regionName(replicaRegion.id())
                    .build())
                .build();

            // Remove witness
            GlobalTableWitnessGroupUpdate witnessUpdate = GlobalTableWitnessGroupUpdate.builder()
                .delete(DeleteGlobalTableWitnessGroupMemberAction.builder()
                    .regionName(witnessRegion.id())
                    .build())
                .build();

            UpdateTableRequest updateTableRequest = UpdateTableRequest.builder()
                .tableName(tableName)
                .replicaUpdates(List.of(replicaUpdate))
                .globalTableWitnessUpdates(List.of(witnessUpdate))
                .build();

            UpdateTableResponse response = dynamoDbClient.updateTable(updateTableRequest);
            LOGGER.info("MRSC cleanup initiated - removing replica and witness. Response: " + response);

            return response;

        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to cleanup MRSC replicas: " + tableName + " - " + e.getMessage());
            throw DynamoDbException.builder()
                .message("Failed to cleanup MRSC replicas: " + tableName)
                .cause(e)
                .build();
        }
    }
```
Complete la demostración del flujo de trabajo de MRSC mediante AWS SDK for Java 2.x.  

```
    public static void demonstrateCompleteMRSCWorkflow(
        final DynamoDbClient primaryClient,
        final DynamoDbClient replicaClient,
        final String tableName,
        final Region replicaRegion,
        final Region witnessRegion)
        throws InterruptedException {

        if (primaryClient == null) {
            throw new IllegalArgumentException("Primary DynamoDB client cannot be null");
        }
        if (replicaClient == null) {
            throw new IllegalArgumentException("Replica DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }
        if (replicaRegion == null) {
            throw new IllegalArgumentException("Replica region cannot be null");
        }
        if (witnessRegion == null) {
            throw new IllegalArgumentException("Witness region cannot be null");
        }

        try {
            LOGGER.info("=== Starting Complete MRSC Workflow Demonstration ===");

            // Step 1: Create an empty single-Region table
            LOGGER.info("Step 1: Creating empty single-Region table");
            createRegionalTable(primaryClient, tableName);

            // Use the existing GlobalTableOperations method for basic table waiting
            LOGGER.info("Intermediate step: Waiting for table [" + tableName + "] to become active before continuing");
            GlobalTableOperations.waitForTableActive(primaryClient, tableName);

            // Step 2: Convert to MRSC with replica and witness
            LOGGER.info("Step 2: Converting to MRSC with replica and witness");
            convertToMRSCWithWitness(primaryClient, tableName, replicaRegion, witnessRegion);

            // Wait for MRSC conversion to complete using MRSC-specific waiter
            LOGGER.info("Waiting for MRSC conversion to complete...");
            waitForMRSCReplicasActive(primaryClient, tableName);

            LOGGER.info("Intermediate step: Waiting for table [" + tableName + "] to become active before continuing");
            GlobalTableOperations.waitForTableActive(primaryClient, tableName);

            // Step 3: Verify MRSC configuration
            LOGGER.info("Step 3: Verifying MRSC configuration");
            describeMRSCTable(primaryClient, tableName);

            // Step 4: Test strong consistency with data operations
            LOGGER.info("Step 4: Testing strong consistency with data operations");

            // Add test item to primary region
            putTestItem(primaryClient, tableName, "The Beatles", "Hey Jude", "The Beatles 1967-1970", "1968");

            // Immediately read from replica region (no wait needed with MRSC)
            LOGGER.info("Reading from replica region immediately (strong consistency):");
            GetItemResponse getResponse =
                getItemWithConsistentRead(replicaClient, tableName, "The Beatles", "Hey Jude");

            if (getResponse.hasItem()) {
                LOGGER.info("✓ Strong consistency verified - item immediately available in replica region");
            } else {
                LOGGER.warning("✗ Item not found in replica region");
            }

            // Test conditional update from replica region
            LOGGER.info("Testing conditional update from replica region:");
            performConditionalUpdate(replicaClient, tableName, "The Beatles", "Hey Jude", "5");
            LOGGER.info("✓ Conditional update successful - demonstrates strong consistency");

            // Step 5: Cleanup
            LOGGER.info("Step 5: Cleaning up resources");
            cleanupMRSCReplicas(primaryClient, tableName, replicaRegion, witnessRegion);

            // Wait for cleanup to complete using basic table waiter
            LOGGER.info("Waiting for replica cleanup to complete...");
            GlobalTableOperations.waitForTableActive(primaryClient, tableName);

            // "Halt" until replica/witness cleanup is complete
            DescribeTableResponse cleanupVerification = describeMRSCTable(primaryClient, tableName);
            int backoffSeconds = 5; // Start with 5 second intervals
            while (cleanupVerification.table().multiRegionConsistency() != null) {
                LOGGER.info("Waiting additional time (" + backoffSeconds + " seconds) for MRSC cleanup to complete...");
                tempWait(backoffSeconds);

                // Exponential backoff with cap
                backoffSeconds = Math.min(backoffSeconds * 2, 30);
                cleanupVerification = describeMRSCTable(primaryClient, tableName);
            }

            // Delete the primary table
            deleteTable(primaryClient, tableName);

            LOGGER.info("=== MRSC Workflow Demonstration Complete ===");
            LOGGER.info("");
            LOGGER.info("Key benefits of Multi-Region Strong Consistency (MRSC):");
            LOGGER.info("- Immediate consistency across all regions (no eventual consistency delays)");
            LOGGER.info("- Simplified application logic (no need to handle eventual consistency)");
            LOGGER.info("- Support for conditional writes and transactions across regions");
            LOGGER.info("- Consistent read operations from any region without waiting");

        } catch (DynamoDbException | InterruptedException e) {
            LOGGER.severe("MRSC workflow failed: " + e.getMessage());
            throw e;
        }
    }
```
+ Para obtener detalles sobre la API, consulte los siguientes temas en la *Referencia de la API de AWS SDK for Java 2.x*.
  + [CreateTable](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/CreateTable)
  + [DeleteTable](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/DeleteTable)
  + [DescribeTable](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/DescribeTable)
  + [GetItem](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/GetItem)
  + [PutItem](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/PutItem)
  + [UpdateItem](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/UpdateItem)
  + [UpdateTable](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/UpdateTable)

------

# Seguridad de las tablas globales de DynamoDB
<a name="globaltables-security"></a>

Las réplicas de tablas globales son tablas de DynamoDB, por lo que se utilizan los mismos métodos para controlar el acceso a las réplicas que se utilizan para las tablas de una sola región, incluyendo las políticas de identidad y las políticas basadas en recursos de AWS Identity and Access Management (IAM).

En este tema, se describe cómo proteger las tablas globales de DynamoDB mediante permisos de IAM y cifrado de AWS Key Management Service (AWS KMS). Obtendrá información sobre los roles vinculados a servicios (SLR) que permiten la replicación y el escalado automático entre regiones, los permisos de IAM necesarios para crear, actualizar y eliminar tablas globales y las diferencias entre las tablas de coherencia final multirregional (MREC) y multirregional de coherencia sólida (MRSC). También obtendrá información sobre las claves de cifrado de AWS KMS para administrar de forma segura la replicación entre regiones.

## Roles vinculados a servicios para tablas globales
<a name="globaltables-slr"></a>

Las tablas globales de DynamoDB se basan en roles vinculados a servicios (SLR) para administrar la replicación entre regiones y las capacidades de escalado automático.

Solo tiene que configurar estos roles una vez por cuenta de AWS. Una vez creados, los mismos roles sirven en todas las tablas globales de la cuenta. Para obtener más información acerca de los roles vinculados a servicios, consulte [Uso de roles vinculados a servicios](https://docs.aws.amazon.com/IAM/latest/UserGuide/using-service-linked-roles.html) en la *Guía del usuario de IAM*.

### Rol vinculado a servicios de replicación
<a name="globaltables-replication-slr"></a>

Amazon DynamoDB crea automáticamente el rol vinculado al servicio (SLR) `AWSServiceRoleForDynamoDBReplication` cuando se crea la primera tabla global. Este rol administra la replicación entre regiones por usted.

Al aplicar políticas basadas en recursos a las réplicas, asegúrese de no denegar ninguno de los permisos definidos en `AWSServiceRoleForDynamoDBReplicationPolicy` a la entidad principal de SLR, ya que esto interrumpiría la replicación. Si deniega los permisos de SLR necesarios, la replicación hacia y desde las réplicas afectadas se detendrá y el estado de la tabla de réplicas cambiará a `REPLICATION_NOT_AUTHORIZED`.
+ Para tablas globales de coherencia final multirregional (MREC), si una réplica permanece en el estado `REPLICATION_NOT_AUTHORIZED` durante más de 20 horas, la réplica se convierte irreversiblemente en una tabla de DynamoDB de una sola región.
+ Para las tablas globales de coherencia alta multirregionales (MRSC), la denegación de los permisos necesarios se traduce en `AccessDeniedException` para operaciones de escritura y lectura altamente coherentes. Si una réplica permanece en el estado `REPLICATION_NOT_AUTHORIZED` durante más de siete días, la réplica se vuelve permanentemente inaccesible y las operaciones de escritura y de lectura altamente coherentes seguirán produciendo un error. Algunas operaciones de administración, como la eliminación de réplicas, se realizarán correctamente.

### Rol vinculado a un servicio de escalado automático
<a name="globaltables-autoscaling-slr"></a>

Al configurar una tabla global para el modo de capacidad aprovisionada, se debe configurar el escalado automático para la tabla global. El escalado automático de DynamoDB utiliza el servicio de escalado automático de aplicaciones de AWS para ajustar dinámicamente la capacidad de rendimiento aprovisionada en las réplicas de tabla global. El servicio de escalado automático de aplicaciones crea un rol vinculado a un servicio (SLR) denominado [https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-service-linked-roles.html](https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-service-linked-roles.html). Este rol vinculado al servicio se crea automáticamente en la cuenta de AWS al configurar el escalado automático para una tabla de DynamoDB. Permite al escalado automático de aplicaciones administrar la capacidad de la tabla aprovisionada y crear alarmas de CloudWatch. 

 Al aplicar políticas basadas en recursos a las réplicas, asegúrese de no denegar ninguno de los permisos definidos en la [https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSApplicationAutoscalingDynamoDBTablePolicy.html](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSApplicationAutoscalingDynamoDBTablePolicy.html) a la entidad principal de SLR de escalado automático de aplicaciones, ya que esto interrumpiría la funcionalidad de escalado automático.

### Políticas de IAM de ejemplo para roles vinculados a servicios
<a name="globaltables-example-slr"></a>

Una política de IAM con la siguiente condición no afecta a los permisos necesarios para la SLR de replicación de DynamoDB y SLR de escalado automático de AWS. Esta condición se puede agregar a políticas que, de otro modo, serían ampliamente restrictivas para evitar interrumpir involuntariamente la replicación o el escalado automático.

#### Exclusión de los permisos de SLR necesarios de las políticas de denegación
<a name="example-exclude-slr-policy"></a>

En el siguiente ejemplo, se muestra cómo excluir a las entidades principales de roles vinculados al servicio de las instrucciones de denegación:

```
"Condition": {
    "StringNotEquals": {
        "aws:PrincipalArn": [
            "arn:aws::iam::111122223333:role/aws-service-role/replication.dynamodb.amazonaws.com/AWSServiceRoleForDynamoDBReplication",
            "arn:aws::iam::111122223333:role/aws-service-role/dynamodb.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_DynamoDBTable"
        ]
    }
}
```

## Uso de las tablas globales de AWS IAM
<a name="globaltables-iam"></a>

En las siguientes secciones, se describen los permisos necesarios para las distintas operaciones de las tablas globales y se proporcionan ejemplos de políticas que le ayudarán a configurar el acceso adecuado para los usuarios y aplicaciones.

**nota**  
Todos los permisos descritos se deben aplicar al ARN del recurso de tabla específico en las regiones afectadas. El ARN del recurso de tabla sigue el formato `arn:aws:dynamodb:region:account-id:table/table-name`, en el que debe especificar los valores reales de la región, el ID de cuenta y el nombre de la tabla.

**Topics**
+ [Creación de tablas globales y agregación de réplicas](#globaltables-creation-iam)
+ [Actualización de tablas globales](#globaltables-update-iam)
+ [Eliminación de tablas globales y réplicas](#globaltables-delete-iam)

### Creación de tablas globales y agregación de réplicas
<a name="globaltables-creation-iam"></a>

Las tablas globales de DynamoDB admiten dos modos de coherencia: coherencia final multirregional (MREC) y coherencia alta multirregional (MRSC). Las tablas globales de MREC pueden tener varias réplicas en cualquier número de regiones y proporcionar coherencia final. Las tablas globales de MRSC requieren exactamente tres regiones (tres réplicas o dos réplicas y un testigo) y proporcionan una coherencia sólida con un objetivo de punto de recuperación (RPO).

Los permisos necesarios para crear tablas globales dependen de si se crea una tabla global con o sin un testigo.

#### Permisos para crear tablas globales
<a name="globaltables-creation-iam-all-types"></a>

Los siguientes permisos son necesarios para la creación inicial de la tabla global y para agregar réplicas posteriormente. Estos permisos se aplican a las tablas globales de coherencia final multirregional (MREC) y coherencia alta multirregional (MRSC).
+ Las tablas globales requieren la replicación entre regiones, que DynamoDB administra mediante el rol vinculado a servicios [`AWSServiceRoleForDynamoDBReplication`](#globaltables-replication-slr) (SLR). El siguiente permiso permite a DynamoDB crear este rol automáticamente al crear una tabla global por primera vez:
  + `iam:CreateServiceLinkedRole`
+ Para crear una tabla global o agregar una réplica mediante la API de [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTable.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTable.html), debe tener el siguiente permiso en el recurso de la tabla de origen:
  + `dynamodb:UpdateTable`
+ Debe tener los siguientes permisos en el recurso de la tabla en las regiones para que se agreguen las réplicas:
  + `dynamodb:CreateTable`
  + `dynamodb:CreateTableReplica`
  + `dynamodb:Query`
  + `dynamodb:Scan`
  + `dynamodb:UpdateItem`
  + `dynamodb:PutItem`
  + `dynamodb:GetItem`
  + `dynamodb:DeleteItem`
  + `dynamodb:BatchWriteItem`

#### Permisos adicionales para las tablas globales de MRSC mediante un testigo
<a name="globaltables-creation-iam-witness"></a>

Al crear una tabla global de coherencia alta multirregional (MRSC) con una región testigo, debe tener el siguiente permiso en el recurso de tabla en todas las regiones participantes (incluidas las regiones réplica y la región testigo):
+ `dynamodb:CreateGlobalTableWitness`

#### Políticas de IAM de ejemplo para crear tablas globales
<a name="globaltables-creation-iam-example"></a>

##### Creación de una tabla global de MREC o MRSC en tres regiones
<a name="globaltables-creation-iam-example-three-regions"></a>

La siguiente política basada en la identidad le permite crear una tabla global de MREC o MRSC denominada “usuarios” en tres regiones, incluida la creación del rol vinculado al servicio de replicación de DynamoDB necesario.

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Sid": "AllowCreatingUsersGlobalTable",
      "Effect": "Allow",
      "Action": [
        "dynamodb:CreateTable",
        "dynamodb:CreateTableReplica",
        "dynamodb:UpdateTable",
        "dynamodb:Query",
        "dynamodb:Scan",
        "dynamodb:UpdateItem",
        "dynamodb:PutItem",
        "dynamodb:GetItem",
        "dynamodb:DeleteItem",
        "dynamodb:BatchWriteItem"
      ],
      "Resource": [
        "arn:aws:dynamodb:us-east-1:123456789012:table/users",
        "arn:aws:dynamodb:us-east-2:123456789012:table/users",
        "arn:aws:dynamodb:us-west-2:123456789012:table/users"
      ]
    },
    {
      "Sid": "AllowCreatingSLR",
      "Effect": "Allow",
      "Action": [
        "iam:CreateServiceLinkedRole"
      ],
      "Resource": [
        "arn:aws:iam::123456789012:role/aws-service-role/replication.dynamodb.amazonaws.com/AWSServiceRoleForDynamoDBReplication"
      ]
    }
  ]
}
```

------

##### Restricción de la creación de tablas globales de MREC o MRSC a regiones específicas
<a name="globaltables-creation-iam-example-restrict-regions"></a>

La siguiente política basada en la identidad le permite crear réplicas de tablas globales de DynamoDB en regiones específicas mediante la clave de condición [aws:RequestedRegion](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-keys.html#condition-keys-requestedregion), incluida la creación del rol vinculado al servicio de replicación de DynamoDB necesario.

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Sid": "AllowAddingReplicasToSourceTable",
      "Effect": "Allow",
      "Action": [
        "dynamodb:UpdateTable"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:RequestedRegion": [
            "us-east-1"
          ]
        }
      }
    },
    {
      "Sid": "AllowCreatingReplicas",
      "Effect": "Allow",
      "Action": [
        "dynamodb:CreateTable",
        "dynamodb:CreateTableReplica",
        "dynamodb:UpdateTable",
        "dynamodb:Query",
        "dynamodb:Scan",
        "dynamodb:UpdateItem",
        "dynamodb:PutItem",
        "dynamodb:GetItem",
        "dynamodb:DeleteItem",
        "dynamodb:BatchWriteItem"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:RequestedRegion": [
            "us-east-2",
            "us-west-2"
          ]
        }
      }
    },
    {
      "Sid": "AllowCreatingSLR",
      "Effect": "Allow",
      "Action": [
        "iam:CreateServiceLinkedRole"
      ],
      "Resource": [
        "arn:aws:iam::123456789012:role/aws-service-role/replication.dynamodb.amazonaws.com/AWSServiceRoleForDynamoDBReplication"
      ]
    }
  ]
}
```

------

##### Creación de una tabla global de MRSC con un testigo
<a name="globaltables-creation-iam-example-witness"></a>

La siguiente política basada en la identidad le permite crear una tabla global de MRSC de DynamoDB denominada “usuarios” con réplicas en us-east-1 y us-east-2 y un testigo en us-west-2, incluida la creación del rol vinculado al servicio de replicación de DynamoDB necesario.

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Sid": "AllowCreatingUsersGlobalTableWithWitness",
      "Effect": "Allow",
      "Action": [
        "dynamodb:CreateTable",
        "dynamodb:CreateTableReplica",
        "dynamodb:CreateGlobalTableWitness",
        "dynamodb:UpdateTable",
        "dynamodb:Query",
        "dynamodb:Scan",
        "dynamodb:UpdateItem",
        "dynamodb:PutItem",
        "dynamodb:GetItem",
        "dynamodb:DeleteItem",
        "dynamodb:BatchWriteItem"
      ],
      "Resource": [
        "arn:aws:dynamodb:us-east-1:123456789012:table/users",
        "arn:aws:dynamodb:us-east-2:123456789012:table/users"
      ]
    },
    {
      "Sid": "AllowCreatingSLR",
      "Effect": "Allow",
      "Action": [
        "iam:CreateServiceLinkedRole"
      ],
      "Resource": [
        "arn:aws:iam::123456789012:role/aws-service-role/replication.dynamodb.amazonaws.com/AWSServiceRoleForDynamoDBReplication"
      ]
    }
  ]
}
```

------

##### Restricción de la creación de testigos de MRSC a regiones específicas
<a name="globaltables-creation-iam-example-restrict-witness-regions"></a>

Esta política basada en la identidad le permite crear una tabla global de MRSC con réplicas restringidas en regiones específicas mediante la clave de condición [aws:RequestedRegion](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-keys.html#condition-keys-requestedregion) y la creación de testigos sin restricción en todas las regiones, incluida la creación del rol vinculado al servicio de replicación de DynamoDB necesario.

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Sid": "AllowCreatingReplicas",
      "Effect": "Allow",
      "Action": [
        "dynamodb:CreateTable",
        "dynamodb:CreateTableReplica",
        "dynamodb:UpdateTable",
        "dynamodb:Query",
        "dynamodb:Scan",
        "dynamodb:UpdateItem",
        "dynamodb:PutItem",
        "dynamodb:GetItem",
        "dynamodb:DeleteItem",
        "dynamodb:BatchWriteItem"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:RequestedRegion": [
            "us-east-1",
            "us-east-2"
          ]
        }
      }
    },
    {
      "Sid": "AllowCreatingWitness",
      "Effect": "Allow",
      "Action": [
        "dynamodb:CreateGlobalTableWitness"
      ],
      "Resource": "*"
    },
    {
      "Sid": "AllowCreatingSLR",
      "Effect": "Allow",
      "Action": [
        "iam:CreateServiceLinkedRole"
      ],
      "Resource": [
        "arn:aws:iam::123456789012:role/aws-service-role/replication.dynamodb.amazonaws.com/AWSServiceRoleForDynamoDBReplication"
      ]
    }
  ]
}
```

------

### Actualización de tablas globales
<a name="globaltables-update-iam"></a>

Para modificar la configuración de réplica de una tabla global existente mediante la API de [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTable.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTable.html), necesita el siguiente permiso en el recurso de tabla de la región en la que va a realizar la llamada a la API:
+ `dynamodb:UpdateTable`

Además, puede actualizar otras configuraciones de tablas globales, como las políticas de escalado automático y la configuración del tiempo de vida. Se requieren los siguientes permisos para estas operaciones de actualización adicionales:
+ Para actualizar una política de escalado automático de réplicas con la API de [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTableReplicaAutoScaling.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTableReplicaAutoScaling.html), debe tener los siguientes permisos en el recurso de la tabla en todas las regiones que contengan réplicas:
  + `application-autoscaling:DeleteScalingPolicy`
  + `application-autoscaling:DeleteScheduledAction`
  + `application-autoscaling:DeregisterScalableTarget`
  + `application-autoscaling:DescribeScalableTargets`
  + `application-autoscaling:DescribeScalingActivities`
  + `application-autoscaling:DescribeScalingPolicies`
  + `application-autoscaling:DescribeScheduledActions`
  + `application-autoscaling:PutScalingPolicy`
  + `application-autoscaling:PutScheduledAction`
  + `application-autoscaling:RegisterScalableTarget`
+ Para actualizar la configuración de Tiempo de vida con la API de [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTimeToLive.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTimeToLive.html), debe tener el siguiente permiso en el recurso de la tabla en todas las regiones que contienen réplicas:
  + `dynamodb:UpdateTimeToLive`

  Tenga en cuenta que el Tiempo de vida (TTL) solo se admite para tablas globales configuradas con coherencia final multirregional (MREC). Para obtener más información sobre cómo funcionan las tablas globales con TTL, consulte [Cómo funcionan las tablas globales de DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/V2globaltables_HowItWorks.html).

### Eliminación de tablas globales y réplicas
<a name="globaltables-delete-iam"></a>

Para eliminar una tabla global, debe eliminar todas las réplicas. Los permisos necesarios para esta operación varían en función de si se elimina una tabla global con o sin una región testigo.

#### Permisos para eliminar tablas globales y réplicas
<a name="globaltables-delete-iam-all-types"></a>

Los siguientes permisos son necesarios para eliminar réplicas individuales y para eliminar por completo tablas globales. Al eliminar una configuración de tabla global, solo se elimina la relación de replicación entre tablas de distintas regiones. No se eliminará la tabla de DynamoDB de la última región restante. La tabla de la última región sigue existiendo como tabla estándar de DynamoDB con los mismos datos y configuraciones. Estos permisos se aplican a las tablas globales de coherencia final multirregional (MREC) y coherencia alta multirregional (MRSC). 
+ Para eliminar réplicas de una tabla global mediante la API de [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTable.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTable.html), necesita el siguiente permiso en el recurso de tabla de la región desde la que va a realizar la llamada a la API:
  + `dynamodb:UpdateTable`
+ Necesita los siguientes permisos en el recurso de tabla en cada región en la que vaya a eliminar una réplica:
  + `dynamodb:DeleteTable`
  + `dynamodb:DeleteTableReplica`

#### Permisos adicionales para las tablas globales de MRSC mediante un testigo
<a name="globaltables-delete-iam-witness"></a>

Para eliminar una tabla global de coherencia alta multirregional (MRSC) con una región testigo, debe tener el siguiente permiso en el recurso de tabla en todas las regiones participantes (incluidas las regiones réplica y la región testigo):
+ `dynamodb:DeleteGlobalTableWitness`

#### Políticas de IAM de ejemplo para eliminar las réplicas de una tabla global
<a name="globaltables-delete-iam-example"></a>

##### Eliminación de réplicas de tablas globales
<a name="globaltables-delete-replicas-iam-example"></a>

Esta política basada en la identidad le permite eliminar una tabla global de DynamoDB denominada “usuarios” y sus réplicas en tres regiones:

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "dynamodb:UpdateTable",
        "dynamodb:DeleteTable",
        "dynamodb:DeleteTableReplica"
      ],
      "Resource": [
        "arn:aws:dynamodb:us-east-1:123456789012:table/users",
        "arn:aws:dynamodb:us-east-2:123456789012:table/users",
        "arn:aws:dynamodb:us-west-2:123456789012:table/users"
      ]
    }
  ]
}
```

------

##### Eliminación de una tabla global de MRSC con un testigo
<a name="globaltables-delete-witness-iam-example"></a>

Esta política basada en la identidad le permite eliminar la réplica y el testigo de una tabla global de MRSC denominada “usuarios”:

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "dynamodb:UpdateTable",
        "dynamodb:DeleteTable",
        "dynamodb:DeleteTableReplica",
        "dynamodb:DeleteGlobalTableWitness"
      ],
      "Resource": [
        "arn:aws:dynamodb:us-east-1:123456789012:table/users",
        "arn:aws:dynamodb:us-east-2:123456789012:table/users"
      ]
    }
  ]
}
```

------

## Uso de las tablas globales de AWS KMS
<a name="globaltables-kms"></a>

Como todas las tablas de DynamoDB, las réplicas de tablas globales siempre cifran los datos en reposo mediante claves de cifrado almacenadas en AWS Key Management Service (AWS KMS).

Todas las réplicas de una tabla global se deben configurar con el mismo tipo de clave de KMS (clave propia de AWS, clave administrada de AWS o clave administrada por el cliente).

**importante**  
DynamoDB requiere acceso a la clave de cifrado de la réplica para eliminarla. Si desea desactivar o eliminar una clave administrada por el cliente que se utiliza para cifrar una réplica porque va a eliminar la réplica, primero debe eliminar la réplica, esperar a que el estado de la tabla en una de las réplicas restantes cambie a `ACTIVE` y luego desactivar o eliminar la clave.

Para una tabla global configurada para una coherencia final de varias regiones (MREC), si desactiva o revoca el acceso de DynamoDB a una clave administrada por el cliente que se utiliza para cifrar una réplica, la replicación hacia y desde la réplica se detendrá y el estado de la réplica cambiará a `INACCESSIBLE_ENCRYPTION_CREDENTIALS`. Si una réplica de una tabla global de MREC permanece en el estado `INACCESSIBLE_ENCRYPTION_CREDENTIALS` durante más de 20 horas, la réplica se convierte irreversiblemente en una tabla de DynamoDB de una sola región.

Para una tabla global configurada para una coherencia alta de varias regiones (MRSC), si desactiva o revoca el acceso de DynamoDB a una clave administrada por el cliente que se utiliza para cifrar una réplica, la replicación hacia y desde la réplica se detendrá, los intentos para realizar escrituras o lecturas altamente coherentes en la réplica devolverán un error y el estado de la réplica cambiará a `INACCESSIBLE_ENCRYPTION_CREDENTIALS`. Si una réplica de una tabla global de MRSC permanece en el estado `INACCESSIBLE_ENCRYPTION_CREDENTIALS` durante más de siete días, dependiendo de los permisos específicos revocados, la réplica se archivará o quedará inaccesible permanentemente.

# Tablas globales de varias cuentas de DynamoDB
<a name="globaltables-MultiAccount"></a>

Las tablas globales de varias cuentas replican automáticamente los datos de las tablas de DynamoDB en varias regiones de AWS y cuentas de AWS para mejorar la resiliencia, aislar las cargas de trabajo por cuenta y aplicar distintos controles de seguridad y gobernanza. Cada tabla de réplica reside en una cuenta de AWS distinta, lo que permite el aislamiento de errores por región y cuenta. También puede alinear las réplicas con la estructura organizativa de AWS. Las tablas globales de varias cuentas ofrecen beneficios adicionales de aislamiento, gobernanza y seguridad en comparación con las tablas globales de una misma cuenta.

Las tablas globales de varias cuentas proporcionan los siguientes beneficios:
+ Replique datos de tablas de DynamoDB automáticamente en las cuentas y regiones de AWS que elija.
+ Mejore la seguridad y la gobernanza replicando los datos en todas las cuentas con políticas, barreras de protección y límites de cumplimiento distintos.
+ Mejore la resiliencia operativa y el aislamiento de errores por cuenta al colocar las réplicas en cuentas de AWS independientes.
+ Alinee las cargas de trabajo por unidad de negocio o propiedad al utilizar una estrategia de varias cuentas.
+ Simplifique la atribución de costos facturando cada réplica a su cuenta de AWS correspondiente.

Para obtener más información, consulte [Beneficios de usar varias cuentas de AWS](https://docs.aws.amazon.com/whitepapers/latest/organizing-your-aws-environment/benefits-of-using-multiple-aws-accounts.html). Si las cargas de trabajo no requieren la replicación de varias cuentas o si desea una administración de réplicas más sencilla con invalidaciones locales, puede seguir utilizando tablas globales de la misma cuenta.

Puede configurar tablas globales de varias cuentas con [Consistencia posterior de varias regiones (MREC)](V2globaltables_HowItWorks.md#V2globaltables_HowItWorks.consistency-modes.mrec). Las tablas globales configuradas para [Coherencia alta de varias regiones (MRSC)](V2globaltables_HowItWorks.md#V2globaltables_HowItWorks.consistency-modes.mrsc) no admiten el modelo de varias cuentas.

**Topics**
+ [Funcionamiento de las tablas globales de DynamoDB](V2globaltables_MA_HowItWorks.md)
+ [Tutoriales: Creación de tablas globales de varias cuentas](V2globaltables_MA.tutorial.md)
+ [Seguridad de las tablas globales de DynamoDB](globaltables_MA_security.md)

# Funcionamiento de las tablas globales de DynamoDB
<a name="V2globaltables_MA_HowItWorks"></a>

Las tablas globales de varias cuentas amplían las capacidades totalmente administradas, sin servidor, de varias regiones y multiactivas de tablas globales de DynamoDB para abarcar varias cuentas de AWS. Las tablas globales de varias cuentas replican los datos de todas las regiones y cuentas de AWS, lo que proporciona la misma funcionalidad activo-activa que las tablas globales de la misma cuenta. Al escribir en cualquier réplica, DynamoDB replica los datos en todas las demás réplicas.

Entre las principales diferencias con respecto a las tablas globales de la misma cuenta se incluyen las siguientes:
+ La replicación de varias cuentas es compatible con las tablas globales de coherencia final de varias regiones (MREC).
+ Solo puede agregar réplicas comenzando con una tabla de una sola región. No se admite la conversión de una tabla global existente de la misma cuenta en una configuración de varias cuentas. Para migrar, debe eliminar las réplicas existentes para volver a una tabla de una sola región antes de crear una nueva tabla global de varias cuentas.
+ Cada réplica debe residir en una cuenta de AWS independiente. Para una tabla global de varias cuentas con *N* réplicas, debe tener *N* cuentas.
+ Las tablas globales de varias cuentas utilizan una configuración de tabla unificada en todas las réplicas de forma predeterminada. Todas las réplicas comparten automáticamente la misma configuración (como el modo de rendimiento y TTL) y, a diferencia de las tablas globales de la misma cuenta, estas configuraciones no se pueden invalidar por réplica.
+ Los clientes deben proporcionar permisos de replicación a la entidad principal del servicio de tablas globales de DynamoDB en sus políticas de recursos.

Las tablas globales de varias cuentas utilizan la misma tecnología de replicación subyacente que las tablas globales de la misma cuenta. La configuración de las tablas se replica automáticamente en todas las réplicas regionales y los clientes no pueden invalidar ni personalizar la configuración por réplica. Esto garantiza una configuración uniforme y un comportamiento predecible en varias cuentas de AWS que participan en la misma tabla global.

La configuración de las tablas globales de DynamoDB define cómo se comporta una tabla y cómo se replican los datos en todas las regiones. Estos ajustes se configuran mediante las API del plano de control de DynamoDB durante la creación de la tabla o al agregar una nueva réplica regional.

Al crear una tabla global de varias cuentas, los clientes deben configurar `GlobalTableSettingsReplicationMode = ENABLED` para cada réplica regional. Esto garantiza que los cambios de configuración realizados en una región se propaguen automáticamente a todas las demás regiones que participan en la tabla global.

Puede habilitar la replicación de la configuración después de crear la tabla. Esto es compatible con el escenario en el que una tabla se crea originalmente como una tabla regional y, posteriormente, se actualiza a una tabla global de varias cuentas.

**Configuración sincronizada**

Los ajustes de tabla siguientes siempre se sincronizan entre todas las réplicas de una tabla global de varias cuentas:

**nota**  
a diferencia de las tablas globales de la misma cuenta, las tablas globales de varias cuentas no permiten anular esta configuración por región. La única excepción es que se permiten las invalidaciones de las políticas de escalado automático de lectura (tablas y GSI), ya que son recursos externos independientes.
+ Modo de capacidad (capacidad aprovisionada o bajo demanda)
+ Capacidad de lectura y escritura aprovisionada de tablas
+ Escalado automático de lectura y escritura de tablas
+ Definición del índice secundario local (LSI)
+ Definición del índice secundario global (GSI)
+ Capacidad de lectura y escritura aprovisionada de GSI
+ Escalado automático de lectura y escritura de GSI
+ Definición de flujos en modo de MREC
+ Tiempo de vida (TTL)
+ Rendimiento en caliente
+ Rendimiento máximo de lectura y escritura bajo demanda

**Configuración no sincronizada**

Los ajustes siguientes no se sincronizan entre las réplicas y se deben configurar de forma independiente para cada tabla de réplicas de cada región.
+ Clase de tabla
+ Tipo de cifrado del servidor (SSE)
+ Recuperación en un momento dado
+ ID de clave de KMS de cifrado del servidor (SSE)
+ Protección contra eliminación
+ Kinesis Data Streams (KDSD)
+ Etiquetas
+ Política de recursos
+ Información de colaboradores de Cloudwatch (CCI) de tablas
+ Información de colaboradores de Cloudwatch (CCI) de GSI

## Supervisión
<a name="V2globaltables_MA_HowItWorks.monitoring"></a>

Las tablas globales configuradas para una coherencia final de varias regiones (MREC) publican la métrica [`ReplicationLatency`](metrics-dimensions.md#ReplicationLatency) en CloudWatch. Esta métrica realiza un seguimiento del tiempo transcurrido entre el momento en que un elemento se escribe en una tabla de réplica y el momento en que dicho elemento aparece en otra réplica de la tabla global. `ReplicationLatency` se expresa en milisegundos y se emite para cada par de región de origen y destino en una tabla global.

Los valores típicos de `ReplicationLatency` dependen de la distancia entre las regiones de AWS elegidas, así como de otras variables, como el tipo de carga de trabajo y el rendimiento. Por ejemplo, una réplica de origen en la región Oeste de EE. UU. (Norte de California) (us-west-1) tiene un nivel más bajo de `ReplicationLatency` hacia la región Oeste de EE. UU. (Oregón) (us-west-2) en comparación con la región África (Ciudad del Cabo) (af-south-1).

Un valor en aumento de `ReplicationLatency` podría indicar que las actualizaciones de una réplica no se están propagando hacia otras tablas de réplica de manera puntual. En este caso, puede redirigir temporalmente la actividad de lectura y escritura de la aplicación a otra región de AWS.

**Gestión de los problemas de latencia de replicación en tablas globales de varias cuentas**

Si `ReplicationLatency` sobrepasa las 3 horas debido a problemas provocados por el cliente en una tabla de réplicas, DynamoDB envía una notificación solicitando al cliente que aborde el problema subyacente. Los problemas más comunes inducidos por el cliente que pueden impedir la replicación son:
+ Eliminar los permisos necesarios de la política de recursos de la tabla de réplicas.
+ Excluirse de una región de AWS que aloja una réplica de la tabla global de varias cuentas.
+ Denegar los permisos de clave de KMS de AWS de la tabla necesarios para descifrar los datos.

DynamoDB envía una notificación inicial en un plazo de 3 horas si la latencia de replicación es elevada, seguida de una segunda notificación tras 20 horas si el problema sigue sin resolverse. Si el problema no se corrige dentro del intervalo de tiempo requerido, DynamoDB desasociará automáticamente la réplica de la tabla global. A continuación, la réplica afectada se convertirá en una tabla regional.

# Tutoriales: Creación de tablas globales de varias cuentas
<a name="V2globaltables_MA.tutorial"></a>

En esta sección, se proporcionan instrucciones paso a paso para crear tablas globales de DynamoDB que abarquen varias cuentas de AWS.

## Creación de una tabla global de varias cuentas mediante la consola de DynamoDB
<a name="create-ma-gt-console"></a>

Siga estos pasos para crear una tabla global de varias cuentas mediante la Consola de administración de AWS. En el ejemplo siguiente, se crea una tabla global con tablas de réplica en Estados Unidos.

1. Inicie sesión en la Consola de administración de AWS y abra la consola de DynamoDB en [https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/) para la primera cuenta (diga *111122223333*).

1. Para este ejemplo, elija **Este de EE. UU. (Ohio)** en el selector de regiones en la barra de navegación.

1. En el panel de navegación del lado izquierdo de la consola, elija **Tablas**.

1. Seleccione **Create Table (Crear tabla)**.

1. En la página **Crear tabla**:

   1. En **Nombre de la tabla**, introduzca **MusicTable**.

   1. En **Partition key** (Clave de partición), ingrese **Artist**.

   1. Para **Clave de clasificación**, ingrese **SongTitle**.

   1. Deje los demás valores predeterminados y elija **Crear tabla**.

1. Adición de la siguiente política de recursos a la tabla

------
#### [ JSON ]

****  

   ```
   {
   "Version":"2012-10-17",		 	 	 
   "Statement": [
       {
           "Sid": "DynamoDBActionsNeededForSteadyStateReplication",
           "Effect": "Allow",
           "Action": [
               "dynamodb:ReadDataForReplication",
               "dynamodb:WriteDataForReplication",
               "dynamodb:ReplicateSettings"
           ],
           "Resource": "arn:aws:dynamodb:us-east-2:111122223333:table/MusicTable",
           "Principal": {"Service": ["replication.dynamodb.amazonaws.com"]},
           "Condition": {
               "StringEquals": {
                   "aws:SourceAccount": ["444455556666","111122223333"],
                   "aws:SourceArn": [
                       "arn:aws:dynamodb:us-east-1:444455556666:table/MusicTable",
                       "arn:aws:dynamodb:us-east-2:111122223333:table/MusicTable"
                   ]
               }
           }
       },
       {
           "Sid": "AllowTrustedAccountsToJoinThisGlobalTable",
           "Effect": "Allow",
           "Action": [
               "dynamodb:AssociateTableReplica"
           ],
           "Resource": "arn:aws:dynamodb:us-east-2:111122223333:table/MusicTable",
           "Principal": {"AWS": ["444455556666"]}
       }
   ]
   }
   ```

------

1. Esta nueva tabla le servirá de primera tabla de réplica en una nueva tabla global. Será el prototipo para crear otras tablas de réplica que quiera añadir más tarde.

1. Espere que la tabla pase a **Activa**. Para la tabla recién creada, en la pestaña **Tablas globales**, vaya a **Configuración y replicación** y haga clic en **Habilitar**.

1. Cierre sesión en esta cuenta (*111122223333* aquí).

1. Inicie sesión en la Consola de administración de AWS y abra la consola de DynamoDB en [https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/) para la segunda cuenta (diga *444455556666*).

1. Para este ejemplo, elija **Este de EE. UU. (Norte de Virginia)** en el selector de regiones en la barra de navegación.

1. La consola asegura que no haya ninguna tabla con el mismo nombre en la región seleccionada. Si ya existe una tabla con el mismo nombre, debe eliminar la tabla existente antes de crear una nueva tabla de réplica en esa región.

1. En el menú desplegable situado junto a **Crear tabla**, elija **Crear desde otra cuenta**

1. En la página **Crear una tabla desde otra cuenta**:

   1. Agregue **arn:aws:dynamodb:us-east-2:*111122223333*:table/MusicTable** como arn de tabla para la tabla de origen.

   1. En los **ARN de la tabla de réplica**, vuelva a agregar el ARN de la tabla de origen **arn:aws:dynamodb:us-east-2:*111122223333*:table/MusicTable**. Si ya existen varias réplicas como parte de una tabla global de varias cuentas, debe agregar todas las réplicas existentes a ReplicaTableARN.

   1. Deje los demás valores predeterminados y elija **Enviar**.

1. La pestaña **Tablas globales** de la tabla Música (y de cualquier otra tabla de réplica) indica que la tabla se ha replicado en varias regiones.

1. Para probar la replicación:

   1. Puede usar cualquiera de las regiones en las que existe una réplica para esta tabla

   1. Elija **Explorar elementos de la tabla**.

   1. Seleccione **Crear elemento**.

   1. Ingrese **item\$11** para **Artista** y **Song Value 1** para **Título de la canción**.

   1. Seleccione **Crear elemento**.

   1. Compruebe la replicación cambiando a las otras regiones:

   1. Compruebe que la tabla Música contiene el elemento que ha creado.

## Creación de una tabla global de varias cuentas mediante la AWS CLI
<a name="ma-gt-cli"></a>

En los siguientes ejemplos, se muestra cómo crear una tabla global de varias cuentas mediante la AWS CLI. Estos ejemplos muestran el flujo de trabajo completo para configurar la replicación entre cuentas.

------
#### [ CLI ]

Use los comandos de la AWS CLI siguientes para crear una tabla global de varias cuentas con replicación entre cuentas.

```
# STEP 1: Setting resource policy for the table in account 111122223333

cat > /tmp/source-resource-policy.json << 'EOF'
{
    "Version": "2012-10-17", 		 	 	 
    "Statement": [
        {
            "Sid": "DynamoDBActionsNeededForSteadyStateReplication",
            "Effect": "Allow",
            "Action": [
                "dynamodb:ReadDataForReplication",
                "dynamodb:WriteDataForReplication",
                "dynamodb:ReplicateSettings"
            ],
            "Resource": "arn:aws:dynamodb:us-east-2:111122223333:table/MusicTable",
            "Principal": {"Service": ["replication.dynamodb.amazonaws.com"]},
            "Condition": {
                "StringEquals": {
                    "aws:SourceAccount": ["444455556666","111122223333"],
                    "aws:SourceArn": [
                        "arn:aws:dynamodb:us-east-1:444455556666:table/MusicTable",
                        "arn:aws:dynamodb:us-east-2:111122223333:table/MusicTable"
                    ]
                }
            }
        },
        {
            "Sid": "AllowTrustedAccountsToJoinThisGlobalTable",
            "Effect": "Allow",
            "Action": [
                "dynamodb:AssociateTableReplica"
            ],
            "Resource": "arn:aws:dynamodb:us-east-2:111122223333:table/MusicTable",
            "Principal": {"AWS": ["444455556666"]}
        }
    ]
}
EOF

# Step 2: Create a new table (MusicTable) in US East (Ohio), 
#   with DynamoDB Streams enabled (NEW_AND_OLD_IMAGES),
#   and Settings Replication ENABLED on the account 111122223333

aws dynamodb create-table \
    --table-name MusicTable \
    --attribute-definitions \
        AttributeName=Artist,AttributeType=S \
        AttributeName=SongTitle,AttributeType=S \
    --key-schema \
        AttributeName=Artist,KeyType=HASH \
        AttributeName=SongTitle,KeyType=RANGE \
    --billing-mode PAY_PER_REQUEST \
    --stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES \
    --global-table-settings-replication-mode ENABLED \
    --resource-policy file:///tmp/source-resource-policy.json \
    --region us-east-2 


# Step 3: Creating replica table in account 444455556666

# Resource policy for account 444455556666
cat > /tmp/dest-resource-policy.json << 'EOF'
{
    "Version": "2012-10-17", 		 	 	 
    "Statement": [
        {
            "Sid": "DynamoDBActionsNeededForSteadyStateReplication",
            "Effect": "Allow",
            "Action": [
                "dynamodb:ReadDataForReplication",
                "dynamodb:WriteDataForReplication",
                "dynamodb:ReplicateSettings"
            ],
            "Resource": "arn:aws:dynamodb:us-east-1:444455556666:table/MusicTable",
            "Principal": {"Service": ["replication.dynamodb.amazonaws.com"]},
            "Condition": {
                "StringEquals": {
                    "aws:SourceAccount": ["444455556666","111122223333"],
                    "aws:SourceArn": [
                        "arn:aws:dynamodb:us-east-1:444455556666:table/MusicTable",
                        "arn:aws:dynamodb:us-east-2:111122223333:table/MusicTable"
                    ]
                }
            }
        }
    ]
}
EOF

# Execute the replica table creation
aws dynamodb create-table \
    --table-name MusicTable \
    --global-table-source-arn "arn:aws:dynamodb:us-east-2:111122223333:table/MusicTable" \
    --resource-policy file:///tmp/dest-resource-policy.json \
    --global-table-settings-replication-mode ENABLED \
    --region us-east-1

# Step 4: View the list of replicas created using describe-table
aws dynamodb describe-table \
    --table-name MusicTable \
    --region us-east-2 \
    --query 'Table.{TableName:TableName,TableStatus:TableStatus,MultiRegionConsistency:MultiRegionConsistency,Replicas:Replicas[*].{Region:RegionName,Status:ReplicaStatus}}'

# Step 5: To verify that replication is working, add a new item to the Music table in US East (Ohio)
aws dynamodb put-item \
    --table-name MusicTable \
    --item '{"Artist": {"S":"item_1"},"SongTitle": {"S":"Song Value 1"}}' \
    --region us-east-2

# Step 6: Wait for a few seconds, and then check to see whether the item has been 
# successfully replicated to US East (N. Virginia) and Europe (Ireland)
aws dynamodb get-item \
    --table-name MusicTable \
    --key '{"Artist": {"S":"item_1"},"SongTitle": {"S":"Song Value 1"}}' \
    --region us-east-1

aws dynamodb get-item \
    --table-name MusicTable \
    --key '{"Artist": {"S":"item_1"},"SongTitle": {"S":"Song Value 1"}}' \
    --region us-east-2

# Step 7: Delete the replica table in US East (N. Virginia) Region
aws dynamodb delete-table \
    --table-name MusicTable \
    --region us-east-1

# Clean up: Delete the primary table
aws dynamodb delete-table \
    --table-name MusicTable \
    --region us-east-2
```

------

# Seguridad de las tablas globales de DynamoDB
<a name="globaltables_MA_security"></a>

Las réplicas de tablas globales son tablas de DynamoDB, por lo que se utilizan los mismos métodos para controlar el acceso a las réplicas que se utilizan para las tablas de una sola región, incluyendo las políticas de identidad y las políticas basadas en recursos de AWS Identity and Access Management (IAM). En este tema, se describe cómo proteger las tablas globales de varias cuentas de DynamoDB mediante permisos de IAM y cifrado de AWS Key Management Service (AWS KMS). Obtendrá información sobre las políticas basadas en recursos y los roles vinculados a servicios (SLR) que permiten la replicación y el escalado automático entre regiones y entre cuentas, los permisos de IAM necesarios para crear, actualizar y eliminar tablas globales, para tablas de coherencia final de varias regiones (MREC). También obtendrá información sobre las claves de cifrado de AWS KMS para administrar de forma segura la replicación entre regiones.

Proporciona información detallada sobre las políticas basadas en recursos y los permisos necesarios para establecer la replicación de tablas entre cuentas y regiones. Comprender este modelo de seguridad es fundamental para los clientes que necesitan implementar soluciones de replicación de datos seguras y entre cuentas.

## Autorización de la entidad principal del servicio para la replicación
<a name="globaltables_MA_service_principal"></a>

Las tablas globales de varias cuentas de DynamoDB utilizan un enfoque de autorización distinto, ya que la replicación se realiza más allá de los límites de las cuentas. Esto se consigue mediante la entidad principal del servicio de replicación de DynamoDB: `replication.dynamodb.amazonaws.com`. Cada cuenta participante debe incluir explícitamente esa entidad principal en la política de recursos de la tabla de réplicas, lo que concede permisos que puedan restringirse a réplicas específicas en función de las condiciones del contexto de origen de las claves como `aws:SourceAccount`, `aws:SourceArn`, etc. (consulte las [claves de condición globales de AWS](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-keys.html) para obtener más información). Los permisos son bidireccionales, lo que significa que todas las réplicas deben concederse permisos de forma explícita entre sí antes de poder establecer la replicación en un par de réplicas concreto.

Los permisos de las entidades principales de servicio siguientes son esenciales para la replicación entre cuentas:
+ `dynamodb:ReadDataForReplication` otorga la capacidad de leer datos con fines de replicación. Este permiso permite leer los cambios de una réplica y propagarlos a otras réplicas.
+ `dynamodb:WriteDataForReplication` permite escribir los datos replicados en las tablas de destino. Este permiso permite sincronizar los cambios en todas las réplicas de la tabla global.
+ `dynamodb:ReplicateSettings` permite la sincronización de la configuración de la tabla entre las réplicas, lo que proporciona una configuración coherente en todas las tablas participantes.

Cada réplica debe conceder los permisos anteriores a todas las demás réplicas y a sí misma, es decir, las condiciones del contexto de origen deben incluir el conjunto completo de réplicas que componen la tabla global. Estos permisos se verifican para cada nueva réplica cuando se agrega a una tabla global de varias cuentas. Esto verifica que las operaciones de replicación las realice solo el servicio DynamoDB autorizado y solo entre las tablas previstas.

## Roles vinculados a servicios para tablas globales de varias cuentas
<a name="globaltables_MA_service_linked_roles"></a>

Las tablas globales de varias cuentas de DynamoDB replican la configuración en todas las réplicas, de modo que cada réplica se configura de forma idéntica con un rendimiento coherente y proporciona una experiencia de conmutación por error perfecta. La replicación de la configuración se controla mediante el permiso `ReplicateSettings` de la entidad principal de servicio, pero también confiamos en roles vinculados a servicios (SLR) para gestionar determinadas capacidades de replicación y escalado automático entre cuentas y entre regiones. Estos roles se configuran solo una vez por cuenta de AWS. Una vez creados, los mismos roles sirven en todas las tablas globales de la cuenta. Para obtener más información acerca de los roles vinculados a servicios, consulte [Uso de roles vinculados a servicios](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create-service-linked-role.html) en la Guía del usuario de IAM.

### Rol vinculado al servicio de administración de configuración
<a name="globaltables_MA_settings_management_slr"></a>

Amazon DynamoDB crea automáticamente el rol vinculado al servicio (SLR) AWSServiceRoleForDynamoDBGlobalTableSettingsManagement cuando se crea la primera réplica de una tabla global de varias cuentas en la cuenta. Este rol administra la replicación de la configuración entre regiones y cuentas por usted.

Al aplicar políticas basadas en recursos a las réplicas, confirme que no deniega ninguno de los permisos definidos en `AWSServiceRoleForDynamoDBGlobalTableSettingsManagement` a la entidad principal de SLR, ya que esto podría interferir con la administración de la configuración y podría afectar la replicación si el rendimiento no coincide en todas las réplicas o GSI. Si deniega los permisos de SLR necesarios, la replicación hacia y desde las réplicas afectadas podría detenerse y el estado de la tabla de réplicas cambiará a `REPLICATION_NOT_AUTHORIZED`. Para tablas globales de varias cuentas, si una réplica permanece en el estado `REPLICATION_NOT_AUTHORIZED` durante más de 20 horas, la réplica se convierte irreversiblemente en una tabla de DynamoDB de una sola región. SLR tiene los siguientes permisos:
+ `application-autoscaling:DeleteScalingPolicy`
+ `application-autoscaling:DescribeScalableTargets`
+ `application-autoscaling:DescribeScalingPolicies`
+ `application-autoscaling:DeregisterScalableTarget`
+ `application-autoscaling:PutScalingPolicy`
+ `application-autoscaling:RegisterScalableTarget`

### Rol vinculado a un servicio de escalado automático
<a name="globaltables_MA_autoscaling_slr"></a>

Al configurar una tabla global para el modo de capacidad aprovisionada, se debe configurar el escalado automático para la tabla global. El escalado automático de DynamoDB utiliza el servicio de escalado automático de aplicaciones de AWS para ajustar dinámicamente la capacidad de rendimiento aprovisionada en las réplicas de tabla global. El servicio de escalado automático de aplicaciones crea un rol vinculado a un servicio (SLR) denominado [AWSServiceRoleForApplicationAutoScaling\$1DynamoDBTable](https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-service-linked-roles.html). Este rol vinculado al servicio se crea automáticamente en la cuenta de AWS al configurar el escalado automático para una tabla de DynamoDB. Permite el escalado automático de aplicaciones para administrar la capacidad de la tabla aprovisionada y crear alarmas de CloudWatch.

Al aplicar políticas basadas en recursos a las réplicas, asegúrese de no denegar ninguno de los permisos definidos en la [AWSApplicationAutoscalingDynamoDBTablePolicy](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSApplicationAutoscalingDynamoDBTablePolicy.html) a la entidad principal de SLR de escalado automático de aplicaciones, ya que esto interrumpiría la funcionalidad de escalado automático.

## Uso de las tablas globales de AWS IAM
<a name="globaltables_MA_iam"></a>

En las siguientes secciones, se describen los permisos necesarios para las distintas operaciones de las tablas globales y se proporcionan ejemplos de políticas que le ayudarán a configurar el acceso adecuado para los usuarios y aplicaciones.

**nota**  
Todos los permisos descritos se deben aplicar al ARN del recurso de tabla específico en las regiones afectadas. El ARN del recurso de tabla sigue el formato `arn:aws:dynamodb:region:account-id:table/table-name`, en el que debe especificar los valores reales de la región, el ID de cuenta y el nombre de la tabla.

A continuación, se muestran los temas paso a paso que cubrimos en las secciones siguientes:
+ Creación de tablas globales de varias cuentas y agregación de réplicas
+ Actualización de una tabla global de varias cuentas
+ Eliminación de tablas globales y réplicas

### Creación de tablas globales y agregación de réplicas
<a name="globaltables_MA_creating"></a>

#### Permisos para crear tablas globales
<a name="globaltables_MA_creating_permissions"></a>

Cuando se agrega una nueva réplica a una tabla regional para formar una tabla global de varias cuentas o a una tabla global de varias cuentas existente, la entidad principal de IAM que realice la acción debe contar con la autorización de todos los miembros existentes. Todos los miembros existentes deben incluir el siguiente permiso en su política de tablas para que la adición de réplicas se realice correctamente:
+ `dynamodb:AssociateTableReplica`: este permiso permite unir las tablas en una configuración de tabla global. Este es el permiso fundamental que permite el establecimiento inicial de la relación de replicación.

Este control preciso permite que solo las cuentas autorizadas participen en la configuración de la tabla global.

#### Políticas de IAM de ejemplo para crear tablas globales
<a name="globaltables_MA_creating_examples"></a>

##### Ejemplo de políticas de IAM para una configuración de 2 réplicas
<a name="globaltables_MA_2replica_example"></a>

La configuración de las tablas globales de varias cuentas sigue un flujo de autorización específico que proporciona una replicación segura. Examinemos cómo funciona esto en la práctica analizando un escenario práctico en el que un cliente desea establecer una tabla global con dos réplicas. La primera réplica (réplica A) reside en la cuenta A de la región ap-east-1, mientras que la segunda réplica (réplica B) se encuentra en la cuenta B de la región eu-south-1.
+ En la cuenta de origen (cuenta A), el proceso comienza con la creación de la tabla de réplicas principal. El administrador de la cuenta debe adjuntar a esta tabla una política basada en recursos que conceda de forma explícita los permisos necesarios a la cuenta de destino (cuenta B) para realizar la asociación. Esta política también autoriza al servicio de replicación de DynamoDB a realizar las acciones de replicación esenciales.
+ La cuenta de destino (cuenta B) sigue un proceso similar al adjuntar la política basada en recursos correspondiente al crear la réplica y hacer referencia al ARN de la tabla de origen que se utilizará para crear la réplica. Esta política refleja los permisos otorgados por la cuenta A, lo que crea una relación bidireccional de confianza. Antes de establecer la replicación, DynamoDB valida estos permisos entre cuentas para comprobar que existe la autorización adecuada.

Para establecer esta configuración:
+ El administrador de la cuenta A debe adjuntar primero la política basada en recursos a réplica A. Esta política concede de forma explícita los permisos necesarios a la cuenta B y al servicio de replicación de DynamoDB.
+ Del mismo modo, el administrador de la cuenta B debe adjuntar una política de coincidencia a la réplica B, con las referencias de cuenta invertidas para conceder los permisos correspondientes a la cuenta A; en la llamada de creación de la tabla para crear la réplica B, se hace referencia a la réplica A como tabla de origen.

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "DynamoDBActionsNeededForSteadyStateReplication",
            "Effect": "Allow",
            "Action": [
                "dynamodb:ReadDataForReplication",
                "dynamodb:WriteDataForReplication",
                "dynamodb:ReplicateSettings"
            ],
            "Resource": "arn:aws:dynamodb:ap-east-1:111122223333:table/ReplicaA",
            "Principal": {"Service": ["replication.dynamodb.amazonaws.com"]},
            "Condition": {
                "StringEquals": {
                    "aws:SourceAccount": [ "111122223333", "444455556666" ],
                    "aws:SourceArn": [
                        "arn:aws:dynamodb:ap-east-1:111122223333:table/ReplicaA",
                        "arn:aws:dynamodb:eu-south-1:444455556666:table/ReplicaB"
                    ]
                }
            }
        },
        {
            "Sid": "AllowTrustedAccountsToJoinThisGlobalTable",
            "Effect": "Allow",
            "Action": [
                "dynamodb:AssociateTableReplica"
            ],
            "Resource": "arn:aws:dynamodb:ap-east-1:111122223333:table/ReplicaA",
            "Principal": {"AWS": ["444455556666"]}
        }
    ]
}
```

------

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "DynamoDBActionsNeededForSteadyStateReplication",
            "Effect": "Allow",
            "Action": [
                "dynamodb:ReadDataForReplication",
                "dynamodb:WriteDataForReplication",
                "dynamodb:ReplicateSettings"
            ],
            "Resource": "arn:aws:dynamodb:eu-south-1:444455556666:table/ReplicaB",
            "Principal": {"Service": ["replication.dynamodb.amazonaws.com"]},
            "Condition": {
                "StringEquals": {
                    "aws:SourceAccount": [ "111122223333", "444455556666" ],
                    "aws:SourceArn": [
                        "arn:aws:dynamodb:ap-east-1:111122223333:table/ReplicaA",
                        "arn:aws:dynamodb:eu-south-1:444455556666:table/ReplicaB"
                    ]
                }
            }
        }
    ]
}
```

------

##### Ejemplo de políticas de IAM para una configuración de 3 réplicas
<a name="globaltables_MA_3replica_example"></a>

En esta configuración, tenemos 3 réplicas: la réplica A, la réplica B y la réplica C en la cuenta A, la cuenta B y la cuenta C, respectivamente. La réplica A es la primera réplica, que comienza como una tabla regional y a la que, a continuación, se agregan la réplica B y la réplica C.
+ El administrador de la cuenta A primero debe adjuntar la política basada en recursos a la réplica A, lo que permitirá la replicación con todos los miembros y permitirá a las entidades principales de IAM de las cuentas B y C agregar réplicas.

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "DynamoDBActionsNeededForSteadyStateReplication",
            "Effect": "Allow",
            "Action": [
                "dynamodb:ReadDataForReplication",
                "dynamodb:WriteDataForReplication",
                "dynamodb:ReplicateSettings"
            ],
            "Resource": "arn:aws:dynamodb:ap-east-1:111122223333:table/ReplicaA",
            "Principal": {"Service": ["replication.dynamodb.amazonaws.com"]},
            "Condition": {
                "StringEquals": {
                    "aws:SourceAccount": [ "111122223333", "444455556666", "123456789012" ],
                    "aws:SourceArn": [
                        "arn:aws:dynamodb:ap-east-1:111122223333:table/ReplicaA",
                        "arn:aws:dynamodb:eu-south-1:444455556666:table/ReplicaB",
                        "arn:aws:dynamodb:us-east-1:123456789012:table/ReplicaC"
                    ]
                }
            }
        },
        {
            "Sid": "AllowTrustedAccountsToJoinThisGlobalTable",
            "Effect": "Allow",
            "Action": [
                "dynamodb:AssociateTableReplica"
            ],
            "Resource": "arn:aws:dynamodb:ap-east-1:111122223333:table/ReplicaA",
            "Principal": { "AWS": [ "444455556666", "123456789012" ] }
        }
    ]
}
```

------
+ El administrador de la cuenta B debe agregar una réplica (réplica B) que apunte a la réplica A como origen. La réplica B tiene la siguiente política que permite la replicación entre todos los miembros y permite a la cuenta C agregar una réplica:

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "DynamoDBActionsNeededForSteadyStateReplication",
            "Effect": "Allow",
            "Action": [
                "dynamodb:ReadDataForReplication",
                "dynamodb:WriteDataForReplication",
                "dynamodb:ReplicateSettings"
            ],
            "Resource": "arn:aws:dynamodb:eu-south-1:444455556666:table/ReplicaB",
            "Principal": {"Service": ["replication.dynamodb.amazonaws.com"]},
            "Condition": {
                "StringEquals": {
                    "aws:SourceAccount": [ "111122223333", "444455556666", "123456789012" ],
                    "aws:SourceArn": [
                        "arn:aws:dynamodb:ap-east-1:111122223333:table/ReplicaA",
                        "arn:aws:dynamodb:eu-south-1:444455556666:table/ReplicaB",
                        "arn:aws:dynamodb:us-east-1:123456789012:table/ReplicaC"
                    ]
                }
            }
        },
        {
            "Sid": "AllowTrustedAccountsToJoinThisGlobalTable",
            "Effect": "Allow",
            "Action": [
                "dynamodb:AssociateTableReplica"
            ],
            "Resource": "arn:aws:dynamodb:eu-south-1:444455556666:table/ReplicaB",
            "Principal": { "AWS": [ "123456789012" ] }
        }
    ]
}
```

------
+ Por último, el administrador de la cuenta C crea una réplica con la siguiente política que permite los permisos de replicación entre todos los miembros. La política no permite agregar más réplicas.

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "DynamoDBActionsNeededForSteadyStateReplication",
            "Effect": "Allow",
            "Action": [
                "dynamodb:ReadDataForReplication",
                "dynamodb:WriteDataForReplication",
                "dynamodb:ReplicateSettings"
            ],
            "Resource": "arn:aws:dynamodb:us-east-1:123456789012:table/ReplicaC",
            "Principal": {"Service": ["replication.dynamodb.amazonaws.com"]},
            "Condition": {
                "StringEquals": {
                    "aws:SourceAccount": [ "111122223333", "444455556666" ],
                    "aws:SourceArn": [
                        "arn:aws:dynamodb:ap-east-1:111122223333:table/ReplicaA",
                        "arn:aws:dynamodb:eu-south-1:444455556666:table/ReplicaB"
                    ]
                }
            }
        }
    ]
}
```

------

### Actualización de una tabla global de varias cuentas
<a name="globaltables_MA_updating"></a>

Para modificar la configuración de réplica de una tabla global existente mediante la API de UpdateTable, necesita el siguiente permiso en el recurso de tabla de la región en la que va a realizar la llamada a la API: `dynamodb:UpdateTable`

Además, puede actualizar otras configuraciones de tablas globales, como las políticas de escalado automático y la configuración del tiempo de vida. Se requieren los siguientes permisos para estas operaciones de actualización adicionales:

Para actualizar la configuración de Periodo de vida con la API de `UpdateTimeToLive`, debe tener el siguiente permiso en el recurso de la tabla en todas las regiones que contienen réplicas: `dynamodb:UpdateTimeToLive`

Para actualizar una política de escalado automático de réplicas con la API de `UpdateTableReplicaAutoScaling`, debe tener los siguientes permisos en el recurso de la tabla en todas las regiones que contengan réplicas:
+ `application-autoscaling:DeleteScalingPolicy`
+ `application-autoscaling:DeleteScheduledAction`
+ `application-autoscaling:DeregisterScalableTarget`
+ `application-autoscaling:DescribeScalableTargets`
+ `application-autoscaling:DescribeScalingActivities`
+ `application-autoscaling:DescribeScalingPolicies`
+ `application-autoscaling:DescribeScheduledActions`
+ `application-autoscaling:PutScalingPolicy`
+ `application-autoscaling:PutScheduledAction`
+ `application-autoscaling:RegisterScalableTarget`

**nota**  
Debe proporcionar permisos de `dynamodb:ReplicateSettings` en todas las regiones y cuentas de réplica para que la tabla de actualización se realice correctamente. Si alguna réplica no proporciona permisos para replicar la configuración en ninguna réplica de la tabla global de varias cuentas, todas las operaciones de actualización de todas las réplicas producirán un error con `AccessDeniedException` hasta que se fijen los permisos.

### Eliminación de tablas globales y réplicas
<a name="globaltables_MA_deleting"></a>

Para eliminar una tabla global, debe eliminar todas las réplicas. A diferencia de la tabla global con la misma cuenta, no puede utilizar `UpdateTable` para eliminar una tabla de réplica en una región remota y cada réplica se debe eliminar mediante la API `DeleteTable` de la cuenta que la controla.

#### Permisos para eliminar tablas globales y réplicas
<a name="globaltables_MA_deleting_permissions"></a>

Los siguientes permisos son necesarios para eliminar réplicas individuales y para eliminar por completo tablas globales. Al eliminar una configuración de tabla global, solo se elimina la relación de replicación entre tablas de distintas regiones. No se eliminará la tabla de DynamoDB de la última región restante. La tabla de la última región sigue existiendo como tabla estándar de DynamoDB con los mismos datos y configuraciones.

Necesita los siguientes permisos en el recurso de tabla en cada región en la que vaya a eliminar una réplica:
+ `dynamodb:DeleteTable`
+ `dynamodb:DeleteTableReplica`

## Uso de las tablas globales de AWS KMS
<a name="globaltables_MA_kms"></a>

Como todas las tablas de DynamoDB, las réplicas de tablas globales siempre cifran los datos en reposo mediante claves de cifrado almacenadas en AWS Key Management Service (AWS KMS).

**nota**  
A diferencia de la tabla global de la misma cuenta, se pueden configurar diferentes réplicas en una tabla global de varias cuentas con un tipo de clave de AWS KMS diferente (clave propia de AWS o clave administrada por el cliente). Las tablas globales de varias cuentas no admiten claves administradas por AWS.

Las tablas globales de varias cuentas que utilizan CMK requieren que la política de claves de cada réplica otorgue permisos a la entidad principal del servicio de replicación de DynamoDB (`replication.dynamodb.amazonaws.com`) para acceder a la clave para la administración de la replicación y la configuración. Los siguientes permisos son necesarios:
+ `kms:Decrypt`
+ `kms:ReEncrypt*`
+ `kms:GenerateDataKey*`
+ `kms:DescribeKey`

**Importante**

DynamoDB requiere acceso a la clave de cifrado de la réplica para eliminarla. Si desea desactivar o eliminar una clave administrada por el cliente que se utiliza para cifrar una réplica porque va a eliminar la réplica, primero debe eliminar la réplica, esperar a que la tabla se elimine del grupo de replicación llamando a describir en una de las demás réplicas y luego desactivar o eliminar la clave.

Si desactiva o revoca el acceso de DynamoDB a una clave administrada por el cliente que se utiliza para cifrar una réplica, la replicación hacia y desde la réplica se detendrá y el estado de la réplica cambiará a `INACCESSIBLE_ENCRYPTION_CREDENTIALS`. Si una réplica permanece en el estado `INACCESSIBLE_ENCRYPTION_CREDENTIALS` durante más de 20 horas, la réplica se convierte irreversiblemente en una tabla de DynamoDB de una sola región.

### Ejemplo de política de AWS KMS
<a name="globaltables_MA_kms_example"></a>

La política de AWS KMS permite a DynamoDB acceder a ambas claves de AWS KMS para la replicación entre las réplicas A y B. Las claves de AWS KMS adjuntas a la réplica de DynamoDB en cada cuenta deben actualizarse con la siguiente política:

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": { "Service": "replication.dynamodb.amazonaws.com" },
        "Action": [
            "kms:Decrypt",
            "kms:ReEncrypt*",
            "kms:GenerateDataKey*",
            "kms:DescribeKey"
        ],
        "Resource": "*",
        "Condition": {
            "StringEquals": {
                "aws:SourceAccount": [ "111122223333", "444455556666" ],
                "aws:SourceArn": [
                    "arn:aws:dynamodb:ap-east-1:111122223333:table/ReplicaA",
                    "arn:aws:dynamodb:eu-south-1:444455556666:table/ReplicaB"
                ]
            }
        }
      }
   ]
 }
```

------

# Descripción de la facturación de tablas globales en Amazon DynamoDB
<a name="global-tables-billing"></a>

En esta guía se describe cómo funciona la facturación de DynamoDB para las tablas globales, se identifican los componentes que contribuyen al costo de las tablas globales y se incluye un ejemplo práctico. 

Las [tablas globales de Amazon DynamoDB](GlobalTables.md) son una base de datos totalmente administrada, sin servidor, multirregional y multiactiva. Las tablas globales están diseñadas para ofrecer una [disponibilidad del 99,999 %](https://aws.amazon.com/dynamodb/sla/), una mayor resiliencia de las aplicaciones y una mejor continuidad empresarial. Las tablas globales replican automáticamente las tablas de DynamoDB en las regiones de AWS que elija para que pueda lograr un rendimiento de lectura y escritura rápido a escala local. 

## Funcionamiento
<a name="global-tables-billing-how-it-works"></a>

El modelo de facturación de las tablas globales difiere del de las tablas de DynamoDB de una sola región. Las operaciones de escritura de las tablas de DynamoDB de una sola región se facturan mediante las siguientes unidades:
+ Unidades de solicitud de escritura (WRU) para el modo de capacidad bajo demanda, en el que se cobra una WRU por cada escritura de hasta 1 KB
+ Unidades de capacidad de escritura (WCU) para el modo de capacidad aprovisionada, en el que una WCU proporciona una escritura por segundo de hasta 1 KB

Al crear una tabla global mediante la adición de una tabla de réplica a una tabla de una sola región existente, dicha tabla se convierte en una tabla de réplica, lo que significa que también cambian las unidades que se usan para facturar las escrituras en la tabla. Las operaciones de escritura en las tablas de réplica se facturan mediante las siguientes unidades: 
+ Unidades de solicitud de escritura replicada (rWRU) para el modo de capacidad bajo demanda, en el que se cobra una rWRU por tabla de réplica por cada escritura de hasta 1 KB
+ Unidades de capacidad de escritura replicada (rWCU) para el modo de capacidad aprovisionada, en el que una WCU por tabla de réplica proporciona una escritura por segundo de hasta 1 KB

Las actualizaciones de los índices secundarios globales (GSI) se facturan mediante las mismas unidades que las tablas de DynamoDB de una sola región, aunque la tabla base del GSI sea una tabla de réplica. Las operaciones de actualización de los GSI se facturan mediante las siguientes unidades:
+ Unidades de solicitud de escritura (WRU) para el modo de capacidad bajo demanda, en el que se cobra una WRU por cada escritura de hasta 1 KB
+ Unidades de capacidad de escritura (WCU) para el modo de capacidad aprovisionada, en el que una WCU proporciona una escritura por segundo de hasta 1 KB

Las unidades de escritura replicada (rWCU y rWRU) tienen el mismo precio que las unidades de escritura de una sola región (WCU y WRU). Se aplican tarifas de transferencia de datos entre regiones a las tablas globales, ya que los datos se replican entre regiones. Los cargos por escritura replicada (rWCU o rWRU) se producen en cada región que contenga una tabla de réplica para la tabla global.

Las operaciones de lectura de tablas de una sola región y de tablas de réplica utilizan las siguientes unidades:
+ Unidades de solicitud de lectura (RRU) para el modo de capacidad bajo demanda, en el que se cobra una RRU por cada lectura altamente coherente de hasta 4 KB
+ Unidades de capacidad de lectura (RCU) para tablas aprovisionadas, en las que una RCU proporciona una lectura altamente coherente por segundo de hasta 4 KB

## Modos de coherencia y facturación
<a name="global-tables-billing-consistency-modes"></a>

Las unidades de escritura replicadas (rWCU y rWRU) que se utilizan para facturar las operaciones de escritura son idénticas en los modos de coherencia sólida de varias regiones (MRSC) y de coherencia final de varias regiones (MREC). Las tablas globales que utilizan el modo de coherencia alta multirregional (MRSC) configurado con un testigo no incurren en costos de unidades de escritura replicadas (rWCU y rWRU), costos de almacenamiento o costos de transferencia de datos para la replicación al testigo.

## Ejemplo de facturación de tablas globales de DynamoDB
<a name="global-tables-billing-example"></a>

Veamos un ejemplo de varios días para comprobar cómo funciona en la práctica la facturación de las solicitudes de escritura de tablas globales (tenga en cuenta que en este ejemplo solo se consideran las solicitudes de escritura y no se incluyen los gastos de restauración de tablas ni de transferencia de datos entre regiones en los que se incurriría en el ejemplo):

**Día 1, tabla de una sola región: **tiene una tabla de DynamoDB bajo demanda de una sola región llamada Table\$1A en la región us-west-2. Escribe 100 elementos de 1 KB en Table\$1A. Para estas operaciones de escritura de una sola región, se le cobrará 1 unidad de solicitud de escritura (WRU) por cada 1 KB escrito. Los cargos del día 1 son los siguientes:
+ 100 WRU en la región us-west-2 por escrituras de una sola región

El total de unidades de solicitud cobradas el día 1 es de **100 WRU**.

**Día 2, creación de una tabla global: **para crear una tabla global, agrega una réplica de Table\$1A a la región us-east-2. Table\$1A es ahora una tabla global con dos tablas de réplica: una en la región us-west-2 y otra en la región us-east-2. Escribe 150 elementos de 1 KB en la tabla de réplica de la región us-west-2. Los cargos del día 2 son los siguientes:
+ 150 rWRU en la región us-west-2 para escrituras replicadas
+ 150 rWRU en la región us-east-2 para escrituras replicadas

El total de unidades de solicitud cobradas el día 2 es de **300 rWRU**.

**Día 3, adición de un índice secundario global: **agrega un índice secundario global (GSI) a la tabla de réplica de la región us-east-2 que proyecta todos los atributos de la tabla base (réplica). La tabla global crea automáticamente para usted el GSI en la tabla de réplica de la región us-west-2. Escribe 200 registros nuevos de 1 KB en la tabla de réplica de la región us-west-2. Los cargos del día 3 son los siguientes:
+ • 200 rWRU en la región us-west-2 para escrituras replicadas
+ • 200 WRU en la región us-west-2 para las actualizaciones de GSI
+ • 200 rWRU en la región us-east-2 para escrituras replicadas
+ • 200 WRU en la región us-east-2 para las actualizaciones de GSI

El total de unidades de solicitud de escritura cobradas el día 3 es **400 WRU y 400 rWRU**.

Los cargos totales por unidad de escritura durante los tres días son de 500 rWRU (100 rWRU el día 1 \$1 400 rWRU el día 3) y 700 rWRU (300 rWRU el día 2 \$1 400 rWRU el día 3).

En resumen, las operaciones de escritura de tabla de réplica se facturan en unidades de escritura replicada en todas las regiones que contienen una tabla de réplica. Si tiene índices secundarios globales, se le cobran unidades de escritura por las actualizaciones de los GSI en todas las regiones que contienen un GSI (que en una tabla global son todas las regiones que contienen una tabla de réplica). 

# Versiones de tablas globales de DynamoDB
<a name="V2globaltables_versions"></a>

Hay disponibles dos versiones de las tablas globales de DynamoDB: versión 2019.11.21 (actual) y versión 2017.11.29 (antigua). Recomendamos utilizar la versión 2019.11.21 (actual) de las tablas globales, ya que es más fácil de usar, se admite en más regiones y tiene un costo más bajo para la mayoría de las cargas de trabajo en comparación con la versión 2017.11.29 (antigua).

## Determinación de la versión de una tabla global
<a name="globaltables.DetermineVersion"></a>

### Determinar la versión mediante la AWS CLI
<a name="globaltables.CLI"></a>

#### Identificación de una réplica de tabla global de la versión 2019.11.21 (actual)
<a name="globaltables.CLI.current"></a>

Para determinar si una tabla es una réplica de tabla global de la versión 2019.11.21 (actual), invoque el comando `describe-table` para la tabla. Si el resultado contiene el atributo de `GlobalTableVersion` con un valor de “2019.11.21”, la tabla es una réplica de tabla global de la versión 2019.11.21 (actual).

Un ejemplo de comando de la CLI para `describe-table`:

```
aws dynamodb describe-table \
--table-name users \
--region us-east-2
```

El resultado (abreviado) contiene el atributo de `GlobalTableVersion` con un valor de “2019.11.21”, por lo que esta tabla es una réplica de la tabla global de la versión 2019.11.21 (actual).

```
{
    "Table": {
        "AttributeDefinitions": [
            {
                "AttributeName": "id",
                "AttributeType": "S"
            },
            {
                "AttributeName": "name",
                "AttributeType": "S"
            }
        ],
        "TableName": "users",
        ...
        "GlobalTableVersion": "2019.11.21",
        "Replicas": [
            {
                "RegionName": "us-west-2",
                "ReplicaStatus": "ACTIVE",
            }
        ],
        ...
    }
}
```

#### Identificación de una réplica de tabla global de la versión 2017.11.29 (antigua)
<a name="globaltables.CLI.legacy"></a>

La versión 2017.11.29 (antigua) de tablas globales utiliza un conjunto dedicado de comandos para la administración de tablas globales. Para determinar si una tabla es una réplica de la versión 2017.11.29 de tablas globales (antigua), invoque el comando `describe-global-table` para la tabla. Si recibe una respuesta exitosa, la tabla es una réplica de tabla global de la versión 2017.11.29 (antigua). Si el comando `describe-global-table` devuelve un error `GlobalTableNotFoundException`, la tabla no es una réplica de la versión 2017.11.29 (antigua).

Un comando de la CLI de ejemplo para `describe-global-table`:

```
aws dynamodb describe-global-table \
--table-name users \
--region us-east-2
```

El comando devuelve una respuesta exitosa, por lo que esta tabla es una réplica de tabla global de la versión 2017.11.29 (antigua).

```
{
    "GlobalTableDescription": {
        "ReplicationGroup": [
            {
                "RegionName": "us-west-2"
            },
            {
                "RegionName": "us-east-2"
            }
        ],
        "GlobalTableArn": "arn:aws:dynamodb::123456789012:global-table/users",
        "CreationDateTime": "2025-06-10T13:55:53.630000-04:00",
        "GlobalTableStatus": "ACTIVE",
        "GlobalTableName": "users"
    }
}
```

### Determinación de la versión mediante la consola de DynamoDB
<a name="globaltables.console"></a>

Para identificar la versión de una réplica de tabla global, realice lo siguiente:

1. Abra la consola de DynamoDB en [https://console.aws.amazon.com/dynamodb/home](https://console.aws.amazon.com/dynamodb/home).

1. En el panel de navegación del lado izquierdo de la consola, elija **Tablas**.

1. Elija la tabla de la que desea identificar la versión de las tablas globales.

1. Elija la pestaña **Global Tables (Tablas globales)**.

   La sección *Resumen* muestra la versión de las tablas globales en uso.

## Diferencias de comportamiento entre las versiones heredada y actual
<a name="DiffLegacyVsCurrent"></a>

En la siguiente lista se describen las diferencias de comportamiento entre las versiones heredada y actual de las tablas globales.
+ La versión 2019.11.21 (actual) consume menos capacidad de escritura para varias operaciones de DynamoDB en comparación con la 2017.11.29 (heredada) y, por lo tanto, es más rentable para la mayoría de los clientes. Las diferencias entre estas operaciones de DynamoDB son las siguientes:
  + La invocación de [PutItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html) para un elemento de 1 KB en una región y replicarlo en otras regiones requiere dos rWRU por región para la versión 2017.11.29 (heredada), pero solo un rWrU para la 2019.11.21 (actual).
  + La invocación de [UpdateItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateItem.html) para un elemento de 1 KB requiere dos rWRU en la región de origen y un rWrU por región de destino para la versión 2017.11.29 (heredada), pero solo un rWrU para las regiones de origen y destino para la 2019.11.21 (actual).
  + La invocación de [DeleteItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DeleteItem.html) para un elemento de 1 KB requiere un rWRU en la región de origen y dos rWrU por región de destino para la versión 2017.11.29 (heredada), pero solo un rWrU para las regiones de origen o destino para la 2019.11.21 (actual).

  La siguiente tabla muestra el consumo de rWRU de las tablas de la versión 2017.11.29 (heredada) y de la 2019.11.21 (actual) para un elemento de 1 KB en dos regiones.    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/V2globaltables_versions.html)
+ La versión 2017.11.29 (heredada) solo está disponible en 11 Regiones de AWS. Sin embargo, la versión 2019.11.21 (actual) está disponible en todas las Regiones de AWS.
+ Para crear tablas globales de la versión 2017.11.29 (heredada), primero debe crear un conjunto de tablas regionales vacías y, a continuación, invocar la API [CreateGlobalTable](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_CreateGlobalTable.html) para formar la tabla global. Para crear tablas globales de la versión 2019.11.21 (actual), invoque la API [UpdateTable](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTable.html) para agregar una réplica a una tabla regional existente.
+ La versión 2017.11.29 (heredada) requiere que se vacíen todas las réplicas de la tabla antes de agregar una réplica en una nueva región (incluso durante la creación). La versión 2019.11.21 (actual) permite agregar y eliminar réplicas de regiones de una tabla que ya contenga datos.
+ La versión 2017.11.29 (heredada) utiliza el siguiente conjunto dedicado de API de plano de control para administrar las réplicas:
  + [CreateGlobalTable](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_CreateGlobalTable.html)
  + [DescribeGlobalTable](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DescribeGlobalTable.html)
  + [DescribeGlobalTableSettings](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DescribeGlobalTableSettings.html)
  + [ListGlobalTables](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_ListGlobalTables.html)
  + [UpdateGlobalTable](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateGlobalTable.html)
  + [UpdateGlobalTableSettings](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateGlobalTableSettings.html)

  La versión 2019.11.21 (actual) usa las API [DescribeTable](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DescribeTable.html) y [UpdateTable](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTable.html) para administrar las réplicas.
+ La versión 2017.11.29 (heredada) publica dos registros de DynamoDB Streams para cada escritura. La versión 2019.11.21 (actual) solo publica un registro de DynamoDB Streams para cada escritura.
+ La versión 2017.11.29 (heredada) rellena y actualiza los atributos `aws:rep:deleting`, `aws:rep:updateregion` y `aws:rep:updatetime`. La versión 2019.11.21 (actual) no rellena ni actualiza estos atributos.
+ La versión 2017.11.29 (heredada) no sincroniza la configuración [Uso del período de vida (TTL) en DynamoDB](TTL.md) entre las réplicas. La versión 2019.11.21 (actual) sincroniza la configuración de TTL entre las réplicas.
+ La versión 2017.11.29 (heredada) no replica las eliminaciones de TTL en otras réplicas. La versión 2019.11.21 (actual) replica las eliminaciones de TTL en todas las réplicas.
+ La versión 2017.11.29 (heredada) no sincroniza la configuración del [escalado automático](AutoScaling.md) entre las réplicas. La versión 2019.11.21 (actual) sincroniza la configuración del escalado automático entre las réplicas.
+ La versión 2017.11.29 (heredada) no sincroniza la configuración del [índice secundario global (GSI)](GSI.md) entre las réplicas. La versión 2019.11.21 (actual) sincroniza la configuración de GSI entre las réplicas.
+ La versión 2017.11.29 (heredada) no sincroniza la configuración del [cifrado en reposo](encryption.usagenotes.md) entre las réplicas. La versión 2019.11.21 (actual) sincroniza la configuración del cifrado en reposo entre las réplicas.
+ La versión 2017.11.29 (heredada) publica la métrica `PendingReplicationCount`. La versión 2019.11.21 (actual) no publica esta métrica.

## Actualización a la versión actual
<a name="upgrading-to-current-version"></a>

### Permisos necesarios para actualizar las tablas globales
<a name="V2globaltables_versions.Notes-permissions"></a>

Para actualizar a la versión 2019.11.21 (actual), debe tener permisos `dynamodb:UpdateGlobalTableversion` en todas las regiones que tengan réplicas. Estos permisos se suman a los permisos necesarios para acceder a la consola de DynamoDB y ver las tablas.

La siguiente política de IAM concede permisos para actualizar cualquier tabla global a la versión 2019.11.21 (actual).

```
{
    "version": "2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "dynamodb:UpdateGlobalTableversion",
            "Resource": "*"
        }
    ]
}
```

La siguiente política de IAM concede permisos para actualizar solo la tabla global `Music`, con réplicas en dos regiones, a la versión 2019.11.21 (actual).

```
{
    "version": "2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "dynamodb:UpdateGlobalTableversion",
            "Resource": [
                "arn:aws:dynamodb::123456789012:global-table/Music",
                "arn:aws:dynamodb:ap-southeast-1:123456789012:table/Music",
                "arn:aws:dynamodb:us-east-2:123456789012:table/Music"
            ]
        }
    ]
}
```

### Qué esperar durante la actualización
<a name="V2GlobalTablesUpgradeExpectations"></a>
+ Todas las réplicas de tablas globales seguirán procesando el tráfico de lectura y escritura durante la actualización.
+ El proceso de actualización tarda entre unos minutos a varias horas, según el tamaño de la tabla y la cantidad de réplicas.
+ Durante el proceso de actualización, el valor de [TableStatus](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TableDescription.html#DDB-Type-TableDescription-TableStatus) cambiará de `ACTIVE` a `UPDATING`. Puede ver el estado de la tabla invocando la API [DescribeTable](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DescribeTable.html) o con la vista **Tablas** de la consola de DynamoDB.
+ El escalado automático no ajustará la configuración de capacidad aprovisionada para una tabla global mientras se esté actualizando la tabla. Le recomendamos encarecidamente que configure la tabla en el modo de capacidad [bajo demanda](capacity-mode.md#capacity-mode-on-demand) durante la actualización.
+ Si decide utilizar capacidad [aprovisionada](provisioned-capacity-mode.md) con el escalado automático durante la actualización, debe aumentar el rendimiento mínimo de lectura y escritura en las políticas para contemplar el aumento esperado de tráfico y evitar cualquier limitación durante la actualización.
+ La métrica `ReplicationLatency` puede informar temporalmente de picos de latencia o dejar de informar de datos métricos durante el proceso de actualización. Para obtener más información, consulte [ReplicationLatency](metrics-dimensions.md#ReplicationLatency). 
+ Cuando finaliza el proceso de actualización, el estado de la tabla cambia a `ACTIVE`.

### Comportamiento de DynamoDB Streams antes, durante y después de la actualización
<a name="V2GlobalTablesUpgradeDDBStreamsBehavior"></a>

[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/V2globaltables_versions.html)

### Actualización a la versión 2019.11.21 (actual)
<a name="V2globaltables_versions.upgrade"></a>

Siga estos pasos para actualizar la versión de las tablas globales de DynamoDB mediante la Consola de administración de AWS.

**Actualización de las tablas globales a la versión 2019.11.21 (actual)**

1. Abra la consola de DynamoDB en [https://console.aws.amazon.com/dynamodb/home](https://console.aws.amazon.com/dynamodb/home). 

1. En el panel de navegación del lado izquierdo de la consola, elija **Tablas** y, a continuación, seleccione la tabla global que desea actualizar a la versión 2019.11.21 (actual). 

1. Elija la pestaña **Global Tables (Tablas globales)**.

1. Elija **Update version (Actualizar versión)**.  
![\[Captura de pantalla de la consola que muestra el botón Update (Actualizar).\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/GlobalTables-upgrade.png)

1. Lea y acepte los nuevos requisitos y, a continuación, elija **Update version** (Actualizar versión).

1. Una vez completado el proceso de actualización, la versión de las tablas globales que aparece en la consola cambia a **2019.11.21**.

# Prácticas recomendadas para tablas globales
<a name="globaltables-bestpractices"></a>

En las siguientes secciones, se describen las prácticas recomendadas para implementar y utilizar las tablas globales.

## Versión
<a name="globaltables-bestpractices-version"></a>

Hay disponibles dos versiones de las tablas globales de DynamoDB: versión 2019.11.21 (actual) y [versión 2017.11.29 (antigua)](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/globaltables.V1.html). Debe utilizar la versión 2019.11.21 (actual) siempre que sea posible. 

## Protección contra eliminación
<a name="globaltables-bestpractices-deletionprotection"></a>

Debe habilitar la protección contra eliminación en las réplicas de tablas globales que desee proteger contra la eliminación accidental. Debe habilitar la protección contra la eliminación en cada réplica.

## Uso de AWS CloudFormation
<a name="globaltables-bestpractices-cloudformation"></a>

CloudFormation actualmente no admite la coordinación de recursos de varias regiones, como tablas globales entre pilas. Si define cada réplica de una tabla global en una pila regional independiente, se producirán errores debido a la desviación detectada entre las pilas al realizar actualizaciones de réplicas. Para evitar este problema, debe elegir una región como región de referencia para implementar las tablas globales y definir todas las réplicas de la tabla global en la pila de esa región.

**importante**  
No puede convertir un recurso de tipo `AWS::DynamoDB::Table` en un recurso de tipo `AWS::DynamoDB::GlobalTable` cambiando su tipo en la plantilla. Intentar convertir una tabla de una sola región en una tabla global cambiando el tipo de recurso de CloudFormation puede resultar en la eliminación de la tabla de DynamoDB.

Puede utilizar el recurso de `AWS::DynamoDB::GlobalTable` para crear una tabla en una sola región. Esta tabla se implementará como cualquier otra tabla de una sola región. Si posteriormente actualiza la pila para agregar otras regiones a un recurso, las réplicas se agregarán a la tabla y esta se convertirá de forma segura en una tabla global.

Si tiene un recurso de `AWS::DynamoDB::Table` existente que desea convertir en un recurso de `AWS::DynamoDB::GlobalTable`, los pasos recomendados para convertir el tipo de recurso son los siguientes:

1. Establezca la política de eliminación de `AWS::DynamoDB::Table` para retenerla.

1. Elimine la tabla de la definición de la pila.

1. Agregue réplicas a la tabla de una sola región en la consola de AWS y conviértala en una tabla global.

1. Importe la nueva tabla global como un nuevo recurso de `AWS::DynamoDB::GlobalTable` a la pila.

## Copias de seguridad y recuperación en un momento dado
<a name="globaltables-bestpractices-backups"></a>

La habilitación de las copias de seguridad automatizadas y la recuperación en un momento dado (PITR) para una réplica en una tabla global puede ser suficiente para cumplir los objetivos de recuperación ante desastres. Las copias de seguridad de réplicas creadas con copia de seguridad de AWS se pueden replicar automáticamente en todas las regiones para una mayor resiliencia. Tenga en cuenta los objetivos del plan de recuperación ante desastres en el contexto de la alta disponibilidad en varias regiones al elegir su estrategia de copia de seguridad y habilitación de PITR.

## Diseño para alta disponibilidad de varias regiones
<a name="globaltables-bestpractices-multiregion"></a>

Para obtener instrucciones prescriptivas sobre la implementación de tablas globales, consulte [Prácticas recomendadas para el diseño de tablas globales de DynamoDB](bp-global-table-design.md).

# Uso de elementos y atributos en DynamoDB
<a name="WorkingWithItems"></a>

En Amazon DynamoDB, *un elemento* es una colección de atributos. Cada atributo tiene un nombre y un valor. Los valores de los atributos pueden ser escalares, conjuntos o tipos de documentos. Para obtener más información, consulte [Funcionamiento de Amazon DynamoDB](HowItWorks.md).

DynamoDB proporciona cuatro operaciones que aportan la funcionalidad básica de creación, lectura, actualización y eliminación (CRUD). Todas estas operaciones son atómicas.
+ `PutItem`: permite crear un elemento.
+ `GetItem`: permite leer un elemento.
+ `UpdateItem`: permite actualizar un elemento.
+ `DeleteItem`: permite eliminar un elemento.

Cada una de estas operaciones requiere que especifique la clave principal del elemento que se va a usar. Por ejemplo, para leer un elemento mediante `GetItem`, debe especificar la clave de partición y la clave de ordenación (si procede) de ese elemento.

Además de las cuatro operaciones CRUD básicas, DynamoDB también ofrece las siguientes:
+ `BatchGetItem`: permite leer hasta 100 elementos de una o varias tablas.
+ `BatchWriteItem`: permite crear o eliminar hasta 25 elementos en una o varias tablas.

Estas operaciones por lotes combinan varias operaciones CRUD en una sola solicitud. Además, las operaciones por lotes leen y escriben los elementos en paralelo, para minimizar las latencias de respuesta.

Esta sección se describe cómo utilizar estas operaciones y se incluyen los temas relacionados, tales como las actualizaciones condicionales y los contadores atómicas. Además, se facilitan ejemplos de código en los que se utilizan los SDK de AWS. 

**Topics**
+ [Tamaños y formatos de elementos de DynamoDB](CapacityUnitCalculations.md)
+ [Lectura de un elemento](#WorkingWithItems.ReadingData)
+ [Escritura de un elemento](#WorkingWithItems.WritingData)
+ [Valores devueltos](#WorkingWithItems.ReturnValues)
+ [Operaciones por lotes](#WorkingWithItems.BatchOperations)
+ [Contadores atómicos](#WorkingWithItems.AtomicCounters)
+ [Escrituras condicionales](#WorkingWithItems.ConditionalUpdate)
+ [Uso de expresiones en DynamoDB](Expressions.md)
+ [Uso del período de vida (TTL) en DynamoDB](TTL.md)
+ [Consulta de tablas en DynamoDB](Query.md)
+ [Análisis de tablas en DynamoDB](Scan.md)
+ [PartiQL: un lenguaje de consulta compatible con SQL para Amazon DynamoDB](ql-reference.md)
+ [Uso de elementos: Java](JavaDocumentAPIItemCRUD.md)
+ [Uso de elementos: .NET](LowLevelDotNetItemCRUD.md)

# Tamaños y formatos de elementos de DynamoDB
<a name="CapacityUnitCalculations"></a>

Las tablas de DynamoDB no tienen esquemas, salvo la clave principal. Por este motivo, todos los elementos de una tabla pueden tener atributos, tamaños y tipos de datos distintos.

El tamaño total de un elemento es la suma de las longitudes de los nombres y los valores de sus atributos, además de cualquier gasto adicional aplicable, como se describe a continuación. Puede utilizar las siguientes directrices para calcular el tamaño de los atributos:
+ Los valores de tipo String son Unicode con codificación binaria UTF-8. El tamaño de una cadena es *(número de bytes con codificación UTF-8 del nombre de atributo) \$1 (número de bytes con codificación UTF-8)*.
+ Los números son de longitud variable, con un máximo de 38 dígitos significativos. Los ceros iniciales y finales se recortan. El tamaño aproximado de un número es *(número de bytes con codificación UTF-8 del nombre de atributo) \$1 (1 byte por cada dos dígitos significativos) \$1 (1 byte)*.
+ Un valor binario se debe codificar previamente en formato base64 para poder enviarlo a DynamoDB. Sin embargo, para calcular su tamaño se utiliza la longitud en bytes sin procesar del valor. El tamaño de un atributo binario es *(número de bytes con codificación UTF-8 del nombre de atributo) \$1 (número de bytes sin procesar)*.
+ El tamaño de un atributo nulo o booleano es *(número de bytes con codificación UTF-8 del nombre de atributo) \$1 (1 byte)*.
+ Un atributo de tipo `List` o `Map` requiere 3 bytes adicionales, independientemente de su contenido. El tamaño de un atributo `List` o `Map` es *(número de bytes con codificación UTF-8 del nombre de atributo) \$1 suma (tamaño de los elementos anidados) \$1 (3 bytes)*. El tamaño de un atributo `List` o `Map` vacío es *(número de bytes con codificación UTF-8 del nombre de atributo) \$1 (3 bytes)*.
+ Cada elemento `List` o `Map` también requiere 1 byte de sobrecarga.

**nota**  
Recomendamos elegir nombres de atributos cortos en lugar de largos. Esto le ayuda a reducir la cantidad de almacenamiento necesario, así como la cantidad de RCU/WCU que utiliza.

A efectos de facturación del almacenamiento, cada elemento incluye una capacidad de almacenamiento adicional por elemento que depende de las características que haya habilitado.
+ Todos los elementos de DynamoDB requieren 100 bytes de capacidad de almacenamiento adicional para la indexación.
+ Algunas características de DynamoDB (tablas globales, transacciones, captura de datos de cambios para Kinesis Data Streams con DynamoDB) requieren una capacidad de almacenamiento adicional para tener en cuenta los atributos creados por el sistema como resultado de la habilitación de dichas características. Por ejemplo, las tablas globales requieren 48 bytes adicionales de capacidad de almacenamiento.

## Lectura de un elemento
<a name="WorkingWithItems.ReadingData"></a>

Para leer un elemento de una tabla de DynamoDB, se utiliza la operación `GetItem`. Debe proporcionar el nombre de la tabla, así como la clave principal del elemento que se desea.

**Example**  
En el siguiente ejemplo de la AWS CLI se muestra cómo leer un elemento de la tabla `ProductCatalog`.  

```
aws dynamodb get-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"1"}}'
```

**nota**  
Con `GetItem`, es preciso especificar la clave principal *completa*, no solo una parte de ella. Por ejemplo, si una tabla contiene una clave principal compuesta (clave de partición y clave de ordenación), tendrá que proporcionar un valor para la clave de partición y un valor para la clave de ordenación.

De forma predeterminada, una solicitud `GetItem` lleva a cabo una lectura consistente final. Puede usar el parámetro `ConsistentRead` para solicitar una lectura de consistencia alta, si lo prefiere. (Esto consume unidades de capacidad de lectura adicionales, pero devuelve la versión más actualizada del elemento).

`GetItem` devuelve todos los atributos del elemento. Puede usar una *expresión de proyección* para devolver solamente algunos de los atributos. Para obtener más información, consulte [Uso de expresiones de proyección en DynamoDB](Expressions.ProjectionExpressions.md).

Para devolver el número de unidades de capacidad de lectura consumidas por `GetItem`, establezca el parámetro `ReturnConsumedCapacity` en `TOTAL`.

**Example**  
En el siguiente ejemplo de la AWS Command Line Interface (AWS CLI) se muestran algunos de los parámetros de `GetItem` opcionales.  

```
aws dynamodb get-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"1"}}' \
    --consistent-read \
    --projection-expression "Description, Price, RelatedItems" \
    --return-consumed-capacity TOTAL
```

## Escritura de un elemento
<a name="WorkingWithItems.WritingData"></a>

Para crear, actualizar o eliminar un elemento de una tabla de DynamoDB, utilice una de las siguientes operaciones:
+ `PutItem`
+ `UpdateItem`
+ `DeleteItem`

Para cada una de estas operaciones, debe especificar la clave principal completa, no solo parte de ella. Por ejemplo, si una tabla contiene una clave principal compuesta (clave de partición y clave de ordenación), tendrá que proporcionar un valor para la clave de partición y un valor para la clave de ordenación.

Para devolver el número de unidades de capacidad de escritura consumidas por cualquiera de estas operaciones, establezca el parámetro `ReturnConsumedCapacity` en uno de los valores siguientes: 
+ `TOTAL`: devuelve el número total de unidades de capacidad de escritura consumidas.
+ `INDEXES`: devuelve el número total de unidades de capacidad de escritura consumidas, con subtotales para la tabla y todos los índices secundarios que se hayan visto afectados por la operación.
+ `NONE`: no devuelve ningún dato de capacidad de escritura consumida. (Esta es la opción predeterminada.)

### PutItem
<a name="WorkingWithItems.WritingData.PutItem"></a>

`PutItem` crea un elemento nuevo. Si ya existe un elemento con la misma clave en la tabla, se sustituirá por el nuevo.

**Example**  
Escriba un nuevo elemento en la tabla `Thread`. La clave principal de `Thread` consta de `ForumName` (clave de partición) y `Subject` (clave de ordenación).  

```
aws dynamodb put-item \
    --table-name Thread \
    --item file://item.json
```
Los argumentos de `--item` se almacenan en el archivo `item.json`.  

```
{
    "ForumName": {"S": "Amazon DynamoDB"},
    "Subject": {"S": "New discussion thread"},
    "Message": {"S": "First post in this thread"},
    "LastPostedBy": {"S": "fred@example.com"},
    "LastPostDateTime": {"S": "201603190422"}
}
```

### UpdateItem
<a name="WorkingWithItems.WritingData.UpdateItem"></a>

Si no existe un elemento con la clave especificada, `UpdateItem` crea uno nuevo. De lo contrario, modifica los atributos de un elemento existente.

Se utiliza una *expresión de actualización* para especificar los atributos que se desea modificar y los nuevos valores. Para obtener más información, consulte [Uso de expresiones de actualización en DynamoDB](Expressions.UpdateExpressions.md). 

En la expresión de actualización, se utilizan valores de atributos de expresión como marcadores de posición de los valores reales. Para obtener más información, consulte [Uso de valores de atributos de expresión en DynamoDB](Expressions.ExpressionAttributeValues.md).

**Example**  
Modifique varios atributos del elemento `Thread`. El parámetro `ReturnValues` opcional muestra el elemento tal y como aparece después de la actualización. Para obtener más información, consulte [Valores devueltos](#WorkingWithItems.ReturnValues).  

```
aws dynamodb update-item \
    --table-name Thread \
    --key file://key.json \
    --update-expression "SET Answered = :zero, Replies = :zero, LastPostedBy = :lastpostedby" \
    --expression-attribute-values file://expression-attribute-values.json \
    --return-values ALL_NEW
```

Los argumentos de `--key` se almacenan en el archivo `key.json`.

```
{
    "ForumName": {"S": "Amazon DynamoDB"},
    "Subject": {"S": "New discussion thread"}
}
```

Los argumentos de `--expression-attribute-values` se almacenan en el archivo `expression-attribute-values.json`.

```
{
    ":zero": {"N":"0"},
    ":lastpostedby": {"S":"barney@example.com"}
}
```

### DeleteItem
<a name="WorkingWithItems.WritingData.DeleteItem"></a>

`DeleteItem` elimina el elemento con la clave especificada.

**Example**  
En el ejemplo siguiente de la AWS CLI, se muestra cómo eliminar el elemento `Thread`.  

```
aws dynamodb delete-item \
    --table-name Thread \
    --key file://key.json
```

## Valores devueltos
<a name="WorkingWithItems.ReturnValues"></a>

En algunos casos, es posible que desee que DynamoDB devuelva los valores de algunos atributos tal y como aparecen antes o después de modificarlos. Las operaciones `PutItem`, `UpdateItem` y `DeleteItem` tienen un parámetro `ReturnValues` que se puede usar para devolver los valores de los atributos antes o después de modificarlos.

El valor predeterminado de `ReturnValues` es `NONE`, es decir, DynamoDB no devuelve ninguna información sobre los atributos modificados. 

A continuación se indican la otra configuración válida de `ReturnValues`, organizados según la operación de la API de DynamoDB.

### PutItem
<a name="WorkingWithItems.ReturnValues.PutItem"></a>
+ `ReturnValues`: `ALL_OLD`
  + Si sobrescribe un elemento existente, `ALL_OLD` devuelve el elemento completo tal y como aparecía antes de sobrescribirlo.
  + Si escribe un elemento que no existía, `ALL_OLD` no surte efecto.

### UpdateItem
<a name="WorkingWithItems.ReturnValues.UpdateItem"></a>

Lo más frecuente es usar `UpdateItem` para actualizar un elemento existente. Sin embargo, `UpdateItem` en realidad lleva a cabo una operación *upsert* (actualización/inserción). Esto quiere decir que creará el elemento automáticamente si este no existe.
+ `ReturnValues`: `ALL_OLD`
  + Si actualiza un elemento existente, `ALL_OLD` devuelve el elemento completo tal y como aparecía antes de actualizarlo.
  + Si actualiza un elemento que no existía (upsert), `ALL_OLD` no surte efecto.
+ `ReturnValues`: `ALL_NEW`
  + Si actualiza un elemento existente, `ALL_NEW` devuelve el elemento completo tal y como aparece después de actualizarlo.
  + Si actualiza un elemento que no existía (upsert), `ALL_NEW` devuelve el elemento completo.
+ `ReturnValues`: `UPDATED_OLD`
  + Si actualiza un elemento existente, `UPDATED_OLD` devuelve solamente los atributos actualizados, tal y como aparecían antes de la actualización.
  + Si actualiza un elemento que no existía (upsert), `UPDATED_OLD` no surte efecto.
+ `ReturnValues`: `UPDATED_NEW`
  + Si actualiza un elemento existente, `UPDATED_NEW` devuelve solamente los atributos afectados, tal y como aparecen después de la actualización.
  + Si actualiza un elemento que no existía (upsert), `UPDATED_NEW` devuelve solamente los atributos actualizados, tal y como aparecen después de la actualización.

### DeleteItem
<a name="WorkingWithItems.ReturnValues.DeleteItem"></a>
+ `ReturnValues`: `ALL_OLD`
  + Si elimina un elemento existente, `ALL_OLD` devuelve el elemento completo tal y como aparecía antes de eliminarlo.
  + Si elimina un elemento que no existía, `ALL_OLD` no devuelve ningún dato.

## Operaciones por lotes
<a name="WorkingWithItems.BatchOperations"></a>

Para las aplicaciones que requieren leer o escribir varios elementos, DynamoDB proporciona las operaciones `BatchGetItem` y `BatchWriteItem`. El uso de estas operaciones puede reducir el número de recorridos de ida y vuelta a través de la red entre la aplicación y DynamoDB. Además, DynamoDB lleva a cabo las operaciones de lectura y escritura individuales en paralelo. Las aplicaciones se benefician de este procesamiento en paralelo sin tener que administrar la concurrencia ni los subprocesos.

En esencia, las operaciones por lotes son encapsuladores que incluyen varias solicitudes de lectura o escritura. Por ejemplo, si una solicitud `BatchGetItem` contiene cinco elementos, DynamoDB lleva a cabo cinco operaciones `GetItem`. De igual modo, si una solicitud `BatchWriteItem` contiene dos solicitudes de colocación y cuatro de eliminación, DynamoDB llevará a cabo dos solicitudes `PutItem` y cuatro solicitudes `DeleteItem`.

En general, una operación por lotes no genera un error a no ser que *todas* las solicitudes del lote generen un error. Por ejemplo, suponga que lleva a cabo una operación `BatchGetItem`, pero que se produce un error en una de las solicitudes `GetItem` individuales del lote. En este caso, `BatchGetItem` devolverá las claves y los datos de la solicitud `GetItem` en la que se ha producido el error. Las demás solicitudes `GetItem` del lote no se ven afectadas.

### BatchGetItem
<a name="WorkingWithItems.BatchOperations.BatchGetItem"></a>

Una sola operación `BatchGetItem` puede contener hasta 100 solicitudes `GetItem` individuales y recuperar hasta 16 MB de datos. Además, una operación `BatchGetItem` puede recuperar elementos de varias tablas.

**Example**  
Recupere dos elementos de la tabla `Thread` usando una expresión de proyección para devolver solo algunos de los atributos.  

```
aws dynamodb batch-get-item \
    --request-items file://request-items.json
```
Los argumentos de `--request-items` se almacenan en el archivo `request-items.json`.  

```
{
    "Thread": {
        "Keys": [
            {
                "ForumName":{"S": "Amazon DynamoDB"},
                "Subject":{"S": "DynamoDB Thread 1"}
            },
            {
                "ForumName":{"S": "Amazon S3"},
                "Subject":{"S": "S3 Thread 1"}
            }
        ],
        "ProjectionExpression":"ForumName, Subject, LastPostedDateTime, Replies"
    }
}
```

### BatchWriteItem
<a name="WorkingWithItems.BatchOperations.BatchWriteItem"></a>

La operación `BatchWriteItem` puede contener hasta 25 solicitudes `PutItem` y `DeleteItem` individuales y puede escribir hasta 16 MB de datos. (El tamaño máximo de un elemento individual es de 400 KB). Además, una operación `BatchWriteItem` puede colocar o eliminar elementos en varias tablas. 

**nota**  
`BatchWriteItem` no admite las solicitudes `UpdateItem`.

**Example**  
Escriba dos elementos en la tabla `ProductCatalog`.  

```
aws dynamodb batch-write-item \
    --request-items file://request-items.json
```
Los argumentos de `--request-items` se almacenan en el archivo `request-items.json`.  

```
{
    "ProductCatalog": [
        {
            "PutRequest": {
                "Item": {
                    "Id": { "N": "601" },
                    "Description": { "S": "Snowboard" },
                    "QuantityOnHand": { "N": "5" },
                    "Price": { "N": "100" }
                }
            }
        },
        {
            "PutRequest": {
                "Item": {
                    "Id": { "N": "602" },
                    "Description": { "S": "Snow shovel" }
                }
            }
        }
    ]
}
```

## Contadores atómicos
<a name="WorkingWithItems.AtomicCounters"></a>

Puede usar la operación `UpdateItem` para implementar un *contador atómico*. Se trata de un atributo numérico que se incrementa de forma incondicional y sin interferir con las demás solicitudes de escritura. Todas las solicitudes de escritura se aplican en el orden en que se reciben. Con un contador atómico, las actualizaciones no son idempotentes. Esto significa que el valor numérico aumenta o disminuye cada vez que se llame a `UpdateItem`. Si el valor de incremento que se usa para actualizar el contador atómico es positivo, se puede producir un recuento excesivo. Si el valor de incremento es negativo, se puede producir un recuento insuficiente.

Es posible utilizar un contador atómico para realizar el seguimiento del número de visitantes de un sitio web. En este caso, la aplicación incrementaría un valor numérico, independientemente del valor actual. En caso de error en la operación `UpdateItem`, la aplicación podría simplemente volver a intentar la operación. Aunque se correría el riesgo de actualizar dos veces el contador, seguramente sería tolerable un pequeño margen de error al alza o a la baja en el número de visitantes del sitio web.

Un contador atómico no sería adecuado en aquellos casos en que no fuese admisible un margen de error al alza o a la baja (por ejemplo, en una aplicación bancaria). En este caso, es más seguro utilizar una actualización condicional en lugar de un contador atómico.

Para obtener más información, consulte [Aumento y reducción de atributos numéricos](Expressions.UpdateExpressions.md#Expressions.UpdateExpressions.SET.IncrementAndDecrement).

**Example**  
En el siguiente ejemplo de la AWS CLI se incrementa el valor de `Price` de un producto en 5. En este ejemplo, se sabía que el elemento existía antes de que se actualizara el contador. Dado que `UpdateItem` no es idempotente, el valor de `Price` aumentará cada vez que se ejecute el código.   

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id": { "N": "601" }}' \
    --update-expression "SET Price = Price + :incr" \
    --expression-attribute-values '{":incr":{"N":"5"}}' \
    --return-values UPDATED_NEW
```

## Escrituras condicionales
<a name="WorkingWithItems.ConditionalUpdate"></a>

De forma predeterminada, las operaciones de escritura de DynamoDB (`PutItem`, `DeleteItem`) son *incondicionales*. Cada una de ellas sobrescribirá cualquier elemento existente que tenga la clave principal especificada.

Opcionalmente, DynamoDB admite las escrituras condicionales para estas operaciones. Una escritura condicional solamente se lleva a cabo si los atributos del elemento cumplen una o varias de las condiciones esperadas. En caso contrario, devuelve un error.

En las escrituras condicionales se comprueban las condiciones con la versión actualizada más reciente del elemento. Tenga en cuenta que si el elemento no existía anteriormente o si la operación más reciente que se realizó de forma correcta con ese elemento fue eliminarlo, la escritura condicional no encontrará ningún elemento anterior.

 Las escrituras condicionales resultan útiles en muchas situaciones. Por ejemplo, puede ser conveniente que una operación `PutItem` solamente se lleve a cabo si no existe ningún elemento que tenga la misma clave principal. O puede que desee impedir que una operación `UpdateItem` modifique un elemento si uno de sus atributos tiene un valor determinado.

Las escrituras condicionales son útiles en aquellos casos en que varios usuarios intentan modificar el mismo elemento. Fíjese en el siguiente diagrama, en el que dos usuarios (Alice y Bob) trabajan con el mismo elemento de una tabla de DynamoDB.

![\[Los usuarios Alice y Bob intentan modificar un elemento con el identificador 1, lo que demuestra la necesidad de escrituras condicionales.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/update-no-condition.png)


Suponga que Alice utiliza la AWS CLI para actualizar el atributo `Price` a 8.

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"1"}}' \
    --update-expression "SET Price = :newval" \
    --expression-attribute-values file://expression-attribute-values.json
```

Los argumentos de `--expression-attribute-values` se almacenan en el archivo `expression-attribute-values.json`:

```
{
    ":newval":{"N":"8"}
}
```

Ahora, supongamos que Bob emite una solicitud `UpdateItem` parecida más adelante, pero cambia el valor de `Price` a 12. Para Bob, el parámetro `--expression-attribute-values` tendrá el siguiente aspecto:

```
{
    ":newval":{"N":"12"}
}
```

Bob la solicitud de Bob se lleva a cabo, pero se pierde la actualización previa de Alice.

Para solicitar una operación `PutItem`, `DeleteItem` o `UpdateItem` condicional, debe especificar una expresión de condición. Una *expresión de condición* es una cadena que contiene nombres de atributos, operadores condicionales y funciones integradas. La totalidad de expresión debe evaluarse en true. De lo contrario, la operación no se llevará a cabo correctamente.

Ahora, fíjese en el siguiente diagrama, en el que se muestra que el uso de escrituras condicionales impediría que la actualización de Alice se sobrescribiese.

![\[La escritura condicional impide que la actualización del usuario Bob sobrescriba el cambio de la usuaria Alice en el mismo elemento.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/update-yes-condition.png)


Alice intenta actualizar el valor de `Price` a 8, pero solamente si el valor de `Price` actual es 10.

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"1"}}' \
    --update-expression "SET Price = :newval" \
    --condition-expression "Price = :currval" \
    --expression-attribute-values file://expression-attribute-values.json
```

Los argumentos de `--expression-attribute-values` se almacenan en el archivo `expression-attribute-values.json`.

```
{
    ":newval":{"N":"8"},
    ":currval":{"N":"10"}
}
```

La actualización de Alice se lleva a cabo correctamente porque el resultado de evaluar la condición es true.

A continuación, Bob intenta actualizar el valor de `Price` a 12, pero solamente si el valor de `Price` actual es 10. Para Bob, el parámetro `--expression-attribute-values` tendrá el siguiente aspecto:

```
{
    ":newval":{"N":"12"},
    ":currval":{"N":"10"}
}
```

Dado que Alice ha cambiado previamente el valor de `Price` a 8, la expresión de condición se evalúa en false, de modo que la actualización de Bob no se lleva a cabo.

Para obtener más información, consulte [Ejemplo de la CLI de expresión de condición de DynamoDB](Expressions.ConditionExpressions.md).

### Idempotencia de las escrituras condicionales
<a name="WorkingWithItems.ConditionalWrites.Idempotence"></a>

Las escrituras condicionales pueden ser *idempotentes* si la comprobación adicional está en el mismo atributo que se va a actualizar. Esto significa que DynamoDB solo realiza una determinada solicitud de escritura si determinados valores de atributo del elemento coinciden con lo que se espera que sean en el momento de la solicitud. 

Por ejemplo, suponga que emite una solicitud `UpdateItem` para aumentar el valor de `Price` de un elemento en 3, pero solamente si el valor de `Price` actual es 20. Después de enviar la solicitud, pero antes de recibir su resultado, se produce un error en la red y usted no sabe si la solicitud se ha realizado correctamente. Como esta escritura condicional es idempotente, puede reintentar la misma solicitud `UpdateItem` y DynamoDB actualiza el elemento solamente si el `Price` de actual es 20.

### Unidades de capacidad consumidas por las escrituras condicionales
<a name="WorkingWithItems.ConditionalWrites.ReturnConsumedCapacity"></a>

Aunque el resultado de evaluar una expresión `ConditionExpression` sea false durante una escritura condicional, DynamoDB consume capacidad de escritura de la tabla. La cantidad consumida depende del tamaño del elemento existente (o de un mínimo de 1). Por ejemplo, si el tamaño de un elemento existente es de 300 KB y el del nuevo elemento que intenta crear o actualizar es de 310 KB, las unidades de capacidad de escritura consumidas serán 300 si falla la condición y 310 si se realiza correctamente. Si se trata de un elemento nuevo (no existente), las unidades de capacidad de escritura consumidas serán 1 si la condición falla y 310 si la condición se realiza correctamente.

**nota**  
Las operaciones de *escritura* solo consumen unidades de capacidad de escritura. Nunca consumen unidades de capacidad de *lectura*.

Una escritura condicional que no se ha llevado a cabo devuelve la excepción `ConditionalCheckFailedException`. Cuando esto ocurre, no se recibe ninguna información en la respuesta sobre la capacidad de escritura que se ha consumido.

Para devolver el número de unidades de capacidad de escritura consumidas durante una escritura condicional, se usa el parámetro `ReturnConsumedCapacity`:
+ `TOTAL`: devuelve el número total de unidades de capacidad de escritura consumidas.
+ `INDEXES`: devuelve el número total de unidades de capacidad de escritura consumidas, con subtotales para la tabla y todos los índices secundarios que se hayan visto afectados por la operación.
+ `NONE`: no devuelve ningún dato de capacidad de escritura consumida. (Esta es la opción predeterminada.)

  

**nota**  
A diferencia de un índice secundario global, un índice secundario local comparte su capacidad de rendimiento aprovisionada con su tabla. La actividad de lectura y escritura en un índice secundario local consume capacidad de rendimiento aprovisionada de la tabla.

# Uso de expresiones en DynamoDB
<a name="Expressions"></a>

En Amazon DynamoDB, puede usar *expresiones* para especificar qué atributos leer de un elemento, escribir datos cuando se cumple una condición, especificar cómo actualizar un elemento, definir consultas y filtrar los resultados de una consulta.

En esta tabla se describen los aspectos gramaticales básicos de las expresiones y los tipos de expresiones disponibles.


| Tipo de expresión | Descripción | 
| --- | --- | 
| Expresión de proyección | Una expresión de proyección identifica los atributos que se desean recuperar de un elemento cuando se utilizan operaciones como GetItem, Query o Scan. | 
| Expresión de condición | Una expresión de condición determina qué elementos deben modificarse al utilizar las operaciones PutItem, UpdateItem y DeleteItem. | 
| Expresión de actualización | Una expresión de actualización especifica cómo UpdateItem modificará los atributos de un elemento; por ejemplo, estableciendo un valor escalar o eliminando elementos de una lista o un mapa. | 
| Expresión de condición de clave | Una expresión de condición clave determina qué elementos leerá una consulta de una tabla o índice. | 
| Expresión de filtro | Una expresión de filtro determina qué elementos entre los resultados de consulta se deben devolver al usuario. Todos los demás resultados se descartan. | 

Para obtener información sobre la sintaxis de expresión e información más detallada sobre cada tipo de expresión, consulte las secciones siguientes.

**Topics**
+ [Referencia a atributos de elementos mediante expresiones en DynamoDB](Expressions.Attributes.md)
+ [Nombres de atributos de expresión (Alias) en DynamoDB](Expressions.ExpressionAttributeNames.md)
+ [Uso de valores de atributos de expresión en DynamoDB](Expressions.ExpressionAttributeValues.md)
+ [Uso de expresiones de proyección en DynamoDB](Expressions.ProjectionExpressions.md)
+ [Uso de expresiones de actualización en DynamoDB](Expressions.UpdateExpressions.md)
+ [Expresiones, operadores y funciones de condición y filtro en DynamoDB](Expressions.OperatorsAndFunctions.md)
+ [Ejemplo de la CLI de expresión de condición de DynamoDB](Expressions.ConditionExpressions.md)

**nota**  
Con el fin de ofrecer compatibilidad retroactiva, DynamoDB también admite parámetros condicionales que no utilizan expresiones. Para obtener más información, consulte [Parámetros condicionales de DynamoDB heredados](LegacyConditionalParameters.md).  
Las nuevas aplicaciones deben utilizar expresiones en lugar de los parámetros heredados.

# Referencia a atributos de elementos mediante expresiones en DynamoDB
<a name="Expressions.Attributes"></a>

En esta sección se describe cómo consultar los atributos de los elementos en una expresión en Amazon DynamoDB. Puede utilizar cualquier atributo, aunque se encuentre anidado profundamente en varias listas y mapas.

**Topics**
+ [Atributos de nivel superior](#Expressions.Attributes.TopLevelAttributes)
+ [Atributos anidados](#Expressions.Attributes.NestedAttributes)
+ [Rutas de documento](#Expressions.Attributes.NestedElements.DocumentPathExamples)

**Ejemplo de elemento: ProductCatalog**  
Los ejemplos de esta página utilizan el siguiente elemento de muestra de la tabla de `ProductCatalog`. Esta tabla se describe en . [Tablas y datos de ejemplo para usar en DynamoDB](AppendixSampleTables.md).)

```
{
    "Id": 123,
    "Title": "Bicycle 123",
    "Description": "123 description",
    "BicycleType": "Hybrid",
    "Brand": "Brand-Company C",
    "Price": 500,
    "Color": ["Red", "Black"],
    "ProductCategory": "Bicycle",
    "InStock": true,
    "QuantityOnHand": null,
    "RelatedItems": [
        341,
        472,
        649
    ],
    "Pictures": {
        "FrontView": "http://example.com/products/123_front.jpg",
        "RearView": "http://example.com/products/123_rear.jpg",
        "SideView": "http://example.com/products/123_left_side.jpg"
    },
    "ProductReviews": {
	    "FiveStar": [
	    		"Excellent! Can't recommend it highly enough! Buy it!",
	    		"Do yourself a favor and buy this."
	    ],
	    "OneStar": [
	    		"Terrible product! Do not buy this."
	    ]
    },
    "Comment": "This product sells out quickly during the summer",
    "Safety.Warning": "Always wear a helmet"
 }
```

Tenga en cuenta lo siguiente:
+ El valor de la clave de partición (`Id`) es `123`. No hay clave de ordenación.
+ Los tipos de datos de la mayoría de los atributos son escalares, tales como `String`, `Number`, `Boolean` y `Null`.
+ Un atributo (`Color`) es de tipo `String Set`.
+ Los siguientes atributos tienen tipos de datos de documento:
  + Una lista de `RelatedItems`. Cada componente es un `Id` de un producto relacionado.
  + Mapa de imágenes () `Pictures`. Cada componente es una descripción breve de una imagen, junto con una dirección URL del archivo de imagen correspondiente.
  + Mapa de imágenes () `ProductReviews`. Cada componente representa una clasificación y una lista de opiniones correspondientes a esa clasificación. Inicialmente, este mapa se rellena con opiniones de cinco estrellas y una estrella.

## Atributos de nivel superior
<a name="Expressions.Attributes.TopLevelAttributes"></a>

Se considera que un atributo es de *nivel superior* si no está integrado en otro atributo. En el elemento `ProductCatalog`, los atributos de nivel superior son:
+ `Id`
+ `Title`
+ `Description`
+ `BicycleType`
+ `Brand`
+ `Price`
+ `Color`
+ `ProductCategory`
+ `InStock`
+ `QuantityOnHand`
+ `RelatedItems`
+ `Pictures`
+ `ProductReviews`
+ `Comment`
+ `Safety.Warning`

Todos estos atributos de nivel superior son escalares, con la salvedad de `Color` (lista), `RelatedItems` (lista), `Pictures` (mapa) y `ProductReviews` (mapa).

## Atributos anidados
<a name="Expressions.Attributes.NestedAttributes"></a>

Se considera que un atributo es *anidado* si está integrado en otro atributo. Para obtener acceso a un atributo anidado, se utilizan los *operadores de desreferenciación*:
+ `[n]`: para elementos de lista
+ `.` (punto): para elementos de mapa

### Acceso a los elementos de la lista
<a name="Expressions.Attributes.NestedElements.AccessingListElements"></a>

El operador de desreferencia de un elemento de la lista es **[*N*]**, donde *n* es el número del elemento. Las entradas de lista están basadas en cero; es decir, [0] representa la primera entrada de la lista, [1] representa la segunda, y así sucesivamente. Estos son algunos ejemplos:
+ `MyList[0]`
+ `AnotherList[12]`
+ `ThisList[5][11]`

La entrada `ThisList[5]` es una lista anidada en sí misma. Por consiguiente, `ThisList[5][11]` se refiere al duodécimo componente de esa lista.

El número contenido entre corchetes debe ser un entero no negativo. Por lo tanto, las siguientes expresiones no son válidas:
+ `MyList[-1]`
+ `MyList[0.4]`

### Acceso a los elementos de un mapa
<a name="Expressions.Attributes.NestedElements.AccessingMapElements"></a>

El operador de desreferenciación de una entrada de un mapa es **. ** (un punto). Utilice el punto como separador entre las entradas de un mapa:
+ `MyMap.nestedField`
+ `MyMap.nestedField.deeplyNestedField`

## Rutas de documento
<a name="Expressions.Attributes.NestedElements.DocumentPathExamples"></a>

En una expresión, se utiliza una *ruta de documento* para indicar a DynamoDB dónde se encuentra un atributo. En el caso de un atributo de nivel superior, la ruta de documento es el nombre de atributo. En el caso de un atributo anidado, se utilizan operadores de desreferenciación para construir la ruta de documento.

A continuación se indican algunos ejemplos de rutas de documentos. Consulte el elemento mostrado en . [Referencia a atributos de elementos mediante expresiones en DynamoDB](#Expressions.Attributes).)
+ Atributo escalar de nivel superior.

   `Description`
+ Atributo de lista de nivel superior. (Devuelve la lista completa, no solo algunos de los componentes).

  `RelatedItems`
+ Tercera entrada de la lista `RelatedItems`. Recuerde que las entradas de lista se basan en cero.

  `RelatedItems[2]`
+ Imagen frontal del producto.

  `Pictures.FrontView`
+ Todas las opiniones de cinco estrellas.

  `ProductReviews.FiveStar`
+ Primera de las opiniones de cinco estrellas.

  `ProductReviews.FiveStar[0]`

**nota**  
La profundidad máxima de una ruta de documento es 32. Por lo tanto, el número de operadores de desreferenciación de una ruta no puede superar este límite.

Puede utilizar cualquier nombre de atributo en la ruta de un documento siempre que cumpla estos requisitos:
+ El primer carácter es `a-z`, `A-Z` o `0-9`
+ El segundo carácter (si está presente) es `a-z` o `A-Z`

**nota**  
Si un nombre de atributo no cumple este requisito, tendrá que definir un nombre de atributo de expresión como marcador de posición.

Para obtener más información, consulte [Nombres de atributos de expresión (Alias) en DynamoDB](Expressions.ExpressionAttributeNames.md).

# Nombres de atributos de expresión (Alias) en DynamoDB
<a name="Expressions.ExpressionAttributeNames"></a>

Un *nombre de atributo de expresión* es un alias (o marcador de posición) que se utiliza en una expresión de Amazon DynamoDB en lugar del nombre de atributo real. Un nombre de atributo de expresión debe comenzar por un signo de almohadilla (`#`) y debe ir seguido de uno o más caracteres alfanuméricos. También se permite el carácter de subrayado (`_`).

En esta sección se describen varias situaciones en las que debe utilizar nombres de atributos de expresión.

**nota**  
En los ejemplos de esta sección se utiliza la AWS Command Line Interface (AWS CLI). 

**Topics**
+ [Palabras reservadas](#Expressions.ExpressionAttributeNames.ReservedWords)
+ [Nombres de atributo que contienen caracteres especiales](#Expressions.ExpressionAttributeNames.AttributeNamesContainingSpecialCharacters)
+ [Atributos anidados](#Expressions.ExpressionAttributeNames.NestedAttributes)
+ [Hacer referencia repetidamente a nombres de atributos](#Expressions.ExpressionAttributeNames.RepeatingAttributeNames)

## Palabras reservadas
<a name="Expressions.ExpressionAttributeNames.ReservedWords"></a>

En algunas ocasiones, es posible que necesite escribir una expresión que contenga un nombre de atributo que entre en conflicto con una palabra reservada de DynamoDB. Para obtener una lista completa de palabras reservadas, consulte . [Palabras reservadas en DynamoDB](ReservedWords.md).)

Por ejemplo, el siguiente ejemplo de la AWS CLI no funcionaría correctamente porque `COMMENT` es una palabra reservada,

```
aws dynamodb get-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"123"}}' \
    --projection-expression "Comment"
```

Para solucionar este problema, puede sustituir `Comment` por un nombre de atributo de expresión; por ejemplo, `#c`. El símbolo de almohadilla (`#`) es obligatorio e indica que se trata de un marcador de posición del nombre de un atributo. Ahora, el ejemplo de la AWS CLI tendría el siguiente aspecto.

```
aws dynamodb get-item \
     --table-name ProductCatalog \
     --key '{"Id":{"N":"123"}}' \
     --projection-expression "#c" \
     --expression-attribute-names '{"#c":"Comment"}'
```

**nota**  
Si un nombre de atributo comienza por un número, contiene un espacio o contiene una palabra reservada, *debe* utilizar un nombre de atributo de expresión para reemplazar el nombre de ese atributo en la expresión.

## Nombres de atributo que contienen caracteres especiales
<a name="Expressions.ExpressionAttributeNames.AttributeNamesContainingSpecialCharacters"></a>

En una expresión, un punto (".") se interpreta como un carácter separador en una ruta de documento. No obstante, DynamoDB también le permite utilizar un carácter de punto y otros caracteres especiales, como un guion ("-") como parte de un nombre de atributo. En algunos casos, esto puede dar lugar a ambigüedades. A modo de ejemplo, supongamos que desea recuperar el atributo `Safety.Warning` de un elemento `ProductCatalog` (consulte [Referencia a atributos de elementos mediante expresiones en DynamoDB](Expressions.Attributes.md)).

Supongamos que desea obtener acceso a `Safety.Warning` mediante una expresión de proyección.

```
aws dynamodb get-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"123"}}' \
    --projection-expression "Safety.Warning"
```

DynamoDB podría devolver un resultado vacío, en lugar de la cadena prevista ("`Always wear a helmet`"). El motivo es que DynamoDB interpreta el punto en una expresión como un separador de ruta de documento. En este caso, tiene que definir un nombre de atributo de expresión (por ejemplo, `#sw`) como sustituto de `Safety.Warning`. A continuación, podría utilizar la siguiente expresión de proyección.

```
aws dynamodb get-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"123"}}' \
    --projection-expression "#sw" \
    --expression-attribute-names '{"#sw":"Safety.Warning"}'
```

Ahora, DynamoDB devolvería el resultado correcto.

**nota**  
Si un nombre de atributo contiene un punto (".") o un guion ("-"), *debe* utilizar un nombre de atributo de expresión para reemplazar el nombre de ese atributo en la expresión.

## Atributos anidados
<a name="Expressions.ExpressionAttributeNames.NestedAttributes"></a>

Suponga que desea acceder al atributo anidado `ProductReviews.OneStar`. En el nombre de un atributo de expresión, DynamoDB trata el punto (".") como un carácter del nombre del atributo. Para hacer referencia al atributo anidado, defina un nombre de atributo de expresión para cada elemento de la ruta del documento:
+ `#pr — ProductReviews`
+ `#1star — OneStar`

A continuación, habría que usar `#pr.#1star` para la expresión de proyección.

```
aws dynamodb get-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"123"}}' \
    --projection-expression "#pr.#1star"  \
    --expression-attribute-names '{"#pr":"ProductReviews", "#1star":"OneStar"}'
```

Ahora, DynamoDB devolvería el resultado correcto.

## Hacer referencia repetidamente a nombres de atributos
<a name="Expressions.ExpressionAttributeNames.RepeatingAttributeNames"></a>

Expresión los nombres de atributos de expresión resultan útiles cuando es preciso consultar repetidamente el mismo nombre de atributo. Por ejemplo, tomemos la siguiente expresión para recuperar algunas de las opiniones de un elemento de `ProductCatalog`.

```
aws dynamodb get-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"123"}}' \
    --projection-expression "ProductReviews.FiveStar, ProductReviews.ThreeStar, ProductReviews.OneStar"
```

Para que resulte más concisa, puede sustituir `ProductReviews` por un nombre de atributo de expresión, como `#pr`. Ahora, la expresión revisada tendría el siguiente aspecto.
+  `#pr.FiveStar, #pr.ThreeStar, #pr.OneStar` 

```
aws dynamodb get-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"123"}}' \
    --projection-expression "#pr.FiveStar, #pr.ThreeStar, #pr.OneStar" \
    --expression-attribute-names '{"#pr":"ProductReviews"}'
```

Si define un nombre de atributo de expresión, debe usarlo de forma coherente en toda la expresión. Además, es importante no omitir el signo `#`. 

# Uso de valores de atributos de expresión en DynamoDB
<a name="Expressions.ExpressionAttributeValues"></a>

*Los valores de los atributos de expresión* de Amazon DynamoDB actúan como variables. Reemplazan a los valores reales que se desea comparar, que podrían no conocerse hasta el tiempo de ejecución. Un valor de atributo de expresión debe comenzar por un signo de dos puntos (`:`) y debe ir seguido de uno o más caracteres alfanuméricos.

Por ejemplo, supongamos que desea devolver todos los elementos de `ProductCatalog`que estén disponibles en el color `Black` y tengan un precio de `500` o menos. Podría utilizar una operación `Scan` con una expresión de filtro, como en este ejemplo de la AWS Command Line Interface (AWS CLI).

```
aws dynamodb scan \
    --table-name ProductCatalog \
    --filter-expression "contains(Color, :c) and Price <= :p" \
    --expression-attribute-values file://values.json
```

Los argumentos de `--expression-attribute-values` se almacenan en el archivo `values.json`.

```
{
    ":c": { "S": "Black" },
    ":p": { "N": "500" }
}
```

Si define un valor de atributo de expresión, debe usarlo de forma coherente en toda la expresión. Además, es importante no omitir el signo `:`. 

Los valores de atributos de expresión se usan con expresiones de condición de clave, expresiones de condición, expresiones de actualización y expresiones de filtro.

# Uso de expresiones de proyección en DynamoDB
<a name="Expressions.ProjectionExpressions"></a>

Para leer datos de una tabla, se utilizan operaciones tales como `GetItem`, `Query` o `Scan`. De forma predeterminada, Amazon DynamoDB devuelve todos los atributos de los elementos. Si desea obtener solo uno en lugar de todos ellos, debe usar una expresión de proyección.

Una *expresión de proyección* es una cadena que identifica los atributos que se desea. Para recuperar un solo atributo, especifique su nombre. Si desea obtener varios atributos, separe sus nombres mediante comas.

A continuación se muestran ejemplos de expresiones de proyección basadas en el elemento `ProductCatalog` de [Referencia a atributos de elementos mediante expresiones en DynamoDB](Expressions.Attributes.md):
+ Un solo atributo de nivel superior.

  `Title `
+ Tres atributos de nivel superior. DynamoDB recupera todo el conjunto `Color`.

  `Title, Price, Color`
+ Cuatro atributos de nivel superior. DynamoDB devolverá todo el contenido de `RelatedItems` y `ProductReviews`.

  `Title, Description, RelatedItems, ProductReviews`

**nota**  
La expresión de proyección no modifica el consumo de rendimiento aprovisionado. DynamoDB determina las unidades de capacidad consumidas según el tamaño de los elementos y no según la cantidad de datos que se devuelven a una aplicación.

**Palabras reservadas y caracteres especiales**

DynamoDB tiene palabras reservadas y caracteres especiales. DynamoDB le permite utilizar estas palabras reservadas y caracteres especiales para nombres, pero le recomendamos que evite hacerlo, porque tendría que usar alias para ellos cada vez que utilizase estos nombres en una expresión. Para ver una lista completa, consulte [Palabras reservadas en DynamoDB](ReservedWords.md).

Deberá usar los nombres de los atributos de la expresión en lugar del nombre real si: 
+ El nombre del atributo está en la lista de palabras reservadas en DynamoDB.
+ El nombre del atributo no cumple el requisito de que el primer carácter sea `a-z` o `A-Z` y que el segundo carácter (si lo hay) sea `a-Z`, `A-Z` o `0-9`.
+ El nombre del atributo contiene un signo **\$1** (hash) o **:** (dos puntos).

En el ejemplo de la AWS CLI siguiente se muestra cómo usar una expresión de proyección con una operación `GetItem`. La expresión de proyección recupera un atributo escalar de nivel superior (`Description`), la primera entrada de una lista (`RelatedItems[0]`) y una lista anidada en un mapa (`ProductReviews.FiveStar`).

```
aws dynamodb get-item \
    --table-name ProductCatalog \
    --key '"Id": { "N": "123" } \
    --projection-expression "Description, RelatedItems[0], ProductReviews.FiveStar"
```

Para este ejemplo, se devolvería el siguiente código JSON.

```
{
    "Item": {
        "Description": {
            "S": "123 description"
        },
        "ProductReviews": {
            "M": {
                "FiveStar": {
                    "L": [
                        {
                            "S": "Excellent! Can't recommend it highly enough! Buy it!"
                        },
                        {
                            "S": "Do yourself a favor and buy this."
                        }
                    ]
                }
            }
        },
        "RelatedItems": {
            "L": [
                {
                    "N": "341"
                }
            ]
        }
    }
}
```

# Uso de expresiones de actualización en DynamoDB
<a name="Expressions.UpdateExpressions"></a>

La acción `UpdateItem` actualiza un elemento existente o agrega uno nuevo a la tabla, si no existe ya. Es preciso proporcionar la clave del elemento que se desea actualizar. Asimismo, debe proporcionar una expresión de actualización que indique los atributos que se van a modificar y los valores que se les asignarán. 

Una *expresión de actualización* especifica cómo `UpdateItem` modificará los atributos de un elemento; por ejemplo, estableciendo un valor escalar o eliminando elementos de una lista o un mapa.

A continuación se muestra un resumen de la sintaxis de las expresiones de actualización.

```
update-expression ::=
    [ SET action [, action] ... ]
    [ REMOVE action [, action] ...]
    [ ADD action [, action] ... ]
    [ DELETE action [, action] ...]
```

Una expresión de actualización consta de una o varias cláusulas. Cada cláusula comienza con una palabra clave `SET`, `REMOVE`, `ADD` o `DELETE`. Puede incluir cualquiera de estas cláusulas en una expresión de actualización, en cualquier orden. Sin embargo, cada palabra clave de acción solo puede aparecer una vez.

Cada cláusula contiene una o más acciones, separadas por comas. Cada acción representa una modificación de datos.

Los ejemplos que aparecen en esta sección se basan en el elemento `ProductCatalog` que se muestra en [Uso de expresiones de proyección en DynamoDB](Expressions.ProjectionExpressions.md).

En los temas siguientes se describen algunos casos de uso diferentes de la acción `SET`.

**Topics**
+ [SET: modificación o adición de atributos de elemento](#Expressions.UpdateExpressions.SET)
+ [REMOVE: eliminación de atributos de un elemento](#Expressions.UpdateExpressions.REMOVE)
+ [ADD: actualización de números y conjuntos](#Expressions.UpdateExpressions.ADD)
+ [DELETE: eliminación de elementos de un conjunto](#Expressions.UpdateExpressions.DELETE)
+ [Uso de varias expresiones de actualización](#Expressions.UpdateExpressions.Multiple)

## SET: modificación o adición de atributos de elemento
<a name="Expressions.UpdateExpressions.SET"></a>

Utilice la acción `SET` en una expresión de actualización para agregar uno o varios atributos a un elemento. Si cualquiera de estos atributos ya existe, se sobrescribirá con los nuevos valores. Si desea evitar sobrescribir un atributo existente, puede utilizar `SET` con la función `if_not_exists`. La función `if_not_exists` es específica de la acción `SET` y solamente se puede utilizar en una expresión de actualización.

Cuando se utiliza `SET` para actualizar una entrada de lista, el contenido de esa entrada se sustituye por los nuevos datos que ha especificado. Si el componente no existe, `SET` adjunta el nuevo componente al final de la lista.

Si agrega varias entradas en una misma operación `SET`, las entradas se ordenan según su número.

También puede utilizar `SET` para sumar o restar un valor de un atributo de tipo `Number`. Para llevar a cabo varias acciones `SET`, debe separarlas por comas.

En el resumen de sintaxis siguiente:
+ El componente *path* es la ruta de documento del elemento.
+ Un componente **operand** puede ser una ruta de documento a un elemento o una función.

```
set-action ::=
    path = value

value ::=
    operand
    | operand '+' operand
    | operand '-' operand

operand ::=
    path | function

function ::=
    if_not_exists (path, value)
```

Si el elemento no contiene un atributo en la ruta especificada, `if_not_exists` se evalúa en `value`. De lo contrario, se evalúa como `path`.

La siguiente operación `PutItem` crea un elemento de muestra al que se refieren los ejemplos.

```
aws dynamodb put-item \
    --table-name ProductCatalog \
    --item file://item.json
```

Los argumentos de `--item` se almacenan en el archivo `item.json`. Para simplificar, se utilizan tan solo algunos de los atributos de elementos.

```
{
    "Id": {"N": "789"},
    "ProductCategory": {"S": "Home Improvement"},
    "Price": {"N": "52"},
    "InStock": {"BOOL": true},
    "Brand": {"S": "Acme"}
}
```

**Topics**
+ [Modificación de atributos](#Expressions.UpdateExpressions.SET.ModifyingAttributes)
+ [Adición de listas y mapas](#Expressions.UpdateExpressions.SET.AddingListsAndMaps)
+ [Adición de elementos a una lista](#Expressions.UpdateExpressions.SET.AddingListElements)
+ [Adición de atributos de mapa anidados](#Expressions.UpdateExpressions.SET.AddingNestedMapAttributes)
+ [Aumento y reducción de atributos numéricos](#Expressions.UpdateExpressions.SET.IncrementAndDecrement)
+ [Adición de elementos a una lista](#Expressions.UpdateExpressions.SET.UpdatingListElements)
+ [Cómo evitar sobrescribir un atributo existente](#Expressions.UpdateExpressions.SET.PreventingAttributeOverwrites)

### Modificación de atributos
<a name="Expressions.UpdateExpressions.SET.ModifyingAttributes"></a>

**Example**  
Actualice los atributos `ProductCategory` y `Price`.  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET ProductCategory = :c, Price = :p" \
    --expression-attribute-values file://values.json \
    --return-values ALL_NEW
```
Los argumentos de `--expression-attribute-values` se almacenan en el archivo `values.json`.  

```
{
    ":c": { "S": "Hardware" },
    ":p": { "N": "60" }
}
```

**nota**  
En la operación `UpdateItem`, `--return-values ALL_NEW` hace que DynamoDB devuelva el elemento tal y como aparece después de la actualización.

### Adición de listas y mapas
<a name="Expressions.UpdateExpressions.SET.AddingListsAndMaps"></a>

**Example**  
Agregue una lista y un mapa nuevos.  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET RelatedItems = :ri, ProductReviews = :pr" \
    --expression-attribute-values file://values.json \
    --return-values ALL_NEW
```
Los argumentos de `--expression-attribute-values` se almacenan en el archivo `values.json`.  

```
{
    ":ri": {
        "L": [
            { "S": "Hammer" }
        ]
    },
    ":pr": {
        "M": {
            "FiveStar": {
                "L": [
                    { "S": "Best product ever!" }
                ]
            }
        }
    }
}
```

### Adición de elementos a una lista
<a name="Expressions.UpdateExpressions.SET.AddingListElements"></a>

**Example**  
Agregue un nuevo atributo a la lista `RelatedItems`. Recuerde que las entradas de lista están basadas en cero; es decir, [0] representa la primera entrada de la lista, [1] representa la segunda, y así sucesivamente.  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET RelatedItems[1] = :ri" \
    --expression-attribute-values file://values.json \
    --return-values ALL_NEW
```
Los argumentos de `--expression-attribute-values` se almacenan en el archivo `values.json`.  

```
{
    ":ri": { "S": "Nails" }
}
```

**nota**  
Cuando se utiliza `SET` para actualizar una entrada de lista, el contenido de esa entrada se sustituye por los nuevos datos que ha especificado. Si el componente no existe, `SET` adjunta el nuevo componente al final de la lista.  
Si agrega varias entradas en una misma operación `SET`, las entradas se ordenan según su número.

### Adición de atributos de mapa anidados
<a name="Expressions.UpdateExpressions.SET.AddingNestedMapAttributes"></a>

**Example**  
Agregue algunos atributos de mapa anidados.  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET #pr.#5star[1] = :r5, #pr.#3star = :r3" \
    --expression-attribute-names file://names.json \
    --expression-attribute-values file://values.json \
    --return-values ALL_NEW
```
Los argumentos de `--expression-attribute-names` se almacenan en el archivo `names.json`.  

```
{
    "#pr": "ProductReviews",
    "#5star": "FiveStar",
    "#3star": "ThreeStar"
}
```
Los argumentos de `--expression-attribute-values` se almacenan en el archivo `values.json`.  

```
{
    ":r5": { "S": "Very happy with my purchase" },
    ":r3": {
        "L": [
            { "S": "Just OK - not that great" }
        ]
    }
}
```

**importante**  
No puede actualizar los atributos de mapa anidados si el mapa principal no existe. Si intenta actualizar un atributo anidado (por ejemplo, `ProductReviews.FiveStar`) cuando el mapa principal (`ProductReviews`) no existe, DynamoDB devuelve un elemento `ValidationException` con el mensaje *«The document path provided in the update expression is invalid for update»*.  
Al crear elementos cuyos atributos de mapa anidados se vayan a actualizar más adelante, inicialice los mapas vacíos para los atributos principales. Por ejemplo:  

```
{
    "Id": {"N": "789"},
    "ProductReviews": {"M": {}},
    "Metadata": {"M": {}}
}
```
Esto le permite actualizar los atributos anidados como `ProductReviews.FiveStar` sin errores.

### Aumento y reducción de atributos numéricos
<a name="Expressions.UpdateExpressions.SET.IncrementAndDecrement"></a>

Puede sumar o restar un valor a un atributo numérico. Para ello, se utilizan los operadores `+` (más) y `-` (menos).

**Example**  
Reduzca el `Price` de un elemento.  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET Price = Price - :p" \
    --expression-attribute-values '{":p": {"N":"15"}}' \
    --return-values ALL_NEW
```
Para aumentar el valor de `Price`, se utiliza el operador `+` en la expresión de actualización.

### Adición de elementos a una lista
<a name="Expressions.UpdateExpressions.SET.UpdatingListElements"></a>

Puede agregar componentes al final de una lista. Para ello, se utiliza `SET` con la función `list_append`. (El nombre de la función distingue entre mayúsculas y minúsculas). La función `list_append` es específica de la acción `SET` y solamente se puede utilizar en una expresión de actualización. La sintaxis es la siguiente.
+ `list_append (list1, list2)`

La función toma dos listas como entrada y anexa todos los elementos de `list2` a ` list1`.

**Example**  
En [Adición de elementos a una lista](#Expressions.UpdateExpressions.SET.AddingListElements), ha creado la lista `RelatedItems` y ha incluido en ella dos componentes: `Hammer` y `Nails`. Ahora, va a agregar dos componentes más al final de `RelatedItems`.  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET #ri = list_append(#ri, :vals)" \
    --expression-attribute-names '{"#ri": "RelatedItems"}' \
    --expression-attribute-values file://values.json  \
    --return-values ALL_NEW
```
Los argumentos de `--expression-attribute-values` se almacenan en el archivo `values.json`.  

```
{
    ":vals": {
        "L": [
            { "S": "Screwdriver" },
            {"S": "Hacksaw" }
        ]
    }
}
```
Por último, vamos a agregar un componente más al *principio* de `RelatedItems`. Para ello, invierta el orden de los componentes de `list_append`. (Recuerde que `list_append` toma dos listas como información de entrada y agrega la segunda lista a la primera).  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET #ri = list_append(:vals, #ri)" \
    --expression-attribute-names '{"#ri": "RelatedItems"}' \
    --expression-attribute-values '{":vals": {"L": [ { "S": "Chisel" }]}}' \
    --return-values ALL_NEW
```
Ahora, el atributo `RelatedItems` resultante contiene cinco entradas, el siguiente orden: `Chisel`, `Hammer`, `Nails`, `Screwdriver` y `Hacksaw`.

### Cómo evitar sobrescribir un atributo existente
<a name="Expressions.UpdateExpressions.SET.PreventingAttributeOverwrites"></a>

**Example**  
Establezca el valor de `Price` de un elemento, pero solo si este no tiene ya un atributo `Price`. (Si `Price` ya existe, no sucede nada).  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET Price = if_not_exists(Price, :p)" \
    --expression-attribute-values '{":p": {"N": "100"}}' \
    --return-values ALL_NEW
```

## REMOVE: eliminación de atributos de un elemento
<a name="Expressions.UpdateExpressions.REMOVE"></a>

Utilice la acción `REMOVE` en una expresión de actualización para eliminar uno o varios atributos de un elemento en Amazon DynamoDB. Para llevar a cabo varias acciones `REMOVE`, debe separarlas por comas.

A continuación se muestra un resumen de la sintaxis de `REMOVE` en una expresión de actualización. El único operando es la ruta de documento del atributo que se desea eliminar.

```
remove-action ::=
    path
```

**Example**  
Elimine algunos atributos de un elemento. (Si el atributo no existe, no sucede nada).  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "REMOVE Brand, InStock, QuantityOnHand" \
    --return-values ALL_NEW
```

### Eliminación de elementos de una lista
<a name="Expressions.UpdateExpressions.REMOVE.RemovingListElements"></a>

Puede utilizar `REMOVE` para eliminar entradas individuales de una lista.

**Example**  
En [Adición de elementos a una lista](#Expressions.UpdateExpressions.SET.UpdatingListElements), ha modificado un atributo de lista (`RelatedItems`) para que contenga cinco componentes:   
+ `[0]`—`Chisel`
+ `[1]`—`Hammer`
+ `[2]`—`Nails`
+ `[3]`—`Screwdriver`
+ `[4]`—`Hacksaw`
En el siguiente ejemplo de la AWS Command Line Interface (AWS CLI) se eliminan `Hammer` y `Nails` de la lista.  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "REMOVE RelatedItems[1], RelatedItems[2]" \
    --return-values ALL_NEW
```
Después de eliminar `Hammer` y `Nails`, los componentes restantes se reordenan. Ahora, la lista contiene lo siguiente:  
+ `[0]`—`Chisel`
+ `[1]`—`Screwdriver`
+ `[2]`—`Hacksaw`

## ADD: actualización de números y conjuntos
<a name="Expressions.UpdateExpressions.ADD"></a>

**nota**  
En general, recomendamos usar `SET` en lugar de `ADD` para garantizar operaciones idempotentes.

Utilice la acción `ADD` en una expresión de actualización para agregar un nuevo atributo y sus valores a un elemento.

Si el atributo ya existe, el comportamiento de `ADD` depende del tipo de datos del atributo:
+ Si el atributo es un número y el valor que se agrega también es un número, entonces el valor se suma matemáticamente al atributo existente. (Si el valor es un número negativo, entonces se resta del atributo existente).
+ Si el atributo es un conjunto y el valor que se agrega también es un conjunto, entonces el valor se agrega al conjunto existente.

**nota**  
La acción `ADD` solo es compatible con los tipos de datos Number y Set.

Para llevar a cabo varias acciones `ADD`, debe separarlas por comas.

En el resumen de sintaxis siguiente:
+ El componente *path* es la ruta de documento de un atributo. El tipo de datos del atributo debe ser `Number` o Set. 
+ El componente *value* es un número que se desea agregar al atributo (si el tipo de datos es `Number`) o un conjunto que se desea agregar al atributo (si el tipo de datos es Set).

```
add-action ::=
    path value
```

En los temas siguientes se describen algunos casos de uso diferentes de la acción `ADD`.

**Topics**
+ [Adición de un número](#Expressions.UpdateExpressions.ADD.Number)
+ [Adición de elementos a un conjunto](#Expressions.UpdateExpressions.ADD.Set)

### Adición de un número
<a name="Expressions.UpdateExpressions.ADD.Number"></a>

Supongamos que el atributo `QuantityOnHand` no existe. En el siguiente ejemplo de la AWS CLI, `QuantityOnHand` se establece en 5.

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "ADD QuantityOnHand :q" \
    --expression-attribute-values '{":q": {"N": "5"}}' \
    --return-values ALL_NEW
```

Ahora que `QuantityOnHand` ya existe, puede volver a ejecutar el ejemplo para incrementar `QuantityOnHand` en 5 cada vez.

### Adición de elementos a un conjunto
<a name="Expressions.UpdateExpressions.ADD.Set"></a>

Supongamos que el atributo `Color` no existe. En el siguiente ejemplo de la AWS CLI, `Color` se establece en un conjunto de cadenas que contiene dos componentes.

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "ADD Color :c" \
    --expression-attribute-values '{":c": {"SS":["Orange", "Purple"]}}' \
    --return-values ALL_NEW
```

Ahora que `Color` ya existe, podemos agregarle más componentes.

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "ADD Color :c" \
    --expression-attribute-values '{":c": {"SS":["Yellow", "Green", "Blue"]}}' \
    --return-values ALL_NEW
```

## DELETE: eliminación de elementos de un conjunto
<a name="Expressions.UpdateExpressions.DELETE"></a>

**importante**  
La acción `DELETE` solo es compatible con tipos de datos `Set`.

Utilice la acción `DELETE` en una expresión de actualización para eliminar una o varias entradas de un conjunto. Para llevar a cabo varias acciones `DELETE`, debe separarlas por comas.

En el resumen de sintaxis siguiente:
+ El componente *path* es la ruta de documento de un atributo. El tipo de datos del atributo debe ser Set.
+ *Subset* representa uno o varios componentes que se van a eliminar de *path*. Para *subset* debe especificar un tipo de datos Set.

```
delete-action ::=
    path subset
```

**Example**  
En [Adición de elementos a un conjunto](#Expressions.UpdateExpressions.ADD.Set), crea el conjunto de cadenas `Color`. En este ejemplo se eliminan algunos de los componentes de ese conjunto.  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "DELETE Color :p" \
    --expression-attribute-values '{":p": {"SS": ["Yellow", "Purple"]}}' \
    --return-values ALL_NEW
```

## Uso de varias expresiones de actualización
<a name="Expressions.UpdateExpressions.Multiple"></a>

Puede utilizar varias acciones en una sola expresión de actualización. Todas las referencias de atributos se resuelven en función del estado del elemento antes de aplicar cualquiera de las acciones.

**Example**  
Dado un elemento `{"id": "1", "a": 1, "b": 2, "c": 3}`, la siguiente expresión elimina `a` y desplaza los valores de `b` y `c`:  

```
aws dynamodb update-item \
    --table-name test \
    --key '{"id":{"S":"1"}}' \
    --update-expression "REMOVE a SET b = a, c = b" \
    --return-values ALL_NEW
```
El resultado es `{"id": "1", "b": 1, "c": 2}`. Aunque `a` se elimina y `b` se reasigna en la misma expresión, ambas referencias se resuelven con sus valores originales.

**Example**  
Si desea modificar el valor de un atributo y eliminar por completo otro, podría utilizar una acción SET y otra REMOVE en una sola instrucción. Esta operación reduciría el valor de `Price` a 15 a la vez que eliminaría el atributo `InStock` del elemento.  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET Price = Price - :p REMOVE InStock" \
    --expression-attribute-values '{":p": {"N":"15"}}' \
    --return-values ALL_NEW
```

**Example**  
Si desea agregar un elemento a una lista al mismo tiempo que cambia el valor de otro atributo, podría utilizar dos acciones SET en una sola instrucción. Esta operación agregaría "Nails" al atributo de la lista `RelatedItems` y también establecería el valor de `Price` a 21.  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET RelatedItems[1] = :newValue, Price = :newPrice" \
    --expression-attribute-values '{":newValue": {"S":"Nails"}, ":newPrice": {"N":"21"}}'  \
    --return-values ALL_NEW
```

# Expresiones, operadores y funciones de condición y filtro en DynamoDB
<a name="Expressions.OperatorsAndFunctions"></a>

Para manipular datos en una tabla de DynamoDB, se usan las operaciones `PutItem`, `UpdateItem` y `DeleteItem`. Para estas operaciones de manipulación de datos, puede especificar una expresión de condición con el fin de determinar qué elementos deben modificarse. Si la expresión de condición se evalúa en true, entonces la operación se realiza correctamente. De lo contrario, la operación no se llevará a cabo correctamente.

En esta sección se describen las funciones y las palabras clave integradas para escribir expresiones de filtro y condición en Amazon DynamoDB. Para obtener más información sobre las funciones y la programación con DynamoDB, consulte [Programación con DynamoDB y los SDK de AWS](Programming.md) y la [Referencia de la API de DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/).

**Topics**
+ [Sintaxis de las expresiones de filtro y condición](#Expressions.OperatorsAndFunctions.Syntax)
+ [Realización de comparaciones](#Expressions.OperatorsAndFunctions.Comparators)
+ [Funciones](#Expressions.OperatorsAndFunctions.Functions)
+ [Evaluaciones lógicas](#Expressions.OperatorsAndFunctions.LogicalEvaluations)
+ [Paréntesis](#Expressions.OperatorsAndFunctions.Parentheses)
+ [Precedencia en las condiciones](#Expressions.OperatorsAndFunctions.Precedence)

## Sintaxis de las expresiones de filtro y condición
<a name="Expressions.OperatorsAndFunctions.Syntax"></a>

En el siguiente resumen de sintaxis, el componente *operand* puede ser uno de los siguientes: 
+ Un nombre de atributo de nivel superior, como por ejemplo `Id`, `Title`, `Description` o `ProductCategory`
+ Una ruta de documento que hace referencia a un atributo anidado

```
condition-expression ::=
      operand comparator operand
    | operand BETWEEN operand AND operand
    | operand IN ( operand (',' operand (, ...) ))
    | function
    | condition AND condition
    | condition OR condition
    | NOT condition
    | ( condition )

comparator ::=
    =
    | <>
    | <
    | <=
    | >
    | >=

function ::=
    attribute_exists (path)
    | attribute_not_exists (path)
    | attribute_type (path, type)
    | begins_with (path, substr)
    | contains (path, operand)
    | size (path)
```

## Realización de comparaciones
<a name="Expressions.OperatorsAndFunctions.Comparators"></a>

Utilice estos comparadores para comparar un operando con un solo valor:
+ `a = b`: es true si *a* es igual que *b*.
+ `a <> b`: es true si *a* es distinto de *b*.
+ `a < b`: es true si *a* es menor que *b*.
+ `a <= b`: es true si *a* es menor o igual que *b*.
+ `a > b`: es true si *a* es mayor que *b*.
+ `a >= b`: es true si *a* es mayor o igual que *b*.

Use las palabras clave `BETWEEN` e `IN` para comparar un operando con un rango o una lista de valores:
+ `a BETWEEN b AND c`: es true si *a* es mayor o igual que *b* y menor o igual que *c*.
+ `a IN (b, c, d) `: es true si *a* es igual a cualquiera de los valores de la lista; por ejemplo, en este caso, a *b*, *c* o *d*. La lista puede contener hasta 100 valores separadas por comas.

## Funciones
<a name="Expressions.OperatorsAndFunctions.Functions"></a>

Utilice las siguientes funciones para determinar si un atributo existe en un elemento o evaluar el valor de un atributo. Los nombres de estas funciones distinguen entre mayúsculas y minúsculas. En el caso de los atributos anidados, debe proporcionar su ruta de documento completa.


****  

| Función | Descripción | 
| --- | --- | 
|  `attribute_exists (path)`  | Es true si el elemento contiene el atributo especificado por `path`. Ejemplo: Comprobación de si un elemento de la tabla `Product` tiene una imagen de vista lateral. [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html)  | 
|  `attribute_not_exists (path)`  | Es true si el atributo especificado en `path` no está presente en el elemento. Ejemplo: Comprobación de si un elemento tiene el atributo `Manufacturer`. [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html)  | 
|  `attribute_type (path, type)`  |  Es true si el atributo de la ruta especificada es de un tipo de datos determinado. El parámetro `type` debe ser uno de los siguientes: [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html) Debe utilizar un valor de atributo de expresión para el parámetro `type`. Ejemplo: Comprobación de si el atributo `QuantityOnHand` es del tipo List. En este ejemplo, `:v_sub` es un marcador de posición para la `L`. [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html) Debe utilizar un valor de atributo de expresión para el parámetro `type`.   | 
|  `begins_with (path, substr)`  |  Es true si el atributo especificado por `path` comienza por una subcadena determinada. Ejemplo: Comprobación de si los primeros caracteres de la URL de la imagen de vista frontal son `http://`. [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html) El valor de atributo de expresión `:v_sub` es un marcador de posición para `http://`.  | 
|  `contains (path, operand)`  | Es true si el atributo especificado por `path` es uno de los siguientes: [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html) Si el atributo especificado por `path` es un `String`, el `operand` debe ser un `String`. Si el atributo especificado por `path` es un `Set`, el `operand` debe ser el tipo de elemento del conjunto. La ruta y el operando deben ser distintos. Es decir, `contains (a, a)` devuelve un error. Ejemplo: Comprobación de si el atributo `Brand` contiene la subcadena `Company`. [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html) El valor de atributo de expresión `:v_sub` es un marcador de posición para `Company`. Ejemplo: Comprobación de si el producto está disponible en color rojo. [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html) El valor de atributo de expresión `:v_sub` es un marcador de posición para `Red`. | 
|  `size (path)`  | Devuelve un número que representa el tamaño de un atributo. A continuación se muestran los tipos de datos válidos para usarlos con `size`.  Si el atributo es de tipo `String`, `size` devuelve la longitud de la cadena. Ejemplo: Comprobación de si la cadena `Brand` es menor o igual que 20 caracteres. El valor de atributo de expresión `:v_sub` es un marcador de posición para `20`. [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html)  Si el atributo es de tipo `Binary`, `size` devuelve el número de bytes del valor del atributo. Ejemplo: supongamos que el elemento `ProductCatalog` tiene un atributo binario denominado `VideoClip` que contiene vídeo breve sobre el uso del producto. En la siguiente expresión se comprueba si `VideoClip` supera los 64 000 bytes. El valor de atributo de expresión `:v_sub` es un marcador de posición para `64000`. [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html)  Si el tipo de datos del atributo es `Set`, `size` devuelve el número de componentes del conjunto.  Ejemplo: Comprobación de si el producto está disponible en más de un color. El valor de atributo de expresión `:v_sub` es un marcador de posición para `1`. [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html)  Si el atributo es de tipo `List` o `Map`, `size` devuelve el número de componentes secundarios. Ejemplo: Comprobación de si el número de opiniones `OneStar` ha superado un umbral determinado. El valor de atributo de expresión `:v_sub` es un marcador de posición para `3`. [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html)  | 

## Evaluaciones lógicas
<a name="Expressions.OperatorsAndFunctions.LogicalEvaluations"></a>

Utilice las palabras clave `AND`, `OR` y `NOT` para llevar a cabo evaluaciones lógicas. En la lista siguiente, *a* y *b* representan las condiciones que se van a evaluar.
+ `a AND b`: es true si *a* y *b* son true.
+ `a OR b`: es true si *a* o *b* (o ambas) son true.
+ `NOT a`: es true si *a* es false. False si *a* es true.

A continuación se muestra un ejemplo de código de AND en una operación.

`dynamodb-local (*)> select * from exprtest where a > 3 and a < 5;`

## Paréntesis
<a name="Expressions.OperatorsAndFunctions.Parentheses"></a>

Los paréntesis se utilizan para cambiar la preferencia de una evaluación lógica. Por ejemplo, supongamos que las condiciones *a* y *b* son true y que la condición *c* es false. La siguiente expresión se evalúa en true:
+ `a OR b AND c`

Sin embargo, si se incluye una condición entre paréntesis, esta se evalúa antes. Por ejemplo, lo siguiente se evalúa en false:
+  `(a OR b) AND c`

**nota**  
En una expresión se pueden utilizar paréntesis anidados. En este caso, se evalúan primero los más internos.

A continuación se muestra un ejemplo de código con paréntesis en una evaluación lógica.

`dynamodb-local (*)> select * from exprtest where attribute_type(b, string) or ( a = 5 and c = “coffee”);`

## Precedencia en las condiciones
<a name="Expressions.OperatorsAndFunctions.Precedence"></a>

 DynamoDB evalúa las condiciones de izquierda a derecha aplicando las siguientes normas de prioridad:
+ `= <> < <= > >=`
+ `IN`
+ `BETWEEN`
+ `attribute_exists attribute_not_exists begins_with contains`
+ Paréntesis
+ `NOT`
+ `AND`
+ `OR`

# Ejemplo de la CLI de expresión de condición de DynamoDB
<a name="Expressions.ConditionExpressions"></a>

A continuación se muestran algunos ejemplos de uso de expresiones de condición en la AWS Command Line Interface (AWS CLI). Estos ejemplos se basan en la tabla `ProductCatalog`, especificada en [Referencia a atributos de elementos mediante expresiones en DynamoDB](Expressions.Attributes.md). La clave de partición de esta tabla es `Id` y no tiene clave de ordenación. La siguiente operación `PutItem` crea un elemento de muestra `ProductCatalog` al que se refieren los ejemplos.

```
aws dynamodb put-item \
    --table-name ProductCatalog \
    --item file://item.json
```

Los argumentos de `--item` se almacenan en el archivo `item.json`. Para simplificar, se utilizan tan solo algunos de los atributos de elementos.

```
{
    "Id": {"N": "456" },
    "ProductCategory": {"S": "Sporting Goods" },
    "Price": {"N": "650" }
}
```

**Topics**
+ [PUT condicional](#Expressions.ConditionExpressions.PreventingOverwrites)
+ [Eliminaciones condicionales](#Expressions.ConditionExpressions.AdvancedComparisons)
+ [Actualizaciones condicionales](#Expressions.ConditionExpressions.SimpleComparisons)
+ [Ejemplos de expresiones condicionales](#Expressions.ConditionExpressions.ConditionalExamples)

## PUT condicional
<a name="Expressions.ConditionExpressions.PreventingOverwrites"></a>

La operación `PutItem` sobrescribe un elemento que tenga la misma clave principal (si existe). Si desea evitar que esto suceda, utilice una expresión de condición. Esto permite que la escritura se lleve a cabo solo si el elemento en cuestión ya no tiene la misma clave principal.

En el siguiente ejemplo se utiliza `attribute_not_exists()` para comprobar si la clave principal existe en la tabla antes de intentar la operación de escritura. 

**nota**  
Si la clave principal consta de una clave de partición (pk) y una clave de clasificación (sk), el parámetro comprobará si `attribute_not_exists(pk)` Y `attribute_not_exists(sk)` se evalúan como verdadero o falso como una instrucción completa antes de intentar la operación de escritura.

```
aws dynamodb put-item \
    --table-name ProductCatalog \
    --item file://item.json \
    --condition-expression "attribute_not_exists(Id)"
```

Si la expresión de condición se evalúa en false (falso), DynamoDB devuelve el siguiente mensaje de error: The conditional request failed (Se produjo un error en la consulta condicional).

**nota**  
Para obtener más información sobre `attribute_not_exists` y otras funciones, consulte [Expresiones, operadores y funciones de condición y filtro en DynamoDB](Expressions.OperatorsAndFunctions.md).

## Eliminaciones condicionales
<a name="Expressions.ConditionExpressions.AdvancedComparisons"></a>

Para realizar una eliminación condicional, se usa una operación `DeleteItem` con una expresión de condición. La expresión de condición debe evaluarse en true para que la operación se lleve a cabo correctamente; de lo contrario, se produce un error.

Tenga en cuenta el elemento definido anteriormente.

Suponga que desea eliminar el elemento, pero solo en las siguientes condiciones:
+  El valor de `ProductCategory` es "Sporting Goods" o "Gardening Supplies".
+  El valor de `Price` está comprendido entre 500 y 600.

En el siguiente ejemplo se intenta eliminar el elemento.

```
aws dynamodb delete-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"456"}}' \
    --condition-expression "(ProductCategory IN (:cat1, :cat2)) and (Price between :lo and :hi)" \
    --expression-attribute-values file://values.json
```

Los argumentos de `--expression-attribute-values` se almacenan en el archivo `values.json`.

```
{
    ":cat1": {"S": "Sporting Goods"},
    ":cat2": {"S": "Gardening Supplies"},
    ":lo": {"N": "500"},
    ":hi": {"N": "600"}
}
```

**nota**  
En la expresión de condición, `:` (signo de dos puntos) indica un *valor de atributo de expresión* (un marcador de posición del valor real). Para obtener más información, consulte [Uso de valores de atributos de expresión en DynamoDB](Expressions.ExpressionAttributeValues.md).  
Para obtener más información sobre `IN`, `AND` y otras palabras clave, consulte [Expresiones, operadores y funciones de condición y filtro en DynamoDB](Expressions.OperatorsAndFunctions.md).

En este ejemplo, la comparación de `ProductCategory` se evalúa en true, pero la comparación de `Price` se evalúa en false. Esto hace que la expresión de condición se evalúe en false y, por consiguiente, la operación `DeleteItem` no se lleva a cabo.

## Actualizaciones condicionales
<a name="Expressions.ConditionExpressions.SimpleComparisons"></a>

Para realizar una actualización condicional, se usa una operación `UpdateItem` con una expresión de condición. La expresión de condición debe evaluarse en true para que la operación se lleve a cabo correctamente; de lo contrario, se produce un error.

**nota**  
`UpdateItem` también admite las *expresiones de actualización*, donde especifica las modificaciones que se desea aplicar a un elemento. Para obtener más información, consulte [Uso de expresiones de actualización en DynamoDB](Expressions.UpdateExpressions.md).

Supongamos que ha comenzado por el elemento definido anteriormente.

En el ejemplo siguiente se realiza una operación `UpdateItem`. Se intenta reducir el valor de `Price` de un producto en 75, pero la expresión de condición impide la actualización si el valor de `Price` actual es menor o igual que 500.

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id": {"N": "456"}}' \
    --update-expression "SET Price = Price - :discount" \
    --condition-expression "Price > :limit" \
    --expression-attribute-values file://values.json
```

Los argumentos de `--expression-attribute-values` se almacenan en el archivo `values.json`.

```
{
    ":discount": { "N": "75"},
    ":limit": {"N": "500"}
}
```

Si el valor inicial de `Price` es 650, la operación `UpdateItem` reduce el `Price` a 575. Si ejecuta la operación `UpdateItem` de nuevo, el valor de `Price` se reduce a 500. Si se ejecuta una tercera vez, la expresión de condición se evalúa en false y la actualización no se lleva a cabo.

**nota**  
En la expresión de condición, `:` (signo de dos puntos) indica un *valor de atributo de expresión* (un marcador de posición del valor real). Para obtener más información, consulte [Uso de valores de atributos de expresión en DynamoDB](Expressions.ExpressionAttributeValues.md).  
Para obtener más información sobre "*>*" y otros operadores, consulte [Expresiones, operadores y funciones de condición y filtro en DynamoDB](Expressions.OperatorsAndFunctions.md).

## Ejemplos de expresiones condicionales
<a name="Expressions.ConditionExpressions.ConditionalExamples"></a>

Para obtener más información acerca de las funciones utilizadas en los ejemplos siguientes, consulte [Expresiones, operadores y funciones de condición y filtro en DynamoDB](Expressions.OperatorsAndFunctions.md). Si desea obtener más información sobre cómo especificar distintos tipos de atributo en una expresión, consulte [Referencia a atributos de elementos mediante expresiones en DynamoDB](Expressions.Attributes.md). 

### Comprobación de los atributos de un elemento
<a name="Expressions.ConditionExpressions.CheckingForAttributes"></a>

Puede comprobar la existencia (o inexistencia) de cualquier atributo. Si la expresión de condición se evalúa en true, entonces la operación se realiza correctamente; de lo contrario, produce un error.

En el siguiente ejemplo se utiliza `attribute_not_exists` para eliminar un producto únicamente si no tiene el atributo `Price`.

```
aws dynamodb delete-item \
    --table-name ProductCatalog \
    --key '{"Id": {"N": "456"}}' \
    --condition-expression "attribute_not_exists(Price)"
```

DynamoDB también proporciona un función `attribute_exists`. En el siguiente ejemplo se elimina un producto únicamente si ha recibido opiniones negativas.

```
aws dynamodb delete-item \
    --table-name ProductCatalog \
    --key '{"Id": {"N": "456"}}' \
    --condition-expression "attribute_exists(ProductReviews.OneStar)"
```

### Comprobación del tipo de atributo
<a name="Expressions.ConditionExpressions.CheckingForAttributeType"></a>

Puede comprobar el tipo de datos de un valor de atributo mediante la función `attribute_type`. Si la expresión de condición se evalúa en true, entonces la operación se realiza correctamente; de lo contrario, produce un error.

En el ejemplo siguiente se utiliza `attribute_type` para eliminar un producto sólo si tiene un atributo `Color` de tipo String Set. 

```
aws dynamodb delete-item \
    --table-name ProductCatalog \
    --key '{"Id": {"N": "456"}}' \
    --condition-expression "attribute_type(Color, :v_sub)" \
    --expression-attribute-values file://expression-attribute-values.json
```

Los argumentos de `--expression-attribute-values` se almacenan en el archivo expression-attribute-values.json.

```
{
    ":v_sub":{"S":"SS"}
}
```

### Comprobación del valor inicial de cadena
<a name="Expressions.ConditionExpressions.CheckingBeginsWith"></a>

Puede comprobar si un valor de atributo String comienza con una subcadena determinada mediante la función `begins_with`. Si la expresión de condición se evalúa en true, entonces la operación se realiza correctamente; de lo contrario, produce un error. 

En el ejemplo siguiente se utiliza `begins_with` para eliminar un producto solo si el elemento `FrontView` del mapa `Pictures` comienza con un valor específico.

```
aws dynamodb delete-item \
    --table-name ProductCatalog \
    --key '{"Id": {"N": "456"}}' \
    --condition-expression "begins_with(Pictures.FrontView, :v_sub)" \
    --expression-attribute-values file://expression-attribute-values.json
```

Los argumentos de `--expression-attribute-values` se almacenan en el archivo expression-attribute-values.json.

```
{
    ":v_sub":{"S":"http://"}
}
```

### Comprobación de un elemento en un conjunto
<a name="Expressions.ConditionExpressions.CheckingForContains"></a>

Puede buscar un elemento en un conjunto o buscar una subcadena dentro de una cadena mediante el uso de la función `contains`. Si la expresión de condición se evalúa en true, entonces la operación se realiza correctamente; de lo contrario, produce un error. 

En el ejemplo siguiente se utiliza `contains` para eliminar un producto sólo si el conjunto de cadenas `Color` tiene un elemento con un valor específico. 

```
aws dynamodb delete-item \
    --table-name ProductCatalog \
    --key '{"Id": {"N": "456"}}' \
    --condition-expression "contains(Color, :v_sub)" \
    --expression-attribute-values file://expression-attribute-values.json
```

Los argumentos de `--expression-attribute-values` se almacenan en el archivo expression-attribute-values.json.

```
{
    ":v_sub":{"S":"Red"}
}
```

### Comprobación del tamaño de un valor de atributo
<a name="Expressions.ConditionExpressions.CheckingForSize"></a>

Puede comprobar el tamaño de un valor de atributo mediante la función `size`. Si la expresión de condición se evalúa en true, entonces la operación se realiza correctamente; de lo contrario, produce un error. 

En el ejemplo siguiente se utiliza `size` para eliminar un producto sólo si el tamaño del atributo Binary `VideoClip` es mayor de `64000` bytes. 

```
aws dynamodb delete-item \
    --table-name ProductCatalog \
    --key '{"Id": {"N": "456"}}' \
    --condition-expression "size(VideoClip) > :v_sub" \
    --expression-attribute-values file://expression-attribute-values.json
```

Los argumentos de `--expression-attribute-values` se almacenan en el archivo expression-attribute-values.json.

```
{
    ":v_sub":{"N":"64000"}
}
```

# Uso del período de vida (TTL) en DynamoDB
<a name="TTL"></a>

Tiempo de vida (TTL) para DynamoDB es un método rentable para eliminar elementos que ya no son relevantes. TTL permite definir una marca de tiempo de vencimiento por elemento que indica cuándo deja de necesitarse un elemento. DynamoDB elimina automáticamente los elementos vencidos a los pocos días de su fecha de vencimiento, sin afectar al rendimiento de escritura. 

Para usar TTL, primero debe activarlo en una tabla y, a continuación, definir un atributo específico para almacenar la marca de tiempo de vencimiento de TTL. La marca de tiempo debe almacenarse como un tipo de datos de [Número](HowItWorks.NamingRulesDataTypes.md#HowItWorks.DataTypes) en [formato de tiempo Epoch de Unix](https://en.wikipedia.org/wiki/Unix_time) con nivel de precisión de segundos. El proceso TTL ignora los elementos con un atributo TTL que no sea un tipo de número. Cada vez que se crea o actualiza un elemento, puede calcular el tiempo de vencimiento y guardarlo en el atributo TTL.

El sistema puede eliminar los elementos con atributos TTL válidos y vencidos en cualquier momento, normalmente a los pocos días de que hayan caducado. Puede seguir actualizando los elementos vencidos que no se hayan eliminado todavía, lo que incluye cambiar o eliminar los atributos TTL. Al actualizar un elemento vencido, le recomendamos que utilice una expresión de condición para asegurarse de que el elemento no se haya eliminado posteriormente. Utilice expresiones de filtro para eliminar los elementos vencidos de los resultados de [Scan](Scan.md#Scan.FilterExpression) y [Query](Query.FilterExpression.md).

Los elementos eliminados funcionan de forma similar a los que se eliminan con las operaciones de eliminación habituales. Una vez eliminados, los elementos pasan a DynamoDB Streams como eliminaciones de servicio en lugar de eliminaciones de usuario. Además, se eliminan de los índices secundarios locales y de los índices secundarios globales de igual modo que con las otras operaciones de eliminación. 

Si usa la [versión 2019.11.21 (actual) de las tablas globales](GlobalTables.md) y también la característica TTL, DynamoDB replicará las eliminaciones de TTL en todas las tablas de réplica. La eliminación de TTL inicial no consume unidades de capacidad de escritura (WCU) en la región donde se produce el vencimiento de TTL. Sin embargo, la eliminación de TTL replicada en las tablas de réplica consume una unidad de capacidad de escritura replicada cuando se usa la capacidad aprovisionada, o una unidad de escritura replicada cuando se usa el modo de capacidad bajo demanda, en cada una de las regiones de réplica. En estos casos, se aplicarán los cargos pertinentes.

Para obtener más información acerca de TTL, consulte estos temas:

**Topics**
+ [Habilitación del período de vida (TTL) de DynamoDB](time-to-live-ttl-how-to.md)
+ [Período de vida (TTL) de computación en DynamoDB](time-to-live-ttl-before-you-start.md)
+ [Uso de elementos caducados y el período de vida (TTL)](ttl-expired-items.md)

# Habilitación del período de vida (TTL) de DynamoDB
<a name="time-to-live-ttl-how-to"></a>

**nota**  
Para facilitar la depuración y la verificación del correcto funcionamiento de la característica TTL, los valores proporcionados para el TTL de un elemento se registran en texto no cifrado en los registros de diagnóstico de DynamoDB.

Puede habilitar TTL en la consola de Amazon DynamoDB, la AWS Command Line Interface (AWS CLI) o mediante la [referencia de la API de Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/) con cualquiera de los AWS SDK supuestos. La habilitación de TTL en todas las particiones tarda aproximadamente una hora.

## Activación del TTL de DynamoDB mediante la consola de AWS
<a name="time-to-live-ttl-how-to-enable-console"></a>

1. Inicie sesión en la Consola de administración de AWS y abra la consola de DynamoDB en [https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/).

1. Elija **Tables (Tablas)** y, a continuación, seleccione la tabla que desee modificar.

1. En la pestaña **Configuración adicional**, dentro de la sección **Tiempo de vida (TTL)**, elija **Activar**.

1. Al habilitar TTL en una tabla, DynamoDB requiere que identifique un nombre de atributo específico que el servicio buscará al determinar si un elemento cumple los requisitos para el vencimiento. El nombre del atributo TTL, que se muestra a continuación, distingue entre mayúsculas y minúsculas y debe coincidir con el atributo definido en las operaciones de lectura y escritura. Si no coinciden, los elementos caducados no se eliminarán. Si se cambia el nombre del atributo TTL, deberá desactivarse el TTL y volverse a activar con el nuevo atributo de ahora en adelante. Una vez desactivado, el TTL seguirá procesando las eliminaciones durante aproximadamente 30 minutos. El TTL debe reconfigurarse en las tablas restauradas.  
![\[Nombre de atributo TTL que distingue entre mayúsculas y minúsculas que DynamoDB utiliza para determinar si un artículo puede caducar.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/EnableTTL-Settings.png)

1. (Opcional) Puede realizar una prueba simulando la fecha y la hora de vencimiento y haciendo coincidir algunos elementos. Al hacer se muestra una lista de elementos de ejemplo donde se confirma que hay elementos que contienen el nombre del atributo TTL junto con la fecha de vencimiento.

Ahora que TTL está activado, el atributo TTL llevará la marca **TTL** cuando consulte los elementos en la consola de DynamoDB. Para ver la fecha y la hora de vencimiento de un elemento, mantenga el cursor del ratón sobre el atributo. 

## Activación del TTL de DynamoDB mediante la API
<a name="time-to-live-ttl-how-to-enable-api"></a>

------
#### [ Python ]

Puede activar el TTL con código con la operación [UpdateTimeToLive](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb/client/update_time_to_live.html).

```
import boto3


def enable_ttl(table_name, ttl_attribute_name):
    """
    Enables TTL on DynamoDB table for a given attribute name
        on success, returns a status code of 200
        on error, throws an exception

    :param table_name: Name of the DynamoDB table
    :param ttl_attribute_name: The name of the TTL attribute being provided to the table.
    """
    try:
        dynamodb = boto3.client('dynamodb')

        # Enable TTL on an existing DynamoDB table
        response = dynamodb.update_time_to_live(
            TableName=table_name,
            TimeToLiveSpecification={
                'Enabled': True,
                'AttributeName': ttl_attribute_name
            }
        )

        # In the returned response, check for a successful status code.
        if response['ResponseMetadata']['HTTPStatusCode'] == 200:
            print("TTL has been enabled successfully.")
        else:
            print(f"Failed to enable TTL, status code {response['ResponseMetadata']['HTTPStatusCode']}")
    except Exception as ex:
        print("Couldn't enable TTL in table %s. Here's why: %s" % (table_name, ex))
        raise


# your values
enable_ttl('your-table-name', 'expirationDate')
```

Puede confirmar que el TTL está activado mediante la operación [DescribeTimeToLive](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb/client/describe_time_to_live.html), que describe el estado del TTL en una tabla. El estado de `TimeToLive` es `ENABLED` o `DISABLED`.

```
# create a DynamoDB client
dynamodb = boto3.client('dynamodb')

# set the table name
table_name = 'YourTable'

# describe TTL
response = dynamodb.describe_time_to_live(TableName=table_name)
```

------
#### [ JavaScript ]

Puede activar el TTL con código con la operación [UpdateTimeToLiveCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-client-dynamodb/Class/UpdateTimeToLiveCommand/).

```
import { DynamoDBClient, UpdateTimeToLiveCommand } from "@aws-sdk/client-dynamodb";

const enableTTL = async (tableName, ttlAttribute) => {

    const client = new DynamoDBClient({});

    const params = {
        TableName: tableName,
        TimeToLiveSpecification: {
            Enabled: true,
            AttributeName: ttlAttribute
        }
    };

    try {
        const response = await client.send(new UpdateTimeToLiveCommand(params));
        if (response.$metadata.httpStatusCode === 200) {
            console.log(`TTL enabled successfully for table ${tableName}, using attribute name ${ttlAttribute}.`);
        } else {
            console.log(`Failed to enable TTL for table ${tableName}, response object: ${response}`);
        }
        return response;
    } catch (e) {
        console.error(`Error enabling TTL: ${e}`);
        throw e;
    }
};

// call with your own values
enableTTL('ExampleTable', 'exampleTtlAttribute');
```

------

## Activación del tiempo de vida mediante la AWS CLI
<a name="time-to-live-ttl-how-to-enable-cli-sdk"></a>

1. Habilite TTL en la tabla `TTLExample`.

   ```
   aws dynamodb update-time-to-live --table-name TTLExample --time-to-live-specification "Enabled=true, AttributeName=ttl"
   ```

1. Describa TTL en la tabla `TTLExample`.

   ```
   aws dynamodb describe-time-to-live --table-name TTLExample
   {
       "TimeToLiveDescription": {
           "AttributeName": "ttl",
           "TimeToLiveStatus": "ENABLED"
       }
   }
   ```

1. Añada un elemento a la tabla `TTLExample` con el atributo de período de vida establecido utilizando el shell de BASH y la AWS CLI. 

   ```
   EXP=`date -d '+5 days' +%s`
   aws dynamodb put-item --table-name "TTLExample" --item '{"id": {"N": "1"}, "ttl": {"N": "'$EXP'"}}'
   ```

Este ejemplo comienza con la fecha actual y añade cinco días para crear una fecha de vencimiento. A continuación, convierte la fecha de vencimiento al formato de tiempo Unix y, por último, agrega un elemento a la tabla "`TTLExample`". 

**nota**  
 Una forma de establecer valores de vencimiento para período de vida consiste en calcular el número de segundos que se sumarán al momento del vencimiento. Por ejemplo, 5 días son 432 000 segundos. Sin embargo, a menudo es preferible comenzar por una fecha y tomarla como punto de partida.

Es bastante sencillo obtener el tiempo actual en formato de tiempo Unix, como en los siguientes ejemplos.
+ Terminal Linux: `date +%s`
+ Python: `import time; int(time.time())`
+ Java: `System.currentTimeMillis() / 1000L`
+ JavaScript: : `Math.floor(Date.now() / 1000)`

## Activación del TTL de DynamoDB mediante CloudFormation
<a name="time-to-live-ttl-how-to-enable-cf"></a>

```
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  TTLExampleTable:
    Type: AWS::DynamoDB::Table
    Description: "A DynamoDB table with TTL Specification enabled"
    Properties:
      AttributeDefinitions:
        - AttributeName: "Album"
          AttributeType: "S"
        - AttributeName: "Artist"
          AttributeType: "S"
      KeySchema:
        - AttributeName: "Album"
          KeyType: "HASH"
        - AttributeName: "Artist"
          KeyType: "RANGE"
      ProvisionedThroughput:
        ReadCapacityUnits: "5"
        WriteCapacityUnits: "5"
      TimeToLiveSpecification:
        AttributeName: "TTLExampleAttribute"
        Enabled: true
```

Puede encontrar [aquí](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-table-timetolivespecification.html) más información sobre el uso del TTL en las plantillas de CloudFormation.

# Período de vida (TTL) de computación en DynamoDB
<a name="time-to-live-ttl-before-you-start"></a>

Una forma habitual de implementar el TTL consiste en establecer una fecha de vencimiento para los elementos en función de cuándo se crearon o se actualizaron por última vez. Esto se puede hacer añadiendo la hora a las marcas de tiempo `createdAt` y `updatedAt`. Por ejemplo, el TTL de los elementos que se acaban de crear se puede establecer en `createdAt` más 90 días. Cuando se actualiza el elemento, el TTL se puede volver a calcular como `updatedAt` más de 90 días.

El tiempo de vencimiento calculado debe estar en formato Epoch, expresado en segundos. Para que se tenga en cuenta el vencimiento y la eliminación, el TTL no puede tener más de cinco años de antigüedad. Si utiliza cualquier otro formato, los procesos de TTL ignoran el elemento. Si establece la fecha de vencimiento en algún momento futuro en el que desee que el artículo caduque, este caducará una vez transcurrido ese plazo. Por ejemplo, supongamos que establece la fecha de vencimiento en 1724241326 (que corresponde al lunes 21 de agosto de 2024 a las 11:55:26 [UTC]). El elemento vence cuando se alcanza la fecha especificada. No hay una duración mínima de TTL. Puede establecer la fecha de vencimiento en cualquier momento futuro, por ejemplo, 5 minutos a partir de la hora actual. Sin embargo, DynamoDB suele eliminar los elementos caducados en un plazo de 48 horas tras su fecha de vencimiento, y no inmediatamente después de que caduquen.

**Topics**
+ [Creación de un elemento y determinación del tiempo de vida](#time-to-live-ttl-before-you-start-create)
+ [Actualización de un elemento y del tiempo de vida](#time-to-live-ttl-before-you-start-update)

## Creación de un elemento y determinación del tiempo de vida
<a name="time-to-live-ttl-before-you-start-create"></a>

En el siguiente ejemplo se muestra cómo calcular el tiempo de vencimiento al crear un elemento nuevo, utilizando `expireAt` como nombre de atributo TTL. Una instrucción de asignación obtiene la hora actual como variable. En el ejemplo, la fecha de vencimiento se calcula en 90 días a partir de la fecha actual. A continuación, la fecha se convierte al formato Epoch y se guarda como un tipo de dato entero en el atributo TTL.

Los siguientes ejemplos de código muestran cómo crear un elemento con TTL.

------
#### [ Java ]

**SDK para Java 2.x**  

```
package com.amazon.samplelib.ttl;

import com.amazon.samplelib.CodeSampleUtils;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
import software.amazon.awssdk.services.dynamodb.model.PutItemResponse;
import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * Creates an item in a DynamoDB table with TTL attributes.
 * This class demonstrates how to add TTL expiration timestamps to DynamoDB items.
 */
public class CreateTTL {

    private static final String USAGE =
        """
            Usage:
                <tableName> <primaryKey> <sortKey> <region>
            Where:
                tableName - The Amazon DynamoDB table being queried.
                primaryKey - The name of the primary key. Also known as the hash or partition key.
                sortKey - The name of the sort key. Also known as the range attribute.
                region (optional) - The AWS region that the Amazon DynamoDB table is located in. (Default: us-east-1)
            """;
    private static final int DAYS_TO_EXPIRE = 90;
    private static final int SECONDS_PER_DAY = 24 * 60 * 60;
    private static final String PRIMARY_KEY_ATTR = "primaryKey";
    private static final String SORT_KEY_ATTR = "sortKey";
    private static final String CREATION_DATE_ATTR = "creationDate";
    private static final String EXPIRE_AT_ATTR = "expireAt";
    private static final String SUCCESS_MESSAGE = "%s PutItem operation with TTL successful.";
    private static final String TABLE_NOT_FOUND_ERROR = "Error: The Amazon DynamoDB table \"%s\" can't be found.";

    private final DynamoDbClient dynamoDbClient;

    /**
     * Constructs a CreateTTL instance with the specified DynamoDB client.
     *
     * @param dynamoDbClient The DynamoDB client to use
     */
    public CreateTTL(final DynamoDbClient dynamoDbClient) {
        this.dynamoDbClient = dynamoDbClient;
    }

    /**
     * Constructs a CreateTTL with a default DynamoDB client.
     */
    public CreateTTL() {
        this.dynamoDbClient = null;
    }

    /**
     * Main method to demonstrate creating an item with TTL.
     *
     * @param args Command line arguments
     */
    public static void main(final String[] args) {
        try {
            int result = new CreateTTL().processArgs(args);
            System.exit(result);
        } catch (Exception e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
    }

    /**
     * Process command line arguments and create an item with TTL.
     *
     * @param args Command line arguments
     * @return 0 if successful, non-zero otherwise
     * @throws ResourceNotFoundException If the table doesn't exist
     * @throws DynamoDbException If an error occurs during the operation
     * @throws IllegalArgumentException If arguments are invalid
     */
    public int processArgs(final String[] args) {
        // Argument validation (remove or replace this line when reusing this code)
        CodeSampleUtils.validateArgs(args, new int[] {3, 4}, USAGE);

        final String tableName = args[0];
        final String primaryKey = args[1];
        final String sortKey = args[2];
        final Region region = Optional.ofNullable(args.length > 3 ? args[3] : null)
            .map(Region::of)
            .orElse(Region.US_EAST_1);

        try (DynamoDbClient ddb = dynamoDbClient != null
            ? dynamoDbClient
            : DynamoDbClient.builder().region(region).build()) {
            final CreateTTL createTTL = new CreateTTL(ddb);
            createTTL.createItemWithTTL(tableName, primaryKey, sortKey);
            return 0;
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * Creates an item in the specified table with TTL attributes.
     *
     * @param tableName The name of the table
     * @param primaryKeyValue The value for the primary key
     * @param sortKeyValue The value for the sort key
     * @return The response from the PutItem operation
     * @throws ResourceNotFoundException If the table doesn't exist
     * @throws DynamoDbException If an error occurs during the operation
     */
    public PutItemResponse createItemWithTTL(
        final String tableName, final String primaryKeyValue, final String sortKeyValue) {
        // Get current time in epoch second format
        final long createDate = System.currentTimeMillis() / 1000;

        // Calculate expiration time 90 days from now in epoch second format
        final long expireDate = createDate + (DAYS_TO_EXPIRE * SECONDS_PER_DAY);

        final Map<String, AttributeValue> itemMap = new HashMap<>();
        itemMap.put(
            PRIMARY_KEY_ATTR, AttributeValue.builder().s(primaryKeyValue).build());
        itemMap.put(SORT_KEY_ATTR, AttributeValue.builder().s(sortKeyValue).build());
        itemMap.put(
            CREATION_DATE_ATTR,
            AttributeValue.builder().n(String.valueOf(createDate)).build());
        itemMap.put(
            EXPIRE_AT_ATTR,
            AttributeValue.builder().n(String.valueOf(expireDate)).build());

        final PutItemRequest request =
            PutItemRequest.builder().tableName(tableName).item(itemMap).build();

        try {
            final PutItemResponse response = dynamoDbClient.putItem(request);
            System.out.println(String.format(SUCCESS_MESSAGE, tableName));
            return response;
        } catch (ResourceNotFoundException e) {
            System.err.format(TABLE_NOT_FOUND_ERROR, tableName);
            throw e;
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            throw e;
        }
    }
}
```
+  Para obtener información sobre la API, consulte [PutItem](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/PutItem) en la *referencia de la API de AWS SDK for Java 2.x*. 

------
#### [ JavaScript ]

**SDK para JavaScript (v3)**  

```
import { DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";

export function createDynamoDBItem(table_name, region, partition_key, sort_key) {
    const client = new DynamoDBClient({
        region: region,
        endpoint: `https://dynamodb.${region}.amazonaws.com`
    });

    // Get the current time in epoch second format
    const current_time = Math.floor(new Date().getTime() / 1000);

    // Calculate the expireAt time (90 days from now) in epoch second format
    const expire_at = Math.floor((new Date().getTime() + 90 * 24 * 60 * 60 * 1000) / 1000);

    // Create DynamoDB item
    const item = {
        'partitionKey': {'S': partition_key},
        'sortKey': {'S': sort_key},
        'createdAt': {'N': current_time.toString()},
        'expireAt': {'N': expire_at.toString()}
    };

    const putItemCommand = new PutItemCommand({
        TableName: table_name,
        Item: item,
        ProvisionedThroughput: {
            ReadCapacityUnits: 1,
            WriteCapacityUnits: 1,
        },
    });

    client.send(putItemCommand, function(err, data) {
        if (err) {
            console.log("Exception encountered when creating item %s, here's what happened: ", data, err);
            throw err;
        } else {
            console.log("Item created successfully: %s.", data);
            return data;
        }
    });
}

// Example usage (commented out for testing)
// createDynamoDBItem('your-table-name', 'us-east-1', 'your-partition-key-value', 'your-sort-key-value');
```
+  Para obtener información sobre la API, consulte [PutItem](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/dynamodb/command/PutItemCommand) en la *referencia de la API de AWS SDK para JavaScript*. 

------
#### [ Python ]

**SDK para Python (Boto3)**  

```
from datetime import datetime, timedelta

import boto3


def create_dynamodb_item(table_name, region, primary_key, sort_key):
    """
    Creates a DynamoDB item with an attached expiry attribute.

    :param table_name: Table name for the boto3 resource to target when creating an item
    :param region: string representing the AWS region. Example: `us-east-1`
    :param primary_key: one attribute known as the partition key.
    :param sort_key: Also known as a range attribute.
    :return: Void (nothing)
    """
    try:
        dynamodb = boto3.resource("dynamodb", region_name=region)
        table = dynamodb.Table(table_name)

        # Get the current time in epoch second format
        current_time = int(datetime.now().timestamp())

        # Calculate the expiration time (90 days from now) in epoch second format
        expiration_time = int((datetime.now() + timedelta(days=90)).timestamp())

        item = {
            "primaryKey": primary_key,
            "sortKey": sort_key,
            "creationDate": current_time,
            "expireAt": expiration_time,
        }
        response = table.put_item(Item=item)

        print("Item created successfully.")
        return response
    except Exception as e:
        print(f"Error creating item: {e}")
        raise e


# Use your own values
create_dynamodb_item(
    "your-table-name", "us-west-2", "your-partition-key-value", "your-sort-key-value"
)
```
+  Para obtener información sobre la API, consulte [PutItem](https://docs.aws.amazon.com/goto/boto3/dynamodb-2012-08-10/PutItem) en la *referencia de la API de AWS SDK para Python (Boto3)*. 

------

## Actualización de un elemento y del tiempo de vida
<a name="time-to-live-ttl-before-you-start-update"></a>

Este ejemplo es la continuación del que aparece en la [sección anterior](#time-to-live-ttl-before-you-start-create). La fecha de vencimiento se puede volver a calcular si se actualiza el elemento. En el siguiente ejemplo, se vuelve a calcular la marca de tiempo `expireAt` para que sea de 90 días a partir de la fecha actual.

En los siguientes ejemplos de código, se muestra cómo actualizar el TTL de un elemento.

------
#### [ Java ]

**SDK para Java 2.x**  
Actualice el TTL en un elemento de DynamoDB existente en una tabla.  

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException;
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
import software.amazon.awssdk.services.dynamodb.model.UpdateItemResponse;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

    public UpdateItemResponse updateItemWithTTL(
        final String tableName, final String primaryKeyValue, final String sortKeyValue) {
        // Get current time in epoch second format
        final long currentTime = System.currentTimeMillis() / 1000;

        // Calculate expiration time 90 days from now in epoch second format
        final long expireDate = currentTime + (DAYS_TO_EXPIRE * SECONDS_PER_DAY);

        // Create the key map for the item to update
        final Map<String, AttributeValue> keyMap = new HashMap<>();
        keyMap.put(PRIMARY_KEY_ATTR, AttributeValue.builder().s(primaryKeyValue).build());
        keyMap.put(SORT_KEY_ATTR, AttributeValue.builder().s(sortKeyValue).build());

        // Create the expression attribute values
        final Map<String, AttributeValue> expressionAttributeValues = new HashMap<>();
        expressionAttributeValues.put(
            ":c", AttributeValue.builder().n(String.valueOf(currentTime)).build());
        expressionAttributeValues.put(
            ":e", AttributeValue.builder().n(String.valueOf(expireDate)).build());

        final UpdateItemRequest request = UpdateItemRequest.builder()
            .tableName(tableName)
            .key(keyMap)
            .updateExpression(UPDATE_EXPRESSION)
            .expressionAttributeValues(expressionAttributeValues)
            .build();

        try {
            final UpdateItemResponse response = dynamoDbClient.updateItem(request);
            System.out.println(String.format(SUCCESS_MESSAGE, tableName));
            return response;
        } catch (ResourceNotFoundException e) {
            System.err.format(TABLE_NOT_FOUND_ERROR, tableName);
            throw e;
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            throw e;
        }
    }
```
+  Para obtener información sobre la API, consulte [UpdateItem](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/UpdateItem) en la *referencia de la API de AWS SDK for Java 2.x*. 

------
#### [ JavaScript ]

**SDK para JavaScript (v3)**  

```
import { DynamoDBClient, UpdateItemCommand } from "@aws-sdk/client-dynamodb";
import { marshall, unmarshall } from "@aws-sdk/util-dynamodb";

export const updateItem = async (tableName, partitionKey, sortKey, region = 'us-east-1') => {
    const client = new DynamoDBClient({
        region: region,
        endpoint: `https://dynamodb.${region}.amazonaws.com`
    });

    const currentTime = Math.floor(Date.now() / 1000);
    const expireAt = Math.floor((Date.now() + 90 * 24 * 60 * 60 * 1000) / 1000);

    const params = {
        TableName: tableName,
        Key: marshall({
            partitionKey: partitionKey,
            sortKey: sortKey
        }),
        UpdateExpression: "SET updatedAt = :c, expireAt = :e",
        ExpressionAttributeValues: marshall({
            ":c": currentTime,
            ":e": expireAt
        }),
    };

    try {
        const data = await client.send(new UpdateItemCommand(params));
        const responseData = unmarshall(data.Attributes);
        console.log("Item updated successfully: %s", responseData);
        return responseData;
    } catch (err) {
        console.error("Error updating item:", err);
        throw err;
    }
}

// Example usage (commented out for testing)
// updateItem('your-table-name', 'your-partition-key-value', 'your-sort-key-value');
```
+  Para obtener información sobre la API, consulte [UpdateItem](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/dynamodb/command/UpdateItemCommand) en la *referencia de la API de AWS SDK para JavaScript*. 

------
#### [ Python ]

**SDK para Python (Boto3)**  

```
from datetime import datetime, timedelta

import boto3


def update_dynamodb_item(table_name, region, primary_key, sort_key):
    """
    Update an existing DynamoDB item with a TTL.
    :param table_name: Name of the DynamoDB table
    :param region: AWS Region of the table - example `us-east-1`
    :param primary_key: one attribute known as the partition key.
    :param sort_key: Also known as a range attribute.
    :return: Void (nothing)
    """
    try:
        # Create the DynamoDB resource.
        dynamodb = boto3.resource("dynamodb", region_name=region)
        table = dynamodb.Table(table_name)

        # Get the current time in epoch second format
        current_time = int(datetime.now().timestamp())

        # Calculate the expireAt time (90 days from now) in epoch second format
        expire_at = int((datetime.now() + timedelta(days=90)).timestamp())

        table.update_item(
            Key={"partitionKey": primary_key, "sortKey": sort_key},
            UpdateExpression="set updatedAt=:c, expireAt=:e",
            ExpressionAttributeValues={":c": current_time, ":e": expire_at},
        )

        print("Item updated successfully.")
    except Exception as e:
        print(f"Error updating item: {e}")


# Replace with your own values
update_dynamodb_item(
    "your-table-name", "us-west-2", "your-partition-key-value", "your-sort-key-value"
)
```
+  Para obtener información la API, consulte [UpdateItem](https://docs.aws.amazon.com/goto/boto3/dynamodb-2012-08-10/UpdateItem) en la *referencia de la API de AWS SDK para Python (Boto3)*. 

------

Los ejemplos de TTL que se analizan en esta introducción muestran un método para garantizar que en la tabla solo se guarden los elementos actualizados recientemente. La vida útil de los elementos actualizados se prolonga, mientras que los que no se actualizan tras su creación caducan y se eliminan sin costo alguno, lo que reduce el almacenamiento y mantiene las tablas limpias.

# Uso de elementos caducados y el período de vida (TTL)
<a name="ttl-expired-items"></a>

Los elementos vencidos que aún deben eliminarse se pueden filtrar de las operaciones de lectura y escritura. Esto resulta útil en situaciones en las que los datos vencidos ya no son válidos y no deben utilizarse. Si no están filtrados, seguirán mostrándose en las operaciones de lectura y escritura hasta que se eliminen en el proceso en segundo plano.

**nota**  
Estos elementos seguirán contabilizándose para los costos de almacenamiento y lectura hasta que se eliminen.

Las eliminaciones de TTL se pueden identificar en DynamoDB Streams, pero solo en la región en la que se han eliminado. Las eliminaciones de TTL que se replican en las regiones de la tabla global no se pueden identificar en las transmisiones de DynamoDB de las regiones en las que se replica la eliminación.

## Filtrado de los elementos vencidos de las operaciones de lectura
<a name="ttl-expired-items-filter"></a>

Para las operaciones de lectura, como [Scan](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html) y [Query](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html), una expresión de filtro puede filtrar los elementos vencidos que aún no se han eliminado. Como se muestra en el siguiente fragmento de código, la expresión de filtro puede filtrar los elementos en los que el tiempo de TTL sea igual o inferior al tiempo actual. Por ejemplo, el código del SDK de Python incluye una declaración de asignación que obtiene la hora actual como una variable (`now`) y la convierte en `int` para el formato de tiempo Epoch.

En los siguientes ejemplos de código, se muestra cómo consultar elementos de TTL.

------
#### [ Java ]

**SDK para Java 2.x**  
Consulte una expresión filtrada para recopilar elementos de TTL en una tabla de DynamoDB mediante AWS SDK for Java 2.x.  

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.QueryRequest;
import software.amazon.awssdk.services.dynamodb.model.QueryResponse;
import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException;

import java.util.Map;
import java.util.Optional;

        final QueryRequest request = QueryRequest.builder()
            .tableName(tableName)
            .keyConditionExpression(KEY_CONDITION_EXPRESSION)
            .filterExpression(FILTER_EXPRESSION)
            .expressionAttributeNames(expressionAttributeNames)
            .expressionAttributeValues(expressionAttributeValues)
            .build();

        try (DynamoDbClient ddb = dynamoDbClient != null
            ? dynamoDbClient
            : DynamoDbClient.builder().region(region).build()) {
            final QueryResponse response = ddb.query(request);
            System.out.println("Query successful. Found " + response.count() + " items that have not expired yet.");

            // Print each item
            response.items().forEach(item -> {
                System.out.println("Item: " + item);
            });

            return 0;
        } catch (ResourceNotFoundException e) {
            System.err.format(TABLE_NOT_FOUND_ERROR, tableName);
            throw e;
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            throw e;
        }
```
+  Para obtener información sobre la API, consulte [Query](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/Query) en la *referencia de la API de AWS SDK for Java 2.x*. 

------
#### [ JavaScript ]

**SDK para JavaScript (v3)**  
Consulte una expresión filtrada para recopilar elementos de TTL en una tabla de DynamoDB mediante AWS SDK para JavaScript.  

```
import { DynamoDBClient, QueryCommand } from "@aws-sdk/client-dynamodb";
import { marshall, unmarshall } from "@aws-sdk/util-dynamodb";

export const queryFiltered = async (tableName, primaryKey, region = 'us-east-1') => {
    const client = new DynamoDBClient({
        region: region,
        endpoint: `https://dynamodb.${region}.amazonaws.com`
    });

    const currentTime = Math.floor(Date.now() / 1000);

    const params = {
        TableName: tableName,
        KeyConditionExpression: "#pk = :pk",
        FilterExpression: "#ea > :ea",
        ExpressionAttributeNames: {
            "#pk": "primaryKey",
            "#ea": "expireAt"
        },
        ExpressionAttributeValues: marshall({
            ":pk": primaryKey,
            ":ea": currentTime
        })
    };

    try {
        const { Items } = await client.send(new QueryCommand(params));
        Items.forEach(item => {
            console.log(unmarshall(item))
        });
        return Items;
    } catch (err) {
        console.error(`Error querying items: ${err}`);
        throw err;
    }
}

// Example usage (commented out for testing)
// queryFiltered('your-table-name', 'your-partition-key-value');
```
+  Para obtener información sobre la API, consulte [Query](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/dynamodb/command/QueryCommand) en la *referencia de la API de AWS SDK para JavaScript*. 

------
#### [ Python ]

**SDK para Python (Boto3)**  
Consulte una expresión filtrada para recopilar elementos de TTL en una tabla de DynamoDB mediante AWS SDK para Python (Boto3).  

```
from datetime import datetime

import boto3


def query_dynamodb_items(table_name, partition_key):
    """

    :param table_name: Name of the DynamoDB table
    :param partition_key:
    :return:
    """
    try:
        # Initialize a DynamoDB resource
        dynamodb = boto3.resource("dynamodb", region_name="us-east-1")

        # Specify your table
        table = dynamodb.Table(table_name)

        # Get the current time in epoch format
        current_time = int(datetime.now().timestamp())

        # Perform the query operation with a filter expression to exclude expired items
        # response = table.query(
        #    KeyConditionExpression=boto3.dynamodb.conditions.Key('partitionKey').eq(partition_key),
        #    FilterExpression=boto3.dynamodb.conditions.Attr('expireAt').gt(current_time)
        # )
        response = table.query(
            KeyConditionExpression=dynamodb.conditions.Key("partitionKey").eq(partition_key),
            FilterExpression=dynamodb.conditions.Attr("expireAt").gt(current_time),
        )

        # Print the items that are not expired
        for item in response["Items"]:
            print(item)

    except Exception as e:
        print(f"Error querying items: {e}")


# Call the function with your values
query_dynamodb_items("Music", "your-partition-key-value")
```
+  Para obtener información sobre la API, consulte [Query](https://docs.aws.amazon.com/goto/boto3/dynamodb-2012-08-10/Query) en la *referencia de la API de AWS SDK para Python (Boto3)*. 

------

## Escritura condicional en los elementos vencidos
<a name="ttl-expired-items-conditional-write"></a>

Se puede usar una expresión condicional para evitar escrituras en los elementos vencidos. El siguiente fragmento de código es una actualización condicional que comprueba si el tiempo de vencimiento es superior al tiempo actual. Si es verdadero, la operación de escritura continuará.

En los siguientes ejemplos de código se muestra cómo actualizar de forma condicional el TTL de un elemento.

------
#### [ Java ]

**SDK para Java 2.x**  
Actualice el TTL en un elemento de DynamoDB existente en una tabla con una condición.  

```
package com.amazon.samplelib.ttl;

import com.amazon.samplelib.CodeSampleUtils;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException;
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
import software.amazon.awssdk.services.dynamodb.model.UpdateItemResponse;

import java.util.Map;
import java.util.Optional;

/**
 * Updates an item in a DynamoDB table with TTL attributes using a conditional expression.
 * This class demonstrates how to conditionally update TTL expiration timestamps.
 */
public class UpdateTTLConditional {

    private static final String USAGE =
        """
            Usage:
                <tableName> <primaryKey> <sortKey> <region>
            Where:
                tableName - The Amazon DynamoDB table being queried.
                primaryKey - The name of the primary key. Also known as the hash or partition key.
                sortKey - The name of the sort key. Also known as the range attribute.
                region (optional) - The AWS region that the Amazon DynamoDB table is located in. (Default: us-east-1)
            """;
    private static final int DAYS_TO_EXPIRE = 90;
    private static final int SECONDS_PER_DAY = 24 * 60 * 60;
    private static final String PRIMARY_KEY_ATTR = "primaryKey";
    private static final String SORT_KEY_ATTR = "sortKey";
    private static final String UPDATED_AT_ATTR = "updatedAt";
    private static final String EXPIRE_AT_ATTR = "expireAt";
    private static final String UPDATE_EXPRESSION = "SET " + UPDATED_AT_ATTR + "=:c, " + EXPIRE_AT_ATTR + "=:e";
    private static final String CONDITION_EXPRESSION = "attribute_exists(" + PRIMARY_KEY_ATTR + ")";
    private static final String SUCCESS_MESSAGE = "%s UpdateItem operation with TTL successful.";
    private static final String CONDITION_FAILED_MESSAGE = "Condition check failed. Item does not exist.";
    private static final String TABLE_NOT_FOUND_ERROR = "Error: The Amazon DynamoDB table \"%s\" can't be found.";

    private final DynamoDbClient dynamoDbClient;

    /**
     * Constructs an UpdateTTLConditional with a default DynamoDB client.
     */
    public UpdateTTLConditional() {
        this.dynamoDbClient = null;
    }

    /**
     * Constructs an UpdateTTLConditional with the specified DynamoDB client.
     *
     * @param dynamoDbClient The DynamoDB client to use
     */
    public UpdateTTLConditional(final DynamoDbClient dynamoDbClient) {
        this.dynamoDbClient = dynamoDbClient;
    }

    /**
     * Main method to demonstrate conditionally updating an item with TTL.
     *
     * @param args Command line arguments
     */
    public static void main(final String[] args) {
        try {
            int result = new UpdateTTLConditional().processArgs(args);
            System.exit(result);
        } catch (Exception e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
    }

    /**
     * Process command line arguments and conditionally update an item with TTL.
     *
     * @param args Command line arguments
     * @return 0 if successful, non-zero otherwise
     * @throws ResourceNotFoundException If the table doesn't exist
     * @throws DynamoDbException If an error occurs during the operation
     * @throws IllegalArgumentException If arguments are invalid
     */
    public int processArgs(final String[] args) {
        // Argument validation (remove or replace this line when reusing this code)
        CodeSampleUtils.validateArgs(args, new int[] {3, 4}, USAGE);

        final String tableName = args[0];
        final String primaryKey = args[1];
        final String sortKey = args[2];
        final Region region = Optional.ofNullable(args.length > 3 ? args[3] : null)
            .map(Region::of)
            .orElse(Region.US_EAST_1);

        // Get current time in epoch second format
        final long currentTime = System.currentTimeMillis() / 1000;

        // Calculate expiration time 90 days from now in epoch second format
        final long expireDate = currentTime + (DAYS_TO_EXPIRE * SECONDS_PER_DAY);

        // Create the key map for the item to update
        final Map<String, AttributeValue> keyMap = Map.of(
            PRIMARY_KEY_ATTR, AttributeValue.builder().s(primaryKey).build(),
            SORT_KEY_ATTR, AttributeValue.builder().s(sortKey).build());

        // Create the expression attribute values
        final Map<String, AttributeValue> expressionAttributeValues = Map.of(
            ":c", AttributeValue.builder().n(String.valueOf(currentTime)).build(),
            ":e", AttributeValue.builder().n(String.valueOf(expireDate)).build());

        final UpdateItemRequest request = UpdateItemRequest.builder()
            .tableName(tableName)
            .key(keyMap)
            .updateExpression(UPDATE_EXPRESSION)
            .conditionExpression(CONDITION_EXPRESSION)
            .expressionAttributeValues(expressionAttributeValues)
            .build();

        try (DynamoDbClient ddb = dynamoDbClient != null
            ? dynamoDbClient
            : DynamoDbClient.builder().region(region).build()) {
            final UpdateItemResponse response = ddb.updateItem(request);
            System.out.println(String.format(SUCCESS_MESSAGE, tableName));
            return 0;
        } catch (ConditionalCheckFailedException e) {
            System.err.println(CONDITION_FAILED_MESSAGE);
            throw e;
        } catch (ResourceNotFoundException e) {
            System.err.format(TABLE_NOT_FOUND_ERROR, tableName);
            throw e;
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            throw e;
        }
    }
}
```
+  Para obtener información sobre la API, consulte [UpdateItem](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/UpdateItem) en la *referencia de la API de AWS SDK for Java 2.x*. 

------
#### [ JavaScript ]

**SDK para JavaScript (v3)**  
Actualice el TTL en un elemento de DynamoDB existente en una tabla con una condición.  

```
import { DynamoDBClient, UpdateItemCommand } from "@aws-sdk/client-dynamodb";
import { marshall, unmarshall } from "@aws-sdk/util-dynamodb";

export const updateItemConditional = async (tableName, partitionKey, sortKey, region = 'us-east-1', newAttribute = 'default-value') => {
    const client = new DynamoDBClient({
        region: region,
        endpoint: `https://dynamodb.${region}.amazonaws.com`
    });

    const currentTime = Math.floor(Date.now() / 1000);

    const params = {
        TableName: tableName,
        Key: marshall({
            artist: partitionKey,
            album: sortKey
        }),
        UpdateExpression: "SET newAttribute = :newAttribute",
        ConditionExpression: "expireAt > :expiration",
        ExpressionAttributeValues: marshall({
            ':newAttribute': newAttribute,
            ':expiration': currentTime
        }),
        ReturnValues: "ALL_NEW"
    };

    try {
        const response = await client.send(new UpdateItemCommand(params));
        const responseData = unmarshall(response.Attributes);
        console.log("Item updated successfully: ", responseData);
        return responseData;
    } catch (error) {
        if (error.name === "ConditionalCheckFailedException") {
            console.log("Condition check failed: Item's 'expireAt' is expired.");
        } else {
            console.error("Error updating item: ", error);
        }
        throw error;
    }
};

// Example usage (commented out for testing)
// updateItemConditional('your-table-name', 'your-partition-key-value', 'your-sort-key-value');
```
+  Para obtener información sobre la API, consulte [UpdateItem](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/dynamodb/command/UpdateItemCommand) en la *referencia de la API de AWS SDK para JavaScript*. 

------
#### [ Python ]

**SDK para Python (Boto3)**  
Actualice el TTL en un elemento de DynamoDB existente en una tabla con una condición.  

```
from datetime import datetime, timedelta

import boto3
from botocore.exceptions import ClientError


def update_dynamodb_item_ttl(table_name, region, primary_key, sort_key, ttl_attribute):
    """
    Updates an existing record in a DynamoDB table with a new or updated TTL attribute.

    :param table_name: Name of the DynamoDB table
    :param region: AWS Region of the table - example `us-east-1`
    :param primary_key: one attribute known as the partition key.
    :param sort_key: Also known as a range attribute.
    :param ttl_attribute: name of the TTL attribute in the target DynamoDB table
    :return:
    """
    try:
        dynamodb = boto3.resource("dynamodb", region_name=region)
        table = dynamodb.Table(table_name)

        # Generate updated TTL in epoch second format
        updated_expiration_time = int((datetime.now() + timedelta(days=90)).timestamp())

        # Define the update expression for adding/updating a new attribute
        update_expression = "SET newAttribute = :val1"

        # Define the condition expression for checking if 'expireAt' is not expired
        condition_expression = "expireAt > :val2"

        # Define the expression attribute values
        expression_attribute_values = {":val1": ttl_attribute, ":val2": updated_expiration_time}

        response = table.update_item(
            Key={"primaryKey": primary_key, "sortKey": sort_key},
            UpdateExpression=update_expression,
            ConditionExpression=condition_expression,
            ExpressionAttributeValues=expression_attribute_values,
        )

        print("Item updated successfully.")
        return response["ResponseMetadata"]["HTTPStatusCode"]  # Ideally a 200 OK
    except ClientError as e:
        if e.response["Error"]["Code"] == "ConditionalCheckFailedException":
            print("Condition check failed: Item's 'expireAt' is expired.")
        else:
            print(f"Error updating item: {e}")
    except Exception as e:
        print(f"Error updating item: {e}")


# replace with your values
update_dynamodb_item_ttl(
    "your-table-name",
    "us-east-1",
    "your-partition-key-value",
    "your-sort-key-value",
    "your-ttl-attribute-value",
)
```
+  Para obtener información la API, consulte [UpdateItem](https://docs.aws.amazon.com/goto/boto3/dynamodb-2012-08-10/UpdateItem) en la *referencia de la API de AWS SDK para Python (Boto3)*. 

------

## Identificación de elementos eliminados en DynamoDB Streams
<a name="ttl-expired-items-identifying"></a>

El registro de secuencias contiene el campo de identidad del usuario `Records[<index>].userIdentity`. Los elementos que elimina el proceso TTL tienen los campos siguientes:

```
Records[<index>].userIdentity.type
"Service"

Records[<index>].userIdentity.principalId
"dynamodb.amazonaws.com"
```

En el código JSON siguiente se muestra la parte pertinente de un registro de secuencias único:

```
"Records": [ 
  { 
	... 
		"userIdentity": {
		"type": "Service", 
      	"principalId": "dynamodb.amazonaws.com" 
   	} 
   ... 
	} 
]
```

# Consulta de tablas en DynamoDB
<a name="Query"></a>

Puede usar la operación de la API `Query` en Amazon DynamoDB para buscar elementos según los valores de clave principal.

Debe proporcionar el nombre del atributo de clave de partición y un único valor para dicho atributo. `Query` devuelve todos los elementos que contengan ese valor de clave de partición. Si lo desea, puede proporcionar un atributo de clave de ordenación y utilizar un operador de comparación para limitar los resultados de búsqueda.

Para obtener más información sobre cómo usar `Query`, como la sintaxis de la solicitud, los parámetros de respuesta y ejemplos adicionales, consulte [Query](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html) en la *Referencia de la API de Amazon DynamoDB*.

**Topics**
+ [Expresiones de condición de clave para la operación Query en DynamoDB](Query.KeyConditionExpressions.md)
+ [Expresiones de filtro para la operación Query en DynamoDB](Query.FilterExpression.md)
+ [Paginación de los resultados de consulta de tabla en DynamoDB](Query.Pagination.md)
+ [Otros aspectos del trabajo con la operación Query en DynamoDB](Query.Other.md)

# Expresiones de condición de clave para la operación Query en DynamoDB
<a name="Query.KeyConditionExpressions"></a>

Puede utilizar cualquier nombre de atributo en una expresión de condición de clave, siempre y cuando el primer carácter sea `a-z` o `A-Z` y el resto de los caracteres (a partir del segundo carácter, si lo hay) sea `a-z`, `A-Z` o `0-9`. Además, el nombre de atributo no debe ser una palabra reservada de DynamoDB. Para obtener una lista completa de estas palabras, consulte . [Palabras reservadas en DynamoDB](ReservedWords.md).) Si un nombre de atributo no cumple estos requisitos, debe definir un nombre de atributo de expresión como marcador de posición. Para obtener más información, consulte [Nombres de atributos de expresión (Alias) en DynamoDB](Expressions.ExpressionAttributeNames.md).

DynamoDB almacena cerca unos de otros y ordenados según su clave de ordenación aquellos elementos que tienen un valor de clave de partición determinado. En una operación `Query`, DynamoDB recupera los elementos de forma ordenada y, a continuación, los procesa mediante las expresiones `KeyConditionExpression` y `FilterExpression` que estén presentes. Solo entonces devuelve los resultados de `Query` al cliente.

Una operación `Query` siempre devuelve un conjunto de resultados. Si no se encuentran elementos coincidentes, el conjunto de resultados está vacío.

`Query`Los resultados de siempre se ordenan según el valor de la clave de ordenación. Si el tipo de datos de la clave de ordenación es `Number`, los resultados se devuelven en orden numérico. De lo contrario, los resultados se devuelven según el orden de los bytes UTF-8. De forma predeterminada, el orden es ascendente. Para invertirlo, establezca el parámetro `ScanIndexForward` en `false`.

En una sola operación `Query` se puede recuperar un máximo de 1 MB de datos. Este límite se aplica antes de que `FilterExpression` o `ProjectionExpression` se apliquen a los resultados. Si `LastEvaluatedKey` está presente en la respuesta y su valor no es null, debe paginar el conjunto de resultados (consulte [Paginación de los resultados de consulta de tabla en DynamoDB](Query.Pagination.md)).

## Ejemplos de expresión de condición de clave
<a name="Query.KeyConditionExpressions-example"></a>

Para especificar los criterios de búsqueda, se utiliza una *expresión de condición de clave*; se trata de una cadena que determina los elementos que se van a leer en la tabla o el índice.

Debe especificar el nombre y valor de la clave de partición como una condición de igualdad. No puede utilizar un atributo no clave en una expresión de condición clave.

Si lo desea, puede proporcionar una segunda condición para la clave de ordenación (en caso de incluirse). En la condición de clave de ordenación se debe utilizar uno de los siguientes operadores de comparación:
+ `a = b`: es true si el atributo *a* es igual al valor *b*.
+ `a < b`: es true si *a* es menor que *b*.
+ `a <= b`: es true si *a* es menor o igual que *b*.
+ `a > b`: es true si *a* es mayor que *b*.
+ `a >= b`: es true si *a* es mayor o igual que *b*.
+ `a BETWEEN b AND c`: es true si *a* es mayor o igual que *b* y menor o igual que *c*.

También se admite la siguiente función:
+ `begins_with (a, substr)`: es true si el valor del atributo `a` comienza por una subcadena determinada.

En los siguientes ejemplos de la AWS Command Line Interface (AWS CLI) se muestra el uso de expresiones de condición de clave. Estas expresiones utilizan marcadores de posición (como `:name` y `:sub`) en lugar de los valores reales. Para obtener más información, consulte [Nombres de atributos de expresión (Alias) en DynamoDB](Expressions.ExpressionAttributeNames.md) y [Uso de valores de atributos de expresión en DynamoDB](Expressions.ExpressionAttributeValues.md).

**Example**  
Consulta la tabla `Thread` para buscar un valor concreto de `ForumName` (clave de partición). La consulta leerá todos los elementos que tengan ese valor de `ForumName`, porque la clave de ordenación (`Subject`) no se incluye en `KeyConditionExpression`.  

```
aws dynamodb query \
    --table-name Thread \
    --key-condition-expression "ForumName = :name" \
    --expression-attribute-values  '{":name":{"S":"Amazon DynamoDB"}}'
```

**Example**  
Consulta la tabla `Thread` para buscar un valor concreto de `ForumName` (clave de partición), pero esta vez devuelve solo los elementos que tienen un valor determinado de `Subject` (clave de ordenación).  

```
aws dynamodb query \
    --table-name Thread \
    --key-condition-expression "ForumName = :name and Subject = :sub" \
    --expression-attribute-values  file://values.json
```
Los argumentos de `--expression-attribute-values` se almacenan en el archivo `values.json`.  

```
{
    ":name":{"S":"Amazon DynamoDB"},
    ":sub":{"S":"DynamoDB Thread 1"}
}
```

**Example**  
Consulta la tabla `Reply` para buscar un valor concreto de `Id` (clave de partición), pero solo devuelve los elementos cuyo valor de `ReplyDateTime` (clave de ordenación) comienza por determinados caracteres.  

```
aws dynamodb query \
    --table-name Reply \
    --key-condition-expression "Id = :id and begins_with(ReplyDateTime, :dt)" \
    --expression-attribute-values  file://values.json
```
Los argumentos de `--expression-attribute-values` se almacenan en el archivo `values.json`.  

```
{
    ":id":{"S":"Amazon DynamoDB#DynamoDB Thread 1"},
    ":dt":{"S":"2015-09"}
}
```

# Expresiones de filtro para la operación Query en DynamoDB
<a name="Query.FilterExpression"></a>

Si tiene que refinar más los resultados de `Query`, si lo desea puede indicar una expresión de filtro. Una *expresión de filtro* determina qué elementos de los resultados de `Query` se deben devolver al usuario. Todos los demás resultados se descartan.

Una expresión de filtro se aplica después de que la operación `Query` haya finalizado, pero antes de devolver los resultados. Por consiguiente, `Query` consume la misma cantidad de capacidad de lectura aunque se especifique una expresión de filtro.

En una operación `Query` se puede recuperar un máximo de 1 MB de datos. Este límite se aplica antes de evaluar la expresión de filtro.

Una expresión de filtro no puede contener atributos de clave de partición ni de clave de ordenación. Esos atributos se deben especificar en la expresión de condición de clave, no en la expresión de filtro.

La sintaxis de una expresión de filtro es similar a la de una expresión de condición de clave. Las expresiones de filtro pueden utilizar los mismos comparadores, funciones y operadores lógicos que las expresiones de condición de clave. Además, las expresiones de filtro pueden usar los operadores “no es igual” (`<>`), `OR`, `CONTAINS`, `IN`, `BEGINS_WITH`, `BETWEEN`, `EXISTS` y `SIZE`. Para obtener más información, consulte [Expresiones de condición de clave para la operación Query en DynamoDB](Query.KeyConditionExpressions.md) y [Sintaxis de las expresiones de filtro y condición](Expressions.OperatorsAndFunctions.md#Expressions.OperatorsAndFunctions.Syntax).

**Example**  
En el siguiente ejemplo de la AWS CLI, se consulta la tabla `Thread` para buscar un valor concreto de `ForumName` (clave de partición) y `Subject` (clave de ordenación). De los elementos encontrados, solo se devuelven las conversaciones más populares; es decir, solo aquellas que tienen un valor de mayor que el número especificado `Views`.  

```
aws dynamodb query \
    --table-name Thread \
    --key-condition-expression "ForumName = :fn and Subject begins_with :sub" \
    --filter-expression "#v >= :num" \
    --expression-attribute-names '{"#v": "Views"}' \
    --expression-attribute-values file://values.json
```
Los argumentos de `--expression-attribute-values` se almacenan en el archivo `values.json`.  

```
{
    ":fn":{"S":"Amazon DynamoDB"},
    ":sub":{"S":"DynamoDB Thread 1"},
    ":num":{"N":"3"}
}
```
Tenga en cuenta que `Views` es una palabra reservada en DynamoDB (consulte [Palabras reservadas en DynamoDB](ReservedWords.md)), por lo que en este ejemplo se utiliza `#v` como marcador de posición. Para obtener más información, consulte [Nombres de atributos de expresión (Alias) en DynamoDB](Expressions.ExpressionAttributeNames.md).

**nota**  
Una expresión de filtro elimina los elementos del conjunto de resultados de `Query`. Si es posible, evite usar `Query` cuando prevea que va a recuperar gran cantidad de elementos, pero que tendrá que descartar la mayoría de ellos.

# Paginación de los resultados de consulta de tabla en DynamoDB
<a name="Query.Pagination"></a>

DynamoDB *pagina* los resultados de las operaciones `Query`. La paginación permite dividir los resultados de `Query` en "páginas" de datos con un tamaño de 1 MB (o menos). Una aplicación puede procesar la primera página de resultados, luego la segunda página, y así sucesivamente.

Una operación `Query` única devuelve solamente un conjunto de resultados que se ajuste al límite de tamaño de 1 MB. Para determinar si hay más resultados y para recuperarlos de página en página, las aplicaciones deben hacer lo siguiente: 

1. Examinar el resultado de `Query` de bajo nivel:
   + Si el resultado contiene una entrada `LastEvaluatedKey`, vaya al paso 2.
   + Si *no* figura `LastEvaluatedKey` en el resultado, no hay más elementos que recuperar.

1. Construya una `Query` con la misma `KeyConditionExpression`. Sin embargo, esta vez, se toma el valor `LastEvaluatedKey` del paso 1 y se usa como parámetro `ExclusiveStartKey` en la nueva solicitud `Query`.

1. Ejecutar la nueva solicitud `Query`.

1. Ir al paso 1.

Es decir, el valor de `LastEvaluatedKey` de la respuesta de `Query` debe utilizarse como valor de `ExclusiveStartKey` en la siguiente solicitud `Query`. Si no hay una entrada `LastEvaluatedKey` en una respuesta de `Query`, significa que se ha recuperado la última página de resultados. Si `LastEvaluatedKey` no está vacía, no significa necesariamente que haya más datos en el conjunto de resultados. La única forma de saber que se ha alcanzado el final del conjunto de resultados es cuando `LastEvaluatedKey` está vacío.

Puede utilizar la AWS CLI para ver este comportamiento. La AWS CLI envía solicitudes `Query` de bajo nivel a DynamoDB una y otra vez hasta que `LastEvaluatedKey` ya no esté presente en los resultados. Considere el siguiente ejemplo de la AWS CLI que recupera títulos de películas de un determinado año:

```
aws dynamodb query --table-name Movies \
    --projection-expression "title" \
    --key-condition-expression "#y = :yyyy" \
    --expression-attribute-names '{"#y":"year"}' \
    --expression-attribute-values '{":yyyy":{"N":"1993"}}' \
    --page-size 5 \
    --debug
```

Normalmente, la AWS CLI se encarga de la paginación automáticamente. No obstante, en este ejemplo, el parámetro AWS CLI de la `--page-size` limita el número de elementos por página. El parámetro `--debug` muestra información de bajo nivel de las solicitudes y las respuestas.

Si ejecuta el ejemplo, la primera respuesta de DynamoDB será similar a la siguiente.

```
2017-07-07 11:13:15,603 - MainThread - botocore.parsers - DEBUG - Response body:
b'{"Count":5,"Items":[{"title":{"S":"A Bronx Tale"}},
{"title":{"S":"A Perfect World"}},{"title":{"S":"Addams Family Values"}},
{"title":{"S":"Alive"}},{"title":{"S":"Benny & Joon"}}],
"LastEvaluatedKey":{"year":{"N":"1993"},"title":{"S":"Benny & Joon"}},
"ScannedCount":5}'
```

El elemento `LastEvaluatedKey` de la respuesta indica que no se han recuperado todos los elementos. La AWS CLI envía entonces otra solicitud `Query` a DynamoDB. Este patrón de solicitud y respuesta continúa hasta la respuesta final.

```
2017-07-07 11:13:16,291 - MainThread - botocore.parsers - DEBUG - Response body:
b'{"Count":1,"Items":[{"title":{"S":"What\'s Eating Gilbert Grape"}}],"ScannedCount":1}'
```

La ausencia de `LastEvaluatedKey` indica que no hay más elementos que recuperar.

**nota**  
Los SDK de AWS se encargan también de las respuestas de DynamoDB de bajo nivel (incluida la presencia o ausencia de `LastEvaluatedKey`) y proporcionan varias abstracciones para paginar los resultados de `Query`. Por ejemplo, la interfaz de documentos de SDK para Java proporciona compatibilidad con `java.util.Iterator` para que pueda examinar los resultados de uno en uno.  
Para ver ejemplos de código en diversos lenguajes de programación, consulte la [Guía de inicio de Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/gettingstartedguide/) y la documentación del SDK de AWS correspondiente a su lenguaje.

También puede reducir el tamaño de página limitando el número de elementos del conjunto de resultados, con el parámetro de `Limit` de la operación de `Query`.

Para obtener más información sobre cómo realizar consultas en DynamoDB, consulte [Consulta de tablas en DynamoDB](Query.md).

# Otros aspectos del trabajo con la operación Query en DynamoDB
<a name="Query.Other"></a>

En esta sección se tratan aspectos adicionales de la operación Query de DynamoDB, como la limitación del tamaño del resultado, el recuento de elementos analizados frente a los devueltos, la supervisión del consumo de capacidad de lectura y el control de la coherencia de lectura.

## Limitación del número de elementos del conjunto de resultados
<a name="Query.Limit"></a>

Con la operación `Query`, puede limitar el número de elementos que lee. Para ello, establezca el parámetro `Limit` en el número máximo de elementos que desee.

Por ejemplo, suponga que utiliza `Query` en una tabla con un valor de `Limit` de `6` y sin expresión de filtro. El resultado de `Query` contendrá los seis primeros elementos de la tabla que coincidan con la expresión de condición de clave de la solicitud.

Ahora, suponga que agrega una expresión de filtro a `Query`. En este caso, DynamoDB lee hasta seis elementos y, a continuación, devuelve solo aquellos que coinciden con la expresión de filtro. El resultado final de `Query` contiene seis elementos o menos, aunque más elementos hubieran coincidido con la expresión de filtro si DynamoDB hubiera seguido leyendo más elementos.

## Recuento de los elementos en los resultados
<a name="Query.Count"></a>

Además de los elementos que coinciden con los criterios, la respuesta de `Query` contiene las entradas siguientes:
+ `ScannedCount`: número de elementos que coinciden con la expresión de condición de clave *antes* de aplicar una expresión de filtro (si la hay).
+ `Count`: número de elementos restantes *después* de aplicar una expresión de filtro (si la hay).

**nota**  
Si no se utiliza una expresión de filtro, `ScannedCount` y `Count` tendrán el mismo valor.

Si el tamaño del conjunto de resultados de `Query` es mayor que 1 MB, entonces `ScannedCount` y `Count` representarán únicamente un recuento parcial de los elementos totales. Deberá llevar a cabo varias operaciones `Query` para recuperar todos los resultados (consulte [Paginación de los resultados de consulta de tabla en DynamoDB](Query.Pagination.md)).

Cada respuesta de `Query` contendrá los valores de `ScannedCount` y `Count` de los elementos que se han procesado en esa solicitud `Query` concreta. Para obtener totales globales para todas las solicitudes `Query`, puede llevar un recuento total de `ScannedCount` y `Count`.

## Unidades de capacidad consumidas por la consulta
<a name="Query.CapacityUnits"></a>

Puede utilizar cualquier tabla o índice secundario `Query`, siempre que proporcione el nombre del atributo de clave de partición y un valor único para ese atributo. `Query` devuelve todos los elementos que contienen ese valor de clave de partición. Opcionalmente, puede proporcionar un atributo de clave de clasificación y utilizar un operador de comparación para limitar los resultados de la búsqueda. `Query` Las operaciones de la API consumen unidades de capacidad de lectura, como se indica a continuación.


****  

| Si `Query` se aplica a... | DynamoDB consume unidades de capacidad de lectura de... | 
| --- | --- | 
| Tabla | La capacidad de lectura aprovisionada de la tabla. | 
| Índice secundario global | La capacidad de lectura aprovisionada del índice. | 
| Índice secundario local | La capacidad de lectura aprovisionada de la tabla base. | 

De forma predeterminada, una operación `Query` no devuelve datos sobre la cantidad de capacidad de lectura que consume. Sin embargo, puede especificar el parámetro `ReturnConsumedCapacity` en una solicitud `Query` para obtener esta información. A continuación se muestran los ajustes válidos de `ReturnConsumedCapacity`:
+ `NONE`: no se devuelven datos de capacidad consumida. (Esta es la opción predeterminada.)
+ `TOTAL`: la respuesta incluye el número total de unidades de capacidad de lectura consumidas.
+ `INDEXES`: la respuesta muestra el número total de unidades de capacidad de lectura consumidas, así como la capacidad consumida de cada tabla e índice a los que se ha obtenido acceso.

DynamoDB calcula el número de unidades de capacidad de lectura consumidas según el número de elementos y el tamaño de dichos elementos, no según la cantidad de datos que se devuelven a una aplicación. Por este motivo, el número de unidades de capacidad consumidas es el mismo si se solicitan todos los atributos (el comportamiento predeterminado) o solo algunos de ellos (mediante una expresión de proyección). El número también es el mismo tanto si se utiliza una expresión de filtro como si no. `Query`consume una unidad de capacidad de lectura mínima para realizar una lectura altamente coherente por segundo o dos lecturas coherentes posteriores por segundo para un elemento de hasta 4 KB. Para leer un elemento mayor que 4 KB, DynamoDB necesita unidades de solicitud de lectura adicionales. Con las tablas vacías y las tablas muy grandes con una cantidad escasa de claves de partición, es posible que se cobren algunas RCU adicionales además de la cantidad de datos consultados. Esto cubre el costo de atender la solicitud `Query`, incluso si no hay datos.

## Coherencia de lectura para la consulta
<a name="Query.ReadConsistency"></a>

De forma predeterminada, una operación `Query` lleva a cabo lecturas consistentes finales. Esto significa que los resultados de `Query` podrían no reflejar los cambios provocados por operaciones `PutItem` o `UpdateItem` realizadas recientemente. Para obtener más información, consulte [Coherencia de lectura de DynamoDB](HowItWorks.ReadConsistency.md).

Si requiere lecturas de consistencia alta, establezca el parámetro `ConsistentRead` en `true` en la solicitud `Query`.

# Análisis de tablas en DynamoDB
<a name="Scan"></a>

Una operación `Scan` en Amazon DynamoDB lee todos los elementos de una tabla o de un índice secundario. De manera predeterminada, una operación `Scan` devuelve todos los atributos de datos de todos los elementos de la tabla o el índice. Puede utilizar el parámetro `ProjectionExpression` para que `Scan` solo devuelva algunos de los atributos, en lugar de todos ellos.

`Scan` siempre devuelve un conjunto de resultados. Si no se encuentran elementos coincidentes, el conjunto de resultados está vacío.

En una sola solicitud `Scan` se puede recuperar un máximo de 1 MB de datos. Si lo desea, DynamoDB puede aplicar una expresión de filtro a estos datos, para reducir los resultados antes de que se devuelvan al usuario.

**Topics**
+ [Expresiones de filtro para el análisis](#Scan.FilterExpression)
+ [Limitación del número de elementos del conjunto de resultados](#Scan.Limit)
+ [Paginación de los resultados](#Scan.Pagination)
+ [Recuento de los elementos en los resultados](#Scan.Count)
+ [Unidades de capacidad que consume el análisis](#Scan.CapacityUnits)
+ [Coherencia de lectura para el análisis](#Scan.ReadConsistency)
+ [Análisis paralelo](#Scan.ParallelScan)

## Expresiones de filtro para el análisis
<a name="Scan.FilterExpression"></a>

Si tiene que refinar más los resultados de `Scan`, si lo desea puede indicar una expresión de filtro. Una *expresión de filtro* determina qué elementos de los resultados de `Scan` se deben devolver al usuario. Todos los demás resultados se descartan.

Una expresión de filtro se aplica después de que la operación `Scan` haya finalizado, pero antes de devolver los resultados. Por consiguiente, `Scan` consume la misma cantidad de capacidad de lectura aunque se especifique una expresión de filtro.

En una operación `Scan` se puede recuperar un máximo de 1 MB de datos. Este límite se aplica antes de evaluar la expresión de filtro.

Con `Scan`, puede especificar cualquier atributo en una expresión de filtro, incluidos los atributos de clave de partición y de clave de ordenación.

La sintaxis de una expresión de filtro es la misma que la de una expresión de condición. Las expresiones de filtro pueden utilizar los comparadores, funciones y operadores lógicos que las expresiones de condición. Consulte [Expresiones, operadores y funciones de condición y filtro en DynamoDB](Expressions.OperatorsAndFunctions.md) para obtener más información sobre operadores lógicos.

**Example**  
En el siguiente ejemplo de la AWS Command Line Interface (AWS CLI) se examina la tabla `Thread` y solo se devuelven los últimos elementos publicados por un usuario determinado.  

```
aws dynamodb scan \
     --table-name Thread \
     --filter-expression "LastPostedBy = :name" \
     --expression-attribute-values '{":name":{"S":"User A"}}'
```

## Limitación del número de elementos del conjunto de resultados
<a name="Scan.Limit"></a>

La operación `Scan` permite limitar el número de elementos que devuelve en el resultado. Para ello, establezca el parámetro `Limit` en el número máximo de elementos que desea que devuelva la operación `Scan`, antes de la evaluación de la expresión de filtro.

Por ejemplo, suponga que utiliza la operación `Scan` en una tabla con un valor de `Limit` de `6` y sin expresión de filtro. El resultado de `Scan` contiene los seis primeros elementos de la tabla.

Ahora, suponga que agrega una expresión de filtro a `Scan`. En este caso, DynamoDB aplica la expresión de filtro a los seis elementos que se hayan devuelto y descarta los que no coincidan. El resultado de `Scan` final contendrá 6 elementos o menos, según el número de elementos que el filtro elimine.

## Paginación de los resultados
<a name="Scan.Pagination"></a>

DynamoDB *pagina* los resultados de las operaciones `Scan`. La paginación permite dividir los resultados de `Scan` en "páginas" de datos con un tamaño de 1 MB (o menos). Una aplicación puede procesar la primera página de resultados, luego la segunda página, y así sucesivamente.

Una operación `Scan` única devuelve solamente un conjunto de resultados que se ajuste al límite de tamaño de 1 MB. 

Para determinar si hay más resultados y para recuperarlos de página en página, las aplicaciones deben hacer lo siguiente:

1. Examinar el resultado de `Scan` de bajo nivel:
   + Si el resultado contiene una entrada `LastEvaluatedKey`, vaya al paso 2.
   + Si *no* figura `LastEvaluatedKey` en el resultado, no hay más elementos que recuperar.

1. Crear una nueva solicitud `Scan` con los mismos parámetros que la anterior. Sin embargo, esta vez, se toma el valor `LastEvaluatedKey` del paso 1 y se usa como parámetro `ExclusiveStartKey` en la nueva solicitud `Scan`.

1. Ejecutar la nueva solicitud `Scan`.

1. Ir al paso 1.

Es decir, el valor de `LastEvaluatedKey` de la respuesta de `Scan` debe utilizarse como valor de `ExclusiveStartKey` en la siguiente solicitud `Scan`. Si no hay un componente `LastEvaluatedKey` en una respuesta de `Scan`, significa que se ha recuperado la última página de resultados. La ausencia de `LastEvaluatedKey` es la única forma de saber que se ha alcanzado el final del conjunto de resultados.

Puede utilizar la AWS CLI para ver este comportamiento. La AWS CLI envía solicitudes `Scan` de bajo nivel a DynamoDB una y otra vez hasta que `LastEvaluatedKey` ya no esté presente en los resultados. Considere el siguiente ejemplo de la AWS CLI que examina toda la tabla `Movies`, pero solo devuelve las películas de un determinado género.

```
aws dynamodb scan \
    --table-name Movies \
    --projection-expression "title" \
    --filter-expression 'contains(info.genres,:gen)' \
    --expression-attribute-values '{":gen":{"S":"Sci-Fi"}}' \
    --page-size 100  \
    --debug
```

Normalmente, la AWS CLI se encarga de la paginación automáticamente. No obstante, en este ejemplo, el parámetro AWS CLI de la `--page-size` limita el número de elementos por página. El parámetro `--debug` muestra información de bajo nivel de las solicitudes y las respuestas.

**nota**  
Los resultados de paginación también diferirán en función de los parámetros de entrada que pase.   
El uso de `aws dynamodb scan --table-name Prices --max-items 1` devuelve un `NextToken`
El uso de `aws dynamodb scan --table-name Prices --limit 1` devuelve un `LastEvaluatedKey`.
También tenga en cuenta que usar un `--starting-token` en particular requiere el valor de `NextToken`. 

Si ejecuta el ejemplo, la primera respuesta de DynamoDB será similar a la siguiente.

```
2017-07-07 12:19:14,389 - MainThread - botocore.parsers - DEBUG - Response body:
b'{"Count":7,"Items":[{"title":{"S":"Monster on the Campus"}},{"title":{"S":"+1"}},
{"title":{"S":"100 Degrees Below Zero"}},{"title":{"S":"About Time"}},{"title":{"S":"After Earth"}},
{"title":{"S":"Age of Dinosaurs"}},{"title":{"S":"Cloudy with a Chance of Meatballs 2"}}],
"LastEvaluatedKey":{"year":{"N":"2013"},"title":{"S":"Curse of Chucky"}},"ScannedCount":100}'
```

El elemento `LastEvaluatedKey` de la respuesta indica que no se han recuperado todos los elementos. La AWS CLI envía entonces otra solicitud `Scan` a DynamoDB. Este patrón de solicitud y respuesta continúa hasta la respuesta final.

```
2017-07-07 12:19:17,830 - MainThread - botocore.parsers - DEBUG - Response body:
b'{"Count":1,"Items":[{"title":{"S":"WarGames"}}],"ScannedCount":6}'
```

La ausencia de `LastEvaluatedKey` indica que no hay más elementos que recuperar.

**nota**  
Los SDK de AWS se encargan también de las respuestas de DynamoDB de bajo nivel (incluida la presencia o ausencia de `LastEvaluatedKey`) y proporcionan varias abstracciones para paginar los resultados de `Scan`. Por ejemplo, la interfaz de documentos de SDK para Java proporciona compatibilidad con `java.util.Iterator` para que pueda examinar los resultados de uno en uno.  
Para ver ejemplos de código en diversos lenguajes de programación, consulte la [Guía de inicio de Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/gettingstartedguide/) y la documentación del SDK de AWS correspondiente a su lenguaje.

## Recuento de los elementos en los resultados
<a name="Scan.Count"></a>

Además de los elementos que coinciden con los criterios, la respuesta de `Scan` contiene las entradas siguientes:
+ `ScannedCount`: El número de elementos evaluados, antes de que se aplique cualquier `ScanFilter`. Un valor de `ScannedCount` elevado, con pocos resultados de `Count` o ninguno indica que la operación `Scan` ha sido ineficiente. Si no ha utilizado un filtro en la solicitud, `ScannedCount` es igual que `Count`. 
+ `Count`: El número de elementos restantes *después* de aplicar una expresión de filtro (si la hay).

**nota**  
Si no se utiliza una expresión de filtro, `ScannedCount` y `Count` tendrán el mismo valor.

Si el tamaño del conjunto de resultados de `Scan` es mayor que 1 MB, entonces `ScannedCount` y `Count` representarán únicamente un recuento parcial de los elementos totales. Deberá llevar a cabo varias operaciones `Scan` para recuperar todos los resultados (consulte [Paginación de los resultados](#Scan.Pagination)).

Cada respuesta de `Scan` contendrá los valores de `ScannedCount` y `Count` de los elementos que se han procesado en esa solicitud `Scan` concreta. Para obtener totales globales para todas las solicitudes `Scan`, puede llevar un recuento total de `ScannedCount` y `Count`.

## Unidades de capacidad que consume el análisis
<a name="Scan.CapacityUnits"></a>

Puede utilizar la operación `Scan` en cualquier tabla o índice secundario. Las operaciones `Scan` consumen las unidades de capacidad de lectura, como se indica a continuación.


****  

| Si `Scan` se aplica a... | DynamoDB consume unidades de capacidad de lectura de... | 
| --- | --- | 
| Tabla | La capacidad de lectura aprovisionada de la tabla. | 
| Índice secundario global | La capacidad de lectura aprovisionada del índice. | 
| Índice secundario local | La capacidad de lectura aprovisionada de la tabla base. | 

**nota**  
Las políticas [basadas en recursos](access-control-resource-based.md) no admiten actualmente el acceso entre cuentas para operaciones de análisis de índices secundarios.

De forma predeterminada, una operación `Scan` no devuelve datos sobre la cantidad de capacidad de lectura que consume. Sin embargo, puede especificar el parámetro `ReturnConsumedCapacity` en una solicitud `Scan` para obtener esta información. A continuación se muestran los ajustes válidos de `ReturnConsumedCapacity`:
+ `NONE`: no se devuelven datos de capacidad consumida. (Esta es la opción predeterminada.)
+ `TOTAL`: la respuesta incluye el número total de unidades de capacidad de lectura consumidas.
+ `INDEXES`: la respuesta muestra el número total de unidades de capacidad de lectura consumidas, así como la capacidad consumida de cada tabla e índice a los que se ha obtenido acceso.

DynamoDB calcula el número de unidades de capacidad de lectura consumidas según el número de elementos y el tamaño de dichos elementos, no según la cantidad de datos que se devuelven a una aplicación. Por este motivo, el número de unidades de capacidad consumidas es el mismo si se solicitan todos los atributos (el comportamiento predeterminado) o solo algunos de ellos (mediante una expresión de proyección). El número también es el mismo tanto si se utiliza una expresión de filtro como si no. `Scan`consume una unidad de capacidad de lectura mínima para realizar una lectura altamente coherente por segundo o dos lecturas coherentes posteriores por segundo para un elemento de hasta 4 KB. Para leer un elemento mayor que 4 KB, DynamoDB necesita unidades de solicitud de lectura adicionales. Con las tablas vacías y las tablas muy grandes con una cantidad escasa de claves de partición, es posible que se cobren algunas RCU adicionales además de la cantidad de datos analizados. Esto cubre el costo de atender la solicitud `Scan`, incluso si no hay datos.

## Coherencia de lectura para el análisis
<a name="Scan.ReadConsistency"></a>

De forma predeterminada, una operación `Scan` lleva a cabo lecturas consistentes finales. Esto significa que los resultados de `Scan` podrían no reflejar los cambios provocados por operaciones `PutItem` o `UpdateItem` realizadas recientemente. Para obtener más información, consulte [Coherencia de lectura de DynamoDB](HowItWorks.ReadConsistency.md).

Si requiere lecturas de consistencia alta desde el momento en que se inicie la operación `Scan`, establezca el parámetro `ConsistentRead` en `true` en la solicitud `Scan`. De este modo, se asegurará de que todas las operaciones de escritura que se han completado antes de iniciar `Scan` se incluyan en la respuesta de `Scan`. 

Establecer `ConsistentRead` en `true` puede resultar útil para realizar backup o replicaciones de tablas, combinado con [DynamoDB Streams](./Streams.html). En primer lugar, se utiliza `Scan` con `ConsistentRead` establecido en true para obtener una copia consistente de los datos de la tabla. Durante la operación `Scan`, DynamoDB Streams registra la actividad de escritura adicional que se produce en la tabla. Una vez que la operación `Scan` ha finalizado, puede aplicar la actividad de escritura de la secuencia a la tabla.

**nota**  
Una operación `Scan` con `ConsistentRead` establecido en `true` consumirá el doble de unidades de capacidad de lectura que si `ConsistentRead` se deja establecido en su valor predeterminado (`false`).

## Análisis paralelo
<a name="Scan.ParallelScan"></a>

De forma predeterminada, la operación `Scan` procesa los datos secuencialmente. Amazon DynamoDB devuelve los datos a la aplicación en incrementos de 1 MB y una aplicación lleva a cabo operaciones `Scan` adicionales para recuperar la siguiente franja de 1 MB de datos. 

Cuanto mayor es la tabla o el índice que se analiza, más tiempo tarda `Scan` en completarse. Además, puede que una operación `Scan` secuencial no consiga siempre utilizar plenamente la capacidad de rendimiento de lectura aprovisionada. Esto se debe a que, aunque DynamoDB distribuye los datos de las tablas grandes entre varias particiones físicas, una operación `Scan` solo puede leer una partición a la vez. Por este motivo, el desempeño de una operación `Scan` se ve restringido por el desempeño máximo de cada partición individual.

Para abordar estos problemas, la operación `Scan` puede dividir una tabla o un índice secundario lógicamente en varios *segmentos*, de tal forma que varios procesos de trabajo de la aplicación puedan examinarlos en paralelo. Cada proceso de trabajo puede ser un subproceso (en los lenguajes de programación que admiten la ejecución de múltiples subprocesos) o un proceso del sistema operativo. Para llevar a cabo un examen en paralelo, cada proceso de trabajo emite su propia solicitud `Scan` con los siguientes parámetros:
+ `Segment` segmento que un proceso de trabajo concreto examinará. Cada proceso de trabajo debe utilizar un valor diferente de `Segment`.
+ `TotalSegments` número total de segmentos del examen en paralelo. Este valor debe ser el mismo que el número de procesos de trabajo que la aplicación va a utilizar.

En el siguiente diagrama se muestra cómo una aplicación de ejecución multiproceso realiza una operación `Scan` en paralelo con tres grados de paralelismo.

![\[Aplicación de varios subprocesos que realiza un análisis paralelo dividiendo una tabla en tres segmentos.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/ParallelScan.png)




En este diagrama, la aplicación crea tres subprocesos y asigna un número a cada uno de ellos. Los segmentos parten de cero, de modo que el primer número siempre es 0. Cada subproceso emite una solicitud `Scan` y establece el valor de `Segment` en el número designado y el valor de `TotalSegments`, en 3. Cada subproceso examina su segmento designado, recupera datos en franjas de 1 MB a la vez y devuelve los datos al subproceso principal de la aplicación.

DynamoDB asigna elementos a los *segmentos* mediante la aplicación de una función hash a la clave de partición de cada elemento. Para un valor de `TotalSegments` determinado, todos los elementos con la misma clave de partición siempre se asignan al mismo `Segment`. Esto significa que en una tabla en la que *elemento 1*, *elemento 2* y *elemento 3* comparten `pk="account#123"` (pero tienen claves de clasificación diferentes), el mismo trabajador procesará estos elementos, independientemente de los valores de las claves de clasificación o del tamaño de la *recopilación de elementos*.

Como la asignación de *segmentos* se basa únicamente en el hash de la clave de partición, los segmentos pueden distribuirse de forma desigual. Es posible que algunos segmentos no contengan elementos, mientras que otros pueden contener muchas claves de partición con grandes recopilaciones de elementos. Como resultado, aumentar el número total de segmentos no garantiza un rendimiento de análisis más rápido, especialmente cuando las claves de partición no están distribuidas uniformemente en el espacio de claves.

Los valores de `Segment` y `TotalSegments` se aplican a las solicitudes `Scan` individuales; puede utilizar valores diferentes en cualquier momento. Es posible que tenga que experimentar con estos valores y con el número de procesos de trabajo utilizados hasta lograr el desempeño óptimo de la aplicación.

**nota**  
Un examen en paralelo con un gran número de procesos de trabajo puede consumir fácilmente todo el rendimiento aprovisionado de la tabla o el índice que se examina. Es preferible evitar este tipo de exámenes si la tabla o el índice también llevan a cabo una intensa actividad de lectura o escritura de otras aplicaciones.  
Para controlar la cantidad de datos devueltos por cada solicitud, utilice el parámetro `Limit`. Esto puede ayudar a evitar situaciones en las que un proceso de trabajo consume todo el desempeño provisionado a costa de todos los demás procesos de trabajo.

# PartiQL: un lenguaje de consulta compatible con SQL para Amazon DynamoDB
<a name="ql-reference"></a>

Compatibilidad con Amazon DynamoDB [PartiQL](https://partiql.org/), un lenguaje de consulta compatible con SQL, para seleccionar, insertar, actualizar y eliminar datos en Amazon DynamoDB. Utilizando PartiQL, puede interactuar fácilmente con tablas de DynamoDB y ejecutar consultas ad hoc utilizando la Consola de administración de AWS, NoSQL Workbench, AWS Command Line Interface y las API de DynamoDB para PartiQL.

Las operaciones PartiQL proporcionan la misma disponibilidad, latencia y rendimiento que las demás operaciones del plano de datos de DynamoDB.

En las siguientes secciones se describe la implementación DynamoDB de PartiQL.

**Topics**
+ [¿Qué es PartiQL?](#ql-reference.what-is)
+ [PartiQL en Amazon DynamoDB](#ql-reference.what-is)
+ [Introducción](ql-gettingstarted.md)
+ [Tipos de datos](ql-reference.data-types.md)
+ [Instrucciones](ql-reference.statements.md)
+ [Funciones](ql-functions.md)
+ [Operadores](ql-operators.md)
+ [Transacciones](ql-reference.multiplestatements.transactions.md)
+ [Operaciones por lotes](ql-reference.multiplestatements.batching.md)
+ [Políticas de IAM](ql-iam.md)

## ¿Qué es PartiQL?
<a name="ql-reference.what-is"></a>

*PartiQL* proporciona acceso a consultas compatible con SQL en múltiples almacenes de datos que contienen datos estructurados, datos semiestructurados y datos anidados. Es ampliamente utilizado en Amazon y ahora está disponible como parte de muchos servicios de AWS, incluido DynamoDB.

Para obtener la especificación PartiQL y un tutorial sobre el lenguaje de consulta principal, consulte la [Documentación de PartiQL](https://partiql.org/docs.html).

**nota**  
Amazon DynamoDB admite *subconjunto* del lenguaje de consulta de [PartiQL](https://partiql.org/).
Amazon DynamoDB no admite el formato de datos [Amazon Ion](http://amzn.github.io/ion-docs/) o literales de Amazon Ion.

## PartiQL en Amazon DynamoDB
<a name="ql-reference.what-is"></a>

Para ejecutar consultas PartiQL en DynamoDB, puede utilizar:
+ La consola de DynamoDB
+ Uso de NoSQL Workbench
+ La AWS Command Line Interface (AWS CLI)
+ Las API de DynamoDB

Para obtener información sobre el uso de estos métodos para acceder a DynamoDB, consulte [Acceso a DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/AccessingDynamoDB.html).

# Introducción a PartiQL para DynamoDB
<a name="ql-gettingstarted"></a>

En esta sección se describe cómo utilizar PartiQL para DynamoDB desde la consola de Amazon DynamoDB, la AWS Command Line Interface (AWS CLI) y las API de DynamoDB.

En los ejemplos siguientes, se usa la tabla de DynamoDB que se define en el tutorial [Introducción a DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GettingStartedDynamoDB.html) como requisito previo.

Para obtener información sobre cómo usar la consola de DynamoDB, AWS Command Line Interface o las API de DynamoDB para acceder a DynamoDB, consulte [Acceso a DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/AccessingDynamoDB.html).

Para [descargar](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/workbench.settingup.html) y usar el [NoSQL Workbench](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/workbench.html) para crear instrucciones [PartiQL para DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.html) elija **PartiQL operations (Operaciones de PartiQL)** en la esquina superior derecha del NoSQL Workbench para DynamoDB [Operation Builder (Creador de operaciones)](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/workbench.querybuilder.operationbuilder.html).

------
#### [ Console ]

![\[Interfaz de editor PartiQL que muestra el resultado de ejecutar la operación de consulta en la tabla Música.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/partiqlgettingstarted.png)


1. Inicie sesión en la Consola de administración de AWS y abra la consola de DynamoDB en [https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/).

1. En el panel de navegación del lado izquierdo de la consola, elija **Editor PartiQL**.

1. Elija la tabla **Música**.

1. Ellija **Tabla de consulta**. Esta acción genera una consulta que no dará como resultado un análisis de tabla completo.

1. Reemplazar `partitionKeyValue` con el valor de la cadena `Acme Band`. Reemplazar `sortKeyValue` con el valor de la cadena `Happy Day`.

1. Seleccione el botón **Run** (Ejecutar). 

1. Puede ver los resultados de la consulta seleccionando los botones **Table view (Vista de tabla)** o **JSON view (Vista JSON)**. 

------
#### [ NoSQL workbench ]

![\[Interfaz de NoSQL Workbench. Muestra una instrucción SELECT de PartiQL que puede ejecutar en la tabla Música.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/workbench/partiql.single.png)


1. Seleccionar **Instrucción PartiQL**.

1. Ingrese la siguiente PartiQL [Instrucción SELECT](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.select.html) 

   ```
   SELECT *                                         
   FROM Music  
   WHERE Artist=? and SongTitle=?
   ```

1. Para especificar un valor para los parámetros `Artist` y `SongTitle`:

   1. Elija **Parámetros de solicitudes opcionales**.

   1. Elija **Agregar nuevos parámetros**.

   1. Elija el tipo de atributo **string** y valor `Acme Band`.

   1. Repita los pasos b y c y elija el tipo **string** y valor `PartiQL Rocks`. 

1. Si desea generar código, seleccione **Generate code (Generar código)**.

   Seleccione el idioma que desee en las pestañas mostradas. Ahora puede copiar este código y utilizarlo en su aplicación.

1. Si desea que la operación se ejecute inmediatamente, elija **Ejecutar**.

------
#### [ AWS CLI ]

1. Cree un elemento en la tabla `Music` utilizando la instrucción INSERT PartiQL. 

   ```
   aws dynamodb execute-statement --statement "INSERT INTO Music  \
   					    VALUE  \
   					    {'Artist':'Acme Band','SongTitle':'PartiQL Rocks'}"
   ```

1. Recuperar un elemento de la tabla Música mediante la instrucción SELECT PartiQL.

   ```
   aws dynamodb execute-statement --statement "SELECT * FROM Music   \
                                               WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'"
   ```

1. Actualice un elemento en la tabla `Music` usando la instrucción UPDATE PartiQL.

   ```
   aws dynamodb execute-statement --statement "UPDATE Music  \
                                               SET AwardsWon=1  \
                                               SET AwardDetail={'Grammys':[2020, 2018]}  \
                                               WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'"
   ```

   Agregue un valor de lista para un elemento en la tabla `Music`. 

   ```
   aws dynamodb execute-statement --statement "UPDATE Music  \
                                               SET AwardDetail.Grammys =list_append(AwardDetail.Grammys,[2016])  \
                                               WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'"
   ```

   Quitar un valor de lista para un elemento en la tabla `Music`. 

   ```
   aws dynamodb execute-statement --statement "UPDATE Music  \
                                               REMOVE AwardDetail.Grammys[2]  \
                                               WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'"
   ```

   Agregue un nuevo miembro de mapa para un elemento la tabla `Music`. 

   ```
   aws dynamodb execute-statement --statement "UPDATE Music  \
                                               SET AwardDetail.BillBoard=[2020]  \
                                               WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'"
   ```

   Agregue un nuevo atributo de conjunto de cadenas para un elemento en la tabla `Music`. 

   ```
   aws dynamodb execute-statement --statement "UPDATE Music  \
                                               SET BandMembers =<<'member1', 'member2'>>  \
                                               WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'"
   ```

   Actualice un atributo de conjunto de cadenas para un elemento en la tabla `Music`. 

   ```
   aws dynamodb execute-statement --statement "UPDATE Music  \
                                               SET BandMembers =set_add(BandMembers, <<'newmember'>>)  \
                                               WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'"
   ```

1. Elimine un elemento de la tabla `Music` mediante la instrucción DELETE PartiQL.

   ```
   aws dynamodb execute-statement --statement "DELETE  FROM Music  \
       WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'"
   ```

------
#### [ Java ]

```
import java.util.ArrayList;
import java.util.List;

import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import software.amazon.dynamodb.AmazonDynamoDB;
import software.amazon.dynamodb.AmazonDynamoDBClientBuilder;
import software.amazon.dynamodb.model.AttributeValue;
import software.amazon.dynamodb.model.ConditionalCheckFailedException;
import software.amazon.dynamodb.model.ExecuteStatementRequest;
import software.amazon.dynamodb.model.ExecuteStatementResult;
import software.amazon.dynamodb.model.InternalServerErrorException;
import software.amazon.dynamodb.model.ItemCollectionSizeLimitExceededException;
import software.amazon.dynamodb.model.ProvisionedThroughputExceededException;
import software.amazon.dynamodb.model.RequestLimitExceededException;
import software.amazon.dynamodb.model.ResourceNotFoundException;
import software.amazon.dynamodb.model.TransactionConflictException;

public class DynamoDBPartiQGettingStarted {

    public static void main(String[] args) {
        // Create the DynamoDB Client with the region you want
        AmazonDynamoDB dynamoDB = createDynamoDbClient("us-west-1");

        try {
            // Create ExecuteStatementRequest
            ExecuteStatementRequest executeStatementRequest = new ExecuteStatementRequest();
            List<AttributeValue> parameters= getPartiQLParameters();

            //Create an item in the Music table using the INSERT PartiQL statement
            processResults(executeStatementRequest(dynamoDB, "INSERT INTO Music value {'Artist':?,'SongTitle':?}", parameters));

            //Retrieve an item from the Music table using the SELECT PartiQL statement.
            processResults(executeStatementRequest(dynamoDB, "SELECT * FROM Music  where Artist=? and SongTitle=?", parameters));

            //Update an item in the Music table using the UPDATE PartiQL statement.
            processResults(executeStatementRequest(dynamoDB, "UPDATE Music SET AwardsWon=1 SET AwardDetail={'Grammys':[2020, 2018]}  where Artist=? and SongTitle=?", parameters));

            //Add a list value for an item in the Music table.
            processResults(executeStatementRequest(dynamoDB, "UPDATE Music SET AwardDetail.Grammys =list_append(AwardDetail.Grammys,[2016])  where Artist=? and SongTitle=?", parameters));

            //Remove a list value for an item in the Music table.
            processResults(executeStatementRequest(dynamoDB, "UPDATE Music REMOVE AwardDetail.Grammys[2]   where Artist=? and SongTitle=?", parameters));

            //Add a new map member for an item in the Music table.
            processResults(executeStatementRequest(dynamoDB, "UPDATE Music set AwardDetail.BillBoard=[2020] where Artist=? and SongTitle=?", parameters));

            //Add a new string set attribute for an item in the Music table.
            processResults(executeStatementRequest(dynamoDB, "UPDATE Music SET BandMembers =<<'member1', 'member2'>> where Artist=? and SongTitle=?", parameters));

            //update a string set attribute for an item in the Music table.
            processResults(executeStatementRequest(dynamoDB, "UPDATE Music SET BandMembers =set_add(BandMembers, <<'newmember'>>) where Artist=? and SongTitle=?", parameters));

            //Retrieve an item from the Music table using the SELECT PartiQL statement.
            processResults(executeStatementRequest(dynamoDB, "SELECT * FROM Music  where Artist=? and SongTitle=?", parameters));

            //delete an item from the Music Table
            processResults(executeStatementRequest(dynamoDB, "DELETE  FROM Music  where Artist=? and SongTitle=?", parameters));
        } catch (Exception e) {
            handleExecuteStatementErrors(e);
        }
    }

    private static AmazonDynamoDB createDynamoDbClient(String region) {
        return AmazonDynamoDBClientBuilder.standard().withRegion(region).build();
    }

    private static List<AttributeValue> getPartiQLParameters() {
        List<AttributeValue> parameters = new ArrayList<AttributeValue>();
        parameters.add(new AttributeValue("Acme Band"));
        parameters.add(new AttributeValue("PartiQL Rocks"));
        return parameters;
    }

    private static ExecuteStatementResult executeStatementRequest(AmazonDynamoDB client, String statement, List<AttributeValue> parameters ) {
        ExecuteStatementRequest request = new ExecuteStatementRequest();
        request.setStatement(statement);
        request.setParameters(parameters);
        return client.executeStatement(request);
    }

    private static void processResults(ExecuteStatementResult executeStatementResult) {
        System.out.println("ExecuteStatement successful: "+ executeStatementResult.toString());

    }

    // Handles errors during ExecuteStatement execution. Use recommendations in error messages below to add error handling specific to
    // your application use-case.
    private static void handleExecuteStatementErrors(Exception exception) {
        try {
            throw exception;
        } catch (ConditionalCheckFailedException ccfe) {
            System.out.println("Condition check specified in the operation failed, review and update the condition " +
                                       "check before retrying. Error: " + ccfe.getErrorMessage());
        } catch (TransactionConflictException tce) {
            System.out.println("Operation was rejected because there is an ongoing transaction for the item, generally " +
                                       "safe to retry with exponential back-off. Error: " + tce.getErrorMessage());
        } catch (ItemCollectionSizeLimitExceededException icslee) {
            System.out.println("An item collection is too large, you\'re using Local Secondary Index and exceeded " +
                                       "size limit of items per partition key. Consider using Global Secondary Index instead. Error: " + icslee.getErrorMessage());
        } catch (Exception e) {
            handleCommonErrors(e);
        }
    }

    private static void handleCommonErrors(Exception exception) {
        try {
            throw exception;
        } catch (InternalServerErrorException isee) {
            System.out.println("Internal Server Error, generally safe to retry with exponential back-off. Error: " + isee.getErrorMessage());
        } catch (RequestLimitExceededException rlee) {
            System.out.println("Throughput exceeds the current throughput limit for your account, increase account level throughput before " +
                                       "retrying. Error: " + rlee.getErrorMessage());
        } catch (ProvisionedThroughputExceededException ptee) {
            System.out.println("Request rate is too high. If you're using a custom retry strategy make sure to retry with exponential back-off. " +
                                       "Otherwise consider reducing frequency of requests or increasing provisioned capacity for your table or secondary index. Error: " +
                                       ptee.getErrorMessage());
        } catch (ResourceNotFoundException rnfe) {
            System.out.println("One of the tables was not found, verify table exists before retrying. Error: " + rnfe.getErrorMessage());
        } catch (AmazonServiceException ase) {
            System.out.println("An AmazonServiceException occurred, indicates that the request was correctly transmitted to the DynamoDB " +
                                       "service, but for some reason, the service was not able to process it, and returned an error response instead. Investigate and " +
                                       "configure retry strategy. Error type: " + ase.getErrorType() + ". Error message: " + ase.getErrorMessage());
        } catch (AmazonClientException ace) {
            System.out.println("An AmazonClientException occurred, indicates that the client was unable to get a response from DynamoDB " +
                                       "service, or the client was unable to parse the response from the service. Investigate and configure retry strategy. "+
                                       "Error: " + ace.getMessage());
        } catch (Exception e) {
            System.out.println("An exception occurred, investigate and configure retry strategy. Error: " + e.getMessage());
        }
    }

}
```

------

## Uso de instrucciones parametrizadas
<a name="ql-gettingstarted.parameterized"></a>

En lugar de incrustar valores directamente en una cadena de instrucciones PartiQL, puede utilizar marcadores de posición con signos de interrogación (`?`) y suministrar los valores de forma independiente en el campo `Parameters`. Cada `?` se sustituye por el valor del parámetro correspondiente, en el orden en que se proporcionan.

El uso de instrucciones parametrizadas es una práctica recomendada porque separa la estructura de las instrucciones de los valores de los datos, lo que facilita su lectura y reutilización. También evita la necesidad de formatear y escapar manualmente de los valores de los atributos de la cadena de la instrucción.

Las instrucciones parametrizadas se admiten en las operaciones `ExecuteStatement`, `BatchExecuteStatement` y `ExecuteTransaction`.

En los siguientes ejemplos, se recupera un elemento de la tabla `Music` mediante valores parametrizados para la clave de partición y la clave de clasificación.

------
#### [ AWS CLI parameterized ]

```
aws dynamodb execute-statement \
    --statement "SELECT * FROM \"Music\" WHERE Artist=? AND SongTitle=?" \
    --parameters '[{"S": "Acme Band"}, {"S": "PartiQL Rocks"}]'
```

------
#### [ Java parameterized ]

```
List<AttributeValue> parameters = new ArrayList<>();
parameters.add(new AttributeValue("Acme Band"));
parameters.add(new AttributeValue("PartiQL Rocks"));

ExecuteStatementRequest request = new ExecuteStatementRequest()
    .withStatement("SELECT * FROM Music WHERE Artist=? AND SongTitle=?")
    .withParameters(parameters);

ExecuteStatementResult result = dynamoDB.executeStatement(request);
```

------
#### [ Python parameterized ]

```
response = dynamodb_client.execute_statement(
    Statement="SELECT * FROM Music WHERE Artist=? AND SongTitle=?",
    Parameters=[
        {'S': 'Acme Band'},
        {'S': 'PartiQL Rocks'}
    ]
)
```

------

**nota**  
El ejemplo de Java de la sección de introducción anterior utiliza en todo momento sentencias parametrizadas. El método `getPartiQLParameters()` crea la lista de parámetros y cada instrucción utiliza marcadores de posición `?` en lugar de valores insertados.

# Tipos de datos de PartiQL para DynamoDB
<a name="ql-reference.data-types"></a>

En la tabla siguiente se enumeran los tipos de datos que puede usar con PartiQL para DynamoDB.

[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/ql-reference.data-types.html)

## Ejemplos
<a name="ql-reference.data-types"></a>

La instrucción siguiente muestra cómo insertar los siguientes tipos de datos: `String`, `Number`, `Map`, `List`, `Number Set` y `String Set`.

```
INSERT INTO TypesTable value {'primarykey':'1', 
'NumberType':1,
'MapType' : {'entryname1': 'value', 'entryname2': 4}, 
'ListType': [1,'stringval'], 
'NumberSetType':<<1,34,32,4.5>>, 
'StringSetType':<<'stringval','stringval2'>>
}
```

La siguiente instrucción muestra cómo insertar nuevos elementos en los tipos `Map`, `List`, `Number Set` y `String Set` y cambiar el valor de un tipo `Number`.

```
UPDATE TypesTable 
SET NumberType=NumberType + 100 
SET MapType.NewMapEntry=[2020, 'stringvalue', 2.4]
SET ListType = LIST_APPEND(ListType, [4, <<'string1', 'string2'>>])
SET NumberSetType= SET_ADD(NumberSetType, <<345, 48.4>>)
SET StringSetType = SET_ADD(StringSetType, <<'stringsetvalue1', 'stringsetvalue2'>>)
WHERE primarykey='1'
```

La siguiente instrucción muestra cómo eliminar elementos de los tipos `Map`, `List`, `Number Set` y `String Set` y cambiar el valor de un tipo `Number`.

```
UPDATE TypesTable 
SET NumberType=NumberType - 1
REMOVE ListType[1]
REMOVE MapType.NewMapEntry
SET NumberSetType = SET_DELETE( NumberSetType, <<345>>)
SET StringSetType = SET_DELETE( StringSetType, <<'stringsetvalue1'>>)
WHERE primarykey='1'
```

Para obtener más información, consulte [Tipos de datos de DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes).

# Instrucciones PartiQL para DynamoDB
<a name="ql-reference.statements"></a>

Amazon DynamoDB admite las siguientes instrucciones PartiQL.

**nota**  
DynamoDB no es compatible con todas las instrucciones PartiQL.  
Esta referencia proporciona sintaxis básica y ejemplos de uso de instrucciones PartiQL que se ejecutan manualmente mediante la AWS CLI o API.

*Lenguaje de manipulación de datos*(DML) es el conjunto de instrucciones PartiQL que se utiliza para administrar datos en tablas de DynamoDB. Puede usar instrucciones DML para agregar, modificar o eliminar datos de una tabla.

Se admiten las siguientes instrucciones DML y lenguaje de consulta:
+ [Instrucciones de selección de PartiQL para DynamoDB](ql-reference.select.md)
+ [Instrucciones de actualización de PartiQL para DynamoDB](ql-reference.update.md)
+ [Instrucciones de inserción de PartiQL para DynamoDB](ql-reference.insert.md)
+ [Instrucciones de eliminación de PartiQL para DynamoDB](ql-reference.delete.md)

[Realización de transacciones con PartiQL para DynamoDB](ql-reference.multiplestatements.transactions.md) y [Ejecución de operaciones por lote con PartiQL para DynamoDB](ql-reference.multiplestatements.batching.md) también son compatibles con PartiQL para DynamoDB.

# Instrucciones de selección de PartiQL para DynamoDB
<a name="ql-reference.select"></a>

Se utiliza la instrucción `SELECT` para recuperar datos de una tabla de Amazon DynamoDB.

Si se usa la instrucción `SELECT` se puede generar un análisis completo de la tabla si no se proporciona una condición de igualdad o IN con una clave de partición en la cláusula WHERE. La operación de análisis examina cada elemento para comprobar si presenta los valores solicitados y permite utilizar el rendimiento aprovisionado para una tabla o un índice grandes en una sola operación. 

Si desea evitar el análisis completo de la tabla en PartiQL, puede:
+ Cree su instrucción `SELECT` para que no resulten en análisis completos de la tabla asegurándose de que su [condición de la cláusula WHERE](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.select.html#ql-reference.select.parameters) se configura en consecuencia.
+ Desactive los análisis completos de tablas mediante la política de IAM especificada en [Ejemplo: permitir instrucciones Select y denegar instrucciones de análisis de tabla completa en PartiQL para DynamoDB](ql-iam.md#access-policy-ql-iam-example6), en la guía para desarrolladores de DynamoDB.

Para obtener más información, consulte [Prácticas recomendadas para consultar y examinar datos](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-query-scan.html), en la guía para desarrolladores de DynamoDB.

**Topics**
+ [Sintaxis](#ql-reference.select.syntax)
+ [Parameters](#ql-reference.select.parameters)
+ [Ejemplos](#ql-reference.select.examples)

## Sintaxis
<a name="ql-reference.select.syntax"></a>

```
SELECT expression  [, ...] 
FROM table[.index]
[ WHERE condition ] [ [ORDER BY key [DESC|ASC] , ...]
```

## Parameters
<a name="ql-reference.select.parameters"></a>

***expression***  
(Requerido) Una proyección formada a partir del comodín `*` o una lista de proyección de uno o más nombres de atributos o rutas de documentos del conjunto de resultados. Una expresión puede consistir en llamadas a [Uso de funciones de PartiQL con DynamoDB](ql-functions.md) o campos modificados por [Operadores aritméticos, comparativos y lógicos de PartiQL para DynamoDB](ql-operators.md).

***table***  
(Necesario) Nombre de la tabla que se va a consultar.

***Índice de***  
(Opcional) El nombre del índice que se consultará.  
Debe agregar comillas dobles al nombre de la tabla y al nombre del índice al consultar uno.  

```
SELECT * 
FROM "TableName"."IndexName"
```

***condition***  
(Opcional) Criterios de selección para la consulta.  
Para asegurarse de que una instrucción `SELECT` no da como resultado un análisis completo de la tabla, la condición de cláusula `WHERE` debe especificar una clave de partición. Utilice el operador de igualdad o IN.  
Por ejemplo, si tiene una tabla `Orders` con una clave de partición `OrderID`y otros atributos que no son clave, incluido una `Address`, las siguientes instrucciones no resultarían en un análisis completo de la tabla:  

```
SELECT * 
FROM "Orders" 
WHERE OrderID = 100

SELECT * 
FROM "Orders" 
WHERE OrderID = 100 and Address='some address'

SELECT * 
FROM "Orders" 
WHERE OrderID = 100 or OrderID = 200

SELECT * 
FROM "Orders" 
WHERE OrderID IN [100, 300, 234]
```
Las siguientes instrucciones de `SELECT`, sin embargo, resultarán en un análisis completo de la tabla:  

```
SELECT * 
FROM "Orders" 
WHERE OrderID > 1

SELECT * 
FROM "Orders" 
WHERE Address='some address'

SELECT * 
FROM "Orders" 
WHERE OrderID = 100 OR Address='some address'
```

***clave***  
(Opcional) Una clave hash o una clave de ordenación que se va a utilizar para ordenar los resultados devueltos. El orden predeterminado es ascendente (`ASC`) especifique `DESC` si desea que los resultados se vuelvan a ejecutar en orden descendente.

**nota**  
Si omite la cláusula `WHERE`, se recuperarán todos los elementos de la tabla.

## Ejemplos
<a name="ql-reference.select.examples"></a>

La siguiente consulta devuelve un elemento, si existe, de la tabla `Orders` especificando la clave de partición, `OrderID` y utilizando el operador de igualdad.

```
SELECT OrderID, Total
FROM "Orders"
WHERE OrderID = 1
```

La siguiente consulta devuelve todos los elementos de la tabla `Orders` que tiene una clave de partición determinada, `OrderID`, valores utilizando el operador OR.

```
SELECT OrderID, Total
FROM "Orders"
WHERE OrderID = 1 OR OrderID = 2
```

La siguiente consulta devuelve todos los elementos de la tabla `Orders` que tiene una clave de partición determinada, `OrderID`, valores utilizando el operador IN. Los resultados devueltos están en orden descendente, basados en el valor del atributo clave `OrderID`.

```
SELECT OrderID, Total
FROM "Orders"
WHERE OrderID IN [1, 2, 3] ORDER BY OrderID DESC
```

La siguiente consulta muestra un análisis de tabla completo que devuelve todos los elementos de la tabla `Orders` que tienen un `Total` mayor que 500, donde `Total` es un atributo que no es clave.

```
SELECT OrderID, Total 
FROM "Orders"
WHERE Total > 500
```

La siguiente consulta muestra un análisis de tabla completo que devuelve todos los elementos de la tabla `Orders` con un rango de orden `Total`, utilizando el operador IN y un atributo que no es clave `Total`.

```
SELECT OrderID, Total 
FROM "Orders"
WHERE Total IN [500, 600]
```

La siguiente consulta muestra un análisis de tabla completo que devuelve todos los elementos de la tabla `Orders` dentro de un rango de orden `Total` específico, utilizando el operador BETWEEN y un atributo que no es clave `Total`.

```
SELECT OrderID, Total 
FROM "Orders" 
WHERE Total BETWEEN 500 AND 600
```

La siguiente consulta devuelve la primera fecha en que se utilizó un dispositivo firestick para ver especificando la clave de partición `CustomerID` y la clave de ordenación `MovieID` en la condición de cláusula WHERE y utilizando rutas de documento en la cláusula SELECT.

```
SELECT Devices.FireStick.DateWatched[0] 
FROM WatchList 
WHERE CustomerID= 'C1' AND MovieID= 'M1'
```

La siguiente consulta muestra un análisis de tabla completo que devuelve la lista de elementos en los que un dispositivo de firestick se utilizó por primera vez después del 24/12/19 mediante rutas de documento en la condición de cláusula WHERE.

```
SELECT Devices 
FROM WatchList 
WHERE Devices.FireStick.DateWatched[0] >= '12/24/19'
```

# Instrucciones de actualización de PartiQL para DynamoDB
<a name="ql-reference.update"></a>

Use la instrucción `UPDATE` para modificar el valor de uno o más atributos dentro de un elemento de una tabla de Amazon DynamoDB. 

**nota**  
Sólo puede actualizar un elemento a la vez; no puede emitir una sola instrucción de DynamoDB PartiQL que actualice varios elementos. Para obtener información sobre cómo actualizar varios elementos, consulte [Realización de transacciones con PartiQL para DynamoDB](ql-reference.multiplestatements.transactions.md) or [Ejecución de operaciones por lote con PartiQL para DynamoDB](ql-reference.multiplestatements.batching.md).

**Topics**
+ [Sintaxis](#ql-reference.update.syntax)
+ [Parameters](#ql-reference.update.parameters)
+ [Valor devuelto](#ql-reference.update.return)
+ [Ejemplos](#ql-reference.update.examples)

## Sintaxis
<a name="ql-reference.update.syntax"></a>

```
UPDATE  table  
[SET | REMOVE]  path  [=  data] […]
WHERE condition [RETURNING returnvalues]
<returnvalues>  ::= [ALL OLD | MODIFIED OLD | ALL NEW | MODIFIED NEW] *
```

## Parameters
<a name="ql-reference.update.parameters"></a>

***table***  
(Necesario) Tabla que contiene los datos que se van a modificar.

***path***  
(Necesario) Nombre de atributo o ruta de documento que se va a crear o modificar.

***data***  
(Necesario) Valor de atributo o resultado de una operación.  
Las operaciones admitidas para usar con SET:  
+ LIST\$1APPEND: agrega un valor a un tipo de lista.
+ SET\$1ADD: agrega un valor a un número o conjunto de cadenas.
+ SET\$1DELETE: elimina un valor de un número o conjunto de cadenas.

***condition***  
(Necesario) Criterios de selección para el elemento que se va a modificar. Esta condición debe resolverse con un único valor de clave principal.

***returnvalues***  
(Opcional) Utilizar `returnvalues` si desea obtener los atributos del elemento tal como aparecen antes o después de que se actualicen. Los valores válidos son:   
+ `ALL OLD *`: devuelve todos los atributos del elemento, tal y como aparecían antes de la operación de actualización.
+ `MODIFIED OLD *`: devuelve sólo los atributos actualizados, tal y como aparecían antes de la operación de actualización.
+ `ALL NEW *`: devuelve todos los atributos del elemento, tal como aparecen después de la operación de actualización.
+ `MODIFIED NEW *`: devuelve solamente los atributos actualizados, tal y como aparecen después de la operación `UpdateItem`.

## Valor devuelto
<a name="ql-reference.update.return"></a>

Esta instrucción no devuelve un valor a menos que se especifique el parámetro `returnvalues`.

**nota**  
Si la cláusula WHERE de la instrucción UPDATE no se evalúa como true (verdadero) para ningún elemento de la tabla DynamoDB, se devuelve `ConditionalCheckFailedException`.

## Ejemplos
<a name="ql-reference.update.examples"></a>

Actualiza el valor del atributo de un elemento existente en. Si el atributo no existe, se crea.

En la siguiente consulta se actualiza un elemento de la tabla `"Music"` agregando un atributo de tipo number (`AwardsWon`) y un atributo de tipo map (`AwardDetail`).

```
UPDATE "Music" 
SET AwardsWon=1 
SET AwardDetail={'Grammys':[2020, 2018]}  
WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'
```

Puede agregar `RETURNING ALL OLD *` para devolver los atributos tal y como aparecían antes de la operación `Update`.

```
UPDATE "Music" 
SET AwardsWon=1 
SET AwardDetail={'Grammys':[2020, 2018]}  
WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'
RETURNING ALL OLD *
```

Devuelve lo siguiente:

```
{
    "Items": [
        {
            "Artist": {
                "S": "Acme Band"
            },
            "SongTitle": {
                "S": "PartiQL Rocks"
            }
        }
    ]
}
```

Puede agregar `RETURNING ALL NEW *` para devolver los atributos tal y como aparecían después de la operación `Update`.

```
UPDATE "Music" 
SET AwardsWon=1 
SET AwardDetail={'Grammys':[2020, 2018]}  
WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'
RETURNING ALL NEW *
```

Devuelve lo siguiente:

```
{
    "Items": [
        {
            "AwardDetail": {
                "M": {
                    "Grammys": {
                        "L": [
                            {
                                "N": "2020"
                            },
                            {
                                "N": "2018"
                            }
                        ]
                    }
                }
            },
            "AwardsWon": {
                "N": "1"
            }
        }
    ]
}
```

En la siguiente consulta se actualiza un elemento de la tabla `"Music"` agregando a una lista `AwardDetail.Grammys`.

```
UPDATE "Music" 
SET AwardDetail.Grammys =list_append(AwardDetail.Grammys,[2016])  
WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'
```

En la siguiente consulta se actualiza un elemento de la tabla `"Music"` eliminando de una lista `AwardDetail.Grammys`.

```
UPDATE "Music" 
REMOVE AwardDetail.Grammys[2]   
WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'
```

En la siguiente consulta se actualiza un elemento de la tabla `"Music"` agregando `BillBoard`al mapa`AwardDetail`.

```
UPDATE "Music" 
SET AwardDetail.BillBoard=[2020] 
WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'
```

En la siguiente consulta se actualiza un elemento de la tabla `"Music"` agregando el atributo de conjunto de string `BandMembers`.

```
UPDATE "Music" 
SET BandMembers =<<'member1', 'member2'>> 
WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'
```

En la siguiente consulta se actualiza un elemento de la tabla `"Music"` agregando `newbandmember` al conjunto de string `BandMembers`.

```
UPDATE "Music" 
SET BandMembers =set_add(BandMembers, <<'newbandmember'>>) 
WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'
```

# Instrucciones de eliminación de PartiQL para DynamoDB
<a name="ql-reference.delete"></a>

Usar la instrucción `DELETE` para eliminar un elemento existente de la tabla de Amazon DynamoDB.

**nota**  
Solo puede eliminar de a un elemento a la vez. No puede emitir una sola instrucción de DynamoDB PartiQL que elimine varios elementos. Para obtener información sobre cómo eliminar varios elementos, consulte [Realización de transacciones con PartiQL para DynamoDB](ql-reference.multiplestatements.transactions.md) or [Ejecución de operaciones por lote con PartiQL para DynamoDB](ql-reference.multiplestatements.batching.md).

**Topics**
+ [Sintaxis](#ql-reference.delete.syntax)
+ [Parameters](#ql-reference.delete.parameters)
+ [Valor devuelto](#ql-reference.delete.return)
+ [Ejemplos](#ql-reference.delete.examples)

## Sintaxis
<a name="ql-reference.delete.syntax"></a>

```
DELETE FROM table 
 WHERE condition [RETURNING returnvalues]
 <returnvalues>  ::= ALL OLD *
```

## Parameters
<a name="ql-reference.delete.parameters"></a>

***table***  
(Necesario) Tabla de DynamoDB que contiene el elemento que se va a eliminar.

***condition***  
(Necesario) Criterios de selección para el elemento que se va a eliminar; esta condición debe resolverse en un único valor de clave principal.

***returnvalues***  
(Opcional) Utilizar `returnvalues` si desea obtener los atributos del elemento tal y como aparecían antes de eliminarlos. Los valores válidos son:   
+ `ALL OLD *`: se devuelve el contenido del elemento anterior.

## Valor devuelto
<a name="ql-reference.delete.return"></a>

Esta instrucción no devuelve un valor a menos que se especifique el parámetro `returnvalues`.

**nota**  
Si la tabla de DynamoDB no tiene ningún elemento con la misma clave principal que la del elemento para el que se emite DELETE, se devuelve SUCCESS con 0 elementos eliminados. Si la tabla tiene un elemento con la misma clave principal, pero la condición de la cláusula WHERE de la instrucción DELETE se evalúa como false (falso), se devuelve `ConditionalCheckFailedException`.

## Ejemplos
<a name="ql-reference.delete.examples"></a>

La siguiente consulta elimina un elemento de la tabla `"Music"`.

```
DELETE FROM "Music" WHERE "Artist" = 'Acme Band' AND "SongTitle" = 'PartiQL Rocks'
```

Puede agregar el parámetro `RETURNING ALL OLD *` para devolver los datos eliminados.

```
DELETE FROM "Music" WHERE "Artist" = 'Acme Band' AND "SongTitle" = 'PartiQL Rocks' RETURNING ALL OLD *
```

La instrucción `Delete` devuelve ahora lo siguiente:

```
{
    "Items": [
        {
            "Artist": {
                "S": "Acme Band"
            },
            "SongTitle": {
                "S": "PartiQL Rocks"
            }
        }
    ]
}
```

# Instrucciones de inserción de PartiQL para DynamoDB
<a name="ql-reference.insert"></a>

Usar la instrucción `INSERT` para agregar un elemento a una tabla de Amazon DynamoDB.

**nota**  
Sólo puede insertar un elemento a la vez; no puede emitir una sola instrucción de DynamoDB PartiQL que inserte varios elementos. Para obtener información sobre cómo insertar varios elementos, consulte [Realización de transacciones con PartiQL para DynamoDB](ql-reference.multiplestatements.transactions.md) or [Ejecución de operaciones por lote con PartiQL para DynamoDB](ql-reference.multiplestatements.batching.md).

**Topics**
+ [Sintaxis](#ql-reference.insert.syntax)
+ [Parameters](#ql-reference.insert.parameters)
+ [Valor devuelto](#ql-reference.insert.return)
+ [Ejemplos](#ql-reference.insert.examples)

## Sintaxis
<a name="ql-reference.insert.syntax"></a>

Inserte un único elemento.

```
INSERT INTO table VALUE item
```

## Parameters
<a name="ql-reference.insert.parameters"></a>

***table***  
(Necesario) La tabla en la que desea insertar los datos. La tabla debe existir previamente.

***elemento***  
(Necesario) Un elemento válido de DynamoDB representado como [tupla PartiQL](https://partiql.org/docs.html). Debe especificar *un solo* elemento y cada nombre de atributo en el elemento distingue entre mayúsculas y minúsculas y se puede denotar con comillas *simples* (`'...'`) en PartiQL.  
Los valores string también se denotan con comillas *simples* (`'...'`) en PartiQL.

## Valor devuelto
<a name="ql-reference.insert.return"></a>

Esta instrucción no devuelve ningún valor.

**nota**  
Si la tabla DynamoDB ya tiene un elemento con la misma clave principal que la clave principal del elemento que se va a insertar, se devuelve `DuplicateItemException`.

## Ejemplos
<a name="ql-reference.insert.examples"></a>

```
INSERT INTO "Music" value {'Artist' : 'Acme Band','SongTitle' : 'PartiQL Rocks'}
```

# Uso de funciones de PartiQL con DynamoDB
<a name="ql-functions"></a>

PartiQL en Amazon DynamoDB admite las siguientes variantes integradas de funciones estándar de SQL.

**nota**  
DynamoDB no admite actualmente ninguna función SQL que no se incluya en esta lista.

## Funciones de agregación
<a name="ql-functions.aggregate"></a>
+ [Uso de la función SIZE con PartiQL para Amazon DynamoDB](ql-functions.size.md)

## Funciones condicionales
<a name="ql-functions.conditional"></a>
+ [Uso de la función EXISTS con PartiQL para DynamoDB](ql-functions.exists.md)
+ [Uso de la función ATTRIBUTE\$1TYPE con PartiQL para DynamoDB](ql-functions.attribute_type.md)
+ [Uso de la función BEGINS\$1WITH con PartiQL para DynamoDB](ql-functions.beginswith.md)
+ [Uso de la función CONTAINS con PartiQL para DynamoDB](ql-functions.contains.md)
+ [Uso de la función MISSING con PartiQL para DynamoDB](ql-functions.missing.md)

# Uso de la función EXISTS con PartiQL para DynamoDB
<a name="ql-functions.exists"></a>

Puede usar EXISTS para realizar la misma función que`ConditionCheck` hace en la API [TransactWriteItems](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-apis-txwriteitems). La función EXISTS sólo se puede utilizar en transacciones.

Dado un valor, devuelve `TRUE` si el valor es una colección no vacía. De lo contrario, devuelve `FALSE`.

**nota**  
Esta función sólo se puede utilizar en operaciones transaccionales.

## Sintaxis
<a name="ql-functions.exists.syntax"></a>

```
EXISTS ( statement )
```

## Argumentos
<a name="ql-functions.exists.arguments"></a>

*statement *  
(Requerido) La instrucción SELECT que la función evalúa.  
La instrucción SELECT debe especificar una clave principal completa y otra condición.

## Tipo de retorno
<a name="ql-functions.exists.return-type"></a>

`bool`

## Ejemplos
<a name="ql-functions.exists.examples"></a>

```
EXISTS(
    SELECT * FROM "Music" 
    WHERE "Artist" = 'Acme Band' AND "SongTitle" = 'PartiQL Rocks')
```

# Uso de la función BEGINS\$1WITH con PartiQL para DynamoDB
<a name="ql-functions.beginswith"></a>

Devuelve `TRUE` si el atributo especificado comienza por una subcadena determinada.

## Sintaxis
<a name="ql-functions.beginswith.syntax"></a>

```
begins_with(path, value )
```

## Argumentos
<a name="ql-functions.beginswith.arguments"></a>

*path*  
(Necesario) Nombre del atributo o ruta del documento que se va a utilizar.

*value*  
(Necesario) La cadena que se va a buscar.

## Tipo de retorno
<a name="ql-functions.beginswith.return-type"></a>

`bool`

## Ejemplos
<a name="ql-functions.beginswith.examples"></a>

```
SELECT * FROM "Orders" WHERE "OrderID"=1 AND begins_with("Address", '7834 24th')
```

# Uso de la función MISSING con PartiQL para DynamoDB
<a name="ql-functions.missing"></a>

Devuelve `TRUE` si el elemento no contiene el atributo especificado. Sólo los operadores de igualdad y desigualdad pueden ser utilizados con esta función.

## Sintaxis
<a name="ql-functions.missing.syntax"></a>

```
 attributename IS | IS NOT  MISSING 
```

## Argumentos
<a name="ql-functions.missing.arguments"></a>

*attributename*  
(Necesario) Nombre del atributo que se va a buscar.

## Tipo de retorno
<a name="ql-functions.missing.return-type"></a>

`bool`

## Ejemplos
<a name="ql-functions.missing.examples"></a>

```
SELECT * FROM Music WHERE "Awards" is MISSING
```

# Uso de la función ATTRIBUTE\$1TYPE con PartiQL para DynamoDB
<a name="ql-functions.attribute_type"></a>

Devuelve `TRUE` si el atributo de la ruta especificada es de un tipo de datos determinado.

## Sintaxis
<a name="ql-functions.attribute_type.syntax"></a>

```
attribute_type( attributename, type )
```

## Argumentos
<a name="ql-functions.attribute_type.arguments"></a>

*attributename*  
(Necesario) Nombre del atributo que se va a utilizar.

*type*  
(Necesario) Tipo de atributo que se va a comprobar. Para obtener una lista de valores válidos, consulte [attribute\$1type](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions) de DynamoDB.

## Tipo de retorno
<a name="ql-functions.attribute_type.return-type"></a>

`bool`

## Ejemplos
<a name="ql-functions.attribute_type.examples"></a>

```
SELECT * FROM "Music" WHERE attribute_type("Artist", 'S')
```

# Uso de la función CONTAINS con PartiQL para DynamoDB
<a name="ql-functions.contains"></a>

Devuelve `TRUE` si el atributo especificado por la ruta es uno de los siguientes:
+ Un valor de tipo String que contiene una determinada subcadena. 
+ Un valor de tipo Set que contiene una entrada determinada perteneciente al conjunto.

Para obtener más información, consulte la función [contains (contiene)](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions) de DynamoDB. 

## Sintaxis
<a name="ql-functions.contains.syntax"></a>

```
contains( path, substring )
```

## Argumentos
<a name="ql-functions.contains.arguments"></a>

*path*  
(Necesario) Nombre del atributo o ruta del documento que se va a utilizar.

*subcadena*  
(Necesario) La subcadena de atributo o miembro de conjunto que se va a comprobar. Para obtener más información, consulte la función [contains (contiene)](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions) de DynamoDB.

## Tipo de retorno
<a name="ql-functions.contains.return-type"></a>

`bool`

## Ejemplos
<a name="ql-functions.contains.examples"></a>

```
SELECT * FROM "Orders" WHERE "OrderID"=1 AND contains("Address", 'Kirkland')
```

# Uso de la función SIZE con PartiQL para Amazon DynamoDB
<a name="ql-functions.size"></a>

Devuelve un número que representa el tamaño de un atributo en bytes. A continuación se muestran los tipos de datos válidos para usarlos con size. Para obtener más información, consulte la función [size (dimensión)](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions) de DynamoDB.

## Sintaxis
<a name="ql-functions.size.syntax"></a>

```
size( path)
```

## Argumentos
<a name="ql-functions.size.arguments"></a>

*path*  
(Necesario) Nombre del atributo o ruta del documento.   
Para conocer los tipos admitidos, consulte la función [size (dimensión)](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions) de DynamoDB.

## Tipo de retorno
<a name="ql-functions.size.return-type"></a>

`int`

## Ejemplos
<a name="ql-functions.size.examples"></a>

```
 SELECT * FROM "Orders" WHERE "OrderID"=1 AND size("Image") >300
```

# Operadores aritméticos, comparativos y lógicos de PartiQL para DynamoDB
<a name="ql-operators"></a>

PartiQL en Amazon DynamoDB admite los siguiente [Operadores estándares de SQL](https://www.w3schools.com/sql/sql_operators.asp).

**nota**  
DynamoDB no admite ningún operador SQL que no se incluya en esta lista.

## Operadores aritméticos
<a name="ql-operators.arithmetic"></a>


****  

| Operador | Descripción | 
| --- | --- | 
| \$1 | Add (Suma) | 
| - | Subtract (Sustracción) | 

## Operadores de comparación
<a name="ql-operators.comparison"></a>


****  

| Operador | Descripción | 
| --- | --- | 
| = | Igual que | 
| <> | No igual que | 
| \$1= | No igual que | 
| > | Mayor que | 
| < | Menor que | 
| >= | Mayor o igual que | 
| <= | Menor o igual que | 

## Operadores lógicos
<a name="ql-operators.logical"></a>


****  

| Operador | Descripción | 
| --- | --- | 
| AND | TRUE si todas las condiciones separadas por AND son TRUE | 
| BETWEEN |  `TRUE` si el operando está en el rango de las comparaciones Este operador incluye los límites inferior y superior de los operandos a los que se aplica.  | 
| IN | `TRUE` si el operando es igual a uno de una lista de expresiones (con un máximo de 50 valores de atributo hash o un máximo de 100 valores de atributo no clave). Los resultados se devuelven en páginas de hasta 10 elementos. Si la lista `IN` contiene más valores, debe utilizar los valores `NextToken` devueltos en la respuesta para recuperar las páginas siguientes. | 
| IS | TRUE si el operando es un tipo de datos PartiQL dado, incluyendo NULL o MISSING | 
| NOT | Invierte el valor de una expresión booleana dada | 
| OR | TRUE si alguna de las condiciones está separada por OR son TRUE | 

Consulte [Realización de comparaciones](Expressions.OperatorsAndFunctions.md#Expressions.OperatorsAndFunctions.Comparators) y [Evaluaciones lógicas](Expressions.OperatorsAndFunctions.md#Expressions.OperatorsAndFunctions.LogicalEvaluations) para obtener más información sobre operadores lógicos.

# Realización de transacciones con PartiQL para DynamoDB
<a name="ql-reference.multiplestatements.transactions"></a>

En esta sección se describe cómo utilizar transacciones con PartiQL para DynamoDB. Las transacciones PartiQL están limitadas a un total de 100 instrucciones (acciones).

Para obtener más información acerca de las transacciones de DynamoDB, consulte [Administración de flujos de trabajo complejos con transacciones de DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transactions.html).

**nota**  
Toda la transacción debe constar de instrucciones de lectura o de escritura. No puede mezclar ambos en una sola transacción. La función EXISTS es una excepción. Se puede utilizar para verificar la condición de atributos específicos del elemento de una manera similar a `ConditionCheck` en la operación de la API [TransactWriteItems](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-apis-txwriteitems).

**Topics**
+ [Sintaxis](#ql-reference.multiplestatements.transactions.syntax)
+ [Parameters](#ql-reference.multiplestatements.transactions.parameters)
+ [Valores devueltos](#ql-reference.multiplestatements.transactions.return)
+ [Ejemplos](#ql-reference.multiplestatements.transactions.examples)

## Sintaxis
<a name="ql-reference.multiplestatements.transactions.syntax"></a>

```
[
   {
      "Statement":" statement ",
      "Parameters":[
         {
            " parametertype " : " parametervalue "
         }, ...]
   } , ...
]
```

## Parameters
<a name="ql-reference.multiplestatements.transactions.parameters"></a>

***statement***  
(Necesario) Una instrucción compatible con PartiQL para DynamoDB.  
Toda la transacción debe constar de instrucciones de lectura o de escritura. No puede mezclar ambos en una sola transacción.

***parametertype***  
(Opcional) Tipo DynamoDB, si se utilizaron parámetros al especificar la instrucción PartiQL.

***parametervalue***  
(Opcional) Valor de parámetro si se utilizaron parámetros al especificar la instrucción PartiQL.

## Valores devueltos
<a name="ql-reference.multiplestatements.transactions.return"></a>

Esta instrucción no devuelve ningún valor para las operaciones de escritura (INSERT, UPDATE o DELETE). Sin embargo, devuelve valores diferentes para las operaciones de lectura (SELECT) en función de las condiciones especificadas en la cláusula WHERE.

**nota**  
Si alguna de las operaciones singleton INSERT, UPDATE o DELETE devuelve un error, las transacciones se cancelan con la excepción `TransactionCanceledException`, y el código de motivo de cancelación incluye los errores de las operaciones singleton individuales.

## Ejemplos
<a name="ql-reference.multiplestatements.transactions.examples"></a>

En el ejemplo siguiente, se ejecutan varias instrucciones como transacción.

------
#### [ AWS CLI ]

1. Guarde el siguiente código JSON en un archivo llamado partiql.json. 

   ```
   [
       {
           "Statement": "EXISTS(SELECT * FROM \"Music\" where Artist='No One You Know' and SongTitle='Call Me Today' and Awards is  MISSING)"
       },
       {
           "Statement": "INSERT INTO Music value {'Artist':?,'SongTitle':'?'}",
           "Parameters": [{\"S\": \"Acme Band\"}, {\"S\": \"Best Song\"}]
       },
       {
           "Statement": "UPDATE \"Music\" SET AwardsWon=1 SET AwardDetail={'Grammys':[2020, 2018]}  where Artist='Acme Band' and SongTitle='PartiQL Rocks'"
       }
   ]
   ```

1. Ejecute el comando siguiente en un símbolo del sistema.

   ```
   aws dynamodb execute-transaction --transact-statements  file://partiql.json
   ```

------
#### [ Java ]

```
public class DynamoDBPartiqlTransaction {

    public static void main(String[] args) {
        // Create the DynamoDB Client with the region you want
        AmazonDynamoDB dynamoDB = createDynamoDbClient("us-west-2");
        
        try {
            // Create ExecuteTransactionRequest
            ExecuteTransactionRequest executeTransactionRequest = createExecuteTransactionRequest();
            ExecuteTransactionResult executeTransactionResult = dynamoDB.executeTransaction(executeTransactionRequest);
            System.out.println("ExecuteTransaction successful.");
            // Handle executeTransactionResult

        } catch (Exception e) {
            handleExecuteTransactionErrors(e);
        }
    }

    private static AmazonDynamoDB createDynamoDbClient(String region) {
        return AmazonDynamoDBClientBuilder.standard().withRegion(region).build();
    }

    private static ExecuteTransactionRequest createExecuteTransactionRequest() {
        ExecuteTransactionRequest request = new ExecuteTransactionRequest();
        
        // Create statements
        List<ParameterizedStatement> statements = getPartiQLTransactionStatements();

        request.setTransactStatements(statements);
        return request;
    }

    private static List<ParameterizedStatement> getPartiQLTransactionStatements() {
        List<ParameterizedStatement> statements = new ArrayList<ParameterizedStatement>();

        statements.add(new ParameterizedStatement()
                               .withStatement("EXISTS(SELECT * FROM "Music" where Artist='No One You Know' and SongTitle='Call Me Today' and Awards is  MISSING)"));

        statements.add(new ParameterizedStatement()
                               .withStatement("INSERT INTO "Music" value {'Artist':'?','SongTitle':'?'}")
                               .withParameters(new AttributeValue("Acme Band"),new AttributeValue("Best Song")));

        statements.add(new ParameterizedStatement()
                               .withStatement("UPDATE "Music" SET AwardsWon=1 SET AwardDetail={'Grammys':[2020, 2018]}  where Artist='Acme Band' and SongTitle='PartiQL Rocks'"));

        return statements;
    }

    // Handles errors during ExecuteTransaction execution. Use recommendations in error messages below to add error handling specific to 
    // your application use-case.
    private static void handleExecuteTransactionErrors(Exception exception) {
        try {
            throw exception;
        } catch (TransactionCanceledException tce) {
            System.out.println("Transaction Cancelled, implies a client issue, fix before retrying. Error: " + tce.getErrorMessage());
        } catch (TransactionInProgressException tipe) {
            System.out.println("The transaction with the given request token is already in progress, consider changing " +
                "retry strategy for this type of error. Error: " + tipe.getErrorMessage());
        } catch (IdempotentParameterMismatchException ipme) {
            System.out.println("Request rejected because it was retried with a different payload but with a request token that was already used, " +
                "change request token for this payload to be accepted. Error: " + ipme.getErrorMessage());
        } catch (Exception e) {
            handleCommonErrors(e);
        }
    }

    private static void handleCommonErrors(Exception exception) {
        try {
            throw exception;
        } catch (InternalServerErrorException isee) {
            System.out.println("Internal Server Error, generally safe to retry with exponential back-off. Error: " + isee.getErrorMessage());
        } catch (RequestLimitExceededException rlee) {
            System.out.println("Throughput exceeds the current throughput limit for your account, increase account level throughput before " + 
                "retrying. Error: " + rlee.getErrorMessage());
        } catch (ProvisionedThroughputExceededException ptee) {
            System.out.println("Request rate is too high. If you're using a custom retry strategy make sure to retry with exponential back-off. " +
                "Otherwise consider reducing frequency of requests or increasing provisioned capacity for your table or secondary index. Error: " + 
                ptee.getErrorMessage());
        } catch (ResourceNotFoundException rnfe) {
            System.out.println("One of the tables was not found, verify table exists before retrying. Error: " + rnfe.getErrorMessage());
        } catch (AmazonServiceException ase) {
            System.out.println("An AmazonServiceException occurred, indicates that the request was correctly transmitted to the DynamoDB " + 
                "service, but for some reason, the service was not able to process it, and returned an error response instead. Investigate and " +
                "configure retry strategy. Error type: " + ase.getErrorType() + ". Error message: " + ase.getErrorMessage());
        } catch (AmazonClientException ace) {
            System.out.println("An AmazonClientException occurred, indicates that the client was unable to get a response from DynamoDB " +
                "service, or the client was unable to parse the response from the service. Investigate and configure retry strategy. "+
                "Error: " + ace.getMessage());
        } catch (Exception e) {
            System.out.println("An exception occurred, investigate and configure retry strategy. Error: " + e.getMessage());
        }
    }

}
```

------

En el ejemplo siguiente se muestran los distintos valores de retorno cuando DynamoDB lee elementos con condiciones diferentes especificadas en la cláusula WHERE.

------
#### [ AWS CLI ]

1. Guarde el siguiente código JSON en un archivo llamado partiql.json.

   ```
   [
       // Item exists and projected attribute exists
       {
           "Statement": "SELECT * FROM "Music" WHERE Artist='No One You Know' and SongTitle='Call Me Today'"
       },
       // Item exists but projected attributes do not exist
       {
           "Statement": "SELECT non_existent_projected_attribute FROM "Music" WHERE Artist='No One You Know' and SongTitle='Call Me Today'"
       },
       // Item does not exist
       {
           "Statement": "SELECT * FROM "Music" WHERE Artist='No One I Know' and SongTitle='Call You Today'"
       }
   ]
   ```

1.  Ejecute el comando siguiente en un símbolo del sistema.

   ```
   aws dynamodb execute-transaction --transact-statements  file://partiql.json
   ```

1. Se devuelve la siguiente respuesta:

   ```
   {
       "Responses": [
           // Item exists and projected attribute exists
           {
               "Item": {
                   "Artist":{
                       "S": "No One You Know"
                   },
                   "SongTitle":{
                       "S": "Call Me Today"
                   }    
               }
           },
           // Item exists but projected attributes do not exist
           {
               "Item": {}
           },
           // Item does not exist
           {}
       ]
   }
   ```

------

# Ejecución de operaciones por lote con PartiQL para DynamoDB
<a name="ql-reference.multiplestatements.batching"></a>

En esta sección se describe cómo utilizar instrucciones por lote con PartiQL para DynamoDB.

**nota**  
Todo el lote debe constar de instrucciones de lectura o de escritura; no se pueden mezclar ambas en un solo lote.
`BatchExecuteStatement` y `BatchWriteItem` no pueden realizar más de 25 instrucciones por lote.
`BatchExecuteStatement` hace uso de `BatchGetItem` que toma una lista de claves principales en instrucciones independientes.

**Topics**
+ [Sintaxis](#ql-reference.multiplestatements.batching.syntax)
+ [Parameters](#ql-reference.multiplestatements.batching.parameters)
+ [Ejemplos](#ql-reference.multiplestatements.batching.examples)

## Sintaxis
<a name="ql-reference.multiplestatements.batching.syntax"></a>

```
[
  {
    "Statement": "SELECT pk FROM ProblemSet WHERE pk = 'p#9StkWHYTxm7x2AqSXcrfu7' AND sk = 'info'"
  },
  {
    "Statement": "SELECT pk FROM ProblemSet WHERE pk = 'p#isC2ChceGbxHgESc4szoTE' AND sk = 'info'"
  }
]
```

```
[
   {
      "Statement":" statement ",
      "Parameters":[
         {
            " parametertype " : " parametervalue "
         }, ...]
   } , ...
]
```

## Parameters
<a name="ql-reference.multiplestatements.batching.parameters"></a>

***statement***  
(Necesario) Una instrucción compatible con PartiQL para DynamoDB.  
+ Todo el lote debe constar de instrucciones de lectura o de escritura; no se pueden mezclar ambas en un solo lote.
+ `BatchExecuteStatement` y `BatchWriteItem` no pueden realizar más de 25 instrucciones por lote.

***parametertype***  
(Opcional) Tipo DynamoDB, si se utilizaron parámetros al especificar la instrucción PartiQL.

***parametervalue***  
(Opcional) Valor de parámetro si se utilizaron parámetros al especificar la instrucción PartiQL.

## Ejemplos
<a name="ql-reference.multiplestatements.batching.examples"></a>

------
#### [ AWS CLI ]

1. Guarde el siguiente json en un archivo llamado partiql.json

   ```
   [
      {
   	 "Statement": "INSERT INTO Music VALUE {'Artist':?,'SongTitle':?}",
   	  "Parameters": [{"S": "Acme Band"}, {"S": "Best Song"}]
   	},
   	{
   	 "Statement": "UPDATE Music SET AwardsWon=1, AwardDetail={'Grammys':[2020, 2018]} WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'"
       }
   ]
   ```

1. Ejecute el comando siguiente en un símbolo del sistema.

   ```
   aws dynamodb batch-execute-statement  --statements  file://partiql.json
   ```

------
#### [ Java ]

```
public class DynamoDBPartiqlBatch {

    public static void main(String[] args) {
        // Create the DynamoDB Client with the region you want
        AmazonDynamoDB dynamoDB = createDynamoDbClient("us-west-2");
        
        try {
            // Create BatchExecuteStatementRequest
            BatchExecuteStatementRequest batchExecuteStatementRequest = createBatchExecuteStatementRequest();
            BatchExecuteStatementResult batchExecuteStatementResult = dynamoDB.batchExecuteStatement(batchExecuteStatementRequest);
            System.out.println("BatchExecuteStatement successful.");
            // Handle batchExecuteStatementResult

        } catch (Exception e) {
            handleBatchExecuteStatementErrors(e);
        }
    }

    private static AmazonDynamoDB createDynamoDbClient(String region) {

        return AmazonDynamoDBClientBuilder.standard().withRegion(region).build();
    }

    private static BatchExecuteStatementRequest createBatchExecuteStatementRequest() {
        BatchExecuteStatementRequest request = new BatchExecuteStatementRequest();

        // Create statements
        List<BatchStatementRequest> statements = getPartiQLBatchStatements();

        request.setStatements(statements);
        return request;
    }

    private static List<BatchStatementRequest> getPartiQLBatchStatements() {
        List<BatchStatementRequest> statements = new ArrayList<BatchStatementRequest>();

        statements.add(new BatchStatementRequest()
                               .withStatement("INSERT INTO Music value {'Artist':'Acme Band','SongTitle':'PartiQL Rocks'}"));

        statements.add(new BatchStatementRequest()
                               .withStatement("UPDATE Music set AwardDetail.BillBoard=[2020] where Artist='Acme Band' and SongTitle='PartiQL Rocks'"));

        return statements;
    }

    // Handles errors during BatchExecuteStatement execution. Use recommendations in error messages below to add error handling specific to 
    // your application use-case.
    private static void handleBatchExecuteStatementErrors(Exception exception) {
        try {
            throw exception;
        } catch (Exception e) {
            // There are no API specific errors to handle for BatchExecuteStatement, common DynamoDB API errors are handled below
            handleCommonErrors(e);
        }
    }

    private static void handleCommonErrors(Exception exception) {
        try {
            throw exception;
        } catch (InternalServerErrorException isee) {
            System.out.println("Internal Server Error, generally safe to retry with exponential back-off. Error: " + isee.getErrorMessage());
        } catch (RequestLimitExceededException rlee) {
            System.out.println("Throughput exceeds the current throughput limit for your account, increase account level throughput before " + 
                "retrying. Error: " + rlee.getErrorMessage());
        } catch (ProvisionedThroughputExceededException ptee) {
            System.out.println("Request rate is too high. If you're using a custom retry strategy make sure to retry with exponential back-off. " +
                "Otherwise consider reducing frequency of requests or increasing provisioned capacity for your table or secondary index. Error: " + 
                ptee.getErrorMessage());
        } catch (ResourceNotFoundException rnfe) {
            System.out.println("One of the tables was not found, verify table exists before retrying. Error: " + rnfe.getErrorMessage());
        } catch (AmazonServiceException ase) {
            System.out.println("An AmazonServiceException occurred, indicates that the request was correctly transmitted to the DynamoDB " + 
                "service, but for some reason, the service was not able to process it, and returned an error response instead. Investigate and " +
                "configure retry strategy. Error type: " + ase.getErrorType() + ". Error message: " + ase.getErrorMessage());
        } catch (AmazonClientException ace) {
            System.out.println("An AmazonClientException occurred, indicates that the client was unable to get a response from DynamoDB " +
                "service, or the client was unable to parse the response from the service. Investigate and configure retry strategy. "+
                "Error: " + ace.getMessage());
        } catch (Exception e) {
            System.out.println("An exception occurred, investigate and configure retry strategy. Error: " + e.getMessage());
        }
    }

}
```

------

# Políticas de seguridad de IAM con PartiQL para DynamoDB
<a name="ql-iam"></a>

Los siguientes permisos son necesarios:
+ Para leer elementos que utilizan PartiQL para DynamoDB, debe tener el permiso `dynamodb:PartiQLSelect` en la tabla o índice.
+ Para insertar elementos utilizando PartiQL para DynamoDB, debe tener el permiso `dynamodb:PartiQLInsert` en la tabla o índice.
+ Para actualizar elementos con PartiQL para DynamoDB, debe tener el permiso `dynamodb:PartiQLUpdate` en la tabla o índice.
+ Para eliminar elementos que utilizan PartiQL para DynamoDB, debe tener el permiso `dynamodb:PartiQLDelete` en la tabla o índice.

## Ejemplo: Permitir todas las instrucciones PartiQL para DynamoDB (Select/Insert/Update/Delete) en una tabla
<a name="access-policy-ql-iam-example1"></a>

La siguiente política de IAM concede permisos para ejecutar todas las instrucciones PartiQL para DynamoDB en una tabla. 

------
#### [ JSON ]

****  

```
{
   "Version":"2012-10-17",		 	 	 
   "Statement":[
      {
         "Effect":"Allow",
         "Action":[
            "dynamodb:PartiQLInsert",
            "dynamodb:PartiQLUpdate",
            "dynamodb:PartiQLDelete",
            "dynamodb:PartiQLSelect"
         ],
         "Resource":[
            "arn:aws:dynamodb:us-west-2:123456789012:table/Music"
         ]
      }
   ]
}
```

------

## Ejemplo: Permitir que PartiQL para DynamoDB seleccione las instrucciones en una tabla
<a name="access-policy-ql-iam-example2"></a>

La siguiente política de IAM concede permisos para ejecutar la instrucción `select`sobre una tabla específica.

------
#### [ JSON ]

****  

```
{
   "Version":"2012-10-17",		 	 	 
   "Statement":[
      {
         "Effect":"Allow",
         "Action":[
            "dynamodb:PartiQLSelect"
         ],
         "Resource":[
            "arn:aws:dynamodb:us-west-2:123456789012:table/Music"
         ]
      }
   ]
}
```

------

## Ejemplo: Permitir instrucciones de inserción de PartiQL para DynamoDB en un índice
<a name="access-policy-ql-iam-example3"></a>

La siguiente política de IAM concede permisos para ejecutar la instrucción `insert` en un índice específico. 

------
#### [ JSON ]

****  

```
{
   "Version":"2012-10-17",		 	 	 
   "Statement":[
      {
         "Effect":"Allow",
         "Action":[
            "dynamodb:PartiQLInsert"
         ],
         "Resource":[
            "arn:aws:dynamodb:us-west-2:123456789012:table/Music/index/index1"
         ]
      }
   ]
}
```

------

## Ejemplo: Permitir las instrucciones transaccionales de PartiQL para DynamoDB sólo en una tabla
<a name="access-policy-ql-iam-example4"></a>

La siguiente política de IAM concede permisos para ejecutar solamente instrucciones transaccionales en una tabla determinada. 

------
#### [ JSON ]

****  

```
{
   "Version":"2012-10-17",		 	 	 
   "Statement":[
      {
         "Effect":"Allow",
         "Action":[
            "dynamodb:PartiQLInsert",
            "dynamodb:PartiQLUpdate",
            "dynamodb:PartiQLDelete",
            "dynamodb:PartiQLSelect"
         ],
         "Resource":[
            "arn:aws:dynamodb:us-west-2:123456789012:table/Music"
         ],
         "Condition":{
            "StringEquals":{
               "dynamodb:EnclosingOperation":[
                  "ExecuteTransaction"
               ]
            }
         }
      }
   ]
}
```

------

## Ejemplo: Permitir lecturas y escrituras no transaccionales de PartiQL para DynamoDB y bloquear lecturas y escrituras transaccionales de PartiQL en una tabla.
<a name="access-policy-ql-iam-example5"></a>

 La siguiente directiva de IAM otorga permisos para ejecutar lecturas y escrituras no transaccionales de PartiQL para DynamoDB a la vez que bloquea lecturas y escrituras transaccionales de PartiQL para DynamoDB.

------
#### [ JSON ]

****  

```
{
   "Version":"2012-10-17",		 	 	 
   "Statement":[
      {
         "Effect":"Deny",
         "Action":[
            "dynamodb:PartiQLInsert",
            "dynamodb:PartiQLUpdate",
            "dynamodb:PartiQLDelete",
            "dynamodb:PartiQLSelect"
         ],
         "Resource":[
            "arn:aws:dynamodb:us-west-2:123456789012:table/Music"
         ],
         "Condition":{
            "StringEquals":{
               "dynamodb:EnclosingOperation":[
                  "ExecuteTransaction"
               ]
            }
         }
      },
      {
         "Effect":"Allow",
         "Action":[
            "dynamodb:PartiQLInsert",
            "dynamodb:PartiQLUpdate",
            "dynamodb:PartiQLDelete",
            "dynamodb:PartiQLSelect"
         ],
         "Resource":[
            "arn:aws:dynamodb:us-west-2:123456789012:table/Music"
         ]
      }
   ]
}
```

------

## Ejemplo: permitir instrucciones Select y denegar instrucciones de análisis de tabla completa en PartiQL para DynamoDB
<a name="access-policy-ql-iam-example6"></a>

La siguiente política de IAM concede permisos para ejecutar la instrucción `select` en una tabla específica mientras se bloquean las instrucciones `select` que dan como resultado un examen completo de toda la tabla.

------
#### [ JSON ]

****  

```
{
   "Version":"2012-10-17",		 	 	 
   "Statement":[
      {
         "Effect":"Deny",
         "Action":[
            "dynamodb:PartiQLSelect"
         ],
         "Resource":[
            "arn:aws:dynamodb:us-west-2:123456789012:table/WatchList"
         ],
         "Condition":{
            "Bool":{
               "dynamodb:FullTableScan":[
                  "true"
               ]
            }
         }
      },
      {
         "Effect":"Allow",
         "Action":[
            "dynamodb:PartiQLSelect"
         ],
         "Resource":[
            "arn:aws:dynamodb:us-west-2:123456789012:table/WatchList"
         ]
      }
   ]
}
```

------

# Uso de elementos: Java
<a name="JavaDocumentAPIItemCRUD"></a>

Puede usar la API de documentos del AWS SDK para Java para realizar operaciones típicas de creación, lectura, actualización y eliminación (CRUD, por sus siglas en inglés) en los elementos de una tabla de Amazon DynamoDB.

**nota**  
Además, SDK para Java proporciona un modelo de persistencia de objetos, que le permite mapear las clases del lado del cliente a las tablas de DynamoDB. Este enfoque puede reducir la cantidad de código que hay que escribir. Para obtener más información, consulte [Java 1.x: DynamoDBMapper](DynamoDBMapper.md).

Esta sección contiene ejemplos de Java que permiten realizar diversas acciones con elementos de la API de documentos de Java y varios ejemplos de trabajo completos.

**Topics**
+ [Colocación de un elemento](#PutDocumentAPIJava)
+ [Obtención de un elemento](#JavaDocumentAPIGetItem)
+ [Escritura por lotes: colocación y eliminación de varios elementos](#BatchWriteDocumentAPIJava)
+ [Obtención por lotes: obtención de varios elementos](#JavaDocumentAPIBatchGetItem)
+ [Actualización de un elemento](#JavaDocumentAPIItemUpdate)
+ [Eliminación de un elemento](#DeleteMidLevelJava)
+ [Ejemplo: operaciones CRUD mediante la API de documentos de AWS SDK para Java](JavaDocumentAPICRUDExample.md)
+ [Ejemplo: operaciones por lotes mediante la API de documentos de AWS SDK para Java](batch-operation-document-api-java.md)
+ [Ejemplo: control de atributos de tipo binario mediante la API de documentos de AWS SDK para Java](JavaDocumentAPIBinaryTypeExample.md)

## Colocación de un elemento
<a name="PutDocumentAPIJava"></a>

El método `putItem` almacena un elemento en una tabla. Si el elemento existe, sustituye el elemento completo. En lugar de ello, si prefiere actualizar solamente algunos atributos concretos, puede usar el método `updateItem`. Para obtener más información, consulte [Actualización de un elemento](#JavaDocumentAPIItemUpdate). 

------
#### [ Java v2 ]

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
import software.amazon.awssdk.services.dynamodb.model.PutItemResponse;
import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException;
import java.util.HashMap;

/**
 * Before running this Java V2 code example, set up your development
 * environment, including your credentials.
 *
 * For more information, see the following documentation topic:
 *
 * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
 *
 * To place items into an Amazon DynamoDB table using the AWS SDK for Java V2,
 * its better practice to use the
 * Enhanced Client. See the EnhancedPutItem example.
 */
public class PutItem {
    public static void main(String[] args) {
        final String usage = """

                Usage:
                    <tableName> <key> <keyVal> <albumtitle> <albumtitleval> <awards> <awardsval> <Songtitle> <songtitleval>

                Where:
                    tableName - The Amazon DynamoDB table in which an item is placed (for example, Music3).
                    key - The key used in the Amazon DynamoDB table (for example, Artist).
                    keyval - The key value that represents the item to get (for example, Famous Band).
                    albumTitle - The Album title (for example, AlbumTitle).
                    AlbumTitleValue - The name of the album (for example, Songs About Life ).
                    Awards - The awards column (for example, Awards).
                    AwardVal - The value of the awards (for example, 10).
                    SongTitle - The song title (for example, SongTitle).
                    SongTitleVal - The value of the song title (for example, Happy Day).
                **Warning** This program will  place an item that you specify into a table!
                """;

        if (args.length != 9) {
            System.out.println(usage);
            System.exit(1);
        }

        String tableName = args[0];
        String key = args[1];
        String keyVal = args[2];
        String albumTitle = args[3];
        String albumTitleValue = args[4];
        String awards = args[5];
        String awardVal = args[6];
        String songTitle = args[7];
        String songTitleVal = args[8];

        Region region = Region.US_EAST_1;
        DynamoDbClient ddb = DynamoDbClient.builder()
                .region(region)
                .build();

        putItemInTable(ddb, tableName, key, keyVal, albumTitle, albumTitleValue, awards, awardVal, songTitle,
                songTitleVal);
        System.out.println("Done!");
        ddb.close();
    }

    public static void putItemInTable(DynamoDbClient ddb,
            String tableName,
            String key,
            String keyVal,
            String albumTitle,
            String albumTitleValue,
            String awards,
            String awardVal,
            String songTitle,
            String songTitleVal) {

        HashMap<String, AttributeValue> itemValues = new HashMap<>();
        itemValues.put(key, AttributeValue.builder().s(keyVal).build());
        itemValues.put(songTitle, AttributeValue.builder().s(songTitleVal).build());
        itemValues.put(albumTitle, AttributeValue.builder().s(albumTitleValue).build());
        itemValues.put(awards, AttributeValue.builder().s(awardVal).build());

        PutItemRequest request = PutItemRequest.builder()
                .tableName(tableName)
                .item(itemValues)
                .build();

        try {
            PutItemResponse response = ddb.putItem(request);
            System.out.println(tableName + " was successfully updated. The request id is "
                    + response.responseMetadata().requestId());

        } catch (ResourceNotFoundException e) {
            System.err.format("Error: The Amazon DynamoDB table \"%s\" can't be found.\n", tableName);
            System.err.println("Be sure that it exists and that you've typed its name correctly!");
            System.exit(1);
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
    }
}
```

------
#### [ Java v1 ]

Siga estos pasos: 

1. Cree una instancia de la clase `DynamoDB`.

1. Cree una instancia de la clase `Table` para representar la tabla que desea usar.

1. Cree una instancia de la clase `Item` para representar el nuevo elemento. Debe especificar la clave principal del nuevo elemento y sus atributos.

1. Llame al método `putItem` del objeto `Table` utilizando el `Item` que creó en el paso anterior.

En el siguiente ejemplo de código Java se muestran las tareas anteriores. El código escribe un nuevo elemento en la tabla `ProductCatalog`.

**Example**  

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

Table table = dynamoDB.getTable("ProductCatalog");

// Build a list of related items
List<Number> relatedItems = new ArrayList<Number>();
relatedItems.add(341);
relatedItems.add(472);
relatedItems.add(649);

//Build a map of product pictures
Map<String, String> pictures = new HashMap<String, String>();
pictures.put("FrontView", "http://example.com/products/123_front.jpg");
pictures.put("RearView", "http://example.com/products/123_rear.jpg");
pictures.put("SideView", "http://example.com/products/123_left_side.jpg");

//Build a map of product reviews
Map<String, List<String>> reviews = new HashMap<String, List<String>>();

List<String> fiveStarReviews = new ArrayList<String>();
fiveStarReviews.add("Excellent! Can't recommend it highly enough!  Buy it!");
fiveStarReviews.add("Do yourself a favor and buy this");
reviews.put("FiveStar", fiveStarReviews);

List<String> oneStarReviews = new ArrayList<String>();
oneStarReviews.add("Terrible product!  Do not buy this.");
reviews.put("OneStar", oneStarReviews);

// Build the item
Item item = new Item()
    .withPrimaryKey("Id", 123)
    .withString("Title", "Bicycle 123")
    .withString("Description", "123 description")
    .withString("BicycleType", "Hybrid")
    .withString("Brand", "Brand-Company C")
    .withNumber("Price", 500)
    .withStringSet("Color",  new HashSet<String>(Arrays.asList("Red", "Black")))
    .withString("ProductCategory", "Bicycle")
    .withBoolean("InStock", true)
    .withNull("QuantityOnHand")
    .withList("RelatedItems", relatedItems)
    .withMap("Pictures", pictures)
    .withMap("Reviews", reviews);

// Write the item to the table
PutItemOutcome outcome = table.putItem(item);
```

En el ejemplo anterior, el elemento tiene atributos que son escalares (`String`, `Number`, `Boolean`, `Null`), conjuntos (`String Set`) y tipos de documentos (`List`, `Map`).

------

### Especificación de parámetros opcionales
<a name="PutItemJavaDocumentAPIOptions"></a>

Junto con los parámetros requeridos, puede especificar también otros opcionales en el método `putItem`. Por ejemplo, en el ejemplo de código Java siguiente se utiliza un parámetro opcional que permite especificar una condición para cargar el elemento. Si la condición especificada no se cumple, AWS SDK para Java genera una excepción `ConditionalCheckFailedException`. En el ejemplo de código se especifican los parámetros opcionales siguientes en el método `putItem`:
+ Una expresión `ConditionExpression` que define las condiciones de la solicitud. El código define la condición siguiente: si el elemento existente tiene la misma clave principal, solo se sustituirá si tiene también un atributo ISBN igual a un valor específico. 
+ Un mapa de `ExpressionAttributeValues` que se usa en la condición. En este caso, solo se requiere una sustitución: el marcador de posición `:val` de la expresión de condición se sustituye en el tiempo de ejecución por el valor de ISBN real que se va a comprobar.

En el siguiente ejemplo se agrega un nuevo elemento de libro utilizando estos parámetros opcionales.

**Example**  

```
Item item = new Item()
    .withPrimaryKey("Id", 104)
    .withString("Title", "Book 104 Title")
    .withString("ISBN", "444-4444444444")
    .withNumber("Price", 20)
    .withStringSet("Authors",
        new HashSet<String>(Arrays.asList("Author1", "Author2")));

Map<String, Object> expressionAttributeValues = new HashMap<String, Object>();
expressionAttributeValues.put(":val", "444-4444444444");

PutItemOutcome outcome = table.putItem(
    item,
    "ISBN = :val", // ConditionExpression parameter
    null,          // ExpressionAttributeNames parameter - we're not using it for this example
    expressionAttributeValues);
```

### PutItem y documentos JSON
<a name="PutItemJavaDocumentAPI.JSON"></a>

Puede almacenar un documento JSON como atributo en una tabla de DynamoDB. Para ello, se utiliza el método `withJSON` de `Item`. Este método analiza el documento JSON y mapea cada componente a un tipo de datos nativo de DynamoDB.

Suponga que desea almacenar el siguiente documento JSON, que contiene los proveedores que pueden servir pedidos de un producto determinado.

**Example**  

```
{
    "V01": {
        "Name": "Acme Books",
        "Offices": [ "Seattle" ]
    },
    "V02": {
        "Name": "New Publishers, Inc.",
        "Offices": ["London", "New York"
        ]
    },
    "V03": {
        "Name": "Better Buy Books",
        "Offices": [ "Tokyo", "Los Angeles", "Sydney"
        ]
    }
}
```

Puede utilizar el método `withJSON` para almacenar esta información en la tabla `ProductCatalog` en un atributo de tipo `Map` denominado `VendorInfo`. En el siguiente ejemplo de código Java se muestra cómo hacerlo.

```
// Convert the document into a String.  Must escape all double-quotes.
String vendorDocument = "{"
    + "    \"V01\": {"
    + "        \"Name\": \"Acme Books\","
    + "        \"Offices\": [ \"Seattle\" ]"
    + "    },"
    + "    \"V02\": {"
    + "        \"Name\": \"New Publishers, Inc.\","
    + "        \"Offices\": [ \"London\", \"New York\"" + "]" + "},"
    + "    \"V03\": {"
    + "        \"Name\": \"Better Buy Books\","
    +          "\"Offices\": [ \"Tokyo\", \"Los Angeles\", \"Sydney\""
    + "            ]"
    + "        }"
    + "    }";

Item item = new Item()
    .withPrimaryKey("Id", 210)
    .withString("Title", "Book 210 Title")
    .withString("ISBN", "210-2102102102")
    .withNumber("Price", 30)
    .withJSON("VendorInfo", vendorDocument);

PutItemOutcome outcome = table.putItem(item);
```

## Obtención de un elemento
<a name="JavaDocumentAPIGetItem"></a>

Para recuperar un solo elemento, utilice el método `getItem` de un objeto `Table`. Siga estos pasos: 

1. Cree una instancia de la clase `DynamoDB`.

1. Cree una instancia de la clase `Table` para representar la tabla que desea usar.

1. Llame al método `getItem` de la instancia de `Table`. Es preciso especificar la clave principal del elemento que se desea recuperar.

En el siguiente ejemplo de código Java se muestran los pasos anteriores. El código obtiene el elemento que tiene la clave de partición especificada.

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

Table table = dynamoDB.getTable("ProductCatalog");

Item item = table.getItem("Id", 210);
```

### Especificación de parámetros opcionales
<a name="GetItemJavaDocumentAPIOptions"></a>

Junto con los parámetros requeridos, puede especificar también otros opcionales del método `getItem`. Por ejemplo, en el siguiente ejemplo de código Java se utiliza un método opcional para recuperar solo una lista concreta de atributos y especificar lecturas de consistencia alta. Para obtener más información sobre la consistencia de lectura, consulte . [Coherencia de lectura de DynamoDB](HowItWorks.ReadConsistency.md).)

Puede usar una expresión `ProjectionExpression` para recuperar solamente algunos atributos o componentes concretos, en lugar de un elemento completo. Una expresión `ProjectionExpression` permite especificar atributos de nivel superior o anidados mediante rutas de documentos. Para obtener más información, consulte [Uso de expresiones de proyección en DynamoDB](Expressions.ProjectionExpressions.md).

Los parámetros del método `getItem` no permiten especificar la consistencia de .lectura Sin embargo, puede crear una especificación `GetItemSpec`, que proporciona acceso pleno a toda la información de entrada de la operación de bajo nivel `GetItem`. En el ejemplo de código siguiente se crea una especificación `GetItemSpec` y se utiliza como información de entrada para el método `getItem`.

**Example**  

```
GetItemSpec spec = new GetItemSpec()
    .withPrimaryKey("Id", 206)
    .withProjectionExpression("Id, Title, RelatedItems[0], Reviews.FiveStar")
    .withConsistentRead(true);

Item item = table.getItem(spec);

System.out.println(item.toJSONPretty());
```

 Para imprimir un `Item` en formato fácil de leer, utilice el método `toJSONPretty`. El resultado del ejemplo anterior tiene este aspecto.

```
{
  "RelatedItems" : [ 341 ],
  "Reviews" : {
    "FiveStar" : [ "Excellent! Can't recommend it highly enough! Buy it!", "Do yourself a favor and buy this" ]
  },
  "Id" : 123,
  "Title" : "20-Bicycle 123"
}
```

### GetItem y documentos JSON
<a name="GetItemJavaDocumentAPI.JSON"></a>

En la sección [PutItem y documentos JSON](#PutItemJavaDocumentAPI.JSON), almacena un documento JSON en un atributo `Map` denominado `VendorInfo`. Puede utilizar el método `getItem` para recuperar todo el documento en formato JSON. O puede usar la notación de ruta de documento para recuperar solo algunos de los componentes de ese documento. En el siguiente ejemplo de código Java se muestran estas técnicas.

```
GetItemSpec spec = new GetItemSpec()
    .withPrimaryKey("Id", 210);

System.out.println("All vendor info:");
spec.withProjectionExpression("VendorInfo");
System.out.println(table.getItem(spec).toJSON());

System.out.println("A single vendor:");
spec.withProjectionExpression("VendorInfo.V03");
System.out.println(table.getItem(spec).toJSON());

System.out.println("First office location for this vendor:");
spec.withProjectionExpression("VendorInfo.V03.Offices[0]");
System.out.println(table.getItem(spec).toJSON());
```

El resultado del ejemplo anterior tiene este aspecto.

```
All vendor info:
{"VendorInfo":{"V03":{"Name":"Better Buy Books","Offices":["Tokyo","Los Angeles","Sydney"]},"V02":{"Name":"New Publishers, Inc.","Offices":["London","New York"]},"V01":{"Name":"Acme Books","Offices":["Seattle"]}}}
A single vendor:
{"VendorInfo":{"V03":{"Name":"Better Buy Books","Offices":["Tokyo","Los Angeles","Sydney"]}}}
First office location for a single vendor:
{"VendorInfo":{"V03":{"Offices":["Tokyo"]}}}
```

**nota**  
Puede usar el método `toJSON` para convertir cualquier elemento (o sus atributos) en una cadena con formato JSON. En el siguiente ejemplo de código se recuperan varios atributos de nivel superior y anidados y se imprimen los resultados como JSON.  

```
GetItemSpec spec = new GetItemSpec()
    .withPrimaryKey("Id", 210)
    .withProjectionExpression("VendorInfo.V01, Title, Price");

Item item = table.getItem(spec);
System.out.println(item.toJSON());
```
El resultado es similar al siguiente.  

```
{"VendorInfo":{"V01":{"Name":"Acme Books","Offices":["Seattle"]}},"Price":30,"Title":"Book 210 Title"}
```

## Escritura por lotes: colocación y eliminación de varios elementos
<a name="BatchWriteDocumentAPIJava"></a>

La *escritura por lotes* se refiere a colocar y eliminar varios elementos en un lote. El método `batchWriteItem` permite colocar y eliminar varios elementos de una o varias tablas con una sola llamada. A continuación se indican los pasos para colocar o eliminar varios elementos mediante la API de documentos del AWS SDK para Java.

1. Cree una instancia de la clase `DynamoDB`.

1. Cree una instancia de la clase `TableWriteItems` que describe todas las operaciones de colocación y eliminación de una tabla. Si desea escribir en varias tablas en una misma operación de escritura por lotes, debe crear una instancia de `TableWriteItems` por cada tabla.

1. Llame al método `batchWriteItem` proporcionando los objetos `TableWriteItems` que creó en el paso anterior. 

1. Procese la respuesta. Debe comprobar si en la respuesta se ha devuelto algún elemento de solicitud sin procesar. Esto puede ocurrir si se alcanza la cuota de rendimiento aprovisionado o se produce algún otro error transitorio. Además, DynamoDB limita el tamaño de la solicitud y el número de operaciones que se pueden especificar en ella. Si supera estos límites, DynamoDB rechaza la solicitud. Para obtener más información, consulte [Cuotas en Amazon DynamoDB](ServiceQuotas.md). 

En el siguiente ejemplo de código Java se muestran los pasos anteriores. El ejemplo lleva a cabo una operación `batchWriteItem` en dos tablas: `Forum` y `Thread`. Los objetos `TableWriteItems` correspondientes definen las siguientes acciones:
+ Colocar un elemento en la tabla `Forum`.
+ Colocar y eliminar un elemento en la tabla `Thread`.

A continuación, el código llama a `batchWriteItem` para llevar a cabo la operación.

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

TableWriteItems forumTableWriteItems = new TableWriteItems("Forum")
    .withItemsToPut(
        new Item()
            .withPrimaryKey("Name", "Amazon RDS")
            .withNumber("Threads", 0));

TableWriteItems threadTableWriteItems = new TableWriteItems("Thread")
    .withItemsToPut(
        new Item()
            .withPrimaryKey("ForumName","Amazon RDS","Subject","Amazon RDS Thread 1")
    .withHashAndRangeKeysToDelete("ForumName","Some partition key value", "Amazon S3", "Some sort key value");

BatchWriteItemOutcome outcome = dynamoDB.batchWriteItem(forumTableWriteItems, threadTableWriteItems);

// Code for checking unprocessed items is omitted in this example
```

Para ver un ejemplo práctico, consulte [Ejemplo: operación de escritura por lotes mediante la API de documentos de AWS SDK para Java](batch-operation-document-api-java.md#JavaDocumentAPIBatchWrite). 

## Obtención por lotes: obtención de varios elementos
<a name="JavaDocumentAPIBatchGetItem"></a>

El método `batchGetItem` permite recuperar varios elementos de una o varias tablas. Para recuperar un solo elemento, puede usar el método `getItem`. 

Siga estos pasos: 

1. Cree una instancia de la clase `DynamoDB`.

1. Cree una instancia de la clase `TableKeysAndAttributes` que describe una lista de valores de clave principal que se van a recuperar de una tabla. Si desea leer de varias tablas en una misma operación de obtención por lotes, debe crear una instancia de `TableKeysAndAttributes` por cada tabla.

1. Llame al método `batchGetItem` proporcionando los objetos `TableKeysAndAttributes` que creó en el paso anterior.

En el siguiente ejemplo de código Java se muestran los pasos anteriores. El ejemplo recupera dos elementos de la tabla `Forum` y tres de la tabla `Thread`.

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

    TableKeysAndAttributes forumTableKeysAndAttributes = new TableKeysAndAttributes(forumTableName);
    forumTableKeysAndAttributes.addHashOnlyPrimaryKeys("Name",
    "Amazon S3",
    "Amazon DynamoDB");

TableKeysAndAttributes threadTableKeysAndAttributes = new TableKeysAndAttributes(threadTableName);
threadTableKeysAndAttributes.addHashAndRangePrimaryKeys("ForumName", "Subject",
    "Amazon DynamoDB","DynamoDB Thread 1",
    "Amazon DynamoDB","DynamoDB Thread 2",
    "Amazon S3","S3 Thread 1");

BatchGetItemOutcome outcome = dynamoDB.batchGetItem(
    forumTableKeysAndAttributes, threadTableKeysAndAttributes);

for (String tableName : outcome.getTableItems().keySet()) {
    System.out.println("Items in table " + tableName);
    List<Item> items = outcome.getTableItems().get(tableName);
    for (Item item : items) {
        System.out.println(item);
    }
}
```

### Especificación de parámetros opcionales
<a name="BatchGetItemJavaDocumentAPIOptions"></a>

Junto con los parámetros requeridos, puede especificar también otros opcionales cuando use `batchGetItem`. Por ejemplo, puede proporcionar una expresión `ProjectionExpression` con cada `TableKeysAndAttributes` que defina. Esto le permite especificar los atributos que desea recuperar de la tabla.

En el siguiente ejemplo de código se recuperan dos elementos de la tabla `Forum`. El parámetro `withProjectionExpression` especifica que solamente hay que recuperar el atributo `Threads`.

**Example**  

```
TableKeysAndAttributes forumTableKeysAndAttributes = new TableKeysAndAttributes("Forum")
    .withProjectionExpression("Threads");

forumTableKeysAndAttributes.addHashOnlyPrimaryKeys("Name",
    "Amazon S3",
    "Amazon DynamoDB");

BatchGetItemOutcome outcome = dynamoDB.batchGetItem(forumTableKeysAndAttributes);
```

## Actualización de un elemento
<a name="JavaDocumentAPIItemUpdate"></a>

El método `updateItem` de un objeto `Table` permite actualizar los valores de atributos presentes, agregar atributos nuevos o eliminarlos de un elemento existente. 

El método `updateItem` se comporta de la siguiente manera:
+ Si un elemento no existe (no hay ningún elemento en la tabla con la clave principal especificada), `updateItem` agrega un elemento nuevo a la tabla.
+ Si un elemento ya existe, `updateItem` lleva a cabo la actualización según lo especificado en el parámetro `UpdateExpression`.

**nota**  
También es posible actualizar un elemento mediante `putItem`. Por ejemplo, si llama a `putItem` para agregar un elemento a la tabla pero ya existe uno con la clave principal especificada, `putItem` sustituye el elemento completo. Si hay atributos en el elemento existente que no se especifican en la información de entrada, `putItem` los elimina del elemento.  
En general, recomendamos usar `updateItem` siempre que desee modificar atributos de elementos. El método `updateItem` solo modifica los atributos del elemento que especifique en la información de entrada, pero deja los demás atributos del elemento tal cual estaban.

Siga estos pasos: 

1. Cree una instancia de la clase `Table` para representar la tabla que desea usar.

1. Llame al método `updateTable` de la instancia de `Table`. Debe especificar la clave principal del elemento que desea recuperar, junto con una expresión `UpdateExpression` que describa los atributos que hay que cambiar y cómo modificarlos.

En el siguiente ejemplo de código Java se muestran las tareas anteriores. El código actualiza un elemento de libro en la tabla `ProductCatalog`. Se agrega un nuevo autor al conjunto `Authors` y se elimina el atributo `ISBN` existente. También se reduce el precio en una unidad.

Se utiliza un mapa `ExpressionAttributeValues` en `UpdateExpression`. Los marcadores de posición `:val1` y `:val2` se sustituyen en tiempo de ejecución por los valores reales de `Authors` y `Price`.

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

Table table = dynamoDB.getTable("ProductCatalog");

Map<String, String> expressionAttributeNames = new HashMap<String, String>();
expressionAttributeNames.put("#A", "Authors");
expressionAttributeNames.put("#P", "Price");
expressionAttributeNames.put("#I", "ISBN");

Map<String, Object> expressionAttributeValues = new HashMap<String, Object>();
expressionAttributeValues.put(":val1",
    new HashSet<String>(Arrays.asList("Author YY","Author ZZ")));
expressionAttributeValues.put(":val2", 1);   //Price

UpdateItemOutcome outcome =  table.updateItem(
    "Id",          // key attribute name
    101,           // key attribute value
    "add #A :val1 set #P = #P - :val2 remove #I", // UpdateExpression
    expressionAttributeNames,
    expressionAttributeValues);
```

### Especificación de parámetros opcionales
<a name="UpdateItemJavaDocumentAPIOptions"></a>

Junto con los parámetros requeridos, puede especificar también parámetros opcionales para el método `updateItem`, incluida una condición que debe cumplirse para que se lleve a cabo la actualización. Si la condición especificada no se cumple, AWS SDK para Java genera una excepción `ConditionalCheckFailedException`. Por ejemplo, el siguiente ejemplo de código Java actualiza de forma condicional el precio de un elemento de libro a 25. Especifica una expresión `ConditionExpression` en la que se indica que el precio solo debe actualizarse si el precio actual es 20.

**Example**  

```
Table table = dynamoDB.getTable("ProductCatalog");

Map<String, String> expressionAttributeNames = new HashMap<String, String>();
expressionAttributeNames.put("#P", "Price");

Map<String, Object> expressionAttributeValues = new HashMap<String, Object>();
expressionAttributeValues.put(":val1", 25);  // update Price to 25...
expressionAttributeValues.put(":val2", 20);  //...but only if existing Price is 20

UpdateItemOutcome outcome = table.updateItem(
    new PrimaryKey("Id",101),
    "set #P = :val1", // UpdateExpression
    "#P = :val2",     // ConditionExpression
    expressionAttributeNames,
    expressionAttributeValues);
```

### Contador atómico
<a name="AtomicCounterJavaDocumentAPI"></a>

Puede usar `updateItem` para implementar un contador atómico y aumentar o reducir el valor de un atributo existente sin interferir con las demás solicitudes de escritura. Para incrementar un contador atómico, use una expresión `UpdateExpression` con la acción `set` para sumar un valor numérico a una atributo existente de tipo `Number`.

En el siguiente ejemplo se pone en práctica lo anterior y se incrementa el atributo `Quantity` en una unidad. También se demuestra cómo usar el parámetro `ExpressionAttributeNames` en una expresión `UpdateExpression`.

```
Table table = dynamoDB.getTable("ProductCatalog");

Map<String,String> expressionAttributeNames = new HashMap<String,String>();
expressionAttributeNames.put("#p", "PageCount");

Map<String,Object> expressionAttributeValues = new HashMap<String,Object>();
expressionAttributeValues.put(":val", 1);

UpdateItemOutcome outcome = table.updateItem(
    "Id", 121,
    "set #p = #p + :val",
    expressionAttributeNames,
    expressionAttributeValues);
```

## Eliminación de un elemento
<a name="DeleteMidLevelJava"></a>

El método `deleteItem` elimina un elemento de una tabla. Es preciso proporcionar la clave principal del elemento que se desea eliminar.

Siga estos pasos: 

1. Cree una instancia del cliente de `DynamoDB`.

1. Llame al método `deleteItem` proporcionando la clave del elemento que desea eliminar. 

En el siguiente ejemplo de Java se muestran estas tareas.

**Example**  

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

Table table = dynamoDB.getTable("ProductCatalog");

DeleteItemOutcome outcome = table.deleteItem("Id", 101);
```

### Especificación de parámetros opcionales
<a name="DeleteItemJavaDocumentAPIOptions"></a>

Puede especificar parámetros opcionales para `deleteItem`. Por ejemplo, el siguiente ejemplo de código Java incluye una expresión `ConditionExpression` que indica que un elemento de libro de `ProductCatalog` solo se puede eliminar si el libro está descatalogado (el atributo `InPublication` es false).

**Example**  

```
Map<String,Object> expressionAttributeValues = new HashMap<String,Object>();
expressionAttributeValues.put(":val", false);

DeleteItemOutcome outcome = table.deleteItem("Id",103,
    "InPublication = :val",
    null, // ExpressionAttributeNames - not used in this example
    expressionAttributeValues);
```

# Ejemplo: operaciones CRUD mediante la API de documentos de AWS SDK para Java
<a name="JavaDocumentAPICRUDExample"></a>

En el siguiente ejemplo de código se ilustran las operaciones CRUD en un elemento de Amazon DynamoDB. En el ejemplo se crea un elemento, se recupera, se llevan a cabo varias actualizaciones y, por último, se elimina.

**nota**  
Además, SDK para Java proporciona un modelo de persistencia de objetos, que le permite mapear las clases del lado del cliente a las tablas de DynamoDB. Este enfoque puede reducir la cantidad de código que hay que escribir. Para obtener más información, consulte [Java 1.x: DynamoDBMapper](DynamoDBMapper.md).

**nota**  
En este ejemplo de código se supone que ya ha cargado datos en DynamoDB para su cuenta siguiendo las instrucciones de la sección [Creación de tablas y carga de datos para ejemplos de código en DynamoDB](SampleData.md).  
Para obtener instrucciones paso a paso acerca de cómo ejecutar el siguiente ejemplo, consulte [Ejemplos de código Java](CodeSamples.Java.md).

```
package com.amazonaws.codesamples.document;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.document.DeleteItemOutcome;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.dynamodbv2.document.UpdateItemOutcome;
import com.amazonaws.services.dynamodbv2.document.spec.DeleteItemSpec;
import com.amazonaws.services.dynamodbv2.document.spec.UpdateItemSpec;
import com.amazonaws.services.dynamodbv2.document.utils.NameMap;
import com.amazonaws.services.dynamodbv2.document.utils.ValueMap;
import com.amazonaws.services.dynamodbv2.model.ReturnValue;

public class DocumentAPIItemCRUDExample {

    static AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
    static DynamoDB dynamoDB = new DynamoDB(client);

    static String tableName = "ProductCatalog";

    public static void main(String[] args) throws IOException {

        createItems();

        retrieveItem();

        // Perform various updates.
        updateMultipleAttributes();
        updateAddNewAttribute();
        updateExistingAttributeConditionally();

        // Delete the item.
        deleteItem();

    }

    private static void createItems() {

        Table table = dynamoDB.getTable(tableName);
        try {

            Item item = new Item().withPrimaryKey("Id", 120).withString("Title", "Book 120 Title")
                    .withString("ISBN", "120-1111111111")
                    .withStringSet("Authors", new HashSet<String>(Arrays.asList("Author12", "Author22")))
                    .withNumber("Price", 20).withString("Dimensions", "8.5x11.0x.75").withNumber("PageCount", 500)
                    .withBoolean("InPublication", false).withString("ProductCategory", "Book");
            table.putItem(item);

            item = new Item().withPrimaryKey("Id", 121).withString("Title", "Book 121 Title")
                    .withString("ISBN", "121-1111111111")
                    .withStringSet("Authors", new HashSet<String>(Arrays.asList("Author21", "Author 22")))
                    .withNumber("Price", 20).withString("Dimensions", "8.5x11.0x.75").withNumber("PageCount", 500)
                    .withBoolean("InPublication", true).withString("ProductCategory", "Book");
            table.putItem(item);

        } catch (Exception e) {
            System.err.println("Create items failed.");
            System.err.println(e.getMessage());

        }
    }

    private static void retrieveItem() {
        Table table = dynamoDB.getTable(tableName);

        try {

            Item item = table.getItem("Id", 120, "Id, ISBN, Title, Authors", null);

            System.out.println("Printing item after retrieving it....");
            System.out.println(item.toJSONPretty());

        } catch (Exception e) {
            System.err.println("GetItem failed.");
            System.err.println(e.getMessage());
        }

    }

    private static void updateAddNewAttribute() {
        Table table = dynamoDB.getTable(tableName);

        try {

            UpdateItemSpec updateItemSpec = new UpdateItemSpec().withPrimaryKey("Id", 121)
                    .withUpdateExpression("set #na = :val1").withNameMap(new NameMap().with("#na", "NewAttribute"))
                    .withValueMap(new ValueMap().withString(":val1", "Some value"))
                    .withReturnValues(ReturnValue.ALL_NEW);

            UpdateItemOutcome outcome = table.updateItem(updateItemSpec);

            // Check the response.
            System.out.println("Printing item after adding new attribute...");
            System.out.println(outcome.getItem().toJSONPretty());

        } catch (Exception e) {
            System.err.println("Failed to add new attribute in " + tableName);
            System.err.println(e.getMessage());
        }
    }

    private static void updateMultipleAttributes() {

        Table table = dynamoDB.getTable(tableName);

        try {

            UpdateItemSpec updateItemSpec = new UpdateItemSpec().withPrimaryKey("Id", 120)
                    .withUpdateExpression("add #a :val1 set #na=:val2")
                    .withNameMap(new NameMap().with("#a", "Authors").with("#na", "NewAttribute"))
                    .withValueMap(
                            new ValueMap().withStringSet(":val1", "Author YY", "Author ZZ").withString(":val2",
                                    "someValue"))
                    .withReturnValues(ReturnValue.ALL_NEW);

            UpdateItemOutcome outcome = table.updateItem(updateItemSpec);

            // Check the response.
            System.out.println("Printing item after multiple attribute update...");
            System.out.println(outcome.getItem().toJSONPretty());

        } catch (Exception e) {
            System.err.println("Failed to update multiple attributes in " + tableName);
            System.err.println(e.getMessage());

        }
    }

    private static void updateExistingAttributeConditionally() {

        Table table = dynamoDB.getTable(tableName);

        try {

            // Specify the desired price (25.00) and also the condition (price =
            // 20.00)

            UpdateItemSpec updateItemSpec = new UpdateItemSpec().withPrimaryKey("Id", 120)
                    .withReturnValues(ReturnValue.ALL_NEW).withUpdateExpression("set #p = :val1")
                    .withConditionExpression("#p = :val2").withNameMap(new NameMap().with("#p", "Price"))
                    .withValueMap(new ValueMap().withNumber(":val1", 25).withNumber(":val2", 20));

            UpdateItemOutcome outcome = table.updateItem(updateItemSpec);

            // Check the response.
            System.out.println("Printing item after conditional update to new attribute...");
            System.out.println(outcome.getItem().toJSONPretty());

        } catch (Exception e) {
            System.err.println("Error updating item in " + tableName);
            System.err.println(e.getMessage());
        }
    }

    private static void deleteItem() {

        Table table = dynamoDB.getTable(tableName);

        try {

            DeleteItemSpec deleteItemSpec = new DeleteItemSpec().withPrimaryKey("Id", 120)
                    .withConditionExpression("#ip = :val").withNameMap(new NameMap().with("#ip", "InPublication"))
                    .withValueMap(new ValueMap().withBoolean(":val", false)).withReturnValues(ReturnValue.ALL_OLD);

            DeleteItemOutcome outcome = table.deleteItem(deleteItemSpec);

            // Check the response.
            System.out.println("Printing item that was deleted...");
            System.out.println(outcome.getItem().toJSONPretty());

        } catch (Exception e) {
            System.err.println("Error deleting item in " + tableName);
            System.err.println(e.getMessage());
        }
    }
}
```

# Ejemplo: operaciones por lotes mediante la API de documentos de AWS SDK para Java
<a name="batch-operation-document-api-java"></a>

En esta sección se proporcionan ejemplos de operaciones de escritura y obtención por lote en Amazon DynamoDB mediante la API de documentos de AWS SDK para Java.

**nota**  
Además, SDK para Java proporciona un modelo de persistencia de objetos, que le permite mapear las clases del lado del cliente a las tablas de DynamoDB. Este enfoque puede reducir la cantidad de código que hay que escribir. Para obtener más información, consulte [Java 1.x: DynamoDBMapper](DynamoDBMapper.md).

**Topics**
+ [Ejemplo: operación de escritura por lotes mediante la API de documentos de AWS SDK para Java](#JavaDocumentAPIBatchWrite)
+ [Ejemplo: operación de obtención por lotes mediante la API de documentos de AWS SDK para Java](#JavaDocumentAPIBatchGet)

## Ejemplo: operación de escritura por lotes mediante la API de documentos de AWS SDK para Java
<a name="JavaDocumentAPIBatchWrite"></a>

En el siguiente ejemplo de código Java se usa el método `batchWriteItem` para llevar a cabo las siguientes operaciones de colocación y eliminación:
+ Colocar un elemento en la tabla `Forum`.
+ Colocar un elemento y eliminar un elemento de la tabla `Thread`. 

Al crear la solicitud de escritura por lotes, puede especificar cualquier cantidad de solicitudes de colocación y eliminación en una o varias tablas. Sin embargo, `batchWriteItem` limita el tamaño de una solicitud de escritura por lotes y el número de operaciones de colocación y eliminación que se pueden llevar a cabo en una misma operación de escritura por lotes. Si la solicitud supera estos límites, se rechaza la solicitud. Si la tabla no cuenta con suficiente desempeño provisionado para atender esta solicitud, los elementos de solicitud sin procesar se devuelven en la respuesta. 

En el siguiente ejemplo se comprueba la respuesta para saber si contiene elementos de solicitud sin transformar. En caso afirmativo, entra en bucle y vuelve a enviar la solicitud `batchWriteItem` con elementos sin procesar. Si ha seguido los ejemplos de esta guía, ya debería haber creado las tablas `Forum` y `Thread`. También puede crear estas tablas y cargar los ejemplos de datos mediante programación. Para obtener más información, consulte [Creación de ejemplos de tablas y carga de datos utilizando AWS SDK para Java](AppendixSampleDataCodeJava.md).

Para obtener instrucciones paso a paso para probar el siguiente ejemplo, consulte [Ejemplos de código Java](CodeSamples.Java.md). 

**Example**  

```
package com.amazonaws.codesamples.document;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.document.BatchWriteItemOutcome;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.TableWriteItems;
import com.amazonaws.services.dynamodbv2.model.WriteRequest;

public class DocumentAPIBatchWrite {

    static AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
    static DynamoDB dynamoDB = new DynamoDB(client);

    static String forumTableName = "Forum";
    static String threadTableName = "Thread";

    public static void main(String[] args) throws IOException {

        writeMultipleItemsBatchWrite();

    }

    private static void writeMultipleItemsBatchWrite() {
        try {

            // Add a new item to Forum
            TableWriteItems forumTableWriteItems = new TableWriteItems(forumTableName) // Forum
                    .withItemsToPut(new Item().withPrimaryKey("Name", "Amazon RDS").withNumber("Threads", 0));

            // Add a new item, and delete an existing item, from Thread
            // This table has a partition key and range key, so need to specify
            // both of them
            TableWriteItems threadTableWriteItems = new TableWriteItems(threadTableName)
                    .withItemsToPut(
                            new Item().withPrimaryKey("ForumName", "Amazon RDS", "Subject", "Amazon RDS Thread 1")
                                    .withString("Message", "ElastiCache Thread 1 message")
                                    .withStringSet("Tags", new HashSet<String>(Arrays.asList("cache", "in-memory"))))
                    .withHashAndRangeKeysToDelete("ForumName", "Subject", "Amazon S3", "S3 Thread 100");

            System.out.println("Making the request.");
            BatchWriteItemOutcome outcome = dynamoDB.batchWriteItem(forumTableWriteItems, threadTableWriteItems);

            do {

                // Check for unprocessed keys which could happen if you exceed
                // provisioned throughput

                Map<String, List<WriteRequest>> unprocessedItems = outcome.getUnprocessedItems();

                if (outcome.getUnprocessedItems().size() == 0) {
                    System.out.println("No unprocessed items found");
                } else {
                    System.out.println("Retrieving the unprocessed items");
                    outcome = dynamoDB.batchWriteItemUnprocessed(unprocessedItems);
                }

            } while (outcome.getUnprocessedItems().size() > 0);

        } catch (Exception e) {
            System.err.println("Failed to retrieve items: ");
            e.printStackTrace(System.err);
        }

    }

}
```

## Ejemplo: operación de obtención por lotes mediante la API de documentos de AWS SDK para Java
<a name="JavaDocumentAPIBatchGet"></a>

En el siguiente ejemplo de código Java se usa el método `batchGetItem` para recuperar varios elementos de las tablas `Forum` y `Thread`. La solicitud `BatchGetItemRequest` especifica los nombres de las tablas y una lista de claves para cada elemento que se desea obtener. En el ejemplo se procesa la respuesta y se imprimen los elementos recuperados.

**nota**  
En este ejemplo de código se supone que ya ha cargado datos en DynamoDB para su cuenta siguiendo las instrucciones de la sección [Creación de tablas y carga de datos para ejemplos de código en DynamoDB](SampleData.md).  
Para obtener instrucciones paso a paso acerca de cómo ejecutar el siguiente ejemplo, consulte [Ejemplos de código Java](CodeSamples.Java.md).

**Example**  

```
package com.amazonaws.codesamples.document;

import java.io.IOException;
import java.util.List;
import java.util.Map;

import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.document.BatchGetItemOutcome;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.TableKeysAndAttributes;
import com.amazonaws.services.dynamodbv2.model.KeysAndAttributes;

public class DocumentAPIBatchGet {
    static AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
    static DynamoDB dynamoDB = new DynamoDB(client);

    static String forumTableName = "Forum";
    static String threadTableName = "Thread";

    public static void main(String[] args) throws IOException {
        retrieveMultipleItemsBatchGet();
    }

    private static void retrieveMultipleItemsBatchGet() {

        try {

            TableKeysAndAttributes forumTableKeysAndAttributes = new TableKeysAndAttributes(forumTableName);
            // Add a partition key
            forumTableKeysAndAttributes.addHashOnlyPrimaryKeys("Name", "Amazon S3", "Amazon DynamoDB");

            TableKeysAndAttributes threadTableKeysAndAttributes = new TableKeysAndAttributes(threadTableName);
            // Add a partition key and a sort key
            threadTableKeysAndAttributes.addHashAndRangePrimaryKeys("ForumName", "Subject", "Amazon DynamoDB",
                    "DynamoDB Thread 1", "Amazon DynamoDB", "DynamoDB Thread 2", "Amazon S3", "S3 Thread 1");

            System.out.println("Making the request.");

            BatchGetItemOutcome outcome = dynamoDB.batchGetItem(forumTableKeysAndAttributes,
                    threadTableKeysAndAttributes);

            Map<String, KeysAndAttributes> unprocessed = null;

            do {
                for (String tableName : outcome.getTableItems().keySet()) {
                    System.out.println("Items in table " + tableName);
                    List<Item> items = outcome.getTableItems().get(tableName);
                    for (Item item : items) {
                        System.out.println(item.toJSONPretty());
                    }
                }

                // Check for unprocessed keys which could happen if you exceed
                // provisioned
                // throughput or reach the limit on response size.
                unprocessed = outcome.getUnprocessedKeys();

                if (unprocessed.isEmpty()) {
                    System.out.println("No unprocessed keys found");
                } else {
                    System.out.println("Retrieving the unprocessed keys");
                    outcome = dynamoDB.batchGetItemUnprocessed(unprocessed);
                }

            } while (!unprocessed.isEmpty());

        } catch (Exception e) {
            System.err.println("Failed to retrieve items.");
            System.err.println(e.getMessage());
        }

    }

}
```

# Ejemplo: control de atributos de tipo binario mediante la API de documentos de AWS SDK para Java
<a name="JavaDocumentAPIBinaryTypeExample"></a>

En el siguiente ejemplo se ilustra cómo se controlan los atributos de tipo Binary. Además, se agrega un elemento a la tabla `Reply`. El elemento incluye un atributo de tipo Binary (`ExtendedMessage`) que almacena datos comprimidos. A continuación, en el ejemplo se recupera el elemento y se imprimen todos los valores de los atributos. Con fines ilustrativos, el ejemplo usa la clase `GZIPOutputStream` para comprimir un ejemplo de secuencia y asignársela al atributo `ExtendedMessage`. Cuando se recupera el atributo binario, se descomprime mediante la clase `GZIPInputStream`. 

**nota**  
Además, SDK para Java proporciona un modelo de persistencia de objetos, que le permite mapear las clases del lado del cliente a las tablas de DynamoDB. Este enfoque puede reducir la cantidad de código que hay que escribir. Para obtener más información, consulte [Java 1.x: DynamoDBMapper](DynamoDBMapper.md).

Si ha seguido la sección [Creación de tablas y carga de datos para ejemplos de código en DynamoDB](SampleData.md), seguramente habrá creado ya la tabla `Reply`. También puede crear esta tabla mediante programación. Para obtener más información, consulte [Creación de ejemplos de tablas y carga de datos utilizando AWS SDK para Java](AppendixSampleDataCodeJava.md).

Para obtener instrucciones paso a paso para probar el siguiente ejemplo, consulte [Ejemplos de código Java](CodeSamples.Java.md). 

**Example**  

```
package com.amazonaws.codesamples.document;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.dynamodbv2.document.spec.GetItemSpec;

public class DocumentAPIItemBinaryExample {

    static AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
    static DynamoDB dynamoDB = new DynamoDB(client);

    static String tableName = "Reply";
    static SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");

    public static void main(String[] args) throws IOException {
        try {

            // Format the primary key values
            String threadId = "Amazon DynamoDB#DynamoDB Thread 2";

            dateFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
            String replyDateTime = dateFormatter.format(new Date());

            // Add a new reply with a binary attribute type
            createItem(threadId, replyDateTime);

            // Retrieve the reply with a binary attribute type
            retrieveItem(threadId, replyDateTime);

            // clean up by deleting the item
            deleteItem(threadId, replyDateTime);
        } catch (Exception e) {
            System.err.println("Error running the binary attribute type example: " + e);
            e.printStackTrace(System.err);
        }
    }

    public static void createItem(String threadId, String replyDateTime) throws IOException {

        Table table = dynamoDB.getTable(tableName);

        // Craft a long message
        String messageInput = "Long message to be compressed in a lengthy forum reply";

        // Compress the long message
        ByteBuffer compressedMessage = compressString(messageInput.toString());

        table.putItem(new Item().withPrimaryKey("Id", threadId).withString("ReplyDateTime", replyDateTime)
                .withString("Message", "Long message follows").withBinary("ExtendedMessage", compressedMessage)
                .withString("PostedBy", "User A"));
    }

    public static void retrieveItem(String threadId, String replyDateTime) throws IOException {

        Table table = dynamoDB.getTable(tableName);

        GetItemSpec spec = new GetItemSpec().withPrimaryKey("Id", threadId, "ReplyDateTime", replyDateTime)
                .withConsistentRead(true);

        Item item = table.getItem(spec);

        // Uncompress the reply message and print
        String uncompressed = uncompressString(ByteBuffer.wrap(item.getBinary("ExtendedMessage")));

        System.out.println("Reply message:\n" + " Id: " + item.getString("Id") + "\n" + " ReplyDateTime: "
                + item.getString("ReplyDateTime") + "\n" + " PostedBy: " + item.getString("PostedBy") + "\n"
                + " Message: "
                + item.getString("Message") + "\n" + " ExtendedMessage (uncompressed): " + uncompressed + "\n");
    }

    public static void deleteItem(String threadId, String replyDateTime) {

        Table table = dynamoDB.getTable(tableName);
        table.deleteItem("Id", threadId, "ReplyDateTime", replyDateTime);
    }

    private static ByteBuffer compressString(String input) throws IOException {
        // Compress the UTF-8 encoded String into a byte[]
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        GZIPOutputStream os = new GZIPOutputStream(baos);
        os.write(input.getBytes("UTF-8"));
        os.close();
        baos.close();
        byte[] compressedBytes = baos.toByteArray();

        // The following code writes the compressed bytes to a ByteBuffer.
        // A simpler way to do this is by simply calling
        // ByteBuffer.wrap(compressedBytes);
        // However, the longer form below shows the importance of resetting the
        // position of the buffer
        // back to the beginning of the buffer if you are writing bytes directly
        // to it, since the SDK
        // will consider only the bytes after the current position when sending
        // data to DynamoDB.
        // Using the "wrap" method automatically resets the position to zero.
        ByteBuffer buffer = ByteBuffer.allocate(compressedBytes.length);
        buffer.put(compressedBytes, 0, compressedBytes.length);
        buffer.position(0); // Important: reset the position of the ByteBuffer
                            // to the beginning
        return buffer;
    }

    private static String uncompressString(ByteBuffer input) throws IOException {
        byte[] bytes = input.array();
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        GZIPInputStream is = new GZIPInputStream(bais);

        int chunkSize = 1024;
        byte[] buffer = new byte[chunkSize];
        int length = 0;
        while ((length = is.read(buffer, 0, chunkSize)) != -1) {
            baos.write(buffer, 0, length);
        }

        String result = new String(baos.toByteArray(), "UTF-8");

        is.close();
        baos.close();
        bais.close();

        return result;
    }
}
```

# Uso de elementos: .NET
<a name="LowLevelDotNetItemCRUD"></a>

Puede usar la API de bajo nivel AWS SDK para .NET para realizar operaciones típicas de creación, lectura, actualización y eliminación (CRUD, por sus siglas en inglés) en los elementos de una tabla. A continuación se indican los pasos que suelen llevarse a cabo para realizar operaciones CRUD a los datos mediante la API de bajo nivel de .NET:

1. Cree una instancia de la clase `AmazonDynamoDBClient` (el cliente).

1. Proporcione los parámetros requeridos específicos de la operación en el objeto de solicitud correspondiente.

   Por ejemplo, use el objeto de solicitud `PutItemRequest` para cargar un elemento y el objeto de solicitud `GetItemRequest` para recuperar un elemento existente. 

   Puede usar el objeto de solicitud para proporcionar los parámetros necesarios y opcionales. 

1. Ejecute el método apropiado proporcionado por el cliente pasándolo en el objeto de solicitud que ha creado en el paso anterior. 

   El cliente `AmazonDynamoDBClient` proporciona los métodos `PutItem`, `GetItem`, `UpdateItem` y `DeleteItem` para las operaciones CRUD.

**Topics**
+ [Colocación de un elemento](#PutItemLowLevelAPIDotNet)
+ [Obtención de un elemento](#GetItemLowLevelDotNET)
+ [Actualización de un elemento](#UpdateItemLowLevelDotNet)
+ [Contador atómico](#AtomicCounterLowLevelDotNet)
+ [Eliminación de un elemento](#DeleteMidLevelDotNet)
+ [Escritura por lotes: colocación y eliminación de varios elementos](#BatchWriteLowLevelDotNet)
+ [Obtención por lotes: obtención de varios elementos](#BatchGetLowLevelDotNet)
+ [Ejemplo: operaciones CRUD con la API de bajo nivel de AWS SDK para .NET](LowLevelDotNetItemsExample.md)
+ [Ejemplo: operaciones por lotes con la API de bajo nivel de AWS SDK para .NET](batch-operation-lowlevel-dotnet.md)
+ [Ejemplo: control de atributos de tipo binario mediante la API de bajo nivel de AWS SDK para .NET](LowLevelDotNetBinaryTypeExample.md)

## Colocación de un elemento
<a name="PutItemLowLevelAPIDotNet"></a>

El método `PutItem` carga un elemento en una tabla. Si el elemento existe, sustituye el elemento completo.

**nota**  
En lugar de ello, si prefiere actualizar solamente algunos atributos concretos, puede usar el método `UpdateItem`. Para obtener más información, consulte [Actualización de un elemento](#UpdateItemLowLevelDotNet).

A continuación se indican los pasos que hay que seguir para cargar un elemento mediante la API de bajo nivel del SDK para .NET:

1. Cree una instancia de la clase `AmazonDynamoDBClient`.

1. Cree una instancia de la clase `PutItemRequest` para proporcionar los parámetros requeridos.

   Para colocar un elemento, debe proporcionar el nombre de la tabla y el elemento. 

1. Ejecute el método `PutItem` proporcionando el objeto `PutItemRequest` que creó en el paso anterior.

En el siguiente ejemplo de C\$1 se ponen en práctica los pasos anteriores. En el ejemplo se carga un elemento en la tabla `ProductCatalog`.

**Example**  

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
string tableName = "ProductCatalog";

var request = new PutItemRequest
{
   TableName = tableName,
   Item = new Dictionary<string, AttributeValue>()
      {
          { "Id", new AttributeValue { N = "201" }},
          { "Title", new AttributeValue { S = "Book 201 Title" }},
          { "ISBN", new AttributeValue { S = "11-11-11-11" }},
          { "Price", new AttributeValue { S = "20.00" }},
          {
            "Authors",
            new AttributeValue
            { SS = new List<string>{"Author1", "Author2"}   }
          }
      }
};
client.PutItem(request);
```

En el ejemplo anterior, hemos cargado un elemento de libro que tiene los atributos `Id`, `Title`, `ISBN` y `Authors`. Tenga en cuenta `Id` es un atributo de tipo numérico y demás son de cadena. Autores es un conjunto `String`.

### Especificación de parámetros opcionales
<a name="PutItemLowLevelAPIDotNetOptions"></a>

También puede usar el objeto `PutItemRequest` para proporcionar parámetros opcionales, como se muestra en el siguiente ejemplo de código C\$1. En el ejemplo se especifican los parámetros opcionales siguientes:
+ `ExpressionAttributeNames`, `ExpressionAttributeValues` y `ConditionExpression` especifican que el elemento existente se puede sustituir únicamente si su atributo ISBN tiene un valor específico.
+ `ReturnValues`El parámetro para solicitar el elemento anterior en la respuesta.

**Example**  

```
var request = new PutItemRequest
 {
   TableName = tableName,
   Item = new Dictionary<string, AttributeValue>()
               {
                   { "Id", new AttributeValue { N = "104" }},
                   { "Title", new AttributeValue { S = "Book 104  Title" }},
                   { "ISBN", new AttributeValue { S = "444-4444444444" }},
                   { "Authors",
                     new AttributeValue { SS = new List<string>{"Author3"}}}
               },
    // Optional parameters.
    ExpressionAttributeNames = new Dictionary<string,string>()
    {
        {"#I", "ISBN"}
    },
    ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
    {
        {":isbn",new AttributeValue {S = "444-4444444444"}}
    },
    ConditionExpression = "#I = :isbn"

};
var response = client.PutItem(request);
```

Para obtener más información, consulte [PutItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html).

## Obtención de un elemento
<a name="GetItemLowLevelDotNET"></a>

El método `GetItem` recupera un elemento.

**nota**  
Para recuperar varios elementos, puede usar el método `BatchGetItem`. Para obtener más información, consulte [Obtención por lotes: obtención de varios elementos](#BatchGetLowLevelDotNet).

A continuación se indican los pasos que hay que seguir para recuperar un elemento existente mediante la API de bajo nivel de AWS SDK para .NET.

1. Cree una instancia de la clase `AmazonDynamoDBClient`.

1. Cree una instancia de la clase `GetItemRequest` para proporcionar los parámetros requeridos.

   Para obtener un elemento, debe proporcionar el nombre de la tabla y la clave principal del elemento. 

1. Ejecute el método `GetItem` proporcionando el objeto `GetItemRequest` que creó en el paso anterior.

En el siguiente ejemplo de C\$1 se ponen en práctica los pasos anteriores. En el siguiente ejemplo se recupera un elemento de la tabla `ProductCatalog`.

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
string tableName = "ProductCatalog";

var request = new GetItemRequest
 {
   TableName = tableName,
   Key = new Dictionary<string,AttributeValue>() { { "Id", new AttributeValue { N = "202" } } },
 };
 var response = client.GetItem(request);

// Check the response.
var result = response.GetItemResult;
var attributeMap = result.Item; // Attribute list in the response.
```

### Especificación de parámetros opcionales
<a name="GetItemLowLevelDotNETOptions"></a>

También puede usar el objeto `GetItemRequest` para proporcionar parámetros opcionales, como se muestra en el siguiente ejemplo de código C\$1. En el ejemplo se especifican los parámetros opcionales siguientes:
+ `ProjectionExpression`El parámetro para especificar los atributos que se van a recuperar.
+ `ConsistentRead`El parámetro para realizar una lectura de consistencia alta. Para obtener más información sobre la consistencia de lectura, consulte [Coherencia de lectura de DynamoDB](HowItWorks.ReadConsistency.md).

**Example**  

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
string tableName = "ProductCatalog";

var request = new GetItemRequest
 {
   TableName = tableName,
   Key = new Dictionary<string,AttributeValue>() { { "Id", new AttributeValue { N = "202" } } },
   // Optional parameters.
   ProjectionExpression = "Id, ISBN, Title, Authors",
   ConsistentRead = true
 };

 var response = client.GetItem(request);

// Check the response.
var result = response.GetItemResult;
var attributeMap = result.Item;
```

Para obtener más información, consulte [GetItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_GetItem.html).

## Actualización de un elemento
<a name="UpdateItemLowLevelDotNet"></a>

El método `UpdateItem` actualiza un elemento si existe. Puede usar la operación `UpdateItem` para actualizar los valores de atributos presentes, agregar atributos nuevos o eliminarlos de la colección existente. Si el elemento que tiene clave principal especificada no se encuentra, se agrega un nuevo elemento.

La operación `UpdateItem` se rige por las directrices siguientes:
+ Si el elemento no existe, `UpdateItem` agrega un elemento nuevo utilizando la clave principal especificada en la información de entrada.
+ Si el elemento existe, `UpdateItem` aplica las actualizaciones como se indica a continuación:
  + Sustituye los valores de los atributos existentes por los valores de la actualización.
  + Si el atributo que se proporciona en la información de entrada no existe, agrega un nuevo atributo al elemento.
  + Si el atributo de entrada es null, elimina el atributo, en caso de que esté presente. 
  + Si se utiliza `ADD` para `Action`, puede agregar valores a un conjunto existente (de tipo String Set o Number Set) o bien sumar (si se usa un número positivo) o restar (si se usa un número negativo) matemáticamente un valor del atributo numérico existente.

**nota**  
La operación `PutItem` también puede llevar a cabo una actualización. Para obtener más información, consulte [Colocación de un elemento](#PutItemLowLevelAPIDotNet). Por ejemplo, si llama a `PutItem` para cargar un elemento y la clave principal ya existe, la operación `PutItem` sustituye el elemento completo. Tenga en cuenta que, si hay atributos en el elemento existente que no se especifican en la información de entrada, la operación `PutItem` los eliminará. Sin embargo, `UpdateItem` solo actualiza los atributos de entrada especificados. Todos los demás atributos existentes de ese elemento permanecen inalterados. 

A continuación se indican los pasos que hay que seguir para actualizar un elemento existente mediante la API de bajo nivel del SDK para .NET:

1. Cree una instancia de la clase `AmazonDynamoDBClient`.

1. Cree una instancia de la clase `UpdateItemRequest` para proporcionar los parámetros requeridos.

   Se trata del objeto de solicitud en el que se describen todas las actualizaciones, tales como agregar atributos o actualizar o eliminar atributos existentes. Para eliminar un atributo, especifique su nombre con el valor null. 

1. Ejecute el método `UpdateItem` proporcionando el objeto `UpdateItemRequest` que creó en el paso anterior. 

En el siguiente ejemplo de código C\$1 se ponen en práctica los pasos anteriores. En el ejemplo se actualiza un elemento libro de la tabla `ProductCatalog`. Se agrega un nuevo autor a la colección `Authors` y se elimina el atributo `ISBN` existente. También se reduce el precio en una unidad.



```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
string tableName = "ProductCatalog";

var request = new UpdateItemRequest
{
    TableName = tableName,
    Key = new Dictionary<string,AttributeValue>() { { "Id", new AttributeValue { N = "202" } } },
    ExpressionAttributeNames = new Dictionary<string,string>()
    {
        {"#A", "Authors"},
        {"#P", "Price"},
        {"#NA", "NewAttribute"},
        {"#I", "ISBN"}
    },
    ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
    {
        {":auth",new AttributeValue { SS = {"Author YY","Author ZZ"}}},
        {":p",new AttributeValue {N = "1"}},
        {":newattr",new AttributeValue {S = "someValue"}},
    },

    // This expression does the following:
    // 1) Adds two new authors to the list
    // 2) Reduces the price
    // 3) Adds a new attribute to the item
    // 4) Removes the ISBN attribute from the item
    UpdateExpression = "ADD #A :auth SET #P = #P - :p, #NA = :newattr REMOVE #I"
};
var response = client.UpdateItem(request);
```

### Especificación de parámetros opcionales
<a name="UpdateItemLowLevelDotNETOptions"></a>

También puede usar el objeto `UpdateItemRequest` para proporcionar parámetros opcionales, como se muestra en el siguiente ejemplo de código C\$1. En él se especifican los parámetros opcionales siguientes:
+ `ExpressionAttributeValues` y `ConditionExpression` para especificar que el precio solo puede actualizarse si el precio actual es 20.00.
+ `ReturnValues`El parámetro para solicitar el elemento actualizado en la respuesta. 

**Example**  

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
string tableName = "ProductCatalog";

var request = new UpdateItemRequest
{
    Key = new Dictionary<string,AttributeValue>() { { "Id", new AttributeValue { N = "202" } } },

    // Update price only if the current price is 20.00.
    ExpressionAttributeNames = new Dictionary<string,string>()
    {
        {"#P", "Price"}
    },
    ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
    {
        {":newprice",new AttributeValue {N = "22"}},
        {":currprice",new AttributeValue {N = "20"}}
    },
    UpdateExpression = "SET #P = :newprice",
    ConditionExpression = "#P = :currprice",
    TableName = tableName,
    ReturnValues = "ALL_NEW" // Return all the attributes of the updated item.
};

var response = client.UpdateItem(request);
```

Para obtener más información, consulte [UpdateItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateItem.html). 

## Contador atómico
<a name="AtomicCounterLowLevelDotNet"></a>

Puede usar `updateItem` para implementar un contador atómico y aumentar o reducir el valor de un atributo existente sin interferir con las demás solicitudes de escritura. Para actualizar un contador atómico, use `updateItem` con un atributo de tipo `Number` en el parámetro `UpdateExpression` y use `ADD` como `Action`.

En el siguiente ejemplo se pone en práctica lo anterior y se incrementa el atributo `Quantity` en una unidad.

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
string tableName = "ProductCatalog";

var request = new UpdateItemRequest
{
    Key = new Dictionary<string, AttributeValue>() { { "Id", new AttributeValue { N = "121" } } },
    ExpressionAttributeNames = new Dictionary<string, string>()
    {
        {"#Q", "Quantity"}
    },
    ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
    {
        {":incr",new AttributeValue {N = "1"}}
    },
    UpdateExpression = "SET #Q = #Q + :incr",
    TableName = tableName
};

var response = client.UpdateItem(request);
```

## Eliminación de un elemento
<a name="DeleteMidLevelDotNet"></a>

El método `DeleteItem` elimina un elemento de una tabla. 

A continuación se indican los pasos que hay que seguir para eliminar un elemento mediante el API de bajo nivel del SDK para .NET. 

1. Cree una instancia de la clase `AmazonDynamoDBClient`.

1. Cree una instancia de la clase `DeleteItemRequest` para proporcionar los parámetros requeridos.

    Para eliminar un elemento, se requieren el nombre de la tabla y la clave principal del elemento. 

1. Ejecute el método `DeleteItem` proporcionando el objeto `DeleteItemRequest` que creó en el paso anterior. 

**Example**  

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
string tableName = "ProductCatalog";

var request = new DeleteItemRequest
{
    TableName = tableName,
    Key = new Dictionary<string,AttributeValue>() { { "Id", new AttributeValue { N = "201" } } },
};

var response = client.DeleteItem(request);
```

### Especificación de parámetros opcionales
<a name="DeleteItemLowLevelDotNETOptions"></a>

También puede usar el objeto `DeleteItemRequest` para proporcionar parámetros opcionales, como se muestra en el siguiente ejemplo de código C\$1. En él se especifican los parámetros opcionales siguientes:
+ `ExpressionAttributeValues` y `ConditionExpression` para especificar que el elemento de libro solo se puede eliminar si está descatalogado (el valor del atributo InPublication es false). 
+ `ReturnValues`El parámetro para solicitar el elemento eliminado en la respuesta.

**Example**  

```
var request = new DeleteItemRequest
{
    TableName = tableName,
    Key = new Dictionary<string,AttributeValue>() { { "Id", new AttributeValue { N = "201" } } },

    // Optional parameters.
    ReturnValues = "ALL_OLD",
    ExpressionAttributeNames = new Dictionary<string, string>()
    {
        {"#IP", "InPublication"}
    },
    ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
    {
        {":inpub",new AttributeValue {BOOL = false}}
    },
    ConditionExpression = "#IP = :inpub"
};

var response = client.DeleteItem(request);
```

Para obtener más información, consulte [DeleteItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DeleteItem.html).

## Escritura por lotes: colocación y eliminación de varios elementos
<a name="BatchWriteLowLevelDotNet"></a>

La *escritura por lotes* se refiere a colocar y eliminar varios elementos en un lote. El método `BatchWriteItem` permite colocar y eliminar varios elementos de una o varias tablas con una sola llamada. A continuación se indican los pasos que hay que seguir para recuperar varios elementos mediante el API de bajo nivel del SDK para .NET.

1. Cree una instancia de la clase `AmazonDynamoDBClient`.

1. Cree una instancia de la clase `BatchWriteItemRequest` para describir todas las operaciones de colocación y eliminación.

1. Ejecute el método `BatchWriteItem` proporcionando el objeto `BatchWriteItemRequest` que creó en el paso anterior.

1. Procese la respuesta. Debe comprobar si en la respuesta se ha devuelto algún elemento de solicitud sin procesar. Esto puede ocurrir si se alcanza la cuota de rendimiento aprovisionado o se produce algún otro error transitorio. Además, DynamoDB limita el tamaño de la solicitud y el número de operaciones que se pueden especificar en ella. Si supera estos límites, DynamoDB rechaza la solicitud. Para obtener más información, consulte [BatchWriteItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html). 

En el siguiente ejemplo de código C\$1 se ponen en práctica los pasos anteriores. En el siguiente ejemplo se crea una solicitud `BatchWriteItemRequest` para realizar las siguientes operaciones de escritura:
+ Colocar un elemento en la tabla `Forum`.
+ Colocar y eliminar un elemento en la tabla `Thread`

A continuación, el código ejecuta `BatchWriteItem` para llevar a cabo una operación por lote.

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();

string table1Name = "Forum";
string table2Name = "Thread";

var request = new BatchWriteItemRequest
 {
   RequestItems = new Dictionary<string, List<WriteRequest>>
    {
      {
        table1Name, new List<WriteRequest>
        {
          new WriteRequest
          {
             PutRequest = new PutRequest
             {
                Item = new Dictionary<string,AttributeValue>
                {
                  { "Name", new AttributeValue { S = "Amazon S3 forum" } },
                  { "Threads", new AttributeValue { N = "0" }}
                }
             }
          }
        }
      } ,
      {
        table2Name, new List<WriteRequest>
        {
          new WriteRequest
          {
            PutRequest = new PutRequest
            {
               Item = new Dictionary<string,AttributeValue>
               {
                 { "ForumName", new AttributeValue { S = "Amazon S3 forum" } },
                 { "Subject", new AttributeValue { S = "My sample question" } },
                 { "Message", new AttributeValue { S = "Message Text." } },
                 { "KeywordTags", new AttributeValue { SS = new List<string> { "Amazon S3", "Bucket" }  } }
               }
            }
          },
          new WriteRequest
          {
             DeleteRequest = new DeleteRequest
             {
                Key = new Dictionary<string,AttributeValue>()
                {
                   { "ForumName", new AttributeValue { S = "Some forum name" } },
                   { "Subject", new AttributeValue { S = "Some subject" } }
                }
             }
          }
        }
      }
    }
 };
response = client.BatchWriteItem(request);
```

Para ver un ejemplo práctico, consulte [Ejemplo: operaciones por lotes con la API de bajo nivel de AWS SDK para .NET](batch-operation-lowlevel-dotnet.md). 

## Obtención por lotes: obtención de varios elementos
<a name="BatchGetLowLevelDotNet"></a>

El método `BatchGetItem` permite recuperar varios elementos de una o varias tablas. 

**nota**  
Para recuperar un solo elemento, puede usar el método `GetItem`. 

A continuación se indican los pasos que hay que seguir para recuperar varios elementos mediante la API de bajo nivel AWS SDK para .NET.

1. Cree una instancia de la clase `AmazonDynamoDBClient`.

1. Cree una instancia de la clase `BatchGetItemRequest` para proporcionar los parámetros requeridos.

   Para recuperar varios elementos, es obligatorio indicar el nombre de la tabla y una lista de valores de clave principal. 

1. Ejecute el método `BatchGetItem` proporcionando el objeto `BatchGetItemRequest` que creó en el paso anterior.

1. Procese la respuesta. Debe comprobar si han quedado claves sin procesar, lo que podría ocurrir si se alcanza la cuota de rendimiento aprovisionada o se produce cualquier otro error transitorio.

En el siguiente ejemplo de código C\$1 se ponen en práctica los pasos anteriores. En el ejemplo se recuperan elementos de dos tablas, `Forum` y `Thread`. La solicitud especifica dos elementos en la tabla `Forum` y tres en la tabla `Thread`. La respuesta incluye elementos de ambas tablas. En el código se muestra cómo procesar la respuesta.



```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();

string table1Name = "Forum";
string table2Name = "Thread";

var request = new BatchGetItemRequest
{
  RequestItems = new Dictionary<string, KeysAndAttributes>()
  {
    { table1Name,
      new KeysAndAttributes
      {
        Keys = new List<Dictionary<string, AttributeValue>>()
        {
          new Dictionary<string, AttributeValue>()
          {
            { "Name", new AttributeValue { S = "DynamoDB" } }
          },
          new Dictionary<string, AttributeValue>()
          {
            { "Name", new AttributeValue { S = "Amazon S3" } }
          }
        }
      }
    },
    {
      table2Name,
      new KeysAndAttributes
      {
        Keys = new List<Dictionary<string, AttributeValue>>()
        {
          new Dictionary<string, AttributeValue>()
          {
            { "ForumName", new AttributeValue { S = "DynamoDB" } },
            { "Subject", new AttributeValue { S = "DynamoDB Thread 1" } }
          },
          new Dictionary<string, AttributeValue>()
          {
            { "ForumName", new AttributeValue { S = "DynamoDB" } },
            { "Subject", new AttributeValue { S = "DynamoDB Thread 2" } }
          },
          new Dictionary<string, AttributeValue>()
          {
            { "ForumName", new AttributeValue { S = "Amazon S3" } },
            { "Subject", new AttributeValue { S = "Amazon S3 Thread 1" } }
          }
        }
      }
    }
  }
};

var response = client.BatchGetItem(request);

// Check the response.
var result = response.BatchGetItemResult;
var responses = result.Responses; // The attribute list in the response.

var table1Results = responses[table1Name];
Console.WriteLine("Items in table {0}" + table1Name);
foreach (var item1 in table1Results.Items)
{
  PrintItem(item1);
}

var table2Results = responses[table2Name];
Console.WriteLine("Items in table {1}" + table2Name);
foreach (var item2 in table2Results.Items)
{
  PrintItem(item2);
}
// Any unprocessed keys? could happen if you exceed ProvisionedThroughput or some other error.
Dictionary<string, KeysAndAttributes> unprocessedKeys = result.UnprocessedKeys;
foreach (KeyValuePair<string, KeysAndAttributes> pair in unprocessedKeys)
{
    Console.WriteLine(pair.Key, pair.Value);
}
```



### Especificación de parámetros opcionales
<a name="BatchGetItemLowLevelDotNETOptions"></a>

También puede usar el objeto `BatchGetItemRequest` para proporcionar parámetros opcionales, como se muestra en el siguiente ejemplo de código C\$1. En el siguiente ejemplo de código se recuperan dos elementos de la tabla `Forum`. En él se especifica el parámetro opcional siguiente:
+  `ProjectionExpression`El parámetro para especificar los atributos que se van a recuperar.

**Example**  

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();

string table1Name = "Forum";

var request = new BatchGetItemRequest
{
  RequestItems = new Dictionary<string, KeysAndAttributes>()
  {
    { table1Name,
      new KeysAndAttributes
      {
        Keys = new List<Dictionary<string, AttributeValue>>()
        {
          new Dictionary<string, AttributeValue>()
          {
            { "Name", new AttributeValue { S = "DynamoDB" } }
          },
          new Dictionary<string, AttributeValue>()
          {
            { "Name", new AttributeValue { S = "Amazon S3" } }
          }
        }
      },
      // Optional - name of an attribute to retrieve.
      ProjectionExpression = "Title"
    }
  }
};

var response = client.BatchGetItem(request);
```

Para obtener más información, consulte [BatchGetItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchGetItem.html). 

# Ejemplo: operaciones CRUD con la API de bajo nivel de AWS SDK para .NET
<a name="LowLevelDotNetItemsExample"></a>

En el siguiente ejemplo de código C\$1 se ilustran las operaciones de creación, lectura, actualización y eliminación (CRUD, Create, Read, Update and Delete) en un elemento de Amazon DynamoDB. En el ejemplo se agrega un elemento a la tabla `ProductCatalog`, se recupera, se llevan a cabo varias actualizaciones y, por último, se elimina. Si no ha creado esta tabla, también puede crearla mediante programación. Para obtener más información, consulte [Creación de ejemplos de tablas y carga de datos utilizando AWS SDK para .NET](AppendixSampleDataCodeDotNET.md).

Para obtener instrucciones paso a paso para probar el siguiente ejemplo, consulte [Ejemplos de código .NET](CodeSamples.DotNet.md). 

**Example**  

```
using System;
using System.Collections.Generic;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.Model;
using Amazon.Runtime;
using Amazon.SecurityToken;

namespace com.amazonaws.codesamples
{
    class LowLevelItemCRUDExample
    {
        private static string tableName = "ProductCatalog";
        private static AmazonDynamoDBClient client = new AmazonDynamoDBClient();

        static void Main(string[] args)
        {
            try
            {
                CreateItem();
                RetrieveItem();

                // Perform various updates.
                UpdateMultipleAttributes();
                UpdateExistingAttributeConditionally();

                // Delete item.
                DeleteItem();
                Console.WriteLine("To continue, press Enter");
                Console.ReadLine();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                Console.WriteLine("To continue, press Enter");
                Console.ReadLine();
            }
        }

        private static void CreateItem()
        {
            var request = new PutItemRequest
            {
                TableName = tableName,
                Item = new Dictionary<string, AttributeValue>()
            {
                { "Id", new AttributeValue {
                      N = "1000"
                  }},
                { "Title", new AttributeValue {
                      S = "Book 201 Title"
                  }},
                { "ISBN", new AttributeValue {
                      S = "11-11-11-11"
                  }},
                { "Authors", new AttributeValue {
                      SS = new List<string>{"Author1", "Author2" }
                  }},
                { "Price", new AttributeValue {
                      N = "20.00"
                  }},
                { "Dimensions", new AttributeValue {
                      S = "8.5x11.0x.75"
                  }},
                { "InPublication", new AttributeValue {
                      BOOL = false
                  } }
            }
            };
            client.PutItem(request);
        }

        private static void RetrieveItem()
        {
            var request = new GetItemRequest
            {
                TableName = tableName,
                Key = new Dictionary<string, AttributeValue>()
            {
                { "Id", new AttributeValue {
                      N = "1000"
                  } }
            },
                ProjectionExpression = "Id, ISBN, Title, Authors",
                ConsistentRead = true
            };
            var response = client.GetItem(request);

            // Check the response.
            var attributeList = response.Item; // attribute list in the response.
            Console.WriteLine("\nPrinting item after retrieving it ............");
            PrintItem(attributeList);
        }

        private static void UpdateMultipleAttributes()
        {
            var request = new UpdateItemRequest
            {
                Key = new Dictionary<string, AttributeValue>()
            {
                { "Id", new AttributeValue {
                      N = "1000"
                  } }
            },
                // Perform the following updates:
                // 1) Add two new authors to the list
                // 1) Set a new attribute
                // 2) Remove the ISBN attribute
                ExpressionAttributeNames = new Dictionary<string, string>()
            {
                {"#A","Authors"},
                {"#NA","NewAttribute"},
                {"#I","ISBN"}
            },
                ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
            {
                {":auth",new AttributeValue {
                     SS = {"Author YY", "Author ZZ"}
                 }},
                {":new",new AttributeValue {
                     S = "New Value"
                 }}
            },

                UpdateExpression = "ADD #A :auth SET #NA = :new REMOVE #I",

                TableName = tableName,
                ReturnValues = "ALL_NEW" // Give me all attributes of the updated item.
            };
            var response = client.UpdateItem(request);

            // Check the response.
            var attributeList = response.Attributes; // attribute list in the response.
                                                     // print attributeList.
            Console.WriteLine("\nPrinting item after multiple attribute update ............");
            PrintItem(attributeList);
        }

        private static void UpdateExistingAttributeConditionally()
        {
            var request = new UpdateItemRequest
            {
                Key = new Dictionary<string, AttributeValue>()
            {
                { "Id", new AttributeValue {
                      N = "1000"
                  } }
            },
                ExpressionAttributeNames = new Dictionary<string, string>()
            {
                {"#P", "Price"}
            },
                ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
            {
                {":newprice",new AttributeValue {
                     N = "22.00"
                 }},
                {":currprice",new AttributeValue {
                     N = "20.00"
                 }}
            },
                // This updates price only if current price is 20.00.
                UpdateExpression = "SET #P = :newprice",
                ConditionExpression = "#P = :currprice",

                TableName = tableName,
                ReturnValues = "ALL_NEW" // Give me all attributes of the updated item.
            };
            var response = client.UpdateItem(request);

            // Check the response.
            var attributeList = response.Attributes; // attribute list in the response.
            Console.WriteLine("\nPrinting item after updating price value conditionally ............");
            PrintItem(attributeList);
        }

        private static void DeleteItem()
        {
            var request = new DeleteItemRequest
            {
                TableName = tableName,
                Key = new Dictionary<string, AttributeValue>()
            {
                { "Id", new AttributeValue {
                      N = "1000"
                  } }
            },

                // Return the entire item as it appeared before the update.
                ReturnValues = "ALL_OLD",
                ExpressionAttributeNames = new Dictionary<string, string>()
            {
                {"#IP", "InPublication"}
            },
                ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
            {
                {":inpub",new AttributeValue {
                     BOOL = false
                 }}
            },
                ConditionExpression = "#IP = :inpub"
            };

            var response = client.DeleteItem(request);

            // Check the response.
            var attributeList = response.Attributes; // Attribute list in the response.
                                                     // Print item.
            Console.WriteLine("\nPrinting item that was just deleted ............");
            PrintItem(attributeList);
        }

        private static void PrintItem(Dictionary<string, AttributeValue> attributeList)
        {
            foreach (KeyValuePair<string, AttributeValue> kvp in attributeList)
            {
                string attributeName = kvp.Key;
                AttributeValue value = kvp.Value;

                Console.WriteLine(
                    attributeName + " " +
                    (value.S == null ? "" : "S=[" + value.S + "]") +
                    (value.N == null ? "" : "N=[" + value.N + "]") +
                    (value.SS == null ? "" : "SS=[" + string.Join(",", value.SS.ToArray()) + "]") +
                    (value.NS == null ? "" : "NS=[" + string.Join(",", value.NS.ToArray()) + "]")
                    );
            }
            Console.WriteLine("************************************************");
        }
    }
}
```

# Ejemplo: operaciones por lotes con la API de bajo nivel de AWS SDK para .NET
<a name="batch-operation-lowlevel-dotnet"></a>

**Topics**
+ [Ejemplo: operación de escritura por lote mediante la API de bajo nivel de AWS SDK para .NET](#batch-write-low-level-dotnet)
+ [Ejemplo: operación de obtención por lotes mediante la API de bajo nivel de AWS SDK para .NET](#LowLevelDotNetBatchGet)

En esta sección se proporcionan ejemplos de las operaciones de *escritura por lote* y *obtención por lote* que Amazon DynamoDB admite.

## Ejemplo: operación de escritura por lote mediante la API de bajo nivel de AWS SDK para .NET
<a name="batch-write-low-level-dotnet"></a>

En el siguiente ejemplo de código C\$1 se usa el método `BatchWriteItem` para llevar a cabo las siguientes operaciones de colocación y eliminación:
+ Colocar un elemento en la tabla `Forum`.
+ Colocar un elemento y eliminar un elemento de la tabla `Thread`. 

Al crear la solicitud de escritura por lotes, puede especificar cualquier cantidad de solicitudes de colocación y eliminación en una o varias tablas. Sin embargo, `BatchWriteItem` de DynamoDB limita el tamaño de una solicitud de escritura por lote y el número de operaciones de colocación y eliminación que se pueden llevar a cabo en una misma operación de escritura por lote. Para obtener más información, consulte [BatchWriteItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html). Si la solicitud supera estos límites, se rechaza la solicitud. Si la tabla no cuenta con suficiente desempeño provisionado para atender esta solicitud, los elementos de solicitud sin procesar se devuelven en la respuesta. 

En el siguiente ejemplo se comprueba la respuesta para saber si contiene elementos de solicitud sin transformar. En caso afirmativo, entra en bucle y vuelve a enviar la solicitud `BatchWriteItem` con elementos sin procesar. También puede crear estos ejemplos de tablas y cargar los ejemplos de datos mediante programación. Para obtener más información, consulte [Creación de ejemplos de tablas y carga de datos utilizando AWS SDK para .NET](AppendixSampleDataCodeDotNET.md).

Para obtener instrucciones paso a paso para probar el siguiente ejemplo, consulte [Ejemplos de código .NET](CodeSamples.DotNet.md). 

**Example**  

```
using System;
using System.Collections.Generic;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.Model;
using Amazon.Runtime;

namespace com.amazonaws.codesamples
{
    class LowLevelBatchWrite
    {
        private static string table1Name = "Forum";
        private static string table2Name = "Thread";
        private static AmazonDynamoDBClient client = new AmazonDynamoDBClient();

        static void Main(string[] args)
        {
            try
            {
                TestBatchWrite();
            }
            catch (AmazonServiceException e) { Console.WriteLine(e.Message); }
            catch (Exception e) { Console.WriteLine(e.Message); }

            Console.WriteLine("To continue, press Enter");
            Console.ReadLine();
        }

        private static void TestBatchWrite()
        {
            var request = new BatchWriteItemRequest
            {
                ReturnConsumedCapacity = "TOTAL",
                RequestItems = new Dictionary<string, List<WriteRequest>>
            {
                {
                    table1Name, new List<WriteRequest>
                    {
                        new WriteRequest
                        {
                            PutRequest = new PutRequest
                            {
                                Item = new Dictionary<string, AttributeValue>
                                {
                                    { "Name", new AttributeValue {
                                          S = "S3 forum"
                                      } },
                                    { "Threads", new AttributeValue {
                                          N = "0"
                                      }}
                                }
                            }
                        }
                    }
                },
                {
                    table2Name, new List<WriteRequest>
                    {
                        new WriteRequest
                        {
                            PutRequest = new PutRequest
                            {
                                Item = new Dictionary<string, AttributeValue>
                                {
                                    { "ForumName", new AttributeValue {
                                          S = "S3 forum"
                                      } },
                                    { "Subject", new AttributeValue {
                                          S = "My sample question"
                                      } },
                                    { "Message", new AttributeValue {
                                          S = "Message Text."
                                      } },
                                    { "KeywordTags", new AttributeValue {
                                          SS = new List<string> { "S3", "Bucket" }
                                      } }
                                }
                            }
                        },
                        new WriteRequest
                        {
                            // For the operation to delete an item, if you provide a primary key value
                            // that does not exist in the table, there is no error, it is just a no-op.
                            DeleteRequest = new DeleteRequest
                            {
                                Key = new Dictionary<string, AttributeValue>()
                                {
                                    { "ForumName",  new AttributeValue {
                                          S = "Some partition key value"
                                      } },
                                    { "Subject", new AttributeValue {
                                          S = "Some sort key value"
                                      } }
                                }
                            }
                        }
                    }
                }
            }
            };

            CallBatchWriteTillCompletion(request);
        }

        private static void CallBatchWriteTillCompletion(BatchWriteItemRequest request)
        {
            BatchWriteItemResponse response;

            int callCount = 0;
            do
            {
                Console.WriteLine("Making request");
                response = client.BatchWriteItem(request);
                callCount++;

                // Check the response.

                var tableConsumedCapacities = response.ConsumedCapacity;
                var unprocessed = response.UnprocessedItems;

                Console.WriteLine("Per-table consumed capacity");
                foreach (var tableConsumedCapacity in tableConsumedCapacities)
                {
                    Console.WriteLine("{0} - {1}", tableConsumedCapacity.TableName, tableConsumedCapacity.CapacityUnits);
                }

                Console.WriteLine("Unprocessed");
                foreach (var unp in unprocessed)
                {
                    Console.WriteLine("{0} - {1}", unp.Key, unp.Value.Count);
                }
                Console.WriteLine();

                // For the next iteration, the request will have unprocessed items.
                request.RequestItems = unprocessed;
            } while (response.UnprocessedItems.Count > 0);

            Console.WriteLine("Total # of batch write API calls made: {0}", callCount);
        }
    }
}
```

## Ejemplo: operación de obtención por lotes mediante la API de bajo nivel de AWS SDK para .NET
<a name="LowLevelDotNetBatchGet"></a>

En el siguiente ejemplo de código C\$1 se usa el método `BatchGetItem` para recuperar varios elementos de las tablas `Forum` y `Thread` en Amazon DynamoDB. La solicitud `BatchGetItemRequest` especifica los nombres de las tablas y una lista de claves principales para cada tabla. En el ejemplo se procesa la respuesta y se imprimen los elementos recuperados. 

Para obtener instrucciones paso a paso para probar el siguiente ejemplo, consulte [Ejemplos de código .NET](CodeSamples.DotNet.md). 

**Example**  

```
using System;
using System.Collections.Generic;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.Model;
using Amazon.Runtime;

namespace com.amazonaws.codesamples
{
    class LowLevelBatchGet
    {
        private static string table1Name = "Forum";
        private static string table2Name = "Thread";
        private static AmazonDynamoDBClient client = new AmazonDynamoDBClient();

        static void Main(string[] args)
        {
            try
            {
                RetrieveMultipleItemsBatchGet();

                Console.WriteLine("To continue, press Enter");
                Console.ReadLine();
            }
            catch (AmazonServiceException e) { Console.WriteLine(e.Message); }
            catch (Exception e) { Console.WriteLine(e.Message); }
        }

        private static void RetrieveMultipleItemsBatchGet()
        {
            var request = new BatchGetItemRequest
            {
                RequestItems = new Dictionary<string, KeysAndAttributes>()
            {
                { table1Name,
                  new KeysAndAttributes
                  {
                      Keys = new List<Dictionary<string, AttributeValue> >()
                      {
                          new Dictionary<string, AttributeValue>()
                          {
                              { "Name", new AttributeValue {
                            S = "Amazon DynamoDB"
                        } }
                          },
                          new Dictionary<string, AttributeValue>()
                          {
                              { "Name", new AttributeValue {
                            S = "Amazon S3"
                        } }
                          }
                      }
                  }},
                {
                    table2Name,
                    new KeysAndAttributes
                    {
                        Keys = new List<Dictionary<string, AttributeValue> >()
                        {
                            new Dictionary<string, AttributeValue>()
                            {
                                { "ForumName", new AttributeValue {
                                      S = "Amazon DynamoDB"
                                  } },
                                { "Subject", new AttributeValue {
                                      S = "DynamoDB Thread 1"
                                  } }
                            },
                            new Dictionary<string, AttributeValue>()
                            {
                                { "ForumName", new AttributeValue {
                                      S = "Amazon DynamoDB"
                                  } },
                                { "Subject", new AttributeValue {
                                      S = "DynamoDB Thread 2"
                                  } }
                            },
                            new Dictionary<string, AttributeValue>()
                            {
                                { "ForumName", new AttributeValue {
                                      S = "Amazon S3"
                                  } },
                                { "Subject", new AttributeValue {
                                      S = "S3 Thread 1"
                                  } }
                            }
                        }
                    }
                }
            }
            };

            BatchGetItemResponse response;
            do
            {
                Console.WriteLine("Making request");
                response = client.BatchGetItem(request);

                // Check the response.
                var responses = response.Responses; // Attribute list in the response.

                foreach (var tableResponse in responses)
                {
                    var tableResults = tableResponse.Value;
                    Console.WriteLine("Items retrieved from table {0}", tableResponse.Key);
                    foreach (var item1 in tableResults)
                    {
                        PrintItem(item1);
                    }
                }

                // Any unprocessed keys? could happen if you exceed ProvisionedThroughput or some other error.
                Dictionary<string, KeysAndAttributes> unprocessedKeys = response.UnprocessedKeys;
                foreach (var unprocessedTableKeys in unprocessedKeys)
                {
                    // Print table name.
                    Console.WriteLine(unprocessedTableKeys.Key);
                    // Print unprocessed primary keys.
                    foreach (var key in unprocessedTableKeys.Value.Keys)
                    {
                        PrintItem(key);
                    }
                }

                request.RequestItems = unprocessedKeys;
            } while (response.UnprocessedKeys.Count > 0);
        }

        private static void PrintItem(Dictionary<string, AttributeValue> attributeList)
        {
            foreach (KeyValuePair<string, AttributeValue> kvp in attributeList)
            {
                string attributeName = kvp.Key;
                AttributeValue value = kvp.Value;

                Console.WriteLine(
                    attributeName + " " +
                    (value.S == null ? "" : "S=[" + value.S + "]") +
                    (value.N == null ? "" : "N=[" + value.N + "]") +
                    (value.SS == null ? "" : "SS=[" + string.Join(",", value.SS.ToArray()) + "]") +
                    (value.NS == null ? "" : "NS=[" + string.Join(",", value.NS.ToArray()) + "]")
                    );
            }
            Console.WriteLine("************************************************");
        }
    }
}
```

# Ejemplo: control de atributos de tipo binario mediante la API de bajo nivel de AWS SDK para .NET
<a name="LowLevelDotNetBinaryTypeExample"></a>

En el siguiente ejemplo de código C\$1 se ilustra cómo se controlan los atributos de tipo Binary. Además, se agrega un elemento a la tabla `Reply`. El elemento incluye un atributo de tipo Binary (`ExtendedMessage`) que almacena datos comprimidos. A continuación, en el ejemplo se recupera el elemento y se imprimen todos los valores de los atributos. Con fines ilustrativos, el ejemplo usa la clase `GZipStream` para comprimir un ejemplo de secuencia y asignársela al atributo `ExtendedMessage`; después, la descomprime al imprimir el valor del atributo. 

Para obtener instrucciones paso a paso para probar el siguiente ejemplo, consulte [Ejemplos de código .NET](CodeSamples.DotNet.md). 

**Example**  

```
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.Model;
using Amazon.Runtime;

namespace com.amazonaws.codesamples
{
    class LowLevelItemBinaryExample
    {
        private static string tableName = "Reply";
        private static AmazonDynamoDBClient client = new AmazonDynamoDBClient();

        static void Main(string[] args)
        {
            // Reply table primary key.
            string replyIdPartitionKey = "Amazon DynamoDB#DynamoDB Thread 1";
            string replyDateTimeSortKey = Convert.ToString(DateTime.UtcNow);

            try
            {
                CreateItem(replyIdPartitionKey, replyDateTimeSortKey);
                RetrieveItem(replyIdPartitionKey, replyDateTimeSortKey);
                // Delete item.
                DeleteItem(replyIdPartitionKey, replyDateTimeSortKey);
                Console.WriteLine("To continue, press Enter");
                Console.ReadLine();
            }
            catch (AmazonDynamoDBException e) { Console.WriteLine(e.Message); }
            catch (AmazonServiceException e) { Console.WriteLine(e.Message); }
            catch (Exception e) { Console.WriteLine(e.Message); }
        }

        private static void CreateItem(string partitionKey, string sortKey)
        {
            MemoryStream compressedMessage = ToGzipMemoryStream("Some long extended message to compress.");
            var request = new PutItemRequest
            {
                TableName = tableName,
                Item = new Dictionary<string, AttributeValue>()
            {
                { "Id", new AttributeValue {
                      S = partitionKey
                  }},
                { "ReplyDateTime", new AttributeValue {
                      S = sortKey
                  }},
                { "Subject", new AttributeValue {
                      S = "Binary type "
                  }},
                { "Message", new AttributeValue {
                      S = "Some message about the binary type"
                  }},
                { "ExtendedMessage", new AttributeValue {
                      B = compressedMessage
                  }}
            }
            };
            client.PutItem(request);
        }

        private static void RetrieveItem(string partitionKey, string sortKey)
        {
            var request = new GetItemRequest
            {
                TableName = tableName,
                Key = new Dictionary<string, AttributeValue>()
            {
                { "Id", new AttributeValue {
                      S = partitionKey
                  } },
                { "ReplyDateTime", new AttributeValue {
                      S = sortKey
                  } }
            },
                ConsistentRead = true
            };
            var response = client.GetItem(request);

            // Check the response.
            var attributeList = response.Item; // attribute list in the response.
            Console.WriteLine("\nPrinting item after retrieving it ............");

            PrintItem(attributeList);
        }

        private static void DeleteItem(string partitionKey, string sortKey)
        {
            var request = new DeleteItemRequest
            {
                TableName = tableName,
                Key = new Dictionary<string, AttributeValue>()
            {
                { "Id", new AttributeValue {
                      S = partitionKey
                  } },
                { "ReplyDateTime", new AttributeValue {
                      S = sortKey
                  } }
            }
            };
            var response = client.DeleteItem(request);
        }

        private static void PrintItem(Dictionary<string, AttributeValue> attributeList)
        {
            foreach (KeyValuePair<string, AttributeValue> kvp in attributeList)
            {
                string attributeName = kvp.Key;
                AttributeValue value = kvp.Value;

                Console.WriteLine(
                    attributeName + " " +
                    (value.S == null ? "" : "S=[" + value.S + "]") +
                    (value.N == null ? "" : "N=[" + value.N + "]") +
                    (value.SS == null ? "" : "SS=[" + string.Join(",", value.SS.ToArray()) + "]") +
                    (value.NS == null ? "" : "NS=[" + string.Join(",", value.NS.ToArray()) + "]") +
                    (value.B == null ? "" : "B=[" + FromGzipMemoryStream(value.B) + "]")
                    );
            }
            Console.WriteLine("************************************************");
        }

        private static MemoryStream ToGzipMemoryStream(string value)
        {
            MemoryStream output = new MemoryStream();
            using (GZipStream zipStream = new GZipStream(output, CompressionMode.Compress, true))
            using (StreamWriter writer = new StreamWriter(zipStream))
            {
                writer.Write(value);
            }
            return output;
        }

        private static string FromGzipMemoryStream(MemoryStream stream)
        {
            using (GZipStream zipStream = new GZipStream(stream, CompressionMode.Decompress))
            using (StreamReader reader = new StreamReader(zipStream))
            {
                return reader.ReadToEnd();
            }
        }
    }
}
```

# Mejora del acceso con índices secundarios en DynamoDB
<a name="SecondaryIndexes"></a>

Amazon DynamoDB proporciona un acceso rápido a los elementos de una tabla especificando sus valores de clave principal. Sin embargo, muchas aplicaciones podrían beneficiarse de disponer de una o varias claves secundarias (o alternativas) para permitir un acceso eficiente a datos con otros atributos aparte de la clave principal. Para responder a esta necesidad, puede crear uno o varios índices secundarios en una tabla y emitir solicitudes `Query` o `Scan` para estos índices.

Un *índice secundario* es una estructura de datos que contiene un subconjunto de atributos de una tabla, además de una clave alternativa para admitir las operaciones `Query`. Puede recuperar datos del índice usando una operación `Query` prácticamente de la misma forma que `Query` se usa en una tabla. Una tabla puede tener varios índices secundarios, lo que permite a las aplicaciones obtener acceso a distintos patrones de consulta.

**nota**  
También se pueden utilizar operaciones `Scan` en los índices, de un modo bastante similar a como `Scan` se usaría en una tabla.   
Las políticas [basadas en recursos](access-control-resource-based.md) no admiten actualmente el acceso entre cuentas para operaciones de análisis de índices secundarios.

Cada índice secundario está asociado exactamente con una tabla de la que obtiene sus datos. Esta se denomina *tabla base* del índice. Al crear un índice, se define una clave alternativa para él (clave de partición y clave de ordenación). También se definen los atributos de la tabla base que se desea *proyectar*, o copiar, en el índice. DynamoDB copia estos atributos en el índice, junto con los atributos de clave principal de la tabla base. A partir de ese momento, puede consultar o examinar el índice exactamente igual que una tabla. 

DynamoDB mantiene automáticamente cada índice secundario. Cuando se agregan, modifican o eliminan elementos en la tabla base, los índices basados en esa tabla se actualizan también para reflejar estos cambios.

DynamoDB admite dos tipos de índices secundarios:
+ **[Índice secundario global](GSI.html):** índice con una clave de partición y una clave de ordenación que pueden diferir de las claves de la tabla base. Un índice secundario global se considera "global" porque las consultas que se realizan en el índice pueden abarcar todos los datos de la tabla base y todas las particiones. Un índice secundario global se almacena en su propio espacio de partición lejos de la tabla base y se escala por separado de la tabla base.
+ **[Índice secundario local](LSI.html):** índice que tiene la misma clave de partición que la tabla base, pero una clave de ordenación distinta. Un índice secundario local se considera "local" en el sentido de que el ámbito de todas sus particiones se corresponde con una partición de la tabla base que tiene el mismo valor de clave de partición.

Para ver una comparación de los índices secundarios globales y los índices secundarios locales, consulte este vídeo.

[![AWS Videos](http://img.youtube.com/vi/https://www.youtube.com/embed/BkEu7zBWge8/0.jpg)](http://www.youtube.com/watch?v=https://www.youtube.com/embed/BkEu7zBWge8)


**Topics**
+ [Uso de índices secundarios globales en DynamoDB](GSI.md)
+ [Índices secundarios locales](LSI.md)

Debe tener en cuenta los requisitos de la aplicación al determinar qué tipo de índice va a utilizar. En la siguiente tabla se muestran las principales diferencias entre un índice secundario global y un índice secundario local.


****  

| Característica | Índice secundario global | Índice secundario local | 
| --- | --- | --- | 
| Esquema de claves | La clave principal de un índice secundario global puede ser simple (clave de partición) o compuesta (clave de partición y clave de ordenación). | La clave principal de un índice secundario local debe ser compuesta (clave de partición y clave de ordenación). | 
| Atributos de clave | La clave de partición y la clave de ordenación (si procede) del índice pueden ser cualesquiera atributos de tipo String, Number o Binary de la tabla base. | La clave de partición del índice es el mismo atributo que la clave de partición de la tabla base. La clave de ordenación puede ser cualquier atributo de tipo String, Number o Binary de la tabla base. | 
| Restricciones de tamaño por valor de clave de partición | No hay restricciones de tamaño para los índices secundarios globales. | Para cada valor de clave de partición, el tamaño total de todos los elementos indexados debe ser de 10 GB o menos. | 
| Operaciones online con índices | Los índices secundarios globales se pueden crear al mismo tiempo que se crea una tabla. También puede agregar un nuevo índice secundario global a una tabla existente o eliminar un índice secundario global existente. Para obtener más información, consulte [Administración de índices secundarios globales en DynamoDB](GSI.OnlineOps.md).  | Los índices secundarios locales se crean a la vez que se crea la tabla. No se puede agregar un índice secundario local a una tabla existente, ni tampoco se puede eliminar ningún índice secundario local que ya exista. | 
| Consultas y particiones | Un índice secundario global permite realizar consultas en toda la tabla y en todas las particiones.  | Un índice secundario local permite consultar una sola partición, según lo especificado por el valor de clave de partición de la consulta. | 
| Consistencia de lectura | Las consultas a los índices secundarios globales solo admiten la consistencia final. | Cuando se realiza una consulta en un índice secundario local, se puede elegir entre la consistencia final o alta. | 
| Consumo de desempeño provisionado | Cada índice secundario global tiene su propia configuración de rendimiento aprovisionado para la actividad de lectura y escritura. Las consultas o exámenes de un índice secundario global consumen unidades de capacidad del índice, no de la tabla base. Esto mismo sucede con las actualizaciones de los índices secundarios globales debidas a escrituras en la tabla. Un índice secundario global asociado a las tablas globales consume unidades de capacidad de escritura.  | Las consultas o análisis de un índice secundario local consumen unidades de capacidad de lectura de la tabla base. Al escribir en una tabla, sus índices secundarios locales también se actualizan y estas actualizaciones consumen unidades de capacidad de escritura de la tabla base. Un índice secundario local asociado a las tablas globales consume unidades de capacidad de escritura replicadas. | 
| Atributos proyectados | Cuando se consulta o analiza un índice secundario global, solo se pueden solicitar los atributos que se han proyectado en él. DynamoDB no recupera ningún atributo de la tabla. | Cuando se consulta o analiza un índice secundario local, se pueden solicitar los atributos que no se hayan proyectado en él. DynamoDB recuperará automáticamente estos atributos de la tabla. | 

Si desea crear más de una tabla con índices secundarios, debe hacerlo de forma secuencial. Por ejemplo, tendría que crear la primera tabla y esperar a que su estado fuese `ACTIVE`, luego la siguiente tabla y esperar a que adquiriese el estado `ACTIVE`, y así sucesivamente. Si intenta crear simultáneamente más de una tabla con un índice secundario, DynamoDB devuelve una excepción `LimitExceededException`.

Cada índice secundario usa la misma [clase de tabla](HowItWorks.TableClasses.html) y el mismo [modo de capacidad](capacity-mode.md) que la tabla base a la que está asociado. Para cada índice secundario debe especificar lo siguiente:
+ Tipo de índice que se va a crear, ya sea un índice secundario global o un índice secundario local.
+ El nombre del índice. Las reglas de nomenclatura de los índices son las mismas que para las tablas, como se indica en [Cuotas en Amazon DynamoDB](ServiceQuotas.md). El nombre debe ser único para la tabla base al que está asociado, pero puede utilizar el mismo nombre para índices que estén asociados a tablas base distintas.
+ El esquema de claves del índice. Cada atributo del esquema de claves del índice debe ser un atributo de nivel superior debe de tipo `String`, `Number` y `Binary`. No se permiten otros tipos de datos, como los documentos y los conjuntos. Los demás requisitos del esquema de claves dependen del tipo de índice: 
  + Si se trata de un índice secundario global, la clave de partición puede ser cualquier atributo escalar de la tabla base. La clave de ordenación es opcional y también puede ser cualquier atributo escalar de la tabla base.
  + Si se trata de un índice secundario local, la clave de partición debe ser igual que la tabla de partición de la tabla base y la clave de ordenación debe ser un atributo sin clave de la tabla base.
+ Los atributos adicionales, si los hay, de la tabla base que se proyectarán en el índice. Estos atributos se agregan a los atributos de clave de la tabla, que se proyectan de forma automática en cada índice. Puede proyectar atributos de cualquier tipo de datos, incluidos escalares, documentos y conjuntos.
+ Los ajustes de desempeño provisionado del índice, si es preciso:
  + Para un índice secundario global, debe especificar los ajustes de unidades de capacidad de lectura y escritura. Estos ajustes de desempeño provisionado son independientes de los ajustes de la tabla base.
  + Para un índice secundario local, no es preciso especificar los ajustes de unidades de capacidad de lectura y escritura. Todas las operaciones de lectura y escritura en un índice secundario local consumen el rendimiento aprovisionado configurado para su tabla base.

Para disfrutar de la máxima flexibilidad en las consultas, puede crear hasta 20 índices secundarios globales (cuota predeterminada) y hasta 5 índices secundarios locales por tabla.

Para obtener un listado detallado de índices secundarios en una tabla, utilice la operación `DescribeTable`. `DescribeTable` devuelve el nombre, el tamaño de almacenamiento y el recuento de elementos de cada índice secundario de la tabla. Estos valores no se actualizan en tiempo real, sino aproximadamente cada seis horas.

Para obtener acceso a los datos de un índice secundario, utilice las operaciones `Query` o `Scan`. Debe especificar el nombre de la tabla base y el nombre del índice que se desea utilizar, los atributos que se devolverán en los resultados y las expresiones de condición o filtros que se van a aplicar. DynamoDB puede devolver los resultados en orden ascendente o descendente.

Al eliminar una tabla, todos los índices asociados a ella se eliminan también.

Para ver las prácticas recomendadas, consulte [Prácticas recomendadas para utilizar índices secundarios en DynamoDB](bp-indexes.md).

# Uso de índices secundarios globales en DynamoDB
<a name="GSI"></a>

Algunas aplicaciones pueden necesitar llevar a cabo muy diversos tipos de consultas en las que se usen distintos atributos como criterios de consulta. Para responder a estos requisitos, puede crear uno o más * índices secundarios globales* y emitir solicitudes `Query` en ellos en Amazon DynamoDB.

**Topics**
+ [Situación: Uso de un índice secundario global](#GSI.scenario)
+ [Proyecciones de atributos](#GSI.Projections)
+ [Esquema de claves de varios atributos](#GSI.MultiAttributeKeys)
+ [Lectura de datos de un índice secundario global](#GSI.Reading)
+ [Sincronización de datos entre tablas e índices secundarios globales](#GSI.Writes)
+ [Clases de tabla con un índice secundario global](#GSI.tableclasses)
+ [Consideraciones sobre el rendimiento aprovisionado para los índices secundarios globales](#GSI.ThroughputConsiderations)
+ [Consideraciones sobre el almacenamiento para los índices secundarios globales](#GSI.StorageConsiderations)
+ [Patrones de diseño](GSI.DesignPatterns.md)
+ [Administración de índices secundarios globales en DynamoDB](GSI.OnlineOps.md)
+ [Detección y corrección de infracciones de la clave del índice en DynamoDB](GSI.OnlineOps.ViolationDetection.md)
+ [Trabajo con índices secundarios globales: Java](GSIJavaDocumentAPI.md)
+ [Trabajar con índices secundarios globales: .NET](GSILowLevelDotNet.md)
+ [Uso de índices secundarios globales en DynamoDB con la AWS CLI](GCICli.md)

## Situación: Uso de un índice secundario global
<a name="GSI.scenario"></a>

Por poner un ejemplo, tomemos una tabla denominada `GameScores` que realiza el seguimiento de los usuarios y las puntuaciones de una aplicación de juegos para móviles. Cada elemento en `GameScores` se identifica por una clave de partición (`UserId`) y una clave de ordenación (`GameTitle`). En el siguiente diagrama se muestra cómo se organizarían los elementos de la tabla. No se muestran todos los atributos.

![\[Tabla GameScores con una lista de ID de usuarios, nombres, puntuaciones, fechas y partidas ganadas/perdidas.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/GSI_01.png)


Ahora, supongamos que desea escribir una aplicación de clasificación para mostrar las puntuaciones máximas de cada juego. Una consulta que especifique los atributos de clave (`UserId` y `GameTitle`) sería muy eficiente. Sin embargo, si la aplicación tuviese que recuperar datos de `GameScores` solo según `GameTitle`, tendría que usar una operación `Scan`. A medida que se agregan elementos a la tabla, los exámenes de todos los datos resultarían lentos e ineficientes. En consecuencia, resultaría difícil responder a preguntas como estas:
+ ¿Cuál es la puntuación máxima que se ha registrado en el juego Meteor Blasters?
+ ¿Qué usuario ha obtenido la mejor puntuación en Galaxy Invaders?
+ ¿Cuál es la mayor proporción de partidas ganadas respecto a las perdidas?

Para agilizar las consultas de atributos sin clave, puede crear un índice secundario global. Un índice secundario global contiene una selección de atributos de la tabla base, pero están organizados por una clave principal distinta de la clave principal de la tabla. La clave de índice no requiere disponer de ninguno de los atributos de clave de la tabla. Ni siquiera necesita el mismo esquema de claves que una tabla.

Por ejemplo, puede crear un índice secundario global llamado `GameTitleIndex`, con una clave de partición de `GameTitle` y una clave de ordenación de `TopScore`. Puesto que los atributos de clave principal de la tabla base siempre se proyectan en un índice, el atributo `UserId` también está presente. En el siguiente diagrama se muestra el aspecto que tendría el índice `GameTitleIndex`.

![\[Tabla GameTitleIndex con una lista de nombres, puntuaciones e ID de usuarios.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/GSI_02.png)


Ahora, puede consultar `GameTitleIndex` y obtener fácilmente las puntuaciones de Meteor Blasters. Los resultados se ordenan según los valores de la clave de ordenación, `TopScore`. Si establece el parámetro `ScanIndexForward` en false, los resultados se devuelven en orden descendente, de modo que la puntuación máxima se devuelve en primer lugar.

Cada índice secundario global debe tener una clave de partición y puede tener una clave de ordenación opcional. El esquema de claves de índice puede ser distinto del de la tabla base. La tabla podría tener una clave principal simple (clave de partición) y, en cambio, se podría crear un índice secundario global con una clave principal compuesta (clave de partición y clave de ordenación), o viceversa. Los atributos de clave del índice pueden constar de cualquier atributo de nivel superior de tipo `String`, `Number` o `Binary` de la tabla base. No se admite ningún otro tipo de escalar, documento ni conjunto.

Puede proyectar otros atributos de la tabla base en el índice si lo desea. Cuando se consulta el índice, DynamoDB puede recuperar estos atributos proyectados de forma eficiente. Sin embargo, las consultas de índice secundario global no permiten recuperar atributos de la tabla base. Por ejemplo, si consulta `GameTitleIndex` como se muestra en el diagrama anterior, la consulta no podría obtener acceso a ningún atributo sin clave excepto a `TopScore` (aunque los atributos de clave `GameTitle` y `UserId` se proyectarían automáticamente).

En una tabla de DynamoDB, cada valor de clave debe ser único. Sin embargo, no es obligatorio que los valores de clave de un índice secundario global sean únicos. A modo de ejemplo, supongamos que un juego denominado Comet Quest es especialmente difícil, de tal forma que muchos usuarios nuevos intentan lograr una puntuación superior a cero, sin conseguirlo. A continuación se muestran algunos datos que podrían representar esta situación.


****  

| UserId | GameTitle | TopScore | 
| --- | --- | --- | 
| 123 | Comet Quest | 0 | 
| 201 | Comet Quest | 0 | 
| 301 | Comet Quest | 0 | 

Cuando estos datos se agregan a la tabla `GameScores`, DynamoDB los propaga a `GameTitleIndex`. Si, a continuación, consultamos el índice con el valor Comet Quest en `GameTitle` y el valor 0 en `TopScore`, se devuelven los valores siguientes.

![\[Tabla con una lista de nombres, puntuaciones máximas e ID de usuarios.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/GSI_05.png)


Solo aparecerán en la respuesta los elementos que tengan los valores de clave especificados. Dentro de ese conjunto de datos, los elementos no aparecen en ningún orden concreto. 

Un índice secundario global solo realiza el seguimiento de los elementos de datos cuyos atributos de clave existen realmente. Por ejemplo, supongamos que ha agregado otro elemento nuevo a la tabla `GameScores`, pero que solo ha proporcionado los atributos de clave principal obligatorios.


****  

| UserId | GameTitle | 
| --- | --- | 
| 400 | Comet Quest | 

Al no haber especificado el atributo `TopScore`, DynamoDB no propagará este elemento a `GameTitleIndex`. Por lo tanto, si consultase `GameScores` para obtener todos los elementos de Comet Quest, obtendría los siguientes cuatro elementos.

![\[Tabla con una lista de 4 nombres, puntuaciones máximas e ID de usuarios.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/GSI_04.png)


Una consulta parecida de `GameTitleIndex` devolvería tres elementos, en lugar de cuatro. Esto se debe a que el elemento cuyo valor de `TopScore` no existe no se propaga al índice.

![\[Tabla con una lista de 3 nombres, puntuaciones máximas e ID de usuarios.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/GSI_05.png)


## Proyecciones de atributos
<a name="GSI.Projections"></a>

Una *proyección* es el conjunto de atributos que se copia de una tabla en un índice secundario. La clave de partición y la clave de ordenación de la tabla siempre se proyectan en el índice; puede proyectar otros atributos para admitir los requisitos de consulta de la aplicación. Cuando consulta un índice, Amazon DynamoDB puede acceder a cualquier atributo de la proyección como si esos atributos estuvieran en una tabla propia.

Al crear un índice secundario, debe especificar los atributos que se proyectarán en el índice. DynamoDB ofrece tres opciones diferentes para esto:
+ *KEYS\$1ONLY*: cada elemento del índice consta únicamente de los valores de la clave de partición y la clave de ordenación de la tabla, así como de los valores de las claves del índice. La opción `KEYS_ONLY` da como resultado el índice secundario más pequeño posible.
+ *INCLUDE*: además de los atributos que se describen en `KEYS_ONLY`, el índice secundario incluirá otros atributos sin clave que se especifiquen.
+ *ALL*: el índice secundario incluye todos los atributos de la tabla de origen. Debido a que todos los datos de la tabla están duplicados en el índice, un resultado de proyección `ALL` en el índice secundario más grande posible.

En el diagrama anterior, `GameTitleIndex` tiene solo un atributo proyectado: `UserId`. Por tanto, mientras una aplicación puede determinar de forma eficiente el `UserId` de los jugadores que tienen mayor puntuación en cada juego utilizando `GameTitle` y `TopScore` en las consultas, no puede determinar de manera eficiente la mayor proporción de partidas ganadas respecto a las perdidas para los jugadores con mayor puntuación. Para ello, la aplicación tendría que realizar una consulta adicional en la tabla base para recuperar las partidas ganadas y perdidas de cada uno de los jugadores con mayor puntuación. Una manera más eficiente de consultar estos datos sería proyectar estos atributos de la tabla base en el índice secundario global, tal y como se muestra en este diagrama. 

![\[Representación de la proyección de atributos sin clave en un GSI para realizar consultas eficientes.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/GSI_06.png)


Dado que los atributos sin clave `Wins` y `Losses` se proyectan en el índice, una aplicación puede determinar la proporción de partidas ganadas respecto a las perdidas en cualquier juego, o para cualquier combinación de juego e identificador de usuario.

Cuando elija los atributos para proyectarlos en un índice secundario global, debe estudiar el equilibrio entre los costes de rendimiento aprovisionado y de almacenamiento:
+ Si solo necesita obtener acceso a algunos atributos con la menor latencia posible, puede ser conveniente proyectar solamente esos atributos en un índice secundario global. Cuando menor es el índice, menos cuesta almacenarlo y menos son los costes de escritura.
+ Si la aplicación va a obtener acceso con frecuencia a determinados atributos sin clave, puede ser interesante proyectarlos en un índice secundario global. Los costes del almacenamiento adicionales del índice secundario global compensarán el coste que supondrían los frecuentes exámenes de la tabla.
+ Si tiene que obtener acceso a la mayoría de los atributos sin clave con frecuencia, puede proyectar estos atributos o, incluso, toda la tabla base, en un índice secundario global. Esto le da la máxima flexibilidad. Sin embargo, el coste del almacenamiento aumentaría y podría llegar a duplicarse.
+ Si la aplicación tiene que consultar una tabla con poca frecuencia, pero tiene que realizar gran cantidad de escrituras o actualizaciones en los datos de la tabla, puede ser conveniente proyectar `KEYS_ONLY`. El índice secundario global tendrá el tamaño mínimo, pero estaría disponible siempre que se requiriese para actividades de consulta. 

## Esquema de claves de varios atributos
<a name="GSI.MultiAttributeKeys"></a>

Los índices secundarios globales admiten claves de varios atributos, lo que le permite componer claves de partición y claves de clasificación a partir de varios atributos. Con las claves de varios atributos, puede crear una clave de partición a partir de un máximo de cuatro atributos y una clave de clasificación a partir de un máximo de cuatro atributos, hasta un total de ocho atributos por esquema de clave.

Las claves de varios atributos simplifican el modelo de datos al eliminar la necesidad de concatenar manualmente los atributos en claves sintéticas. En lugar de crear cadenas compuestas como `TOURNAMENT#WINTER2024#REGION#NA-EAST`, puede utilizar directamente los atributos naturales del modelo de dominio. DynamoDB gestiona automáticamente la lógica de clave compuesta, agrupando varios atributos de clave de partición para distribuir los datos y manteniendo el orden jerárquico de varios atributos de clave de clasificación.

Por ejemplo, considere un sistema de torneos de juegos en el que desee organizar las partidas por torneo y región. Con las claves de varios atributos, puede definir la clave de partición como dos atributos independientes: `tournamentId` y `region`. Del mismo modo, puede definir la clave de clasificación mediante varios atributos como `round`, `bracket` y `matchId` para crear una jerarquía natural. Este enfoque mantiene los datos mecanografiados y el código limpio, sin manipular ni analizar las cadenas.

Al consultar un índice secundario global con claves de varios atributos, debe especificar todos los atributos de las claves de partición mediante condiciones de igualdad. Para los atributos de las claves de clasificación, puede consultarlos de izquierda a derecha en el orden en que están definidos en el esquema de claves. Esto significa que puede consultar solo el primer atributo de clave de clasificación, los dos primeros atributos juntos o todos los atributos juntos, pero no puede omitir los atributos del medio. Condiciones de desigualdad como `>`, `<`, `BETWEEN` o `begins_with()` deben ser la última condición de la consulta.

Las claves de varios atributos funcionan especialmente bien al crear índices secundarios globales en tablas existentes. Puede usar atributos que ya existen en la tabla sin rellenar los datos con claves sintéticas. Esto facilita la agregación de nuevos patrones de consulta a la aplicación mediante la creación de índices que reorganizan los datos mediante diferentes combinaciones de atributos.

Cada atributo de una clave de varios atributos puede tener su propio tipo de datos: `String` (S), `Number` (N) o `Binary` (B). Al elegir los tipos de datos, tenga en cuenta que los atributos de `Number` se ordenan numéricamente sin necesidad de rellenar ceros, mientras que los atributos de `String` se ordenan lexicográficamente. Por ejemplo, si utiliza un tipo de `Number` para un atributo de puntuación, los valores 5, 50, 500 y 1000 se ordenan en orden numérico natural. Los mismos valores que el tipo de `String` se ordenarían como “1000”, “5”, “50” o “500”, a menos que los rellene con ceros a la izquierda.

Al diseñar claves de varios atributos, ordene los atributos del más general al más específico. En el caso de las claves de partición, combine los atributos que siempre se consulten juntos y que proporcionen una buena distribución de los datos. En el caso de las claves de clasificación, coloque los atributos consultados con frecuencia en primer lugar de la jerarquía para maximizar la flexibilidad de las consultas. Este orden le permite realizar consultas en cualquier nivel de detalle que coincida con los patrones de acceso.

Consulte los ejemplos de [Claves de varios atributos](GSI.DesignPattern.MultiAttributeKeys.md) para implementación.

## Lectura de datos de un índice secundario global
<a name="GSI.Reading"></a>

Puede recuperar elementos de un índice secundario global mediante las operaciones `Query` y `Scan`. Las opreciones `GetItem` y `BatchGetItem` no se pueden usar en un índice secundario global.

### Consulta a un índice secundario global
<a name="GSI.Querying"></a>

Puede utilizar la operación `Query` para obtener acceso a uno o varios elementos de un índice secundario global. En la consulta se debe especificar el nombre de la tabla base y el nombre del índice que se desea utilizar, los atributos que se devolverán en los resultados de la consulta y las condiciones de consulta que se van a aplicar. DynamoDB puede devolver los resultados en orden ascendente o descendente.

Tomemos los datos siguientes devueltos por una operación `Query` que solicita datos de juegos para una aplicación de clasificación de juegos.

```
{
    "TableName": "GameScores",
    "IndexName": "GameTitleIndex",
    "KeyConditionExpression": "GameTitle = :v_title",
    "ExpressionAttributeValues": {
        ":v_title": {"S": "Meteor Blasters"}
    },
    "ProjectionExpression": "UserId, TopScore",
    "ScanIndexForward": false
}
```

En esta consulta:
+ DynamoDB accede *GameTitleIndex* utilizando la clave de partición *GameTitle* para localizar los elementos de índice correspondientes a Meteor Blasters. Todos los elementos de índice que tienen esta clave se almacenan en posiciones adyacentes, para agilizar su recuperación.
+ En este juego, DynamoDB utiliza el índice para obtener acceso a todos los identificadores de los usuarios y a las puntuaciones máximas de este juego.
+ Los resultados se devuelven ordenados por orden descendente, porque el parámetro `ScanIndexForward` se ha establecido en false.

### Análisis de un índice secundario global
<a name="GSI.Scanning"></a>

Puede usar la operación `Scan` para recuperar todos los datos de un índice secundario global. Debe proporcionar el nombre de la tabla base y el nombre del índice en la solicitud. Con una operación `Scan`, DynamoDB lee todos los datos del índice y los devuelve a la aplicación. También puede solicitar que solo se devuelvan algunos de los datos y se descarten los demás. Para ello, se utiliza el parámetro `FilterExpression` de la operación `Scan`. Para obtener más información, consulte [Expresiones de filtro para el análisis](Scan.md#Scan.FilterExpression).

## Sincronización de datos entre tablas e índices secundarios globales
<a name="GSI.Writes"></a>

DynamoDB sincroniza automáticamente cada índice secundario global con su tabla base. Cuando una aplicación escribe o elimina elementos en una tabla, cualquier índice secundario global de esa tabla se actualizan de forma asincrónica, aplicando un modelo de consistencia final. Las aplicaciones nunca escriben directamente en un índice. Sin embargo, es importante que comprenda las implicaciones de cómo DynamoDB mantiene estos índices.

 Los índices secundarios globales heredan el modo de capacidad de lectura/escritura de la tabla base. Para obtener más información, consulte [Aspectos a tener en cuenta al cambiar los modos de capacidad en DynamoDB](bp-switching-capacity-modes.md). 

Al crear un índice secundario global, debe especificar uno o varios atributos de clave de índice y sus tipos de datos. Esto significa que, cada vez que se escribe un elemento en la tabla base, los tipos de datos de esos atributos deben coincidir con los tipos de datos del esquema de claves de índice. En el caso de `GameTitleIndex`, el tipo de datos de la clave de partición `GameTitle` del índice es `String`. La clave de ordenación `TopScore` del índice es de tipo `Number`. Si intenta agregar un elemento a la tabla `GameScores`, pero especifica un tipo de datos distinto para `GameTitle` o `TopScore`, DynamoDB devolverá una excepción `ValidationException`, porque los tipos de datos no coinciden.

Al colocar o eliminar elementos en una tabla, sus índices secundarios globales se actualizan de forma consistente final. En condiciones normales, los cambios en los datos de la tabla se propagan a los índices secundarios globales casi al instante. No obstante, en algunos escenarios de error improbables, pueden producirse retardos de propagación más prolongados. Debido a ello, las aplicaciones deben prever y controlar las situaciones en las que una consulta de un índice secundario global devuelva resultados que no se encuentren actualizados.

Si escribe un elemento en una tabla, no tiene que especificar los atributos de ninguna clave de ordenación del índice secundario global. Si utilizamos `GameTitleIndex` a modo de ejemplo, no sería preciso especificar un valor para el atributo `TopScore` para poder escribir un nuevo elemento en la tabla `GameScores`. En este caso, DynamoDB no escribe ningún dato en el índice para este elemento concreto.

Una tabla con muchos índices secundarios globales devengará costes más elevados por la actividad de escritura que las tablas con menos índices. Para obtener más información, consulte [Consideraciones sobre el rendimiento aprovisionado para los índices secundarios globales](#GSI.ThroughputConsiderations).

## Clases de tabla con un índice secundario global
<a name="GSI.tableclasses"></a>

Un índice secundario global siempre utilizará la misma clase de tabla que su tabla base. Cada vez que se agrega un nuevo índice secundario global para una tabla, el nuevo índice utilizará la misma clase de tabla que su tabla base. Cuando se actualiza la clase de tabla de una tabla, también se actualizan todos los índices secundarios globales asociados.

## Consideraciones sobre el rendimiento aprovisionado para los índices secundarios globales
<a name="GSI.ThroughputConsiderations"></a>

Al crear un índice secundario global en una tabla en modo aprovisionado, debe especificar las unidades de capacidad de lectura y escritura para la carga de trabajo prevista de ese índice. Los ajustes de rendimiento aprovisionado de un índice secundario global son independientes de los de su tabla base. Una operación `Query` en un consume unidades de capacidad de lectura del índice, no de la tabla base. Al colocar, actualizar o eliminar elementos en una tabla, sus índices secundarios globales se actualizan de forma consistente final. Estas actualizaciones de índices consumen unidades de capacidad de escritura del índice, no de la tabla base.

Por ejemplo, si realiza una operación `Query` en un índice secundario global y supera su capacidad de lectura aprovisionada, la solicitud se someterá a una limitación controlada. Si realiza una intensa actividad de escritura en la tabla, pero un índice secundario global de esa tabla no tiene suficiente capacidad de escritura, entonces la actividad de escritura de la tabla se someterá a una limitación controlada.

**importante**  
 Para evitar una posible limitación controlada, la capacidad de escritura provisionada para un índice secundario global debe ser igual o mayor que la capacidad de escritura de la tabla base, ya que las nuevas actualizaciones se escribirán tanto en la tabla base como en el índice secundario global. 

Para modificar los ajustes de rendimiento aprovisionado de un índice secundario global, use la operación `DescribeTable`. Se devuelve información detallada sobre cada índices secundario global de la tabla.

### Unidades de capacidad de lectura
<a name="GSI.ThroughputConsiderations.Reads"></a>

Los índices secundarios globales admiten las lecturas consistentes finales, cada una de la cuales consume la mitad de una unidad de capacidad de lectura. Esto quiere decir que una sola consulta en un índice secundario global permite recuperar hasta 2 × 4 = 8 KB por unidad de capacidad de lectura.

Para las consultas en un índce secundario global, DynamoDB calcula la actividad de lectura provisionada de la misma forma que para las consultas en una tabla. La única diferencia es que el cálculo se basa en el tamaño de las entradas del índice, no en el tamaño del elemento en la tabla base. El número de unidades de capacidad de lectura es la suma de todos los tamaños de los atributos proyectados para todos los elementos devueltos. El resultado se redondea al múltiplo de 4 KB inmediatamente superior. Para obtener más información sobre cómo calcula DynamoDB el consumo de rendimiento aprovisionado, consulte [Modo de capacidad aprovisionada de DynamoDB](provisioned-capacity-mode.md).

El tamaño máximo de los resultados que devuelve una operación `Query` es de 1 MB. Esta cifra incluye los tamaños de todos los nombres y valores de los atributos de todos los elementos devueltos.

Por ejemplo, tomemos un índice secundario global en el que cada elemento contiene 2000 bytes de datos. Ahora, supongamos que se utiliza una operación `Query` en este índice y que la consulta `KeyConditionExpression` devuelve ocho elementos. El tamaño total de los elementos coincidentes es de 2000 bytes x 8 elementos = 16 000 bytes. El resultado se redondea al múltiplo de 4 KB inmediatamente superior. Puesto que las consultas en un índice secundario global presentan consistencia final, el coste total es de 0,5 (16 KB / 4 KB), lo que equivale a 2 unidades de capacidad de lectura.

### Unidades de capacidad de escritura
<a name="GSI.ThroughputConsiderations.Writes"></a>

Cuando se agrega, actualiza o elimina un elemento de una tabla y esto afecta a un índice secundario global, este índice secundario global consume unidades de capacidad de escritura provisionadas por esta operación. El coste total de desempeño provisionado de una escritura es la suma de las unidades de capacidad de escritura consumidas al escribir en la tabla base y aquellas consumidas al actualizar los índices secundarios globales. Si una escritura en una tabla no requiere que se actualice un índice secundario global, no se consume ninguna capacidad de escritura del índice.

Para que una escritura en una tabla se lleve a cabo correctamente, la capacidad de desempeño provisionada definida para la tabla y todos sus índices secundarios globales debe ser suficiente para admitir esa escritura. De lo contrario, la escritura en la tabla se someterá a una limitación controlada. 

**importante**  
Al crear un índice secundario global (GSI), las operaciones de escritura en la tabla base se pueden limitar si la actividad del GSI resultante de las escrituras en la tabla base supera la capacidad de escritura aprovisionada del GSI. Esta limitación afecta a todas las operaciones de escritura, desde el proceso de indexación hasta la posible interrupción de las cargas de trabajo de producción. Para obtener más información, consulte [Solución de problemas de limitación en Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TroubleshootingThrottling.html).

El coste de escribir un elemento en un índice secundario global depende de varios factores:
+ Si escribe un nuevo elemento en la tabla que define un atributo indexado o actualiza un elemento existente para definir un atributo indexado no definido previamente, se requiere una operación de escritura para colocar el elemento en el índice.
+ Si al actualizar la tabla se cambia el valor de un atributo de clave indexado (de A a B), se requieren dos escrituras, una para eliminar el elemento anterior del índice y otra para colocar el nuevo elemento en el índice.  
+ Si ya hay un elemento en el índice, pero al escribir en la tabla se elimina el atributo indexado, se requiere una escritura para eliminar la proyección del elemento anterior del índice.
+ Si no hay ningún elemento presente en el índice antes o después de actualizar el elemento, no se devenga ningún coste de escritura adicional para el índice.
+ Si al actualizar la tabla solo se cambia el valor de los atributos proyectados en el esquema de claves del índice, pero no se cambia el valor de ningún atributo de clave indexado, se requiere una escritura para actualizar los valores de los atributos proyectados en el índice.

Al calcular las unidades de capacidad de escritura, en todos estos factores se presupone que el tamaño de cada elemento del índice es menor o igual que el tamaño de elemento de 1 KB. Las entradas de índice de mayor tamaño requieren más unidades de capacidad de escritura. Para minimizar los costos de escritura, es conveniente estudiar qué atributos tendrán que devolver las consultas y proyectar solamente esos atributos en el índice.

## Consideraciones sobre el almacenamiento para los índices secundarios globales
<a name="GSI.StorageConsiderations"></a>

Cuando una aplicación escribe un elemento en una tabla, DynamoDB copia automáticamente el subconjunto de atributos correcto en todos los índices secundarios globales en los que deban aparecer esos atributos. Se aplica un cargo en su cuenta de AWS por el almacenamiento del elemento en la tabla base y también por el almacenamiento de los atributos en todos los índices secundarios globales de esa tabla.

La cantidad de espacio utilizado por un elemento de índice es la suma de lo siguiente:
+ El tamaño en bytes de la clave principal (clave de partición y clave de ordenación) de la tabla base
+ El tamaño en bytes del atributo de clave de índice
+ El tamaño en bytes de los atributos proyectados (si procede)
+ 100 bytes de gastos generales por cada elemento de índice

Para calcular los requisitos de almacenamiento de un índice secundario global, puede calcular el tamaño medio de un elemento del índice y, a continuación, multiplicarlo por el número de elementos de la tabla base que tienen atributos de clave del índice secundario global.

Si una tabla contiene un elemento en el que no se han definido atributos determinados, pero ese atributo se ha definido como clave de partición o clave de clasificación del índice, DynamoDB no escribe ningún dato para ese elemento en el índice.

# Patrones de diseño
<a name="GSI.DesignPatterns"></a>

Los patrones de diseño ofrecen soluciones probadas a los desafíos más comunes cuando se trabaja con índices secundarios globales. Estos patrones le ayudan a crear aplicaciones eficientes y escalables, ya que le muestran cómo estructurar los índices para casos de uso específicos.

Cada patrón incluye una guía de implementación completa con ejemplos de código, prácticas recomendadas y casos de uso reales que le ayudarán a aplicar el patrón a sus propias aplicaciones.

**Topics**
+ [Claves de varios atributos](GSI.DesignPattern.MultiAttributeKeys.md)

# Patrón de claves de varios atributos
<a name="GSI.DesignPattern.MultiAttributeKeys"></a>

## Descripción general
<a name="GSI.DesignPattern.MultiAttributeKeys.Overview"></a>

Las claves de varios atributos le permiten crear claves de partición y clasificación del índice secundario global (GSI) compuestas por un máximo de cuatro atributos cada una. Esto reduce el código del cliente y facilita el modelado inicial de los datos y la posterior agregación de nuevos patrones de acceso.

Pensemos en un escenario común: para crear un GSI que consulte los elementos por varios atributos jerárquicos, tradicionalmente se necesitaría crear claves sintéticas concatenando valores. Por ejemplo, en una aplicación de juegos, para consultar las partidas de un torneo por torneo, región y ronda, puede crear una clave de partición GSI sintética, como TOURNAMENT\$1WINTER2024\$1REGION\$1NA-EAST y una clave de clasificación sintética, como ROUND\$1SEMIFINALS\$1BRACKET\$1UPPER. Este enfoque funciona, pero requiere la concatenación de cadenas al escribir datos, el análisis al leer y el relleno de claves sintéticas en todos los elementos existentes si va a agregar el GSI a una tabla existente. Esto hace que el código sea más desordenado y difícil de mantener la seguridad tipográfica en los componentes clave individuales.

Las claves de varios atributos resuelven este problema para los GSI. La clave de partición de GSI se define mediante varios atributos existentes, como el tournamentId y la región. DynamoDB gestiona automáticamente la lógica de claves compuestas y las agrupa para la distribución de los datos. Los elementos se escriben con los atributos naturales del modelo de dominio y el GSI los indexa automáticamente. Sin concatenación, sin análisis, sin relleno. El código se mantiene limpio, los datos se mantienen escritos y las consultas se mantienen sencillas. Este enfoque resulta especialmente útil cuando se dispone de datos jerárquicos con agrupaciones de atributos naturales (como torneo → región → ronda u organización → departamento → equipo).

## Ejemplo de aplicación
<a name="GSI.DesignPattern.MultiAttributeKeys.ApplicationExample"></a>

Esta guía explica cómo crear un sistema de seguimiento de partidos de torneos para una plataforma de deportes electrónicos. La plataforma necesita consultar los partidos de manera eficiente en múltiples dimensiones: por torneo y región para administrar los grupos, por jugador para ver el historial de partidos y por fecha para programarlos.

## Modelo de datos
<a name="GSI.DesignPattern.MultiAttributeKeys.DataModel"></a>

En este tutorial, el sistema de seguimiento de los partidos del torneo admite tres patrones de acceso principales, cada uno de los cuales requiere una estructura de clave diferente:

**Patrón de acceso 1:** busque un partido específico por su ID único
+ **Solución:** tabla base con `matchId` como clave de partición

**Patrón de acceso 2:** consulte todos los partidos de un torneo y una región específicos y, si lo prefiere, filtre por ronda, cuadro o partido
+ **Solución:** índice secundario global con clave de partición de varios atributos (`tournamentId` \$1 `region`) y clave de clasificación de varios atributos (`round` \$1 `bracket` \$1 `matchId`)
+ **Consultas de ejemplo:** “Todos los partidos de WINTER2024 en la región NA-EAST” o “Todos los partidos de SEMIFINALES en el corchete SUPERIOR para WINTER2024/NA-EAST”

**Patrón de acceso 3:** consulte el historial de partidos de un jugador o, si lo prefiere, filtre por intervalo de fechas o por ronda del torneo
+ **Solución:** índice secundario global con clave de partición única (`player1Id`) y clave de clasificación de varios atributos (`matchDate` \$1 `round`)
+ **Consultas de ejemplo:** “Todos los partidos del jugador 101” o “Los partidos del jugador 101 en enero de 2024”

La diferencia clave entre el enfoque tradicional y el de varios atributos queda clara al examinar la estructura de los elementos:

**Enfoque de índice secundario global tradicional (claves concatenadas):**

```
// Manual concatenation required for GSI keys
const item = {
    matchId: 'match-001',                                          // Base table PK
    tournamentId: 'WINTER2024',
    region: 'NA-EAST',
    round: 'SEMIFINALS',
    bracket: 'UPPER',
    player1Id: '101',
    // Synthetic keys needed for GSI
    GSI_PK: `TOURNAMENT#${tournamentId}#REGION#${region}`,       // Must concatenate
    GSI_SK: `${round}#${bracket}#${matchId}`,                    // Must concatenate
    // ... other attributes
};
```

**Enfoque de índice secundario global de varios atributos (claves nativas):**

```
// Use existing attributes directly - no concatenation needed
const item = {
    matchId: 'match-001',                                          // Base table PK
    tournamentId: 'WINTER2024',
    region: 'NA-EAST',
    round: 'SEMIFINALS',
    bracket: 'UPPER',
    player1Id: '101',
    matchDate: '2024-01-18',
    // No synthetic keys needed - GSI uses existing attributes directly
    // ... other attributes
};
```

Con las claves de varios atributos, los elementos se escriben una sola vez con atributos de dominio naturales. DynamoDB los indexa automáticamente en varios GSI sin necesidad de claves concatenadas sintéticas.

**Esquema de tabla base:**
+ Clave de partición: `matchId` (1 atributo)

**Esquema de índice secundario global (TournamentRegionIndex con claves de varios atributos):**
+ Clave de partición: `tournamentId`, `region` (2 atributos)
+ Clave de clasificación: `round`, `bracket`, `matchId` (3 atributos)

**Esquema de índice secundario global (PlayerMatchHistoryIndex con claves de varios atributos):**
+ Clave de partición: `player1Id` (1 atributo)
+ Clave de clasificación: `matchDate`, `round` (2 atributos)

### Tabla base: TournamentMatches
<a name="GSI.DesignPattern.MultiAttributeKeys.BaseTable"></a>


| matchId (PK) | tournamentId | region | round | soporte | player1Id | player2Id | matchDate | ganador | puntuación | 
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | 
| match-001 | WINTER2024 | NA-EAST | FINALES | CAMPEONATO MUNDIAL | 101 | 103 | 2024-01-20 | 101 | 3-1 | 
| match-002 | WINTER2024 | NA-EAST | SEMIFINALES | UPPER | 101 | 105 | 2024-01-18 | 101 | 3-2 | 
| match-003 | WINTER2024 | NA-EAST | SEMIFINALES | UPPER | 103 | 107 | 2024-01-18 | 103 | 3-0 | 
| match-004 | WINTER2024 | NA-EAST | CUARTOS DE FINAL | UPPER | 101 | 109 | 2024-01-15 | 101 | 3-1 | 
| match-005 | WINTER2024 | NA-WEST | FINALES | CAMPEONATO MUNDIAL | 102 | 104 | 2024-01-20 | 102 | 3-2 | 
| match-006 | WINTER2024 | NA-WEST | SEMIFINALES | UPPER | 102 | 106 | 2024-01-18 | 102 | 3-1 | 
| match-007 | SPRING2024 | NA-EAST | CUARTOS DE FINAL | UPPER | 101 | 108 | 2024-03-15 | 101 | 3-0 | 
| match-008 | SPRING2024 | NA-EAST | CUARTOS DE FINAL | LOWER | 103 | 110 | 2024-03-15 | 103 | 3-2 | 

### GSI: TournamentRegionIndex (claves de varios atributos)
<a name="GSI.DesignPattern.MultiAttributeKeys.TournamentRegionIndexTable"></a>


| tournamentId (PK) | región (PK) | ronda (SK) | soporte (SK) | matchId (SK) | player1Id | player2Id | matchDate | ganador | puntuación | 
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | 
| WINTER2024 | NA-EAST | FINALES | CAMPEONATO MUNDIAL | match-001 | 101 | 103 | 2024-01-20 | 101 | 3-1 | 
| WINTER2024 | NA-EAST | CUARTOS DE FINAL | UPPER | match-004 | 101 | 109 | 2024-01-15 | 101 | 3-1 | 
| WINTER2024 | NA-EAST | SEMIFINALES | UPPER | match-002 | 101 | 105 | 2024-01-18 | 101 | 3-2 | 
| WINTER2024 | NA-EAST | SEMIFINALES | UPPER | match-003 | 103 | 107 | 2024-01-18 | 103 | 3-0 | 
| WINTER2024 | NA-WEST | FINALES | CAMPEONATO MUNDIAL | match-005 | 102 | 104 | 2024-01-20 | 102 | 3-2 | 
| WINTER2024 | NA-WEST | SEMIFINALES | UPPER | match-006 | 102 | 106 | 2024-01-18 | 102 | 3-1 | 
| SPRING2024 | NA-EAST | CUARTOS DE FINAL | LOWER | match-008 | 103 | 110 | 2024-03-15 | 103 | 3-2 | 
| SPRING2024 | NA-EAST | CUARTOS DE FINAL | UPPER | match-007 | 101 | 108 | 2024-03-15 | 101 | 3-0 | 

### GSI: PlayerMatchHistoryIndex (claves de varios atributos)
<a name="GSI.DesignPattern.MultiAttributeKeys.PlayerMatchHistoryIndexTable"></a>


| player1Id (PK) | matchDate (SK) | ronda (SK) | tournamentId | region | soporte | matchId | player2Id | ganador | puntuación | 
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | 
| 101 | 2024-01-15 | CUARTOS DE FINAL | WINTER2024 | NA-EAST | UPPER | match-004 | 109 | 101 | 3-1 | 
| 101 | 2024-01-18 | SEMIFINALES | WINTER2024 | NA-EAST | UPPER | match-002 | 105 | 101 | 3-2 | 
| 101 | 2024-01-20 | FINALES | WINTER2024 | NA-EAST | CAMPEONATO MUNDIAL | match-001 | 103 | 101 | 3-1 | 
| 101 | 2024-03-15 | CUARTOS DE FINAL | SPRING2024 | NA-EAST | UPPER | match-007 | 108 | 101 | 3-0 | 
| 102 | 2024-01-18 | SEMIFINALES | WINTER2024 | NA-WEST | UPPER | match-006 | 106 | 102 | 3-1 | 
| 102 | 2024-01-20 | FINALES | WINTER2024 | NA-WEST | CAMPEONATO MUNDIAL | match-005 | 104 | 102 | 3-2 | 
| 103 | 2024-01-18 | SEMIFINALES | WINTER2024 | NA-EAST | UPPER | match-003 | 107 | 103 | 3-0 | 
| 103 | 2024-03-15 | CUARTOS DE FINAL | SPRING2024 | NA-EAST | LOWER | match-008 | 110 | 103 | 3-2 | 

## Requisitos previos
<a name="GSI.DesignPattern.MultiAttributeKeys.Prerequisites"></a>

Antes de comenzar, asegúrese de que dispone de lo siguiente:

### Permisos y cuenta
<a name="GSI.DesignPattern.MultiAttributeKeys.Prerequisites.AWSAccount"></a>
+ Una cuenta de AWS activa ([cree una aquí](https://aws.amazon.com/free/) si es necesario)
+ Permisos de IAM para las operaciones de DynamoDB:
  + `dynamodb:CreateTable`
  + `dynamodb:DeleteTable`
  + `dynamodb:DescribeTable`
  + `dynamodb:PutItem`
  + `dynamodb:Query`
  + `dynamodb:BatchWriteItem`

**nota**  
**Nota de seguridad:** para uso de producción, cree una política de IAM personalizada con solo los permisos que necesite. Para este tutorial, puede usar la política administrada de AWS `AmazonDynamoDBFullAccessV2`.

### Entorno de desarrollo
<a name="GSI.DesignPattern.MultiAttributeKeys.Prerequisites.DevEnvironment"></a>
+ Node.js se ha instalado en el equipo
+ Credenciales de AWS configuradas mediante uno de estos métodos:

**Opción 1: CLI de AWS**

```
aws configure
```

**Opción 2: Variables de entorno**

```
export AWS_ACCESS_KEY_ID=your_access_key_here
export AWS_SECRET_ACCESS_KEY=your_secret_key_here
export AWS_DEFAULT_REGION=us-east-1
```

### Instalación de los paquetes obligatorios
<a name="GSI.DesignPattern.MultiAttributeKeys.Prerequisites.InstallPackages"></a>

```
npm install @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb
```

## Implementación
<a name="GSI.DesignPattern.MultiAttributeKeys.Implementation"></a>

### Paso 1: creación de una tabla con los GSI mediante claves de varios atributos
<a name="GSI.DesignPattern.MultiAttributeKeys.CreateTable"></a>

Cree una tabla con una estructura de clave básica simple y con GSI que utilicen claves de varios atributos.

#### Ejemplo de código
<a name="w2aac19c13c45c23b9c11b3b5b1"></a>

```
import { DynamoDBClient, CreateTableCommand } from "@aws-sdk/client-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });

const response = await client.send(new CreateTableCommand({
    TableName: 'TournamentMatches',
    
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'matchId', KeyType: 'HASH' }              // Simple PK
    ],
    
    AttributeDefinitions: [
        { AttributeName: 'matchId', AttributeType: 'S' },
        { AttributeName: 'tournamentId', AttributeType: 'S' },
        { AttributeName: 'region', AttributeType: 'S' },
        { AttributeName: 'round', AttributeType: 'S' },
        { AttributeName: 'bracket', AttributeType: 'S' },
        { AttributeName: 'player1Id', AttributeType: 'S' },
        { AttributeName: 'matchDate', AttributeType: 'S' }
    ],
    
    // GSIs with multi-attribute keys
    GlobalSecondaryIndexes: [
        {
            IndexName: 'TournamentRegionIndex',
            KeySchema: [
                { AttributeName: 'tournamentId', KeyType: 'HASH' },    // GSI PK attribute 1
                { AttributeName: 'region', KeyType: 'HASH' },          // GSI PK attribute 2
                { AttributeName: 'round', KeyType: 'RANGE' },          // GSI SK attribute 1
                { AttributeName: 'bracket', KeyType: 'RANGE' },        // GSI SK attribute 2
                { AttributeName: 'matchId', KeyType: 'RANGE' }         // GSI SK attribute 3
            ],
            Projection: { ProjectionType: 'ALL' }
        },
        {
            IndexName: 'PlayerMatchHistoryIndex',
            KeySchema: [
                { AttributeName: 'player1Id', KeyType: 'HASH' },       // GSI PK
                { AttributeName: 'matchDate', KeyType: 'RANGE' },      // GSI SK attribute 1
                { AttributeName: 'round', KeyType: 'RANGE' }           // GSI SK attribute 2
            ],
            Projection: { ProjectionType: 'ALL' }
        }
    ],
    
    BillingMode: 'PAY_PER_REQUEST'
}));

console.log("Table with multi-attribute GSI keys created successfully");
```

**Decisiones de diseño de clave:**

**Tabla base:** la tabla base utiliza una clave de partición de `matchId` simple para las búsquedas de coincidencias directas, lo que mantiene la estructura de la tabla base sencilla, mientras que los GSI proporcionan patrones de consulta complejos.

**Índice secundario global TournamentRegionIndex**: el índice secundario global `TournamentRegionIndex` utiliza `tournamentId` \$1 `region` como clave de partición con varios atributos, lo que aísla la región del torneo, ya que los datos se distribuyen mediante el hash de ambos atributos combinados, lo que permite realizar consultas eficaces dentro de un contexto específico de una región o torneo. La clave de clasificación de varios atributos (`round` \$1 `bracket` \$1 `matchId`) proporciona una clasificación jerárquica que permite realizar consultas en cualquier nivel de la jerarquía con un orden natural, desde general (ronda) hasta específico (ID de partido).

**Índice secundario global PlayerMatchHistoryIndex:** el índice secundario global `PlayerMatchHistoryIndex` reorganiza los datos por jugador usando `player1Id` como clave de partición, lo que permite realizar consultas entre torneos para un jugador específico. La clave de clasificación con varios atributos (`matchDate` \$1 `round`) proporciona un orden cronológico y permite filtrar por intervalos de fechas o rondas específicas del torneo.

### Paso 2: insertar datos con atributos nativos
<a name="GSI.DesignPattern.MultiAttributeKeys.InsertData"></a>

Agrega los datos de los partidos del torneo con atributos naturales. El GSI indexará automáticamente estos atributos sin necesidad de claves sintéticas.

#### Ejemplo de código
<a name="w2aac19c13c45c23b9c11b5b5b1"></a>

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, PutCommand } from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

// Tournament match data - no synthetic keys needed for GSIs
const matches = [
    // Winter 2024 Tournament, NA-EAST region
    {
        matchId: 'match-001',
        tournamentId: 'WINTER2024',
        region: 'NA-EAST',
        round: 'FINALS',
        bracket: 'CHAMPIONSHIP',
        player1Id: '101',
        player2Id: '103',
        matchDate: '2024-01-20',
        winner: '101',
        score: '3-1'
    },
    {
        matchId: 'match-002',
        tournamentId: 'WINTER2024',
        region: 'NA-EAST',
        round: 'SEMIFINALS',
        bracket: 'UPPER',
        player1Id: '101',
        player2Id: '105',
        matchDate: '2024-01-18',
        winner: '101',
        score: '3-2'
    },
    {
        matchId: 'match-003',
        tournamentId: 'WINTER2024',
        region: 'NA-EAST',
        round: 'SEMIFINALS',
        bracket: 'UPPER',
        player1Id: '103',
        player2Id: '107',
        matchDate: '2024-01-18',
        winner: '103',
        score: '3-0'
    },
    {
        matchId: 'match-004',
        tournamentId: 'WINTER2024',
        region: 'NA-EAST',
        round: 'QUARTERFINALS',
        bracket: 'UPPER',
        player1Id: '101',
        player2Id: '109',
        matchDate: '2024-01-15',
        winner: '101',
        score: '3-1'
    },
    
    // Winter 2024 Tournament, NA-WEST region
    {
        matchId: 'match-005',
        tournamentId: 'WINTER2024',
        region: 'NA-WEST',
        round: 'FINALS',
        bracket: 'CHAMPIONSHIP',
        player1Id: '102',
        player2Id: '104',
        matchDate: '2024-01-20',
        winner: '102',
        score: '3-2'
    },
    {
        matchId: 'match-006',
        tournamentId: 'WINTER2024',
        region: 'NA-WEST',
        round: 'SEMIFINALS',
        bracket: 'UPPER',
        player1Id: '102',
        player2Id: '106',
        matchDate: '2024-01-18',
        winner: '102',
        score: '3-1'
    },
    
    // Spring 2024 Tournament, NA-EAST region
    {
        matchId: 'match-007',
        tournamentId: 'SPRING2024',
        region: 'NA-EAST',
        round: 'QUARTERFINALS',
        bracket: 'UPPER',
        player1Id: '101',
        player2Id: '108',
        matchDate: '2024-03-15',
        winner: '101',
        score: '3-0'
    },
    {
        matchId: 'match-008',
        tournamentId: 'SPRING2024',
        region: 'NA-EAST',
        round: 'QUARTERFINALS',
        bracket: 'LOWER',
        player1Id: '103',
        player2Id: '110',
        matchDate: '2024-03-15',
        winner: '103',
        score: '3-2'
    }
];

// Insert all matches
for (const match of matches) {
    await docClient.send(new PutCommand({
        TableName: 'TournamentMatches',
        Item: match
    }));
    
    console.log(`Added: ${match.matchId} - ${match.tournamentId}/${match.region} - ${match.round} ${match.bracket}`);
}

console.log(`\nInserted ${matches.length} tournament matches`);
console.log("No synthetic keys created - GSIs use native attributes automatically");
```

**Explicación de la estructura de datos:**

**Uso de atributos naturales:** cada atributo representa un concepto de torneo real sin necesidad de concatenar cadenas ni analizar, lo que proporciona una asignación directa al modelo de dominio.

**Indexación automática del índice secundario global:** los GSI indexan automáticamente los elementos utilizando los atributos existentes (`tournamentId`, `region`, `round`, `bracket`, `matchId` para TournamentRegionIndex y `player1Id`, `matchDate`, `round` para PlayerMatchHistoryIndex) sin necesidad de claves concatenadas sintéticas.

**No es necesario rellenar:** al agregar un nuevo índice secundario global con claves de varios atributos a una tabla existente, DynamoDB indexa automáticamente todos los elementos existentes con sus atributos naturales, sin necesidad de actualizar los elementos con claves sintéticas.

### Paso 3: consulte el índice secundario global de TournamentRegionIndex con todos los atributos de las claves de partición
<a name="GSI.DesignPattern.MultiAttributeKeys.QueryAllPartitionKeys"></a>

En este ejemplo, se consulta el índice secundario global TournamentRegionIndex, que tiene una clave de partición de varios atributos (`tournamentId` \$1 `region`). Todos los atributos de la clave de partición se deben especificar con condiciones de igualdad en las consultas; no se pueden consultar solo con `tournamentId` ni utilizar operadores de desigualdad en los atributos de la clave de partición.

#### Ejemplo de código
<a name="w2aac19c13c45c23b9c11b7b5b1"></a>

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

// Query GSI: All matches for WINTER2024 tournament in NA-EAST region
const response = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region',
    ExpressionAttributeNames: {
        '#region': 'region',  // 'region' is a reserved keyword
        '#tournament': 'tournament'
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST'
    }
}));

console.log(`Found ${response.Items.length} matches for WINTER2024/NA-EAST:\n`);
response.Items.forEach(match => {
    console.log(`  ${match.round} | ${match.bracket} | ${match.matchId}`);
    console.log(`    Players: ${match.player1Id} vs ${match.player2Id}`);
    console.log(`    Winner: ${match.winner}, Score: ${match.score}\n`);
});
```

**Resultado previsto:**

```
Found 4 matches for WINTER2024/NA-EAST:

  FINALS | CHAMPIONSHIP | match-001
    Players: 101 vs 103
    Winner: 101, Score: 3-1

  QUARTERFINALS | UPPER | match-004
    Players: 101 vs 109
    Winner: 101, Score: 3-1

  SEMIFINALS | UPPER | match-002
    Players: 101 vs 105
    Winner: 101, Score: 3-2

  SEMIFINALS | UPPER | match-003
    Players: 103 vs 107
    Winner: 103, Score: 3-0
```

**Consultas no válidas:**

```
// Missing region attribute
KeyConditionExpression: 'tournamentId = :tournament'

// Using inequality on partition key attribute
KeyConditionExpression: 'tournamentId = :tournament AND #region > :region'
```

**Rendimiento:** las claves de partición de varios atributos se codifican entre sí, lo que proporciona el mismo rendimiento de búsqueda O(1) que las claves de un solo atributo.

### Paso 4: consulte las claves de clasificación del índice secundario global de izquierda a derecha
<a name="GSI.DesignPattern.MultiAttributeKeys.QuerySortKeysLeftToRight"></a>

Los atributos de clave de clasificación se deben consultar de izquierda a derecha en el orden en que están definidos en el índice secundario global. En este ejemplo, se muestra cómo consultar el TournamentRegionIndex en diferentes niveles jerárquicos: filtrando solo por `round`, por `round` \$1 `bracket`, o por los tres atributos de clave de clasificación. No puede omitir los atributos que están en el centro; por ejemplo, no puede consultar `round` y `matchId` mientras se omite `bracket`.

#### Ejemplo de código
<a name="w2aac19c13c45c23b9c11b9b5b1"></a>

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

// Query 1: Filter by first sort key attribute (round)
console.log("Query 1: All SEMIFINALS matches");
const query1 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round = :round',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':round': 'SEMIFINALS'
    }
}));
console.log(`  Found ${query1.Items.length} matches\n`);

// Query 2: Filter by first two sort key attributes (round + bracket)
console.log("Query 2: SEMIFINALS UPPER bracket matches");
const query2 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round = :round AND bracket = :bracket',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':round': 'SEMIFINALS',
        ':bracket': 'UPPER'
    }
}));
console.log(`  Found ${query2.Items.length} matches\n`);

// Query 3: Filter by all three sort key attributes (round + bracket + matchId)
console.log("Query 3: Specific match in SEMIFINALS UPPER bracket");
const query3 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round = :round AND bracket = :bracket AND matchId = :matchId',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':round': 'SEMIFINALS',
        ':bracket': 'UPPER',
        ':matchId': 'match-002'
    }
}));
console.log(`  Found ${query3.Items.length} matches\n`);

// Query 4: INVALID - skipping round
console.log("Query 4: Attempting to skip first sort key attribute (WILL FAIL)");
try {
    const query4 = await docClient.send(new QueryCommand({
        TableName: 'TournamentMatches',
        IndexName: 'TournamentRegionIndex',
        KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND bracket = :bracket',
        ExpressionAttributeNames: {
            '#region': 'region'  // 'region' is a reserved keyword
        },
        ExpressionAttributeValues: {
            ':tournament': 'WINTER2024',
            ':region': 'NA-EAST',
            ':bracket': 'UPPER'
        }
    }));
} catch (error) {
    console.log(`  Error: ${error.message}`);
    console.log(`  Cannot skip sort key attributes - must query left-to-right\n`);
}
```

**Resultado previsto:**

```
Query 1: All SEMIFINALS matches
  Found 2 matches

Query 2: SEMIFINALS UPPER bracket matches
  Found 2 matches

Query 3: Specific match in SEMIFINALS UPPER bracket
  Found 1 matches

Query 4: Attempting to skip first sort key attribute (WILL FAIL)
  Error: Query key condition not supported
  Cannot skip sort key attributes - must query left-to-right
```

**Reglas de consulta de izquierda a derecha:** debe consultar los atributos en orden de izquierda a derecha, sin omitir ninguno.

**Patrones validos:**
+ Solo el primer atributo: `round = 'SEMIFINALS'`
+ Los dos primeros atributos: `round = 'SEMIFINALS' AND bracket = 'UPPER'`
+ Los tres atributos: `round = 'SEMIFINALS' AND bracket = 'UPPER' AND matchId = 'match-002'`

**Patrones no válidos:**
+ Omisión del primer atributo: `bracket = 'UPPER'` (se salta)
+ La consulta está fuera de orden: `matchId = 'match-002' AND round = 'SEMIFINALS'`
+ Dejar huecos: `round = 'SEMIFINALS' AND matchId = 'match-002'` (se salta el corchete)

**nota**  
**Consejo de diseño:** ordene los atributos de la clave de clasificación desde los más generales hasta los más específicos para maximizar la flexibilidad de las consultas.

### Paso 5: uso de las condiciones de desigualdad en las claves de clasificación del índice secundario global
<a name="GSI.DesignPattern.MultiAttributeKeys.InequalityConditions"></a>

Las condiciones de desigualdad deben ser la última condición de la consulta. En este ejemplo, se muestra el uso de los operadores de comparación (`>=`, `BETWEEN`) y la coincidencia de prefijos (`begins_with()`) en los atributos de las claves de clasificación. Una vez que utilice un operador de desigualdad, no podrá agregar ninguna condición de clave de clasificación adicional después de este; la desigualdad debe ser la condición final de la expresión de condición de la clave.

#### Ejemplo de código
<a name="w2aac19c13c45c23b9c11c11b5b1"></a>

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

// Query 1: Round comparison (inequality on first sort key attribute)
console.log("Query 1: Matches from QUARTERFINALS onwards");
const query1 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round >= :round',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':round': 'QUARTERFINALS'
    }
}));
console.log(`  Found ${query1.Items.length} matches\n`);

// Query 2: Round range with BETWEEN
console.log("Query 2: Matches between QUARTERFINALS and SEMIFINALS");
const query2 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round BETWEEN :start AND :end',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':start': 'QUARTERFINALS',
        ':end': 'SEMIFINALS'
    }
}));
console.log(`  Found ${query2.Items.length} matches\n`);

// Query 3: Prefix matching with begins_with (treated as inequality)
console.log("Query 3: Matches in brackets starting with 'U'");
const query3 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round = :round AND begins_with(bracket, :prefix)',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':round': 'SEMIFINALS',
        ':prefix': 'U'
    }
}));
console.log(`  Found ${query3.Items.length} matches\n`);

// Query 4: INVALID - condition after inequality
console.log("Query 4: Attempting condition after inequality (WILL FAIL)");
try {
    const query4 = await docClient.send(new QueryCommand({
        TableName: 'TournamentMatches',
        IndexName: 'TournamentRegionIndex',
        KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round > :round AND bracket = :bracket',
        ExpressionAttributeNames: {
            '#region': 'region'  // 'region' is a reserved keyword
        },
        ExpressionAttributeValues: {
            ':tournament': 'WINTER2024',
            ':region': 'NA-EAST',
            ':round': 'QUARTERFINALS',
            ':bracket': 'UPPER'
        }
    }));
} catch (error) {
    console.log(`  Error: ${error.message}`);
    console.log(`  Cannot add conditions after inequality - it must be last\n`);
}
```

**Reglas de operadores de desigualdad:** puede usar operadores de comparación (`>`, `>=`, `<`, `<=`) `BETWEEN` para consultas de rango y `begins_with()` para hacer coincidir prefijos. La desigualdad deben ser la última condición de la consulta.

**Patrones validos:**
+ Condiciones de igualdad seguidas de desigualdad: `round = 'SEMIFINALS' AND bracket = 'UPPER' AND matchId > 'match-001'`
+ Desigualdad en el primer atributo: `round BETWEEN 'QUARTERFINALS' AND 'SEMIFINALS'`
+ La coincidencia de prefijos como condición final: `round = 'SEMIFINALS' AND begins_with(bracket, 'U')`

**Patrones no válidos:**
+ Agregación de condiciones después de una desigualdad: `round > 'QUARTERFINALS' AND bracket = 'UPPER'`
+ Uso de múltiples desigualdades: `round > 'QUARTERFINALS' AND bracket > 'L'`

**importante**  
`begins_with()` se trata como una condición de desigualdad, por lo que no puede seguirla ninguna condición de clave de clasificación adicional.

### Paso 6: consulta del índice secundario global PlayerMatchHistoryIndex con la clave de clasificación de varios atributos
<a name="GSI.DesignPattern.MultiAttributeKeys.QueryPlayerHistory"></a>

En este ejemplo, se consulta el PlayerMatchHistoryIndex, que tiene una clave de partición única (`player1Id`) y una clave de clasificación de varios atributos (`matchDate` \$1 `round`). Esto permite analizar todos los torneos consultando todos los partidos de un jugador específico sin conocer los ID del torneo, mientras que la tabla base requeriría consultas independientes por combinación de torneo y región.

#### Ejemplo de código
<a name="w2aac19c13c45c23b9c11c13b5b1"></a>

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

// Query 1: All matches for Player 101 across all tournaments
console.log("Query 1: All matches for Player 101");
const query1 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'PlayerMatchHistoryIndex',
    KeyConditionExpression: 'player1Id = :player',
    ExpressionAttributeValues: {
        ':player': '101'
    }
}));

console.log(`  Found ${query1.Items.length} matches for Player 101:`);
query1.Items.forEach(match => {
    console.log(`    ${match.tournamentId}/${match.region} - ${match.matchDate} - ${match.round}`);
});
console.log();

// Query 2: Player 101 matches on specific date
console.log("Query 2: Player 101 matches on 2024-01-18");
const query2 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'PlayerMatchHistoryIndex',
    KeyConditionExpression: 'player1Id = :player AND matchDate = :date',
    ExpressionAttributeValues: {
        ':player': '101',
        ':date': '2024-01-18'
    }
}));

console.log(`  Found ${query2.Items.length} matches\n`);

// Query 3: Player 101 SEMIFINALS matches on specific date
console.log("Query 3: Player 101 SEMIFINALS matches on 2024-01-18");
const query3 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'PlayerMatchHistoryIndex',
    KeyConditionExpression: 'player1Id = :player AND matchDate = :date AND round = :round',
    ExpressionAttributeValues: {
        ':player': '101',
        ':date': '2024-01-18',
        ':round': 'SEMIFINALS'
    }
}));

console.log(`  Found ${query3.Items.length} matches\n`);

// Query 4: Player 101 matches in date range
console.log("Query 4: Player 101 matches in January 2024");
const query4 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'PlayerMatchHistoryIndex',
    KeyConditionExpression: 'player1Id = :player AND matchDate BETWEEN :start AND :end',
    ExpressionAttributeValues: {
        ':player': '101',
        ':start': '2024-01-01',
        ':end': '2024-01-31'
    }
}));

console.log(`  Found ${query4.Items.length} matches\n`);
```

## Variaciones de patrones
<a name="GSI.DesignPattern.MultiAttributeKeys.PatternVariations"></a>

### Datos de series temporales con claves de varios atributos
<a name="GSI.DesignPattern.MultiAttributeKeys.TimeSeries"></a>

Optimización de las consultas de series temporales con atributos temporales jerárquicos

#### Ejemplo de código
<a name="w2aac19c13c45c23b9c13b3b5b1"></a>

```
{
    TableName: 'IoTReadings',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'readingId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'readingId', AttributeType: 'S' },
        { AttributeName: 'deviceId', AttributeType: 'S' },
        { AttributeName: 'locationId', AttributeType: 'S' },
        { AttributeName: 'year', AttributeType: 'S' },
        { AttributeName: 'month', AttributeType: 'S' },
        { AttributeName: 'day', AttributeType: 'S' },
        { AttributeName: 'timestamp', AttributeType: 'S' }
    ],
    // GSI with multi-attribute keys for time-series queries
    GlobalSecondaryIndexes: [{
        IndexName: 'DeviceLocationTimeIndex',
        KeySchema: [
            { AttributeName: 'deviceId', KeyType: 'HASH' },
            { AttributeName: 'locationId', KeyType: 'HASH' },
            { AttributeName: 'year', KeyType: 'RANGE' },
            { AttributeName: 'month', KeyType: 'RANGE' },
            { AttributeName: 'day', KeyType: 'RANGE' },
            { AttributeName: 'timestamp', KeyType: 'RANGE' }
        ],
        Projection: { ProjectionType: 'ALL' }
    }],
    BillingMode: 'PAY_PER_REQUEST'
}

// Query patterns enabled via GSI:
// - All readings for device in location
// - Readings for specific year
// - Readings for specific month in year
// - Readings for specific day
// - Readings in time range
```

**Beneficios:** la jerarquía temporal natural (año → mes → día → marca temporal) permite realizar consultas eficientes con una granularidad en cualquier momento sin necesidad de analizar ni manipular la fecha. El índice secundario global indexa automáticamente todas las lecturas con sus atributos de tiempo natural.

### Pedidos de comercio electrónico con claves de varios atributos
<a name="GSI.DesignPattern.MultiAttributeKeys.ECommerce"></a>

Realización de un seguimiento de los pedidos con múltiples dimensiones

#### Ejemplo de código
<a name="w2aac19c13c45c23b9c13b5b5b1"></a>

```
{
    TableName: 'Orders',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'orderId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'orderId', AttributeType: 'S' },
        { AttributeName: 'sellerId', AttributeType: 'S' },
        { AttributeName: 'region', AttributeType: 'S' },
        { AttributeName: 'orderDate', AttributeType: 'S' },
        { AttributeName: 'category', AttributeType: 'S' },
        { AttributeName: 'customerId', AttributeType: 'S' },
        { AttributeName: 'orderStatus', AttributeType: 'S' }
    ],
    GlobalSecondaryIndexes: [
        {
            IndexName: 'SellerRegionIndex',
            KeySchema: [
                { AttributeName: 'sellerId', KeyType: 'HASH' },
                { AttributeName: 'region', KeyType: 'HASH' },
                { AttributeName: 'orderDate', KeyType: 'RANGE' },
                { AttributeName: 'category', KeyType: 'RANGE' },
                { AttributeName: 'orderId', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        },
        {
            IndexName: 'CustomerOrdersIndex',
            KeySchema: [
                { AttributeName: 'customerId', KeyType: 'HASH' },
                { AttributeName: 'orderDate', KeyType: 'RANGE' },
                { AttributeName: 'orderStatus', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        }
    ],
    BillingMode: 'PAY_PER_REQUEST'
}

// SellerRegionIndex GSI queries:
// - Orders by seller and region
// - Orders by seller, region, and date
// - Orders by seller, region, date, and category

// CustomerOrdersIndex GSI queries:
// - Customer's orders
// - Customer's orders by date
// - Customer's orders by date and status
```

### Datos de organización jerárquica
<a name="GSI.DesignPattern.MultiAttributeKeys.Hierarchical"></a>

Jerarquías organizativas de modelos

#### Ejemplo de código
<a name="w2aac19c13c45c23b9c13b7b5b1"></a>

```
{
    TableName: 'Employees',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'employeeId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'employeeId', AttributeType: 'S' },
        { AttributeName: 'companyId', AttributeType: 'S' },
        { AttributeName: 'divisionId', AttributeType: 'S' },
        { AttributeName: 'departmentId', AttributeType: 'S' },
        { AttributeName: 'teamId', AttributeType: 'S' },
        { AttributeName: 'skillCategory', AttributeType: 'S' },
        { AttributeName: 'skillLevel', AttributeType: 'S' },
        { AttributeName: 'yearsExperience', AttributeType: 'N' }
    ],
    GlobalSecondaryIndexes: [
        {
            IndexName: 'OrganizationIndex',
            KeySchema: [
                { AttributeName: 'companyId', KeyType: 'HASH' },
                { AttributeName: 'divisionId', KeyType: 'HASH' },
                { AttributeName: 'departmentId', KeyType: 'RANGE' },
                { AttributeName: 'teamId', KeyType: 'RANGE' },
                { AttributeName: 'employeeId', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        },
        {
            IndexName: 'SkillsIndex',
            KeySchema: [
                { AttributeName: 'skillCategory', KeyType: 'HASH' },
                { AttributeName: 'skillLevel', KeyType: 'RANGE' },
                { AttributeName: 'yearsExperience', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'INCLUDE', NonKeyAttributes: ['employeeId', 'name'] }
        }
    ],
    BillingMode: 'PAY_PER_REQUEST'
}

// OrganizationIndex GSI query patterns:
// - All employees in company/division
// - Employees in specific department
// - Employees in specific team

// SkillsIndex GSI query patterns:
// - Employees by skill and experience level
```

### Claves dispersas de varios atributos
<a name="GSI.DesignPattern.MultiAttributeKeys.Sparse"></a>

Combinación de claves de varios atributos para crear un GSI disperso

#### Ejemplo de código
<a name="w2aac19c13c45c23b9c13b9b5b1"></a>

```
{
    TableName: 'Products',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'productId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'productId', AttributeType: 'S' },
        { AttributeName: 'categoryId', AttributeType: 'S' },
        { AttributeName: 'subcategoryId', AttributeType: 'S' },
        { AttributeName: 'averageRating', AttributeType: 'N' },
        { AttributeName: 'reviewCount', AttributeType: 'N' }
    ],
    GlobalSecondaryIndexes: [
        {
            IndexName: 'CategoryIndex',
            KeySchema: [
                { AttributeName: 'categoryId', KeyType: 'HASH' },
                { AttributeName: 'subcategoryId', KeyType: 'HASH' },
                { AttributeName: 'productId', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        },
        {
            IndexName: 'ReviewedProductsIndex',
            KeySchema: [
                { AttributeName: 'categoryId', KeyType: 'HASH' },
                { AttributeName: 'averageRating', KeyType: 'RANGE' },  // Optional attribute
                { AttributeName: 'reviewCount', KeyType: 'RANGE' }     // Optional attribute
            ],
            Projection: { ProjectionType: 'ALL' }
        }
    ],
    BillingMode: 'PAY_PER_REQUEST'
}

// Only products with reviews appear in ReviewedProductsIndex GSI
// Automatic filtering without application logic
// Multi-attribute sort key enables rating and count queries
```

### Multitenencia de SaaS
<a name="GSI.DesignPattern.MultiAttributeKeys.SaaS"></a>

Plataforma SaaS multiusuario con aislamiento de clientes

#### Ejemplo de código
<a name="w2aac19c13c45c23b9c13c11b5b1"></a>

```
// Table design
{
    TableName: 'SaasData',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'resourceId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'resourceId', AttributeType: 'S' },
        { AttributeName: 'tenantId', AttributeType: 'S' },
        { AttributeName: 'customerId', AttributeType: 'S' },
        { AttributeName: 'resourceType', AttributeType: 'S' }
    ],
    // GSI with multi-attribute keys for tenant-customer isolation
    GlobalSecondaryIndexes: [{
        IndexName: 'TenantCustomerIndex',
        KeySchema: [
            { AttributeName: 'tenantId', KeyType: 'HASH' },
            { AttributeName: 'customerId', KeyType: 'HASH' },
            { AttributeName: 'resourceType', KeyType: 'RANGE' },
            { AttributeName: 'resourceId', KeyType: 'RANGE' }
        ],
        Projection: { ProjectionType: 'ALL' }
    }],
    BillingMode: 'PAY_PER_REQUEST'
}

// Query GSI: All resources for tenant T001, customer C001
const resources = await docClient.send(new QueryCommand({
    TableName: 'SaasData',
    IndexName: 'TenantCustomerIndex',
    KeyConditionExpression: 'tenantId = :tenant AND customerId = :customer',
    ExpressionAttributeValues: {
        ':tenant': 'T001',
        ':customer': 'C001'
    }
}));

// Query GSI: Specific resource type for tenant/customer
const documents = await docClient.send(new QueryCommand({
    TableName: 'SaasData',
    IndexName: 'TenantCustomerIndex',
    KeyConditionExpression: 'tenantId = :tenant AND customerId = :customer AND resourceType = :type',
    ExpressionAttributeValues: {
        ':tenant': 'T001',
        ':customer': 'C001',
        ':type': 'document'
    }
}));
```

**Beneficios:** consultas eficientes en el contexto de inquilino-cliente y organización natural de los datos.

### Transacciones financieras
<a name="GSI.DesignPattern.MultiAttributeKeys.Financial"></a>

Sistema bancario que rastrea las transacciones de las cuentas mediante GSI

#### Ejemplo de código
<a name="w2aac19c13c45c23b9c13c13b5b1"></a>

```
// Table design
{
    TableName: 'BankTransactions',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'transactionId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'transactionId', AttributeType: 'S' },
        { AttributeName: 'accountId', AttributeType: 'S' },
        { AttributeName: 'year', AttributeType: 'S' },
        { AttributeName: 'month', AttributeType: 'S' },
        { AttributeName: 'day', AttributeType: 'S' },
        { AttributeName: 'transactionType', AttributeType: 'S' }
    ],
    GlobalSecondaryIndexes: [
        {
            IndexName: 'AccountTimeIndex',
            KeySchema: [
                { AttributeName: 'accountId', KeyType: 'HASH' },
                { AttributeName: 'year', KeyType: 'RANGE' },
                { AttributeName: 'month', KeyType: 'RANGE' },
                { AttributeName: 'day', KeyType: 'RANGE' },
                { AttributeName: 'transactionId', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        },
        {
            IndexName: 'TransactionTypeIndex',
            KeySchema: [
                { AttributeName: 'accountId', KeyType: 'HASH' },
                { AttributeName: 'transactionType', KeyType: 'RANGE' },
                { AttributeName: 'year', KeyType: 'RANGE' },
                { AttributeName: 'month', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        }
    ],
    BillingMode: 'PAY_PER_REQUEST'
}

// Query AccountTimeIndex GSI: All transactions for account in 2023
const yearTransactions = await docClient.send(new QueryCommand({
    TableName: 'BankTransactions',
    IndexName: 'AccountTimeIndex',
    KeyConditionExpression: 'accountId = :account AND #year = :year',
    ExpressionAttributeNames: { '#year': 'year' },
    ExpressionAttributeValues: {
        ':account': 'ACC-12345',
        ':year': '2023'
    }
}));

// Query AccountTimeIndex GSI: Transactions in specific month
const monthTransactions = await docClient.send(new QueryCommand({
    TableName: 'BankTransactions',
    IndexName: 'AccountTimeIndex',
    KeyConditionExpression: 'accountId = :account AND #year = :year AND #month = :month',
    ExpressionAttributeNames: { '#year': 'year', '#month': 'month' },
    ExpressionAttributeValues: {
        ':account': 'ACC-12345',
        ':year': '2023',
        ':month': '11'
    }
}));

// Query TransactionTypeIndex GSI: Deposits in 2023
const deposits = await docClient.send(new QueryCommand({
    TableName: 'BankTransactions',
    IndexName: 'TransactionTypeIndex',
    KeyConditionExpression: 'accountId = :account AND transactionType = :type AND #year = :year',
    ExpressionAttributeNames: { '#year': 'year' },
    ExpressionAttributeValues: {
        ':account': 'ACC-12345',
        ':type': 'deposit',
        ':year': '2023'
    }
}));
```

## Ejemplo completo
<a name="GSI.DesignPattern.MultiAttributeKeys.CompleteExample"></a>

El siguiente ejemplo muestra las claves de varios atributos desde la configuración hasta la limpieza:

### Ejemplo de código
<a name="w2aac19c13c45c23b9c15b5b1"></a>

```
import { 
    DynamoDBClient, 
    CreateTableCommand, 
    DeleteTableCommand, 
    waitUntilTableExists 
} from "@aws-sdk/client-dynamodb";
import { 
    DynamoDBDocumentClient, 
    PutCommand, 
    QueryCommand 
} from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

async function multiAttributeKeysDemo() {
    console.log("Starting Multi-Attribute GSI Keys Demo\n");
    
    // Step 1: Create table with GSIs using multi-attribute keys
    console.log("1. Creating table with multi-attribute GSI keys...");
    await client.send(new CreateTableCommand({
        TableName: 'TournamentMatches',
        KeySchema: [
            { AttributeName: 'matchId', KeyType: 'HASH' }
        ],
        AttributeDefinitions: [
            { AttributeName: 'matchId', AttributeType: 'S' },
            { AttributeName: 'tournamentId', AttributeType: 'S' },
            { AttributeName: 'region', AttributeType: 'S' },
            { AttributeName: 'round', AttributeType: 'S' },
            { AttributeName: 'bracket', AttributeType: 'S' },
            { AttributeName: 'player1Id', AttributeType: 'S' },
            { AttributeName: 'matchDate', AttributeType: 'S' }
        ],
        GlobalSecondaryIndexes: [
            {
                IndexName: 'TournamentRegionIndex',
                KeySchema: [
                    { AttributeName: 'tournamentId', KeyType: 'HASH' },
                    { AttributeName: 'region', KeyType: 'HASH' },
                    { AttributeName: 'round', KeyType: 'RANGE' },
                    { AttributeName: 'bracket', KeyType: 'RANGE' },
                    { AttributeName: 'matchId', KeyType: 'RANGE' }
                ],
                Projection: { ProjectionType: 'ALL' }
            },
            {
                IndexName: 'PlayerMatchHistoryIndex',
                KeySchema: [
                    { AttributeName: 'player1Id', KeyType: 'HASH' },
                    { AttributeName: 'matchDate', KeyType: 'RANGE' },
                    { AttributeName: 'round', KeyType: 'RANGE' }
                ],
                Projection: { ProjectionType: 'ALL' }
            }
        ],
        BillingMode: 'PAY_PER_REQUEST'
    }));
    
    await waitUntilTableExists({ client, maxWaitTime: 120 }, { TableName: 'TournamentMatches' });
    console.log("Table created\n");
    
    // Step 2: Insert tournament matches
    console.log("2. Inserting tournament matches...");
    const matches = [
        { matchId: 'match-001', tournamentId: 'WINTER2024', region: 'NA-EAST', round: 'FINALS', bracket: 'CHAMPIONSHIP', player1Id: '101', player2Id: '103', matchDate: '2024-01-20', winner: '101', score: '3-1' },
        { matchId: 'match-002', tournamentId: 'WINTER2024', region: 'NA-EAST', round: 'SEMIFINALS', bracket: 'UPPER', player1Id: '101', player2Id: '105', matchDate: '2024-01-18', winner: '101', score: '3-2' },
        { matchId: 'match-003', tournamentId: 'WINTER2024', region: 'NA-WEST', round: 'FINALS', bracket: 'CHAMPIONSHIP', player1Id: '102', player2Id: '104', matchDate: '2024-01-20', winner: '102', score: '3-2' },
        { matchId: 'match-004', tournamentId: 'SPRING2024', region: 'NA-EAST', round: 'QUARTERFINALS', bracket: 'UPPER', player1Id: '101', player2Id: '108', matchDate: '2024-03-15', winner: '101', score: '3-0' }
    ];
    
    for (const match of matches) {
        await docClient.send(new PutCommand({ TableName: 'TournamentMatches', Item: match }));
    }
    console.log(`Inserted ${matches.length} tournament matches\n`);
    
    // Step 3: Query GSI with multi-attribute partition key
    console.log("3. Query TournamentRegionIndex GSI: WINTER2024/NA-EAST matches");
    const gsiQuery1 = await docClient.send(new QueryCommand({
        TableName: 'TournamentMatches',
        IndexName: 'TournamentRegionIndex',
        KeyConditionExpression: 'tournamentId = :tournament AND #region = :region',
        ExpressionAttributeNames: { '#region': 'region' },
        ExpressionAttributeValues: { ':tournament': 'WINTER2024', ':region': 'NA-EAST' }
    }));
    
    console.log(`  Found ${gsiQuery1.Items.length} matches:`);
    gsiQuery1.Items.forEach(match => {
        console.log(`    ${match.round} - ${match.bracket} - ${match.winner} won`);
    });
    
    // Step 4: Query GSI with multi-attribute sort key
    console.log("\n4. Query PlayerMatchHistoryIndex GSI: All matches for Player 101");
    const gsiQuery2 = await docClient.send(new QueryCommand({
        TableName: 'TournamentMatches',
        IndexName: 'PlayerMatchHistoryIndex',
        KeyConditionExpression: 'player1Id = :player',
        ExpressionAttributeValues: { ':player': '101' }
    }));
    
    console.log(`  Found ${gsiQuery2.Items.length} matches for Player 101:`);
    gsiQuery2.Items.forEach(match => {
        console.log(`    ${match.tournamentId}/${match.region} - ${match.matchDate} - ${match.round}`);
    });
    
    console.log("\nDemo complete");
    console.log("No synthetic keys needed - GSIs use native attributes automatically");
}

async function cleanup() {
    console.log("Deleting table...");
    await client.send(new DeleteTableCommand({ TableName: 'TournamentMatches' }));
    console.log("Table deleted");
}

// Run demo
multiAttributeKeysDemo().catch(console.error);

// Uncomment to cleanup:
// cleanup().catch(console.error);
```

**Estructura de código mínima**

### Ejemplo de código
<a name="w2aac19c13c45c23b9c15b9b1"></a>

```
// 1. Create table with GSI using multi-attribute keys
await client.send(new CreateTableCommand({
    TableName: 'MyTable',
    KeySchema: [
        { AttributeName: 'id', KeyType: 'HASH' }        // Simple base table PK
    ],
    AttributeDefinitions: [
        { AttributeName: 'id', AttributeType: 'S' },
        { AttributeName: 'attr1', AttributeType: 'S' },
        { AttributeName: 'attr2', AttributeType: 'S' },
        { AttributeName: 'attr3', AttributeType: 'S' },
        { AttributeName: 'attr4', AttributeType: 'S' }
    ],
    GlobalSecondaryIndexes: [{
        IndexName: 'MyGSI',
        KeySchema: [
            { AttributeName: 'attr1', KeyType: 'HASH' },    // GSI PK attribute 1
            { AttributeName: 'attr2', KeyType: 'HASH' },    // GSI PK attribute 2
            { AttributeName: 'attr3', KeyType: 'RANGE' },   // GSI SK attribute 1
            { AttributeName: 'attr4', KeyType: 'RANGE' }    // GSI SK attribute 2
        ],
        Projection: { ProjectionType: 'ALL' }
    }],
    BillingMode: 'PAY_PER_REQUEST'
}));

// 2. Insert items with native attributes (no concatenation needed for GSI)
await docClient.send(new PutCommand({
    TableName: 'MyTable',
    Item: {
        id: 'item-001',
        attr1: 'value1',
        attr2: 'value2',
        attr3: 'value3',
        attr4: 'value4',
        // ... other attributes
    }
}));

// 3. Query GSI with all partition key attributes
await docClient.send(new QueryCommand({
    TableName: 'MyTable',
    IndexName: 'MyGSI',
    KeyConditionExpression: 'attr1 = :v1 AND attr2 = :v2',
    ExpressionAttributeValues: {
        ':v1': 'value1',
        ':v2': 'value2'
    }
}));

// 4. Query GSI with sort key attributes (left-to-right)
await docClient.send(new QueryCommand({
    TableName: 'MyTable',
    IndexName: 'MyGSI',
    KeyConditionExpression: 'attr1 = :v1 AND attr2 = :v2 AND attr3 = :v3',
    ExpressionAttributeValues: {
        ':v1': 'value1',
        ':v2': 'value2',
        ':v3': 'value3'
    }
}));

// Note: If any attribute name is a DynamoDB reserved keyword, use ExpressionAttributeNames:
// KeyConditionExpression: 'attr1 = :v1 AND #attr2 = :v2'
// ExpressionAttributeNames: { '#attr2': 'attr2' }
```

## Recursos adicionales
<a name="GSI.DesignPattern.MultiAttributeKeys.AdditionalResources"></a>
+ [Prácticas recomendadas de DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/best-practices.html)
+ [Uso de tablas y datos](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WorkingWithTables.html)
+ [Índices secundarios globales](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html)
+ [Operaciones de consulta y análisis](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html)

# Administración de índices secundarios globales en DynamoDB
<a name="GSI.OnlineOps"></a>

En esta sección se describe cómo crear, modificar y eliminar índices secundarios globales en Amazon DynamoDB.

**Topics**
+ [Creación de una tabla con índices secundarios globales](#GSI.Creating)
+ [Descripción de los índices secundarios globales en una tabla](#GSI.Describing)
+ [Adición de un índice secundario global a una tabla existente](#GSI.OnlineOps.Creating)
+ [Eliminación de un índice secundario global](#GSI.OnlineOps.Deleting)
+ [Modificación de un índice secundario global durante la creación](#GSI.OnlineOps.Creating.Modify)

## Creación de una tabla con índices secundarios globales
<a name="GSI.Creating"></a>

Para crear una tabla con uno o varios índices secundarios globales, use la operación `CreateTable` con el parámetro `GlobalSecondaryIndexes`. Para disfrutar de la máxima flexibilidad en las consultas, puede crear hasta 20 índices secundarios globales (cuota predeterminada) por tabla. 

Debe especificar un atributo que actúe como clave de partición del índice. También tiene la opción de especificar otro atributo para la clave de ordenación del índice. No es necesario que ninguno de estos atributos de clave sea igual que un atributo de clave de la tabla. Por ejemplo, en la tabla *GameScores* (consulte [Uso de índices secundarios globales en DynamoDB](GSI.md)), ni `TopScore` ni `TopScoreDateTime` son atributos de clave. Puede crear un índice secundario global que tenga una clave de partición de `TopScore` y una clave de ordenación de `TopScoreDateTime`. Se podría usar un índice de este tipo para determinar si existe una correlación entre las mejores puntuaciones y la hora del día a la que se juega.

Todos los atributos de clave del índice deben ser escalares y pueden ser de tipo `String`, `Number` o `Binary`. No pueden ser documentos ni conjuntos. Puede proyectar atributos de cualquier tipo de datos en un índice secundario global. Esto incluye escalares, documentos y conjuntos. Para obtener una lista completa de los tipos de datos, consulte [Tipos de datos](HowItWorks.NamingRulesDataTypes.md#HowItWorks.DataTypes).

Si usa el modo aprovisionado, debe proporcionar para el índice los ajustes de `ProvisionedThroughput`, que constan de `ReadCapacityUnits` y `WriteCapacityUnits`. Estos ajustes de rendimiento aprovisionado son independientes de los de la tabla pero se comportan de forma parecida. Para obtener más información, consulte [Consideraciones sobre el rendimiento aprovisionado para los índices secundarios globales](GSI.md#GSI.ThroughputConsiderations).

 Los índices secundarios globales heredan el modo de capacidad de lectura/escritura de la tabla base. Para obtener más información, consulte [Aspectos a tener en cuenta al cambiar los modos de capacidad en DynamoDB](bp-switching-capacity-modes.md). 

**nota**  
 Al crear un nuevo índice secundario global, resulta importante verificar si la elección de clave de partición produce una distribución desigual o restringida de datos o tráfico entre los valores de clave de partición del nuevo índice. Si esto ocurre, podría ver operaciones de reposición y escritura que se producen al mismo tiempo y que se limiten las escrituras en la tabla base. El servicio adopta medidas para minimizar el potencial de este escenario, pero no tiene información de la forma de los datos del cliente con respecto a la clave de partición de índice, la proyección elegida o la escasez de la clave principal del índice.  
Si sospecha que el nuevo índice secundario global puede tener datos estrechos o sesgados, o bien una distribución del tráfico entre los valores clave de partición, tenga en cuenta lo siguiente antes de agregar nuevos índices a tablas importantes desde el punto de vista operativo.  
Es posible que sea más seguro agregar el índice en un momento en que la aplicación esté generando la menor cantidad de tráfico.
Considere la posibilidad de habilitar CloudWatch Contributor Insights en la tabla base y los índices. Esto le proporcionará información valiosa sobre la distribución del tráfico.
 Vea las métricas de `WriteThrottleEvents`, `ThrottledRequests` y `OnlineIndexPercentageProgress` de CloudWatch durante todo el proceso. Ajuste la capacidad de escritura aprovisionada según sea necesario para completar la reposición en un tiempo razonable sin ningún efecto de limitación significativo en las operaciones en curso. Se espera que `OnlineIndexConsumedWriteCapacity` y `OnlineThrottleEvents` muestren 0 durante la reposición del índice.
Prepárese para cancelar la creación del índice si experimenta un impacto operativo debido a la limitación de escritura.

## Descripción de los índices secundarios globales en una tabla
<a name="GSI.Describing"></a>

Para ver el estado de todos los índices secundarios globales de una tabla, se usa la operación `DescribeTable`. La parte `GlobalSecondaryIndexes` de la respuesta enumera todos los índices de la tabla, junto con el estado actual de cada uno de ellos (`IndexStatus`).

El `IndexStatus` para un índice secundario global será uno de los siguientes:
+ `CREATING`: el índice está en proceso de creación y aún no está disponible para usarlo.
+ `ACTIVE`: el índice está listo para usarlo y las aplicaciones pueden realizar operaciones `Query` en él.
+ `UPDATING`: se están cambiando los ajustes de desempeño provisionado del índice.
+ `DELETING`: el índice se está eliminando y ya no se puede utilizar.

Cuando DynamoDB ha terminado de crear un índice secundario global, el estado de este último cambia de `CREATING` a `ACTIVE`.

## Adición de un índice secundario global a una tabla existente
<a name="GSI.OnlineOps.Creating"></a>

Para agregar un índice secundario global a una tabla existente, se usa la operación `UpdateTable` con el parámetro `GlobalSecondaryIndexUpdates`. Debe proporcionar lo siguiente:
+ El nombre del índice. Este nombre debe ser único entre todos los índices de la tabla.
+ El esquema de claves del índice. Debe especificar un atributo para la clave de partición del índice y, si lo desea, otro atributo para la clave de ordenación del índice. No es necesario que ninguno de estos atributos de clave sea igual que un atributo de clave de la tabla. Los tipos de datos de cada atributo del esquema deben ser escalares: `String`, `Number` o `Binary`.
+ Los atributos de la tabla que se van a proyectar en el índice:
  + `KEYS_ONLY`: cada elemento del índice consta únicamente de los valores de la clave de partición y la clave de ordenación de la tabla, así como de los valores de las claves del índice. 
  + `INCLUDE`: además de los atributos que se describen en `KEYS_ONLY`, el índice secundario incluye otros atributos sin clave que se especifiquen.
  + `ALL`: el índice incluye todos los atributos de la tabla de origen.
+ Los ajustes de rendimiento aprovisionado del índice, que constan de `ReadCapacityUnits` y `WriteCapacityUnits`. Estos ajustes de rendimiento aprovisionado son independientes de los de la tabla.

Solo puede crear un índice secundario global por operación `UpdateTable`.

### Fases de creación del índice
<a name="GSI.OnlineOps.Creating.Phases"></a>

Cuando se agrega un nuevo índice secundario global a una tabla existente, la tabla sigue estando disponible mientras se crea el índice. Sin embargo, el nuevo índice no estará disponible para las operaciones de consulta hasta que el estado cambie de `CREATING` a `ACTIVE`.

**nota**  
La creación de índices secundarios globales no utiliza Application Auto Scaling. El aumento de la capacidad de `MIN` de Application Auto Scaling no disminuirá el tiempo de creación del índice secundario global.

En segundo plano, DynamoDB crea el índice en dos fases:

**Asignación de recursos**  
DynamoDB asigna los recursos informáticos y de almacenamiento que se necesitan para crear el índice.  
Durante la fase de asignación de recursos, el valor del atributo `IndexStatus` es `CREATING` y el valor del atributo `Backfilling` es false. Use la operación `DescribeTable` para recuperar el estado de una tabla y todos sus índices secundarios.  
Mientras el índice se encuentra en la fase de asignación de recursos, no se puede eliminar el índice ni eliminar su tabla principal. Tampoco se puede modificar el rendimiento aprovisionado del índice ni de la tabla. No se pueden agregar ni eliminar otros índices de la tabla. Sin embargo, se puede modificar el rendimiento aprovisionado de estos otros índices.

**Reposición**  
Para cada elemento de la tabla, DynamoDB determina el conjunto de atributos que se escribirá en el índice según su proyección (`KEYS_ONLY`, `INCLUDE` o `ALL`). A continuación, escribe estos atributos en el índice. Durante la fase de reposición, DynamoDB lleva un seguimiento de los elementos que se agregan, eliminan o actualizan en la tabla. Los atributos de estos elementos también se agregan, eliminan o actualizan en el índice, según proceda.  
Durante la fase de reposición, el atributo `IndexStatus` está establecido en `CREATING` y el atributo `Backfilling` está establecido en true. Use la operación `DescribeTable` para recuperar el estado de una tabla y todos sus índices secundarios.  
Mientras el índice se encuentra en la fase de reposición, no se puede eliminar su tabla principal. Sin embargo, sí se puede eliminar el índice o modificar el rendimiento aprovisionado de la tabla y de cualquiera de sus índices secundarios globales.  
Durante la fase de reposición, algunas escrituras de elementos infractores podrían realizarse correctamente y otras se rechazarán. Después de la reposición, se rechazarán todas las escrituras en elementos que infrinjan el esquema de claves del nuevo índice. Recomendamos ejecutar la herramienta Violation Detector una vez que haya finalizado la fase de reposición para detectar y resolver cualquier infracción de clave que pueda haberse producido. Para obtener más información, consulte [Detección y corrección de infracciones de la clave del índice en DynamoDB](GSI.OnlineOps.ViolationDetection.md).

Mientras las fases de asignación de recursos y reposición están en curso, el índice se encuentra en el estado `CREATING`. Durante este plazo de tiempo, DynamoDB realiza operaciones de lectura en al tabla. No se le cobrará por las operaciones de lectura de la tabla base para rellenar el índice secundario global.

Una vez que se completa la creación del índice, su estado cambia a `ACTIVE`. No puede `Query` ni `Scan` el índice hasta que esté `ACTIVE`.

**nota**  
En algunos casos, DynamoDB no puede escribir datos de la tabla en el índice porque se producen infracciones en las claves del índice. Esto puede ocurrir si:  
El tipo de dato de un valor de atributo no coincide con el del esquema de claves de índice.
El tamaño de un atributo supera la longitud máxima de un atributo de clave del índice.
Un atributo de clave del índice tiene un valor binario o de cadena vacío.
Las infracciones de claves del índice no interfieren con la creación de un índice secundario global. Sin embargo, cuando el índice adquiera el estado `ACTIVE`, las claves infractoras no estarán presentes en él.  
DynamoDB proporciona una herramienta independiente para encontrar y resolver estos problemas. Para obtener más información, consulte [Detección y corrección de infracciones de la clave del índice en DynamoDB](GSI.OnlineOps.ViolationDetection.md).

### Adición de un índice secundario global a una tabla grande
<a name="GSI.OnlineOps.Creating.LargeTable"></a>

El tiempo necesario para crear un índice secundario global depende de varios factores, como los siguientes:
+ El tamaño de la tabla.
+ El número de elementos de la tabla que son aptos para incluirlos en el índice.
+ El número de atributos que se proyectan en el índice.
+ La actividad de escritura en la tabla principal mientras se crean los índices.

Si se va a agregar un índice secundario global a una tabla muy grande, el proceso de creación puede tardar mucho tiempo en completarse. Para monitorear el progreso y determinar si el índice tiene suficiente capacidad de escritura, consulte las siguientes métricas de Amazon CloudWatch:
+ `OnlineIndexPercentageProgress`

Para obtener más información acerca de cómo las métricas de CloudWatch se relacionan con DynamoDB, consulte [Métricas de DynamoDB](metrics-dimensions.md#dynamodb-metrics).

**importante**  
Es posible que tenga que permitir listas de tablas muy grandes antes de crear o actualizar un índice secundario global. Póngase en contacto con AWS Support para incluir las tablas en la lista de permitidos.

Mientras el índice está en la fase de reposición, DynamoDB utiliza la capacidad interna del sistema para leer la tabla. Esto permite minimizar el impacto de la creación del índice y asegurarse de que no se agote la capacidad de lectura de la tabla.

## Eliminación de un índice secundario global
<a name="GSI.OnlineOps.Deleting"></a>

Si ya no necesita un índice secundario global, puede eliminarlo mediante la ooperación `UpdateTable`.

Solo se puede eliminar un índice secundario global por operación `UpdateTable`.

Mientras se elimina el índice secundario global, no afecta en absoluto a la actividad de lectura o escritura en la tabla principal. Mientras la eliminación está en curso, puede modificar el rendimiento aprovisionado de los demás índices.

**nota**  
Al eliminar una tabla mediante la acción `DeleteTable`, todos los índices secundarios globales de esa tabla se eliminan también.
No se cargará en su cuenta la operación de eliminación del índice secundario global.

## Modificación de un índice secundario global durante la creación
<a name="GSI.OnlineOps.Creating.Modify"></a>

Mientras se está creando un índice, puede usar la operación `DescribeTable` para determinar en qué fase se encuentra. La descripción del índice incluye un atributo de tipo Boolean, `Backfilling`, que indica si DynamoDB está cargando elementos de la tabla en el índice en un momento dado. Si `Backfilling` es true, significa que la fase de asignación de recursos se ha completado y el índice se encuentra en la fase de reposición. 

Durante la fase de reposición, puede eliminar el índice que se está creando. Durante esta fase, no se pueden agregar ni eliminar otros índices de la tabla.

**nota**  
En el caso de los índices que se han creado como parte de una operación `CreateTable`, el atributo `Backfilling` no aparece en el resultado de `DescribeTable`. Para obtener más información, consulte [Fases de creación del índice](#GSI.OnlineOps.Creating.Phases).

# Detección y corrección de infracciones de la clave del índice en DynamoDB
<a name="GSI.OnlineOps.ViolationDetection"></a>

Durante la fase de reposición de la creación de un índice secundario global, Amazon DynamoDB examina cada elemento de la tabla para determinar si es apto para incluirlo en el índice. Algunos elementos podrían no ser aptos por la posibilidad de que causen infracciones de claves de índice. En estos casos, el elemento en cuestión permanecen en la tabla, pero el índice no incluye la entrada correspondiente a dicho elemento.

Un registro *infracción de clave de índice* se produce en las siguientes situaciones:
+ Los tipos de datos del valor de un atributo y del esquema de claves del índice no coinciden. Por ejemplo, supongamos que uno de los elementos de la tabla `GameScores` tiene un atributo `TopScore` cuyo valor es de tipo `String`. Si agrega un índice secundario global con una clave de partición de `TopScore` de tipo `Number`, el elemento de la tabla infringiría la clave de índice.
+ El valor de atributo de la tabla supera la longitud máxima de un atributo de clave del índice. La longitud máxima de una clave de partición es de 2048 bytes y la longitud máxima de una clave de ordenación es de 1024 bytes. Si cualquiera de los valores de atributos correspondientes de la tabla supera estos límites, el elemento de la tabla infringiría la clave de índice.

**nota**  
Si se establece un valor binario o de cadena en un atributo que se utiliza como clave de índice, este valor debe tener una longitud mayor que cero; de lo contrario, el elemento de la tabla podría infringir la clave del índice.  
En la actualidad, esta herramienta no indica esta infracción de clave de índice de ninguna forma.

Si se produce una infracción de índice, la fase de reposición continúa sin interrupción. Sin embargo, los elementos infractores no se incluyen en el índice. Una vez que haya finalizado la fase de reposición, se rechazarán todas las escrituras en elementos que infringen el esquema de claves del nuevo índice.

Para identificar y corregir los valores de los atributos de una tabla que infringen una clave de índice, se utiliza la herramienta Violation Detector. Para ejecutar Violation Detector, debe crear un archivo de configuración que especifique el nombre de la tabla que se va a examinar, los nombres y los tipos de datos de las claves de partición y de ordenación del índice secundario global y qué medidas deben adoptarse si se detecta cualquier infracción de clave de índice. Violation Detector puede ejecutarse en dos modos diferentes:
+ **Modo de detección**: detecta las infracciones de claves de índice. El modo de detección se utiliza para registrar los elementos de la tabla que causarían infracciones de claves en un índice secundario global. (Si lo desea, puede solicitar que estos elementos infractores de la tabla se eliminen de inmediato en cuanto se encuentren). El resultado del modo de detección se escribe en un archivo, que se puede utilizar para analizar los datos posteriormente.
+ **Modo de corrección**: corrige las infracciones de claves de índice. En el modo de corrección, Violation Detector lee un archivo de información de entrada con el mismo formato que el archivo de resultados del modo de detección. En el modo de corrección se leen los registros del archivo de información de entrada y, para cada registro, se elimina o actualiza el elemento correspondiente de la tabla. Tenga en cuenta que si decide actualizar los elementos, deberá editar el archivo de información de entrada y establecer valores adecuados para estas actualizaciones.

## Descargar y ejecutar Violation Detector
<a name="GSI.OnlineOps.ViolationDetection.Running"></a>

Violation Detector está disponible como archivo Java ejecutable (`.jar`) y se ejecuta en ordenadores Windows, Mac o Linux. Violation Detector requiere Java 1.7 (o posterior) y Apache Maven.
+ [Descargar Violation Detector de GitHub](https://github.com/awslabs/dynamodb-online-index-violation-detector)

Siga las instrucciones del archivo `README.md` para descargar e instalar Violation Detector mediante Maven.

Para iniciar Violation Detector, vaya al directorio donde ha construido `ViolationDetector.java` e ingrese el siguiente comando.

```
java -jar ViolationDetector.jar [options]
```

La línea de comandos de Violation Detector acepta las siguientes opciones:
+ `-h | --help`: imprime un resumen de uso y las opciones posibles de Violation Detector.
+ `-p | --configFilePath` `value`: nombre completo de un archivo de configuración de Violation Detector. Para obtener más información, consulte [El archivo de configuración de Violation Detector](#GSI.OnlineOps.ViolationDetection.ConfigFile).
+ `-t | --detect` `value`: detecta las infracciones de claves de índice de la tabla y las escribe en el archivo de resultados de Violation Detector. Si el valor de este parámetro se establece en `keep`, los elementos con infracciones de claves no se modifican. Si el valor se establece en `delete`, los elementos con infracciones de claves se eliminan de la tabla.
+ `-c | --correct` `value`: lee las infracciones de claves de índice en un archivo de información de entrada y adopta medidas para corregir los elementos de la tabla. Si el valor de este parámetro se establece en `update`, los elementos con infracciones de claves se actualizan a otros valores sin infracciones. Si el valor se establece en `delete`, los elementos con infracciones de claves se eliminan de la tabla.

## El archivo de configuración de Violation Detector
<a name="GSI.OnlineOps.ViolationDetection.ConfigFile"></a>

En tiempo de ejecución, la herramienta Violation Detector requiere un archivo de configuración. Los parámetros de este archivo determinan a qué recursos de DynamoDB puede obtener acceso Violation Detector y cuánto rendimiento aprovisionado puede consumir. En la siguiente tabla se describen estos parámetros.


****  

| Nombre del parámetro | Descripción | ¿Obligatorio? | 
| --- | --- | --- | 
|  `awsCredentialsFile`  |  Nombre completo de un archivo que contiene las credenciales de AWS. El archivo de credenciales debe tener el siguiente formato: <pre>accessKey = access_key_id_goes_here<br />secretKey = secret_key_goes_here </pre>  |  Sí  | 
|  `dynamoDBRegion`  |  Región de AWS en la que la reside la tabla. Por ejemplo: `us-west-2`.  |  Sí  | 
|  `tableName`  | Nombre de la tabla de DynamoDB que se va a examinar. |  Sí  | 
|  `gsiHashKeyName`  |  Nombre de la clave de partición del índice.  |  Sí  | 
|  `gsiHashKeyType`  |  Tipo de datos de la clave de partición del índice: `String`, `Number` o `Binary`: `S \| N \| B`  |  Sí  | 
|  `gsiRangeKeyName`  |  Nombre de la clave de ordenación del índice. No especifique este parámetro si el índice solo tiene una clave principal simple (clave de partición).  |  No  | 
|  `gsiRangeKeyType`  |  Tipo de datos de la clave de ordenación del índice: `String`, `Number` o `Binary`: `S \| N \| B`  No especifique este parámetro si el índice solo tiene una clave principal simple (clave de partición).  |  No  | 
|  `recordDetails`  |  Indica si se deben escribir todos los detalles de las infracciones de claves de índice en el archivo de resultados. Si se establece en `true` (valor predeterminado), se registra toda la información sobre los elementos infractores. Si se establece en `false`, solo se registra el número de infracciones.  |  No  | 
|  `recordGsiValueInViolationRecord`  |  Indica si se deben escribir los valores de las claves de índice infractoras en el archivo de resultados. Si se establece en `true` (predeterminado), se registran los valores de las claves. Si se establece en `false`, los valores de las claves no se registran.  |  No  | 
|  `detectionOutputPath`  |  Ruta completa del archivo de resultados de Violation Detector;. Este parámetro es compatible con la escritura en un directorio local o en Amazon Simple Storage Service (Amazon S3). A continuación se muestran algunos ejemplos: `detectionOutputPath = ``//local/path/filename.csv` `detectionOutputPath = ``s3://bucket/filename.csv` La información del archivo de resultados aparece en formato CSV (valores separados por comas). Si no se establece `detectionOutputPath`, el archivo de resultados se denomina `violation_detection.csv` y se escribe en el directorio de trabajo actual.  |  No  | 
|  `numOfSegments`  | El número de segmentos de examen en paralelo que se utilizarán cuando Violation Detector analice la tabla. El valor predeterminado es 1, lo que significa que la tabla se examina de forma secuencial. Si el valor es 2 o superior, entonces Violation Detector dividirá la tabla en esa cantidad de segmentos lógicos y un número igual de subprocesos de examen. El ajuste máximo de `numOfSegments` es 4096.En el caso de las tablas de mayor tamaño, un examen en paralelo suele resultar más rápido que un examen secuencial. Además, si la tabla es lo bastante grande para abarcar varias particiones, un examen en paralelo distribuye su actividad de lectura de manera uniforme entre varias particiones.Para obtener más información sobre los análisis en paralelo en DynamoDB, consulte [Análisis paralelo](Scan.md#Scan.ParallelScan). |  No  | 
|  `numOfViolations`  |  Límite superior de infracciones de claves de índice que se escribirán en el archivo de resultados. Si se establece en `-1` (valor predeterminado), se examina toda la tabla. Si se establece en un número entero positivo, entonces Violation Detector se detendrá después de haber detectado esa cantidad de infracciones.  |  No  | 
|  `numOfRecords`  |  Número de elementos de la tabla que se va a examinar. Si se establece en -1 (valor predeterminado), se examinará toda la tabla. Si se establece en un número entero positivo, entonces Violation Detector se detendrá después de haber examinado esa cantidad de elementos en la tabla.  |  No  | 
|  `readWriteIOPSPercent`  |  Regula el porcentaje de unidades de capacidad de lectura provisionadas que se consumen durante un examen de la tabla. Los valores válidos van de `1` a `100`. El valor predeterminado (`25`) significa que Violation Detector consumirá no más del 25 % del rendimiento de lectura aprovisionado de la tabla.  |  No  | 
|  `correctionInputPath`  |  Ruta completa del archivo de información de entrada de corrección de Violation Detector. Si ejecuta Violation Detector en modo de corrección, el contenido de este archivo se usa para modificar o eliminar los elementos de datos de la tabla que infringen el índice secundario global. El formato del archivo `correctionInputPath` es el mismo que el del archivo `detectionOutputPath`. Esto permite procesar el resultado del modo de detección como información de entrada en el modo de corrección.  |  No  | 
|  `correctionOutputPath`  |  Ruta completa del archivo de resultados de corrección de Violation Detector. Este archivo se crea solo si existen errores de actualización. Este parámetro es compatible con la escritura en un directorio local o en Amazon S3. A continuación se muestran algunos ejemplos: `correctionOutputPath = ``//local/path/filename.csv` `correctionOutputPath = ``s3://bucket/filename.csv` La información del archivo de resultados aparece en formato CSV. Si no se establece `correctionOutputPath`, el archivo de resultados se denomina `violation_update_errors.csv` y se escribe en el directorio de trabajo actual.  |  No  | 

## Detección
<a name="GSI.OnlineOps.ViolationDetection.Detection"></a>

Para detectar infracciones de claves de índice, use Violation Detector con la opción de línea de comandos `--detect`. Para ver cómo funciona esta opción, tomemos la tabla `ProductCatalog`. A continuación se muestra una lista de elementos de la tabla. Solo se muestran la clave principal (`Id`) y el atributo `Price`.


****  

| Id (clave principal) | Precio | 
| --- | --- | 
| 101 |  5  | 
| 102 |  20  | 
| 103 | 200  | 
| 201 |  100  | 
| 202 |  200  | 
| 203 |  300  | 
| 204 |  400  | 
| 205 |  500  | 

Todos los valores de `Price` son de tipo `Number`. No obstante, dado que en DynamoDB no se usan esquemas, es posible agregar un elemento con un valor de no numérico `Price`. Por ejemplo, supongamos que agrega otro elemento a la tabla `ProductCatalog`.


****  

| Id (clave principal) | Precio | 
| --- | --- | 
| 999 | "Hello" | 

Ahora, la tabla contiene un total de nueve elementos.

Ahora agrega un nuevo índice secundario global a la tabla: `PriceIndex`. La clave principal de este índice es una clave de partición, `Price`, que es de tipo `Number`. Después de crearlo, el índice contendrá ocho elementos. Sin embargo, la tabla `ProductCatalog` tiene nueve elementos. El motivo de esta discrepancia es que, aunque el valor `"Hello"` es de tipo `String`, la clave principal de `PriceIndex` es de tipo `Number`. El valor `String` infringe la clave del índice secundario global, por lo que no está presente en el índice.

Para usar Violation Detector en esta situación, primero se crea un archivo de configuración, como el siguiente.

```
# Properties file for violation detection tool configuration.
# Parameters that are not specified will use default values.

awsCredentialsFile = /home/alice/credentials.txt
dynamoDBRegion = us-west-2
tableName = ProductCatalog
gsiHashKeyName = Price
gsiHashKeyType = N
recordDetails = true
recordGsiValueInViolationRecord = true
detectionOutputPath = ./gsi_violation_check.csv
correctionInputPath = ./gsi_violation_check.csv
numOfSegments = 1
readWriteIOPSPercent = 40
```

A continuación, se ejecuta el Violation Detector como en el siguiente ejemplo.

```
$  java -jar ViolationDetector.jar --configFilePath config.txt --detect keep

Violation detection started: sequential scan, Table name: ProductCatalog, GSI name: PriceIndex
Progress: Items scanned in total: 9,    Items scanned by this thread: 9,    Violations found by this thread: 1, Violations deleted by this thread: 0
Violation detection finished: Records scanned: 9, Violations found: 1, Violations deleted: 0, see results at: ./gsi_violation_check.csv
```

Si el parámetro de configuración `recordDetails` se establece en `true`, entonces Violation Detector escribe los detalles de cada infracción en el archivo de resultados, como en el siguiente ejemplo.

```
Table Hash Key,GSI Hash Key Value,GSI Hash Key Violation Type,GSI Hash Key Violation Description,GSI Hash Key Update Value(FOR USER),Delete Blank Attributes When Updating?(Y/N) 

999,"{""S"":""Hello""}",Type Violation,Expected: N Found: S,,
```

El archivo de salida está en formato CSV. La primera línea del archivo es un encabezado, seguido de un registro por cada elemento que infringe la clave del índice. Los campos de estos registros de infracción son los siguientes:
+ **Table hash key**: valor de la clave de partición del elemento de la tabla.
+ **Table range key**: valor de la clave de clasificación del elemento de la tabla.
+ **GSI hash key value**: valor de la clave de partición del índice secundario global.
+ **GSI hash key violation type**: puede ser `Type Violation` o `Size Violation`.
+ **GSI hash key violation description**: causa de la infracción.
+ **GSI hash key update value (FOR USER)**: en el modo de corrección, nuevo valor suministrado por el usuario para el atributo.
+ **GSI range key values**: el valor de la clave de clasificación del índice secundario global.
+ **GSI range key violation type**: puede ser `Type Violation` o `Size Violation`.
+ **GSI range key violation description**: causa de la infracción.
+ **GSI range key update value (FOR USER)**: en el modo de corrección, nuevo valor suministrado por el usuario para el atributo.
+ **Delete blank attribute when updating (Y/N)**: en el modo de corrección, determina si el elemento infractor se eliminará (Y) o se conservará (N) en la tabla, pero solamente si cualquiera de los campos siguientes está en blanco:
  + `GSI Hash Key Update Value(FOR USER)`
  + `GSI Range Key Update Value(FOR USER)`

  Si alguno de estos campos no está en blanco, entonces `Delete Blank Attribute When Updating(Y/N)` no surte ningún efecto.

**nota**  
El formato de los resultados puede variar, en función del archivo de configuración y de las opciones de la línea de comandos. Por ejemplo, si la tabla tiene una clave principal simple (sin clave de ordenación), no habrá ningún campo de clave de ordenación en el resultado.  
Los registros de infracción del archivo podrían no estar ordenados.

## Corrección
<a name="GSI.OnlineOps.ViolationDetection.Correction"></a>

Para corregir infracciones de claves de índice, use Violation Detector con la opción de línea de comandos `--correct`. En el modo de corrección, Violation Detector lee el archivo de información de entrada especificado por el parámetro `correctionInputPath`. Este archivo tiene el mismo formato que el archivo `detectionOutputPath`, lo que permite utilizar el resultado de la detección como información de entrada para la corrección.

Violation Detector ofrece dos maneras distintas de corregir las infracciones de claves de índice:
+ **Eliminar las infracciones**: se eliminan los elementos de la tabla cuyos valores de atributos infringen alguna clave.
+ **Actualizar las infracciones**: se actualizan los elementos de la tabla, para lo cual se sustituyen los atributos infractores por otros que no causan ninguna infracción.

En ambos casos, puede utilizar el archivo de resultados del modo de detección como información de entrada para el modo de corrección.

Si continuamos con el ejemplo anterior de `ProductCatalog`, supongamos que desea eliminar el elemento infractor de la tabla. Para ello, utilizamos la línea de comandos siguiente.

```
$  java -jar ViolationDetector.jar --configFilePath config.txt --correct delete
```

En este momento, le pedirá que confirme si desea eliminar los elementos infractores.

```
Are you sure to delete all violations on the table?y/n
y
Confirmed, will delete violations on the table...
Violation correction from file started: Reading records from file: ./gsi_violation_check.csv, will delete these records from table.
Violation correction from file finished: Violations delete: 1, Violations Update: 0
```

Ahora, tanto `ProductCatalog` como `PriceIndex` tienen el mismo número de elementos.

# Trabajo con índices secundarios globales: Java
<a name="GSIJavaDocumentAPI"></a>

Puede utilizar la API de documentos AWS SDK para Java para crear una tabla Amazon DynamoDB con uno o varios índices secundarios globales, describir los índices de la tabla y utilizarlos para realizar consultas. 

A continuación se indican los pasos comunes para las operaciones con tablas. 

1. Cree una instancia de la clase `DynamoDB`.

1. Cree los objetos de solicitud correspondientes para proporcionar los parámetros obligatorios y opcionales de la operación. 

1. Llame al método apropiado proporcionado por el cliente que ha creado en el paso anterior. 

**Topics**
+ [Creación de una tabla con un índice secundario global](#GSIJavaDocumentAPI.CreateTableWithIndex)
+ [Descripción de una tabla con un índice secundario global](#GSIJavaDocumentAPI.DescribeTableWithIndex)
+ [Consulta de un índice secundario local](#GSIJavaDocumentAPI.QueryAnIndex)
+ [Ejemplo: índices secundarios globales que utilizan la API de documentos de AWS SDK para Java](GSIJavaDocumentAPI.Example.md)

## Creación de una tabla con un índice secundario global
<a name="GSIJavaDocumentAPI.CreateTableWithIndex"></a>

Puede crear índices secundarios globales a la vez que crea la tabla. Para ello, utilice `CreateTable` e indique las especificaciones para uno o varios índices secundarios globales. En el ejemplo de código Java siguiente, se crea una tabla para contener información sobre datos meteorológicos. La clave de partición es `Location` y la de ordenación, `Date`. Un índice secundario global denominado `PrecipIndex` permite obtener acceso rápido a los datos sobre precipitaciones de varias ubicaciones.

A continuación se indican los pasos que hay que seguir para crear una tabla con un índice secundario global mediante el API de documentos de DynamoDB. 

1. Cree una instancia de la clase `DynamoDB`.

1. Cree una instancia de la clase `CreateTableRequest` para proporcionar la información de solicitud.

   Debe proporcionar el nombre de la tabla, su clave principal y los valores de rendimiento aprovisionado. Para el índice secundario global, debe proporcionar el nombre del índice, los ajustes de rendimiento aprovisionado, las definiciones de atributos de la clave de ordenación del índice, el esquema de claves del índice y la proyección de atributos.

1. Llame al método `createTable` proporcionando el objeto de solicitud como parámetro.

En el siguiente ejemplo de código Java se muestran los pasos anteriores. El código crea una tabla (`WeatherData`) con un índice secundario global (`PrecipIndex`). La clave de partición del índice es `Date` y la de ordenación, `Precipitation`. Todos los atributos de la tabla se proyectan en el índice. Los usuarios pueden consultar este índice para obtener los datos meteorológicos de una determinada fecha y, si lo desean, ordenarlos según la cantidad de precipitación. 

Ya que `Precipitation` no es un atributo de clave para la tabla, no es obligatorio. Sin embargo, los elementos`WeatherData` sin `Precipitation` no aparecen en `PrecipIndex`.

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

// Attribute definitions
ArrayList<AttributeDefinition> attributeDefinitions = new ArrayList<AttributeDefinition>();

attributeDefinitions.add(new AttributeDefinition()
    .withAttributeName("Location")
    .withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition()
    .withAttributeName("Date")
    .withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition()
    .withAttributeName("Precipitation")
    .withAttributeType("N"));

// Table key schema
ArrayList<KeySchemaElement> tableKeySchema = new ArrayList<KeySchemaElement>();
tableKeySchema.add(new KeySchemaElement()
    .withAttributeName("Location")
    .withKeyType(KeyType.HASH));  //Partition key
tableKeySchema.add(new KeySchemaElement()
    .withAttributeName("Date")
    .withKeyType(KeyType.RANGE));  //Sort key

// PrecipIndex
GlobalSecondaryIndex precipIndex = new GlobalSecondaryIndex()
    .withIndexName("PrecipIndex")
    .withProvisionedThroughput(new ProvisionedThroughput()
        .withReadCapacityUnits((long) 10)
        .withWriteCapacityUnits((long) 1))
        .withProjection(new Projection().withProjectionType(ProjectionType.ALL));

ArrayList<KeySchemaElement> indexKeySchema = new ArrayList<KeySchemaElement>();

indexKeySchema.add(new KeySchemaElement()
    .withAttributeName("Date")
    .withKeyType(KeyType.HASH));  //Partition key
indexKeySchema.add(new KeySchemaElement()
    .withAttributeName("Precipitation")
    .withKeyType(KeyType.RANGE));  //Sort key

precipIndex.setKeySchema(indexKeySchema);

CreateTableRequest createTableRequest = new CreateTableRequest()
    .withTableName("WeatherData")
    .withProvisionedThroughput(new ProvisionedThroughput()
        .withReadCapacityUnits((long) 5)
        .withWriteCapacityUnits((long) 1))
    .withAttributeDefinitions(attributeDefinitions)
    .withKeySchema(tableKeySchema)
    .withGlobalSecondaryIndexes(precipIndex);

Table table = dynamoDB.createTable(createTableRequest);
System.out.println(table.getDescription());
```

Debe esperar hasta que DynamoDB cree la tabla y establezca el estado de esta última en `ACTIVE`. A partir de ese momento, puede comenzar a incluir elementos de datos en la tabla.

## Descripción de una tabla con un índice secundario global
<a name="GSIJavaDocumentAPI.DescribeTableWithIndex"></a>

Para obtener información sobre los índices secundarios globales en una tabla, use `DescribeTable`. Para cada índice, puede obtener acceso a su nombre, esquema de claves y atributos proyectados.

Los siguientes son los pasos para acceder a la información global de índice secundario de una tabla. 

1. Cree una instancia de la clase `DynamoDB`.

1. Cree una instancia de la clase `Table` para representar el índice que desea usar.

1. Llame al método `describe` del objeto `Table`.

En el siguiente ejemplo de código Java se muestran los pasos anteriores.

**Example**  

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

Table table = dynamoDB.getTable("WeatherData");
TableDescription tableDesc = table.describe();
    

Iterator<GlobalSecondaryIndexDescription> gsiIter = tableDesc.getGlobalSecondaryIndexes().iterator();
while (gsiIter.hasNext()) {
    GlobalSecondaryIndexDescription gsiDesc = gsiIter.next();
    System.out.println("Info for index "
         + gsiDesc.getIndexName() + ":");

    Iterator<KeySchemaElement> kseIter = gsiDesc.getKeySchema().iterator();
    while (kseIter.hasNext()) {
        KeySchemaElement kse = kseIter.next();
        System.out.printf("\t%s: %s\n", kse.getAttributeName(), kse.getKeyType());
    }
    Projection projection = gsiDesc.getProjection();
    System.out.println("\tThe projection type is: "
        + projection.getProjectionType());
    if (projection.getProjectionType().toString().equals("INCLUDE")) {
        System.out.println("\t\tThe non-key projected attributes are: "
            + projection.getNonKeyAttributes());
    }
}
```

## Consulta de un índice secundario local
<a name="GSIJavaDocumentAPI.QueryAnIndex"></a>

Puede utilizar `Query` en un índice secundario global, de un modo bastante similar al que realiza una `Query` en una tabla. Debe especificar el nombre del índice, los criterios de consulta de la clave de partición y la clave de ordenación (si procede) del índice y los atributos que desea devolver. En este ejemplo, el índice es `PrecipIndex`, cuyas clave de partición y ordenación son, respectivamente, `Date` y `Precipitation`. La consulta del índice devuelve todos los datos meteorológicos de una determinada fecha cuando el valor de Precipitation sea mayor que cero.

A continuación se indican los pasos que hay que seguir para consultar un índice secundario global mediante la API Document de AWS SDK para Java. 

1. Cree una instancia de la clase `DynamoDB`.

1. Cree una instancia de la clase `Table` para representar el índice que desea usar.

1. Cree una instancia de la clase `Index` para el índice que desea consultar.

1. Llame al método `query` del objeto `Index`.

El nombre de atributo `Date` es una palabra reservada de DynamoDB. Por consiguiente, debe usar un nombre de atributo de expresión como marcador de posición en la expresión `KeyConditionExpression`.

En el siguiente ejemplo de código Java se muestran los pasos anteriores.

**Example**  

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

Table table = dynamoDB.getTable("WeatherData");
Index index = table.getIndex("PrecipIndex");

QuerySpec spec = new QuerySpec()
    .withKeyConditionExpression("#d = :v_date and Precipitation = :v_precip")
    .withNameMap(new NameMap()
        .with("#d", "Date"))
    .withValueMap(new ValueMap()
        .withString(":v_date","2013-08-10")
        .withNumber(":v_precip",0));

ItemCollection<QueryOutcome> items = index.query(spec);
Iterator<Item> iter = items.iterator(); 
while (iter.hasNext()) {
    System.out.println(iter.next().toJSONPretty());
}
```

# Ejemplo: índices secundarios globales que utilizan la API de documentos de AWS SDK para Java
<a name="GSIJavaDocumentAPI.Example"></a>

En el siguiente ejemplo de código Java se muestra cómo usar índices secundarios globales. En el ejemplo se crea una tabla denominada `Issues`, que puede utilizarse en un sistema sencillo de seguimiento de errores con fines de desarrollo de software. La clave de partición es `IssueId` y la de ordenación, `Title`. Hay tres índices secundarios globales en esta tabla:
+ `CreateDateIndex`: la clave de partición es `CreateDate` y la de ordenación `IssueId`. Además de las claves de tabla, se proyectan en el índice los atributos `Description` y `Status`.
+ `TitleIndex`: la clave de partición es `Title` y la de ordenación `IssueId`. No se proyecta en el índice ningún otro atributo excepto las claves de tabla.
+ `DueDateIndex`: la clave de partición es `DueDate` y no hay clave de ordenación. Todos los atributos de la tabla se proyectan en el índice.

Una vez que se ha creado la tabla `Issues`, el programa carga la tabla con datos que representan informes de errores de software. A continuación, consulta los datos utilizando los índices secundarios globales. Por último, el programa elimina la tabla `Issues`.

Para obtener instrucciones paso a paso para probar el siguiente ejemplo, consulte [Ejemplos de código Java](CodeSamples.Java.md).

**Example**  

```
package com.amazonaws.codesamples.document;

import java.util.ArrayList;
import java.util.Iterator;

import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Index;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.ItemCollection;
import com.amazonaws.services.dynamodbv2.document.QueryOutcome;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.dynamodbv2.document.spec.QuerySpec;
import com.amazonaws.services.dynamodbv2.document.utils.ValueMap;
import com.amazonaws.services.dynamodbv2.model.AttributeDefinition;
import com.amazonaws.services.dynamodbv2.model.CreateTableRequest;
import com.amazonaws.services.dynamodbv2.model.GlobalSecondaryIndex;
import com.amazonaws.services.dynamodbv2.model.KeySchemaElement;
import com.amazonaws.services.dynamodbv2.model.KeyType;
import com.amazonaws.services.dynamodbv2.model.Projection;
import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;

public class DocumentAPIGlobalSecondaryIndexExample {

    static AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
    static DynamoDB dynamoDB = new DynamoDB(client);

    public static String tableName = "Issues";

    public static void main(String[] args) throws Exception {

        createTable();
        loadData();

        queryIndex("CreateDateIndex");
        queryIndex("TitleIndex");
        queryIndex("DueDateIndex");

        deleteTable(tableName);

    }

    public static void createTable() {

        // Attribute definitions
        ArrayList<AttributeDefinition> attributeDefinitions = new ArrayList<AttributeDefinition>();

        attributeDefinitions.add(new AttributeDefinition().withAttributeName("IssueId").withAttributeType("S"));
        attributeDefinitions.add(new AttributeDefinition().withAttributeName("Title").withAttributeType("S"));
        attributeDefinitions.add(new AttributeDefinition().withAttributeName("CreateDate").withAttributeType("S"));
        attributeDefinitions.add(new AttributeDefinition().withAttributeName("DueDate").withAttributeType("S"));

        // Key schema for table
        ArrayList<KeySchemaElement> tableKeySchema = new ArrayList<KeySchemaElement>();
        tableKeySchema.add(new KeySchemaElement().withAttributeName("IssueId").withKeyType(KeyType.HASH)); // Partition
                                                                                                           // key
        tableKeySchema.add(new KeySchemaElement().withAttributeName("Title").withKeyType(KeyType.RANGE)); // Sort
                                                                                                          // key

        // Initial provisioned throughput settings for the indexes
        ProvisionedThroughput ptIndex = new ProvisionedThroughput().withReadCapacityUnits(1L)
                .withWriteCapacityUnits(1L);

        // CreateDateIndex
        GlobalSecondaryIndex createDateIndex = new GlobalSecondaryIndex().withIndexName("CreateDateIndex")
                .withProvisionedThroughput(ptIndex)
                .withKeySchema(new KeySchemaElement().withAttributeName("CreateDate").withKeyType(KeyType.HASH), // Partition
                                                                                                                 // key
                        new KeySchemaElement().withAttributeName("IssueId").withKeyType(KeyType.RANGE)) // Sort
                                                                                                        // key
                .withProjection(
                        new Projection().withProjectionType("INCLUDE").withNonKeyAttributes("Description", "Status"));

        // TitleIndex
        GlobalSecondaryIndex titleIndex = new GlobalSecondaryIndex().withIndexName("TitleIndex")
                .withProvisionedThroughput(ptIndex)
                .withKeySchema(new KeySchemaElement().withAttributeName("Title").withKeyType(KeyType.HASH), // Partition
                                                                                                            // key
                        new KeySchemaElement().withAttributeName("IssueId").withKeyType(KeyType.RANGE)) // Sort
                                                                                                        // key
                .withProjection(new Projection().withProjectionType("KEYS_ONLY"));

        // DueDateIndex
        GlobalSecondaryIndex dueDateIndex = new GlobalSecondaryIndex().withIndexName("DueDateIndex")
                .withProvisionedThroughput(ptIndex)
                .withKeySchema(new KeySchemaElement().withAttributeName("DueDate").withKeyType(KeyType.HASH)) // Partition
                                                                                                              // key
                .withProjection(new Projection().withProjectionType("ALL"));

        CreateTableRequest createTableRequest = new CreateTableRequest().withTableName(tableName)
                .withProvisionedThroughput(
                        new ProvisionedThroughput().withReadCapacityUnits((long) 1).withWriteCapacityUnits((long) 1))
                .withAttributeDefinitions(attributeDefinitions).withKeySchema(tableKeySchema)
                .withGlobalSecondaryIndexes(createDateIndex, titleIndex, dueDateIndex);

        System.out.println("Creating table " + tableName + "...");
        dynamoDB.createTable(createTableRequest);

        // Wait for table to become active
        System.out.println("Waiting for " + tableName + " to become ACTIVE...");
        try {
            Table table = dynamoDB.getTable(tableName);
            table.waitForActive();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void queryIndex(String indexName) {

        Table table = dynamoDB.getTable(tableName);

        System.out.println("\n***********************************************************\n");
        System.out.print("Querying index " + indexName + "...");

        Index index = table.getIndex(indexName);

        ItemCollection<QueryOutcome> items = null;

        QuerySpec querySpec = new QuerySpec();

        if (indexName == "CreateDateIndex") {
            System.out.println("Issues filed on 2013-11-01");
            querySpec.withKeyConditionExpression("CreateDate = :v_date and begins_with(IssueId, :v_issue)")
                    .withValueMap(new ValueMap().withString(":v_date", "2013-11-01").withString(":v_issue", "A-"));
            items = index.query(querySpec);
        } else if (indexName == "TitleIndex") {
            System.out.println("Compilation errors");
            querySpec.withKeyConditionExpression("Title = :v_title and begins_with(IssueId, :v_issue)")
                    .withValueMap(
                            new ValueMap().withString(":v_title", "Compilation error").withString(":v_issue", "A-"));
            items = index.query(querySpec);
        } else if (indexName == "DueDateIndex") {
            System.out.println("Items that are due on 2013-11-30");
            querySpec.withKeyConditionExpression("DueDate = :v_date")
                    .withValueMap(new ValueMap().withString(":v_date", "2013-11-30"));
            items = index.query(querySpec);
        } else {
            System.out.println("\nNo valid index name provided");
            return;
        }

        Iterator<Item> iterator = items.iterator();

        System.out.println("Query: printing results...");

        while (iterator.hasNext()) {
            System.out.println(iterator.next().toJSONPretty());
        }

    }

    public static void deleteTable(String tableName) {

        System.out.println("Deleting table " + tableName + "...");

        Table table = dynamoDB.getTable(tableName);
        table.delete();

        // Wait for table to be deleted
        System.out.println("Waiting for " + tableName + " to be deleted...");
        try {
            table.waitForDelete();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void loadData() {

        System.out.println("Loading data into table " + tableName + "...");

        // IssueId, Title,
        // Description,
        // CreateDate, LastUpdateDate, DueDate,
        // Priority, Status

        putItem("A-101", "Compilation error", "Can't compile Project X - bad version number. What does this mean?",
                "2013-11-01", "2013-11-02", "2013-11-10", 1, "Assigned");

        putItem("A-102", "Can't read data file", "The main data file is missing, or the permissions are incorrect",
                "2013-11-01", "2013-11-04", "2013-11-30", 2, "In progress");

        putItem("A-103", "Test failure", "Functional test of Project X produces errors", "2013-11-01", "2013-11-02",
                "2013-11-10", 1, "In progress");

        putItem("A-104", "Compilation error", "Variable 'messageCount' was not initialized.", "2013-11-15",
                "2013-11-16", "2013-11-30", 3, "Assigned");

        putItem("A-105", "Network issue", "Can't ping IP address 127.0.0.1. Please fix this.", "2013-11-15",
                "2013-11-16", "2013-11-19", 5, "Assigned");

    }

    public static void putItem(

            String issueId, String title, String description, String createDate, String lastUpdateDate, String dueDate,
            Integer priority, String status) {

        Table table = dynamoDB.getTable(tableName);

        Item item = new Item().withPrimaryKey("IssueId", issueId).withString("Title", title)
                .withString("Description", description).withString("CreateDate", createDate)
                .withString("LastUpdateDate", lastUpdateDate).withString("DueDate", dueDate)
                .withNumber("Priority", priority).withString("Status", status);

        table.putItem(item);
    }

}
```

# Trabajar con índices secundarios globales: .NET
<a name="GSILowLevelDotNet"></a>

Puede utilizar la API de bajo nivel AWS SDK para .NET para crear una tabla Amazon DynamoDB con uno o varios índices secundarios globales, describir los índices de la tabla y utilizarlos para realizar consultas. Estas operaciones se mapean a las operaciones correspondientes de DynamoDB. Para obtener más información, consulte la [Referencia de la API de Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/). 

A continuación se indican los pasos comunes para las operaciones con tablas mediante el API de bajo nivel de .NET. 

1. Cree una instancia de la clase `AmazonDynamoDBClient`.

1. Cree los objetos de solicitud correspondientes para proporcionar los parámetros obligatorios y opcionales de la operación.

   Por ejemplo, cree un objeto `CreateTableRequest` para crear una tabla y un objeto `QueryRequest` para consultar una tabla o un índice. 

1. Ejecute el método apropiado proporcionado por el cliente que ha creado en el paso anterior. 

**Topics**
+ [Creación de una tabla con un índice secundario global](#GSILowLevelDotNet.CreateTableWithIndex)
+ [Descripción de una tabla con un índice secundario global](#GSILowLevelDotNet.DescribeTableWithIndex)
+ [Consulta de un índice secundario local](#GSILowLevelDotNet.QueryAnIndex)
+ [Ejemplo: índices secundarios globales que usan la API de bajo nivel de AWS SDK para .NET](GSILowLevelDotNet.Example.md)

## Creación de una tabla con un índice secundario global
<a name="GSILowLevelDotNet.CreateTableWithIndex"></a>

Puede crear índices secundarios globales a la vez que crea la tabla. Para ello, utilice `CreateTable` e indique las especificaciones para uno o varios índices secundarios globales. En el ejemplo de código C\$1 siguiente, se crea una tabla para contener información sobre datos meteorológicos. La clave de partición es `Location` y la de ordenación, `Date`. Un índice secundario global denominado `PrecipIndex` permite obtener acceso rápido a los datos sobre precipitaciones de varias ubicaciones.

A continuación se indican los pasos que hay que seguir para crear una tabla con un índice secundario global mediante el API de bajo nivel de .NET. 

1. Cree una instancia de la clase `AmazonDynamoDBClient`.

1. Cree una instancia de la clase `CreateTableRequest` para proporcionar la información de solicitud. 

   Debe proporcionar el nombre de la tabla, su clave principal y los valores de rendimiento aprovisionado. Para el índice secundario global, debe proporcionar el nombre del índice, los ajustes de rendimiento aprovisionado, las definiciones de atributos de la clave de ordenación del índice, el esquema de claves del índice y la proyección de atributos.

1. Ejecute el método `CreateTable` proporcionando el objeto de solicitud como parámetro.

En el siguiente ejemplo de código C\$1 se ponen en práctica los pasos anteriores. El código crea una tabla (`WeatherData`) con un índice secundario global (`PrecipIndex`). La clave de partición del índice es `Date` y la de ordenación, `Precipitation`. Todos los atributos de la tabla se proyectan en el índice. Los usuarios pueden consultar este índice para obtener los datos meteorológicos de una determinada fecha y, si lo desean, ordenarlos según la cantidad de precipitación. 

Ya que `Precipitation` no es un atributo de clave para la tabla, no es obligatorio. Sin embargo, los elementos`WeatherData` sin `Precipitation` no aparecen en `PrecipIndex`.

```
client = new AmazonDynamoDBClient();
string tableName = "WeatherData";

// Attribute definitions
var attributeDefinitions = new List<AttributeDefinition>()
{
    {new AttributeDefinition{
        AttributeName = "Location",
        AttributeType = "S"}},
    {new AttributeDefinition{
        AttributeName = "Date",
        AttributeType = "S"}},
    {new AttributeDefinition(){
        AttributeName = "Precipitation",
        AttributeType = "N"}
    }
};

// Table key schema
var tableKeySchema = new List<KeySchemaElement>()
{
    {new KeySchemaElement {
        AttributeName = "Location",
        KeyType = "HASH"}},  //Partition key
    {new KeySchemaElement {
        AttributeName = "Date",
        KeyType = "RANGE"}  //Sort key
    }
};

// PrecipIndex
var precipIndex = new GlobalSecondaryIndex
{
    IndexName = "PrecipIndex",
    ProvisionedThroughput = new ProvisionedThroughput
    {
        ReadCapacityUnits = (long)10,
        WriteCapacityUnits = (long)1
    },
    Projection = new Projection { ProjectionType = "ALL" }
};

var indexKeySchema = new List<KeySchemaElement> {
    {new KeySchemaElement { AttributeName = "Date", KeyType = "HASH"}},  //Partition key
    {new KeySchemaElement{AttributeName = "Precipitation",KeyType = "RANGE"}}  //Sort key
};

precipIndex.KeySchema = indexKeySchema;

CreateTableRequest createTableRequest = new CreateTableRequest
{
    TableName = tableName,
    ProvisionedThroughput = new ProvisionedThroughput
    {
        ReadCapacityUnits = (long)5,
        WriteCapacityUnits = (long)1
    },
    AttributeDefinitions = attributeDefinitions,
    KeySchema = tableKeySchema,
    GlobalSecondaryIndexes = { precipIndex }
};

CreateTableResponse response = client.CreateTable(createTableRequest);
Console.WriteLine(response.CreateTableResult.TableDescription.TableName);
Console.WriteLine(response.CreateTableResult.TableDescription.TableStatus);
```

Debe esperar hasta que DynamoDB cree la tabla y establezca el estado de esta última en `ACTIVE`. A partir de ese momento, puede comenzar a incluir elementos de datos en la tabla.

## Descripción de una tabla con un índice secundario global
<a name="GSILowLevelDotNet.DescribeTableWithIndex"></a>

Para obtener información sobre los índices secundarios globales en una tabla, use `DescribeTable`. Para cada índice, puede obtener acceso a su nombre, esquema de claves y atributos proyectados.

A continuación se indican los pasos que hay que seguir para acceder a la información de un índice secundario global de una tabla mediante el API de bajo nivel de .NET. 

1. Cree una instancia de la clase `AmazonDynamoDBClient`.

1. Ejecute el método `describeTable` proporcionando el objeto de solicitud como parámetro.

   Cree una instancia de la clase `DescribeTableRequest` para proporcionar la información de solicitud. Debe proporcionar el nombre de la tabla.

En el siguiente ejemplo de código C\$1 se ponen en práctica los pasos anteriores.

**Example**  

```
client = new AmazonDynamoDBClient();
string tableName = "WeatherData";

DescribeTableResponse response = client.DescribeTable(new DescribeTableRequest { TableName = tableName});

List<GlobalSecondaryIndexDescription> globalSecondaryIndexes =
response.DescribeTableResult.Table.GlobalSecondaryIndexes;

// This code snippet will work for multiple indexes, even though
// there is only one index in this example.

foreach (GlobalSecondaryIndexDescription gsiDescription in globalSecondaryIndexes) {
     Console.WriteLine("Info for index " + gsiDescription.IndexName + ":");

     foreach (KeySchemaElement kse in gsiDescription.KeySchema) {
          Console.WriteLine("\t" + kse.AttributeName + ": key type is " + kse.KeyType);
     }

      Projection projection = gsiDescription.Projection;
      Console.WriteLine("\tThe projection type is: " + projection.ProjectionType);

      if (projection.ProjectionType.ToString().Equals("INCLUDE")) {
           Console.WriteLine("\t\tThe non-key projected attributes are: "
                + projection.NonKeyAttributes);
      }
}
```

## Consulta de un índice secundario local
<a name="GSILowLevelDotNet.QueryAnIndex"></a>

Puede utilizar `Query` en un índice secundario global, de un modo bastante similar al que realiza una `Query` en una tabla. Debe especificar el nombre del índice, los criterios de consulta de la clave de partición y la clave de ordenación (si procede) del índice y los atributos que desea devolver. En este ejemplo, el índice es `PrecipIndex`, cuyas clave de partición y ordenación son, respectivamente, `Date` y `Precipitation`. La consulta del índice devuelve todos los datos meteorológicos de una determinada fecha cuando el valor de Precipitation sea mayor que cero.

A continuación se indican los pasos que hay que seguir para consultar un índice secundario global mediante el API de bajo nivel de .NET. 

1. Cree una instancia de la clase `AmazonDynamoDBClient`.

1. Cree una instancia de la clase `QueryRequest` para proporcionar la información de solicitud.

1. Ejecute el método `query` proporcionando el objeto de solicitud como parámetro.

El nombre de atributo `Date` es una palabra reservada de DynamoDB. Por consiguiente, debe usar un nombre de atributo de expresión como marcador de posición en la expresión `KeyConditionExpression`.

En el siguiente ejemplo de código C\$1 se ponen en práctica los pasos anteriores.

**Example**  

```
client = new AmazonDynamoDBClient();

QueryRequest queryRequest = new QueryRequest
{
    TableName = "WeatherData",
    IndexName = "PrecipIndex",
    KeyConditionExpression = "#dt = :v_date and Precipitation > :v_precip",
    ExpressionAttributeNames = new Dictionary<String, String> {
        {"#dt", "Date"}
    },
    ExpressionAttributeValues = new Dictionary<string, AttributeValue> {
        {":v_date", new AttributeValue { S =  "2013-08-01" }},
        {":v_precip", new AttributeValue { N =  "0" }}
    },
    ScanIndexForward = true
};

var result = client.Query(queryRequest);

var items = result.Items;
foreach (var currentItem in items)
{
    foreach (string attr in currentItem.Keys)
    {
        Console.Write(attr + "---> ");
        if (attr == "Precipitation")
        {
            Console.WriteLine(currentItem[attr].N);
    }
    else
    {
        Console.WriteLine(currentItem[attr].S);
    }

         }
     Console.WriteLine();
}
```

# Ejemplo: índices secundarios globales que usan la API de bajo nivel de AWS SDK para .NET
<a name="GSILowLevelDotNet.Example"></a>

En el siguiente ejemplo de código C\$1 se muestra cómo usar índices secundarios globales. En el ejemplo se crea una tabla denominada `Issues`, que puede utilizarse en un sistema sencillo de seguimiento de errores con fines de desarrollo de software. La clave de partición es `IssueId` y la de ordenación, `Title`. Hay tres índices secundarios globales en esta tabla:
+ `CreateDateIndex`: la clave de partición es `CreateDate` y la de ordenación `IssueId`. Además de las claves de tabla, se proyectan en el índice los atributos `Description` y `Status`.
+ `TitleIndex`: la clave de partición es `Title` y la de ordenación `IssueId`. No se proyecta en el índice ningún otro atributo excepto las claves de tabla.
+ `DueDateIndex`: la clave de partición es `DueDate` y no hay clave de ordenación. Todos los atributos de la tabla se proyectan en el índice.

Una vez que se ha creado la tabla `Issues`, el programa carga la tabla con datos que representan informes de errores de software. A continuación, consulta los datos utilizando los índices secundarios globales. Por último, el programa elimina la tabla `Issues`.

Para obtener instrucciones paso a paso para probar el siguiente ejemplo, consulte [Ejemplos de código .NET](CodeSamples.DotNet.md).

**Example**  

```
using System;
using System.Collections.Generic;
using System.Linq;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;
using Amazon.DynamoDBv2.DocumentModel;
using Amazon.DynamoDBv2.Model;
using Amazon.Runtime;
using Amazon.SecurityToken;

namespace com.amazonaws.codesamples
{
    class LowLevelGlobalSecondaryIndexExample
    {
        private static AmazonDynamoDBClient client = new AmazonDynamoDBClient();
        public static String tableName = "Issues";

        public static void Main(string[] args)
        {
            CreateTable();
            LoadData();

            QueryIndex("CreateDateIndex");
            QueryIndex("TitleIndex");
            QueryIndex("DueDateIndex");

            DeleteTable(tableName);

            Console.WriteLine("To continue, press enter");
            Console.Read();
        }

        private static void CreateTable()
        {
            // Attribute definitions
            var attributeDefinitions = new List<AttributeDefinition>()
        {
            {new AttributeDefinition {
                 AttributeName = "IssueId", AttributeType = "S"
             }},
            {new AttributeDefinition {
                 AttributeName = "Title", AttributeType = "S"
             }},
            {new AttributeDefinition {
                 AttributeName = "CreateDate", AttributeType = "S"
             }},
            {new AttributeDefinition {
                 AttributeName = "DueDate", AttributeType = "S"
             }}
        };

            // Key schema for table
            var tableKeySchema = new List<KeySchemaElement>() {
            {
                new KeySchemaElement {
                    AttributeName= "IssueId",
                    KeyType = "HASH" //Partition key
                }
            },
            {
                new KeySchemaElement {
                    AttributeName = "Title",
                    KeyType = "RANGE" //Sort key
                }
            }
        };

            // Initial provisioned throughput settings for the indexes
            var ptIndex = new ProvisionedThroughput
            {
                ReadCapacityUnits = 1L,
                WriteCapacityUnits = 1L
            };

            // CreateDateIndex
            var createDateIndex = new GlobalSecondaryIndex()
            {
                IndexName = "CreateDateIndex",
                ProvisionedThroughput = ptIndex,
                KeySchema = {
                new KeySchemaElement {
                    AttributeName = "CreateDate", KeyType = "HASH" //Partition key
                },
                new KeySchemaElement {
                    AttributeName = "IssueId", KeyType = "RANGE" //Sort key
                }
            },
                Projection = new Projection
                {
                    ProjectionType = "INCLUDE",
                    NonKeyAttributes = {
                    "Description", "Status"
                }
                }
            };

            // TitleIndex
            var titleIndex = new GlobalSecondaryIndex()
            {
                IndexName = "TitleIndex",
                ProvisionedThroughput = ptIndex,
                KeySchema = {
                new KeySchemaElement {
                    AttributeName = "Title", KeyType = "HASH" //Partition key
                },
                new KeySchemaElement {
                    AttributeName = "IssueId", KeyType = "RANGE" //Sort key
                }
            },
                Projection = new Projection
                {
                    ProjectionType = "KEYS_ONLY"
                }
            };

            // DueDateIndex
            var dueDateIndex = new GlobalSecondaryIndex()
            {
                IndexName = "DueDateIndex",
                ProvisionedThroughput = ptIndex,
                KeySchema = {
                new KeySchemaElement {
                    AttributeName = "DueDate",
                    KeyType = "HASH" //Partition key
                }
            },
                Projection = new Projection
                {
                    ProjectionType = "ALL"
                }
            };



            var createTableRequest = new CreateTableRequest
            {
                TableName = tableName,
                ProvisionedThroughput = new ProvisionedThroughput
                {
                    ReadCapacityUnits = (long)1,
                    WriteCapacityUnits = (long)1
                },
                AttributeDefinitions = attributeDefinitions,
                KeySchema = tableKeySchema,
                GlobalSecondaryIndexes = {
                createDateIndex, titleIndex, dueDateIndex
            }
            };

            Console.WriteLine("Creating table " + tableName + "...");
            client.CreateTable(createTableRequest);

            WaitUntilTableReady(tableName);
        }

        private static void LoadData()
        {
            Console.WriteLine("Loading data into table " + tableName + "...");

            // IssueId, Title,
            // Description,
            // CreateDate, LastUpdateDate, DueDate,
            // Priority, Status

            putItem("A-101", "Compilation error",
                "Can't compile Project X - bad version number. What does this mean?",
                "2013-11-01", "2013-11-02", "2013-11-10",
                1, "Assigned");

            putItem("A-102", "Can't read data file",
                "The main data file is missing, or the permissions are incorrect",
                "2013-11-01", "2013-11-04", "2013-11-30",
                2, "In progress");

            putItem("A-103", "Test failure",
                "Functional test of Project X produces errors",
                "2013-11-01", "2013-11-02", "2013-11-10",
                1, "In progress");

            putItem("A-104", "Compilation error",
                "Variable 'messageCount' was not initialized.",
                "2013-11-15", "2013-11-16", "2013-11-30",
                3, "Assigned");

            putItem("A-105", "Network issue",
                "Can't ping IP address 127.0.0.1. Please fix this.",
                "2013-11-15", "2013-11-16", "2013-11-19",
                5, "Assigned");
        }

        private static void putItem(
            String issueId, String title,
            String description,
            String createDate, String lastUpdateDate, String dueDate,
            Int32 priority, String status)
        {
            Dictionary<String, AttributeValue> item = new Dictionary<string, AttributeValue>();

            item.Add("IssueId", new AttributeValue
            {
                S = issueId
            });
            item.Add("Title", new AttributeValue
            {
                S = title
            });
            item.Add("Description", new AttributeValue
            {
                S = description
            });
            item.Add("CreateDate", new AttributeValue
            {
                S = createDate
            });
            item.Add("LastUpdateDate", new AttributeValue
            {
                S = lastUpdateDate
            });
            item.Add("DueDate", new AttributeValue
            {
                S = dueDate
            });
            item.Add("Priority", new AttributeValue
            {
                N = priority.ToString()
            });
            item.Add("Status", new AttributeValue
            {
                S = status
            });

            try
            {
                client.PutItem(new PutItemRequest
                {
                    TableName = tableName,
                    Item = item
                });
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        private static void QueryIndex(string indexName)
        {
            Console.WriteLine
                ("\n***********************************************************\n");
            Console.WriteLine("Querying index " + indexName + "...");

            QueryRequest queryRequest = new QueryRequest
            {
                TableName = tableName,
                IndexName = indexName,
                ScanIndexForward = true
            };


            String keyConditionExpression;
            Dictionary<string, AttributeValue> expressionAttributeValues = new Dictionary<string, AttributeValue>();

            if (indexName == "CreateDateIndex")
            {
                Console.WriteLine("Issues filed on 2013-11-01\n");

                keyConditionExpression = "CreateDate = :v_date and begins_with(IssueId, :v_issue)";
                expressionAttributeValues.Add(":v_date", new AttributeValue
                {
                    S = "2013-11-01"
                });
                expressionAttributeValues.Add(":v_issue", new AttributeValue
                {
                    S = "A-"
                });
            }
            else if (indexName == "TitleIndex")
            {
                Console.WriteLine("Compilation errors\n");

                keyConditionExpression = "Title = :v_title and begins_with(IssueId, :v_issue)";
                expressionAttributeValues.Add(":v_title", new AttributeValue
                {
                    S = "Compilation error"
                });
                expressionAttributeValues.Add(":v_issue", new AttributeValue
                {
                    S = "A-"
                });

                // Select
                queryRequest.Select = "ALL_PROJECTED_ATTRIBUTES";
            }
            else if (indexName == "DueDateIndex")
            {
                Console.WriteLine("Items that are due on 2013-11-30\n");

                keyConditionExpression = "DueDate = :v_date";
                expressionAttributeValues.Add(":v_date", new AttributeValue
                {
                    S = "2013-11-30"
                });

                // Select
                queryRequest.Select = "ALL_PROJECTED_ATTRIBUTES";
            }
            else
            {
                Console.WriteLine("\nNo valid index name provided");
                return;
            }

            queryRequest.KeyConditionExpression = keyConditionExpression;
            queryRequest.ExpressionAttributeValues = expressionAttributeValues;

            var result = client.Query(queryRequest);
            var items = result.Items;
            foreach (var currentItem in items)
            {
                foreach (string attr in currentItem.Keys)
                {
                    if (attr == "Priority")
                    {
                        Console.WriteLine(attr + "---> " + currentItem[attr].N);
                    }
                    else
                    {
                        Console.WriteLine(attr + "---> " + currentItem[attr].S);
                    }
                }
                Console.WriteLine();
            }
        }

        private static void DeleteTable(string tableName)
        {
            Console.WriteLine("Deleting table " + tableName + "...");
            client.DeleteTable(new DeleteTableRequest
            {
                TableName = tableName
            });
            WaitForTableToBeDeleted(tableName);
        }

        private static void WaitUntilTableReady(string tableName)
        {
            string status = null;
            // Let us wait until table is created. Call DescribeTable.
            do
            {
                System.Threading.Thread.Sleep(5000); // Wait 5 seconds.
                try
                {
                    var res = client.DescribeTable(new DescribeTableRequest
                    {
                        TableName = tableName
                    });

                    Console.WriteLine("Table name: {0}, status: {1}",
                              res.Table.TableName,
                              res.Table.TableStatus);
                    status = res.Table.TableStatus;
                }
                catch (ResourceNotFoundException)
                {
                    // DescribeTable is eventually consistent. So you might
                    // get resource not found. So we handle the potential exception.
                }
            } while (status != "ACTIVE");
        }

        private static void WaitForTableToBeDeleted(string tableName)
        {
            bool tablePresent = true;

            while (tablePresent)
            {
                System.Threading.Thread.Sleep(5000); // Wait 5 seconds.
                try
                {
                    var res = client.DescribeTable(new DescribeTableRequest
                    {
                        TableName = tableName
                    });

                    Console.WriteLine("Table name: {0}, status: {1}",
                              res.Table.TableName,
                              res.Table.TableStatus);
                }
                catch (ResourceNotFoundException)
                {
                    tablePresent = false;
                }
            }
        }
    }
}
```

# Uso de índices secundarios globales en DynamoDB con la AWS CLI
<a name="GCICli"></a>

Puede utilizar la AWS CLI para crear una tabla de Amazon DynamoDB con uno o varios índices secundarios globales, describir los índices de la tabla y utilizarlos para realizar consultas.

**Topics**
+ [Creación de una tabla con un índice secundario global](#GCICli.CreateTableWithIndex)
+ [Adición de un índice secundario global a una tabla existente](#GCICli.CreateIndexAfterTable)
+ [Descripción de una tabla con un índice secundario global](#GCICli.DescribeTableWithIndex)
+ [Consulta de un índice secundario local](#GCICli.QueryAnIndex)

## Creación de una tabla con un índice secundario global
<a name="GCICli.CreateTableWithIndex"></a>

Se pueden crear índices secundarios globales al mismo tiempo que se crea una tabla. Para ello, utilice el parámetro `create-table` y proporcione sus especificaciones para uno o más índices secundarios globales. En el siguiente ejemplo, se crea una tabla llamada `GameScores` con un índice secundario global `GameTitleIndex`. La tabla base tiene una clave de partición de `UserId` y una clave de ordenación de `GameTitle`, lo que le permite encontrar eficientemente la mejor puntuación de un usuario individual para un juego específico, mientras que el GSI tiene una clave de partición de `GameTitle` y una clave de ordenación de `TopScore`, lo que te permite encontrar rápidamente la puntuación más alta en general para un juego en particular.

```
aws dynamodb create-table \
    --table-name GameScores \
    --attribute-definitions AttributeName=UserId,AttributeType=S \
                            AttributeName=GameTitle,AttributeType=S \
                            AttributeName=TopScore,AttributeType=N  \
    --key-schema AttributeName=UserId,KeyType=HASH \
                 AttributeName=GameTitle,KeyType=RANGE \
    --provisioned-throughput ReadCapacityUnits=10,WriteCapacityUnits=5 \
    --global-secondary-indexes \
        "[
            {
                \"IndexName\": \"GameTitleIndex\",
                \"KeySchema\": [{\"AttributeName\":\"GameTitle\",\"KeyType\":\"HASH\"},
                                {\"AttributeName\":\"TopScore\",\"KeyType\":\"RANGE\"}],
                \"Projection\":{
                    \"ProjectionType\":\"INCLUDE\",
                    \"NonKeyAttributes\":[\"UserId\"]
                },
                \"ProvisionedThroughput\": {
                    \"ReadCapacityUnits\": 10,
                    \"WriteCapacityUnits\": 5
                }
            }
        ]"
```

Debe esperar hasta que DynamoDB cree la tabla y establezca el estado de esta última en `ACTIVE`. A partir de ese momento, puede comenzar a incluir elementos de datos en la tabla. Puede utilizar [describe-table](https://docs.aws.amazon.com/cli/latest/reference/dynamodb/describe-table.html) Para determinar el estado de la creación de tablas.

## Adición de un índice secundario global a una tabla existente
<a name="GCICli.CreateIndexAfterTable"></a>

Los índices secundarios globales también se pueden agregar o modificar después de la creación de la tabla. Para ello, utilice el parámetro `update-table` y proporcione sus especificaciones para uno o más índices secundarios globales. En el siguiente ejemplo se utiliza el mismo esquema que el ejemplo anterior, pero se supone que la tabla ya se ha creado y que vamos a agregar el GSI más adelante.

```
aws dynamodb update-table \
    --table-name GameScores \
    --attribute-definitions AttributeName=TopScore,AttributeType=N  \
    --global-secondary-index-updates \
        "[
            {
                \"Create\": {
                    \"IndexName\": \"GameTitleIndex\",
                    \"KeySchema\": [{\"AttributeName\":\"GameTitle\",\"KeyType\":\"HASH\"},
                                    {\"AttributeName\":\"TopScore\",\"KeyType\":\"RANGE\"}],
                    \"Projection\":{
                        \"ProjectionType\":\"INCLUDE\",
                        \"NonKeyAttributes\":[\"UserId\"]
                    }
                }
            }
        ]"
```

## Descripción de una tabla con un índice secundario global
<a name="GCICli.DescribeTableWithIndex"></a>

Para obtener información acerca de los índices secundarios globales en una tabla, utilice el parámetro `describe-table`. Para cada índice, puede obtener acceso a su nombre, esquema de claves y atributos proyectados.

```
aws dynamodb describe-table --table-name GameScores
```

## Consulta de un índice secundario local
<a name="GCICli.QueryAnIndex"></a>

Puede utilizar la operación `query` en un índice secundario global de un modo bastante similar a como se usa `query` en una tabla. Debe especificar el nombre del índice, los criterios de consulta de la clave de ordenación del índice y los atributos que desea devolver. En este ejemplo, el índice es `GameTitleIndex` y la clave de ordenación del índice es `GameTitle`.

Los únicos atributos devueltos son aquellos que se han proyectado en el índice. Puede modificar esta consulta de modo que también seleccione atributos sin clave, pero esto requeriría realizar actividad de recuperación en la tabla, lo que resulta relativamente costoso. Para obtener más información sobre recuperaciones de tablas, consulte [Proyecciones de atributos](GSI.md#GSI.Projections).

```
aws dynamodb query --table-name GameScores\
    --index-name GameTitleIndex \
    --key-condition-expression "GameTitle = :v_game" \
    --expression-attribute-values '{":v_game":{"S":"Alien Adventure"} }'
```

# Índices secundarios locales
<a name="LSI"></a>

Algunas aplicaciones solo necesitan consultar datos mediante la clave principal de la tabla base. Sin embargo, puede haber situaciones en las que una clave de ordenación alternativa sería útil. Para que la aplicación disponga de diversas claves de ordenación entre las que elegir, puede crear uno o varios índices secundarios en una tabla de Amazon DynamoDB y emitir solicitudes `Query` o `Scan` para estos índices.

**Topics**
+ [Situación: Uso de un índice secundario local](#LSI.Scenario)
+ [Proyecciones de atributos](#LSI.Projections)
+ [Creación de un índice secundario local](#LSI.Creating)
+ [Lectura de datos de un índice secundario local](#LSI.Reading)
+ [Escrituras de elementos e índices secundarios locales](#LSI.Writes)
+ [Consideraciones sobre el rendimiento aprovisionado para los índices secundarios locales](#LSI.ThroughputConsiderations)
+ [Consideraciones sobre el almacenamiento para los índices secundarios locales](#LSI.StorageConsiderations)
+ [Colecciones de elementos en los índices secundarios locales](#LSI.ItemCollections)
+ [Trabajar con índices secundarios locales: Java](LSIJavaDocumentAPI.md)
+ [Trabajar con índices secundarios locales: .NET](LSILowLevelDotNet.md)
+ [Uso de índices secundarios locales en la AWS CLI de DynamoDB](LCICli.md)

## Situación: Uso de un índice secundario local
<a name="LSI.Scenario"></a>

Como ejemplo, veamos la tabla `Thread`. Esta tabla es útil para una aplicación como, por ejemplo, [Foros de debate de AWS](https://forums.aws.amazon.com/). En el siguiente diagrama se muestra cómo se organizarían los elementos de la tabla. No se muestran todos los atributos.

![\[Tabla de subprocesos que contiene una lista de nombres de foro, temas, hora de la última publicación y número de respuestas.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/LSI_01.png)


DynamoDB almacena todos los elementos que tienen el mismo valor de clave de partición de forma continua. En este ejemplo, para un valor determinado de `ForumName`, una operación `Query` podría localizar inmediatamente todas las conversaciones de ese foro. Dentro de un grupo de elementos con el mismo valor de clave de partición, los elementos se ordenan según el valor de la clave de ordenación. Si la clave de ordenación (`Subject`) también se incluye en la consulta, DynamoDB puede afinar los resultados que se devuelven; por ejemplo, devolvería todas las conversaciones del foro "S3" cuyo valor de `Subject` comience por la letra "a".

Algunas solicitudes podrían requerir patrones más complejos de acceso a los datos. Por ejemplo:
+ ¿Qué conversaciones del foro son las que suscitan más visualizaciones y respuestas?
+ ¿Qué conversación de un foro determinado tiene mayor cantidad de mensajes?
+ ¿Cuántas conversaciones se han publicado en un foro determinado dentro de un periodo concreto?

Para responder a estas preguntas, la acción `Query` no bastaría. En su lugar, habría que aplicar `Scan` a la tabla completa. Si la tabla contiene millones de elementos, esto consumiría una cantidad enorme de desempeño de lectura provisionado y tardaría mucho tiempo en completarse.

Sin embargo, puede especificar uno o más índices secundarios locales en atributos que no sean clave, tales como `Replies` or `LastPostDateTime`.

Un *índice secundario local* mantiene una clave de ordenación alternativa para un determinado valor de clave de partición. Un índice secundario local también contiene una copia parcial o total de los atributos de la tabla base. Al crear la tabla, se especifican qué atributos se proyectarán en el índice secundario local. Los datos de un índice secundario local se organizan según la misma clave de partición que la tabla base, pero con una clave de ordenación distinta. Esto le permite obtener acceso a los elementos de datos de forma eficiente en esta otra dimensión. Para lograr una mayor flexibilidad de consulta o examen, puede crear hasta cinco índices secundarios locales por tabla. 

Supongamos que una aplicación tiene que encontrar todas las conversaciones que se han publicado en los últimos tres meses en un foro particular. Sin un índice secundario local, la aplicación tendría que aplicar la operación `Scan` a toda la tabla `Thread` y, después, descartar aquellas publicaciones que no estuviesen comprendidos en el periodo especificado. Con un índice secundario local una operación `Query` podría usar `LastPostDateTime` como clave de ordenación para encontrar rápidamente los datos.

En el siguiente diagrama se muestra un índice secundario local denominado `LastPostIndex`. Tenga en cuenta que la clave de partición es la misma que para la tabla `Thread`, pero que la clave de ordenación es `LastPostDateTime`.

![\[La tabla LastPostIndex que contiene una lista de nombres de foro, temas y última hora de publicación.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/LSI_02.png)


Cada índice secundario local debe cumplir las condiciones siguientes:
+ La clave de partición ha de ser la misma que aquella de la tabla base.
+ La clave de ordenación consta exactamente de un atributo escalar.
+ La clave de ordenación de la tabla base se proyectará en el índice, donde actuará como atributo sin clave.

En este ejemplo, la clave de partición es `ForumName` y la clave de ordenación del índice secundario local es `LastPostDateTime`. Además, el valor de clave de ordenación de la tabla base (en este ejemplo, `Subject`) se proyecta en el índice, aunque no forma parte de la clave de índice. Si una aplicación requiere una lista basada en `ForumName` y `LastPostDateTime`, puede emitir una solicitud `Query` para `LastPostIndex`. Los resultados de la consulta se ordenan por `LastPostDateTime` y se pueden devolver en orden ascendente o descendente. La consulta también puede aplicar condiciones de clave, tales como devolver solamente aquellos elementos cuyo valor de `LastPostDateTime` esté comprendido en un periodo determinado.

Cada índice secundario local contiene automáticamente las claves de partición y ordenación de su tabla base. Si lo desea, puede proyectar atributos sin clave en el índice. Cuando se consulta el índice, DynamoDB puede recuperar estos atributos proyectados de forma eficiente. Cuando se consulta un índice secuandario local, la consulta también puede recuperar atributos *no* proyectados en el índice. DynamoDB recuperará automáticamente estos atributos de la tabla base, pero con una mayor latencia y costes más elevados de rendimiento aprovisionado.

Para cualquier índice secundario local, puede almacenar hasta 10 GB de datos por cada valor diferente de clave de partición. Esta cifra incluye todos los elementos de la tabla base, además de todos los elementos de los índices, que tengan el mismo valor de clave de partición. Para obtener más información, consulte [Colecciones de elementos en los índices secundarios locales](#LSI.ItemCollections).

## Proyecciones de atributos
<a name="LSI.Projections"></a>

Con `LastPostIndex`, una aplicación podría usar `ForumName` y `LastPostDateTime` como criterios de consulta. Sin embargo, para recuperar cualquier atributo adicional, DynamoDB debe realizar operaciones de lectura adicionales en la tabla `Thread`. Estas lecturas adicionales se denominan *recuperaciones* y pueden aumentar la cantidad total de desempeño provisionado necesario para una consulta.

Supongamos que desea rellenar una página web con una lista de todas las conversaciones del foro "S3" y el número de respuestas de cada conversación, ordenadas según la fecha y hora de la última respuesta y comenzando por la respuesta más reciente. Para rellenar esta lista, se requieren los siguientes atributos:
+ `Subject`
+ `Replies`
+ `LastPostDateTime`

La forma más eficiente de consultar estos datos y evitar operaciones de recuperación, sería proyectar el atributo `Replies`de la tabla en el índice secundario global, tal y como se muestra en este diagrama.

![\[Tabla LastPostIndex que contiene una lista de nombres de foro, horas de las últimas publicaciones, temas y respuestas.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/LSI_03.png)




Una *proyección* es el conjunto de atributos que se copia de una tabla en un índice secundario. La clave de partición y la clave de ordenación de la tabla siempre se proyectan en el índice; puede proyectar otros atributos para admitir los requisitos de consulta de la aplicación. Cuando consulta un índice, Amazon DynamoDB puede acceder a cualquier atributo de la proyección como si esos atributos estuvieran en una tabla propia.

Al crear un índice secundario, debe especificar los atributos que se proyectarán en el índice. DynamoDB ofrece tres opciones diferentes para esto:
+ *KEYS\$1ONLY*: cada elemento del índice consta únicamente de los valores de la clave de partición y la clave de ordenación de la tabla, así como de los valores de las claves del índice. La opción `KEYS_ONLY` da como resultado el índice secundario más pequeño posible.
+ *INCLUDE*: además de los atributos que se describen en `KEYS_ONLY`, el índice secundario incluirá otros atributos sin clave que se especifiquen.
+ *ALL*: el índice secundario incluye todos los atributos de la tabla de origen. Debido a que todos los datos de la tabla están duplicados en el índice, un resultado de proyección `ALL` en el índice secundario más grande posible.

En el diagrama anterior, el atributo sin clave `Replies` se proyecta en `LastPostIndex`. Una aplicación puede consultar `LastPostIndex` en lugar de `Thread` completo para rellenar una página web con `Subject`, `Replies` y `LastPostDateTime`. Si se solicitasen otros atributos sin clave, DynamoDB tendría que recuperarlos en la tabla `Thread`. 

Desde el punto de vista de una aplicación, la recuperación de atributos adicionales de la tabla base se lleva a cabo de forma automática y transparente, por lo que no es necesario volver a escribir la lógica de la aplicación. No obstante, este tipo de recuperaciones puede reducir en gran medida el efecto beneficioso para el desempeño que aporta el uso de un índice secundario local.

Cuando elija los atributos para proyectarlos en un índice secundario local, debe estudiar el equilibrio entre los costes de rendimiento aprovisionado y de almacenamiento:
+ Si solo necesita acceder a algunos atributos con la menor latencia posible, puede ser conveniente proyectar solamente esos atributos en un índice secundario local. Cuando menor es el índice, menos cuesta almacenarlo y menos son los costes de escritura. Si hay atributos que solo tiene que recuperar de vez en cuando, el costo del desempeño provisionado podría superar con creces el costo a largo plazo de almacenar esos atributos.
+ Si la aplicación va a obtener acceso con frecuencia a determinados atributos sin clave, puede ser interesante proyectarlos en un índice secundario local. Los costes del almacenamiento adicionales del índice secundario local compensarán el coste que supondrían los frecuentes exámenes de la tabla.
+ Si tiene que acceder a la mayoría de los atributos sin clave con frecuencia, puede proyectar estos atributos o, incluso, toda la tabla base, en un índice secundario local. De este modo, dispondrá de la máxima flexibilidad y el menor consumo de desempeño provisionado, porque no será preciso realizar recuperaciones. Sin embargo, el coste del almacenamiento aumentaría y podría llegar a duplicarse si se proyectan todos los atributos.
+ Si la aplicación tiene que consultar una tabla con poca frecuencia, pero tiene que realizar gran cantidad de escrituras o actualizaciones en los datos de la tabla, puede ser conveniente proyectar *KEYS\$1ONLY*. El índice secundario local tendrá el tamaño mínimo, pero estaría disponible siempre que se requiriese para actividades de consulta. 

## Creación de un índice secundario local
<a name="LSI.Creating"></a>

Para crear una tabla con uno o varios índices secundarios locales, use el parámetro `LocalSecondaryIndexes` de la operación `CreateTable`. Los índices secundarios locales de una tabla se crean cuando se crea la tabla. Al eliminar una tabla, todos los índices secundarios globales de esa tabla se eliminan también.

Debe especificar un atributo sin clave que actúe como clave de ordenación del índice secundario local. El atributo que elija debe ser un escalar `String`, `Number` o `Binary`. No se admite ningún otro tipo de escalar, documento ni conjunto. Para obtener una lista completa de los tipos de datos, consulte [Tipos de datos](HowItWorks.NamingRulesDataTypes.md#HowItWorks.DataTypes).

**importante**  
Para las tablas con índices secundarios locales, existe un límite de tamaño de 10 GB por valor de clave de partición. Una tabla con índices secundarios locales puede almacenar cualquier número de elementos, siempre y cuando el tamaño total de cualquier valor de clave de partición individual no supere los 10 GB. Para obtener más información, consulte [Límite del tamaño de una colección de elementos](#LSI.ItemCollections.SizeLimit).

Puede proyectar atributos de cualquier tipo de datos en un índice secundario local. Esto incluye escalares, documentos y conjuntos. Para obtener una lista completa de los tipos de datos, consulte [Tipos de datos](HowItWorks.NamingRulesDataTypes.md#HowItWorks.DataTypes).

## Lectura de datos de un índice secundario local
<a name="LSI.Reading"></a>

Puede recuperar elementos de un índice secundario local utilizando las operaciones `Query` y `Scan`. Las operaciones `GetItem` y `BatchGetItem` no se pueden usar en un índice secundario local.

### Consulta a un índice secundario local
<a name="LSI.Querying"></a>

En una tabla de DynamoDB, la combinación de valor de clave de partición y valor de clave de ordenación de cada elemento debe ser única. Sin embargo, en un índice secundario local, no es preciso que el valor de la clave de ordenación sea exclusivo para un determinado valor de clave de partición. Si un índice secundario local contiene varios elementos que tienen el mismo valor de clave de ordenación, una operación `Query` devolverá todos los elementos que tengan el mismo valor de clave de partición. En la respuesta, los elementos coincidentes no se devuelven en ningún orden concreto.

Puede consultar un índice secundario local usando lecturas consistentes finales o de consistencia alta. Para especificar qué tipo de consistencia desea aplicar, se utiliza el parámetro `ConsistentRead`Consistentead de la operación `Query`. Una lectura de consistencia alta de un índice secundario local siempre devolverá los valores actualizados más recientemente. Si la consulta tiene que recuperar atributos adicionales de la tabla base, estos serán consistentes respecto al índice.

**Example**  
Fíjese en los datos siguientes devueltos por una operación `Query` que solicita datos de las conversaciones de un foro determinado.  

```
{
    "TableName": "Thread",
    "IndexName": "LastPostIndex",
    "ConsistentRead": false,
    "ProjectionExpression": "Subject, LastPostDateTime, Replies, Tags",
    "KeyConditionExpression": 
        "ForumName = :v_forum and LastPostDateTime between :v_start and :v_end",
    "ExpressionAttributeValues": {
        ":v_start": {"S": "2015-08-31T00:00:00.000Z"},
        ":v_end": {"S": "2015-11-31T00:00:00.000Z"},
        ":v_forum": {"S": "EC2"}
    }
}
```
En esta consulta:  
+ DynamoDB accede a `LastPostIndex`. Utiliza la clave de partición `ForumName` para localizar los elementos del índice correspondientes a "EC2". Todos los elementos de índice que tienen esta clave se almacenan en posiciones adyacentes, para agilizar su recuperación.
+ En este foro, DynamoDB utiliza el índice para buscar las claves que coinciden con la condición `LastPostDateTime` especificada.
+ Dado que el atributo `Replies` se proyecta en el índice, DynamoDB puede recuperar este atributo sin consumir ningún rendimiento provisionado adicional.
+ El atributo `Tags` no se proyecta en el índice, de tal forma que DynamoDB debe acceder a la tabla `Thread` y recuperar este atributo.
+ Se devuelven los resultados ordenados según `LastPostDateTime`. Las entradas de índice se ordenan según el valor de la clave de partición y, a continuación, según el valor de la clave de ordenación; `Query` las devuelve en el orden en el que se encuentran almacenadas. Puede utilizar el parámetro `ScanIndexForward` para devolver los resultados en orden descendente.
Puesto que el atributo `Tags` no se ha proyectado en el índice secundario local, DynamoDB debe consumir unidades de capacidad de lectura adicionales para recuperar este atributo de la tabla base. Si necesita ejecutar esta consulta a menudo, debe proyectar `Tags` en `LastPostIndex` para evitar la obtención de la tabla base. Sin embargo, si necesitaba acceder a `Tags` sólo ocasionalmente, el coste de almacenamiento adicional para proyectar `Tags` en el índice puede no valer la pena.

### Análisis de un índice secundario local
<a name="LSI.Scanning"></a>

Puede utilizar `Scan` para recuperar todos los datos de un índice secundario local. Debe proporcionar el nombre de la tabla base y el nombre del índice en la solicitud. Con una operación `Scan`, DynamoDB lee todos los datos del índice y los devuelve a la aplicación. También puede solicitar que solo se devuelvan algunos de los datos y se descarten los demás. Para ello, se utiliza el parámetro `FilterExpression` del API `Scan`. Para obtener más información, consulte [Expresiones de filtro para el análisis](Scan.md#Scan.FilterExpression).

## Escrituras de elementos e índices secundarios locales
<a name="LSI.Writes"></a>

DynamoDB mantiene sincronizados automáticamente todos los índices secundarios locales con sus tablas base respectivas. Las aplicaciones nunca escriben directamente en un índice. Sin embargo, es importante que comprenda las implicaciones de cómo DynamoDB mantiene estos índices.

Al crear un índice secundario local, se especifica un atributo que actuará como clave de ordenación del índice. También se puede especificar el tipo de datos de ese atributo. Esto significa que, cada vez que se escribe un elemento en la tabla base, si el elemento define un atributo de clave de índice, su tipo debe coincidir con el tipo de datos del esquema de claves de índice. En el caso de `LastPostIndex`, el tipo de datos de la clave de ordenación `LastPostDateTime` del índice es `String`. Si intenta agregar un elemento a la tabla `Thread`, pero especifica un tipo de datos distinto para `LastPostDateTime` (tal como`Number`), DynamoDB devolverá una excepción `ValidationException`, porque los tipos de datos no coinciden.

No es obligatorio que exista una relación biunívoca entre los elementos de una tabla base y los elementos en un índice secundario local. De hecho, este comportamiento puede ser ventajoso para muchas aplicaciones. 

Una tabla con muchos índices secundarios locales devengará costes más elevados por la actividad de escritura que las tablas con menos índices. Para obtener más información, consulte [Consideraciones sobre el rendimiento aprovisionado para los índices secundarios locales](#LSI.ThroughputConsiderations).

**importante**  
Para las tablas con índices secundarios locales, existe un límite de tamaño de 10 GB por valor de clave de partición. Una tabla con índices secundarios locales puede almacenar cualquier número de elementos, siempre y cuando el tamaño total de cualquier valor de clave de partición individual no supere los 10 GB. Para obtener más información, consulte [Límite del tamaño de una colección de elementos](#LSI.ItemCollections.SizeLimit).

## Consideraciones sobre el rendimiento aprovisionado para los índices secundarios locales
<a name="LSI.ThroughputConsiderations"></a>

Al crear una tabla en DynamoDB, se provisionan unidades de capacidad de lectura y escritura para la carga de trabajo prevista de esa tabla. Esta carga de trabajo incluye la actividad de lectura y escritura en los índices secundarios locales de la tabla.

Para ver las tarifas vigentes de la capacidad de rendimiento aprovisionada, visite [Precios de Amazon DynamoDB](https://aws.amazon.com/dynamodb/pricing).

### Unidades de capacidad de lectura
<a name="LSI.ThroughputConsiderations.Reads"></a>

Cuando se realiza una consulta en un índice secundario local, el número de unidades de capacidad de lectura consumidas depende de cómo se obtiene acceso a los datos.

Al igual que ocurre con las consultas de tablas, las consultas de índices pueden utilizar lecturas consistentes finales o de consistencia alta, según el valor de `ConsistentRead`. Una lectura de consistencia alta consume una unidad de capacidad de lectura; una lectura eventualmente consistente consume solo la mitad. Por lo tanto, elegir la opción de lecturas consistentes finales permite reducir los cargos de unidades de capacidad de lectura.

Para las consultas de índices que solo solicitan claves de índice y atributos proyectados, DynamoDB calcula la actividad de lectura provisionada de la misma forma que para las consultas de tablas. La única diferencia es que el cálculo se basa en el tamaño de las entradas del índice, no en el tamaño del elemento en la tabla base. El número de unidades de capacidad de lectura es la suma de todos los tamaños de los atributos proyectados para todos los elementos devueltos; el resultado se redondea al múltiplo de 4 KB inmediatamente superior. Para obtener más información sobre cómo calcula DynamoDB el consumo de rendimiento aprovisionado, consulte [Modo de capacidad aprovisionada de DynamoDB](provisioned-capacity-mode.md).

Para indexar consultas que leen atributos no proyectados en el índice secundario, DynamoDB tendrá que recuperar esos atributos de la tabla base, además de leer los atributos proyectados en el índice. Estas recuperaciones tienen lugar cuando se incluyen atributos no proyectados en los parámetros `Select` o `ProjectionExpression` de la operación `Query`. Las recuperaciones provocan latencia adicional en las respuestas a las consultas y también devengan un mayor coste de desempeño provisionado. Esto se debe a que, además de las lecturas en el índice secundario local que se han descrito anteriormente, se le cobrarán unidades de capacidad de lectura por cada elemento recuperado de la tabla base. Este cargo incluye la lectura de cada elemento completo de la tabla, no solo de los atributos solicitados.

El tamaño máximo de los resultados que devuelve una operación `Query` es de 1 MB. Esta cifra incluye los tamaños de todos los nombres y valores de los atributos de todos los elementos devueltos. Sin embargo, si una consulta de un índice secundario local hace que DynamoDB recupere atributos de elementos de la tabla base, el tamaño máximo de los datos de los resultados podría ser inferior. En este caso, el tamaño del resultado es la suma de:
+ El tamaño de los elementos coincidentes del índice, redondeado al múltiplo de 4 KB inmediatamente superior.
+ El tamaño de cada elemento coincidente de la tabla base, redondeado individualmente al múltiplo de 4 KB inmediatamente superior.

Usando esta fórmula, el tamaño máximo de los resultados que devuelve una operación Query también es de 1 MB.

Por ejemplo, tomemos una tabla en la que el tamaño de cada elemento es de 300 bytes. Existe un índice secundario local en esa tabla, pero solo se proyectan en él 200 bytes de cada elemento. Ahora, supongamos que se ejecuta `Query` en este índice, que la consulta requiere recuperaciones en la tabla para cada elemento y que la consulta devuelve 4 elementos. DynamoDB calcula lo siguiente:
+ El tamaño de los elementos coincidentes en el índice: 200 bytes × 4 elementos = 800 bytes; este valor se redondea hasta 4 KB.
+ El tamaño de cada elemento coincidente de la tabla base: (300 bytes, redondeados al múltiplo de 4 KB inmediatamente superior) × 4 elementos = 16 KB.

Por lo tanto, el tamaño total de los datos del resultado es de 20 KB.

### Unidades de capacidad de escritura
<a name="LSI.ThroughputConsiderations.Writes"></a>

Cuando se agrega, actualiza o elimina un elemento de una tabla, la actualización de los índices secundarios locales consumirá unidades de capacidad de escritura provisionadas para la tabla. El coste total de rendimiento aprovisionado de una escritura es la suma de las unidades de capacidad de escritura consumidas al escribir en la tabla y aquellas consumidas al actualizar los índices secundarios locales.

El coste de escribir un elemento en un índice secundario local depende de varios factores:
+ Si escribe un nuevo elemento en la tabla que define un atributo indexado o actualiza un elemento existente para definir un atributo indexado no definido previamente, se requiere una operación de escritura para colocar el elemento en el índice.
+ Si al actualizar la tabla se cambia el valor de un atributo de clave indexado (de A a B), se requieren dos escrituras: una para eliminar el elemento anterior del índice y otra para colocar el nuevo elemento en el índice.  
+ Si ya hay un elemento en el índice, pero al escribir en la tabla se elimina el atributo indexado, se requiere una escritura para eliminar la proyección del elemento anterior del índice.
+ Si no hay ningún elemento presente en el índice antes o después de actualizar el elemento, no se devenga ningún coste de escritura adicional para el índice.

Al calcular las unidades de capacidad de escritura, en todos estos factores se presupone que el tamaño de cada elemento del índice es menor o igual que el tamaño de elemento de 1 KB. Las entradas de índice de mayor tamaño requieren más unidades de capacidad de escritura. Para minimizar los costes de escritura, es conveniente estudiar qué atributos tendrán que devolver las consultas y proyectar solamente esos atributos en el índice.

## Consideraciones sobre el almacenamiento para los índices secundarios locales
<a name="LSI.StorageConsiderations"></a>

Cuando una aplicación escribe un elemento en una tabla, DynamoDB copia automáticamente el subconjunto de atributos correcto en todos los índices secundarios locales en los que deban aparecer esos atributos. Se aplica un cargo en su cuenta de AWS por el almacenamiento del elemento en la tabla base y también por el almacenamiento de los atributos en todos los índices secundarios locales de esa tabla.

La cantidad de espacio utilizado por un elemento de índice es la suma de lo siguiente:
+ El tamaño en bytes de la clave principal (clave de partición y clave de ordenación) de la tabla base
+ El tamaño en bytes del atributo de clave de índice
+ El tamaño en bytes de los atributos proyectados (si procede)
+ 100 bytes de gastos generales por cada elemento de índice

Para calcular los requisitos de almacenamiento de un índice secundario local, puede calcular el tamaño medio de un elemento del índice y, a continuación, multiplicarlo por el número de elementos del índice.

Si una tabla contiene un elemento en el que no se ha definido un atributo determinado, pero ese atributo se ha definido como clave de ordenación del índice, DynamoDB no escribirá ningún dato para ese elemento en el índice. 

## Colecciones de elementos en los índices secundarios locales
<a name="LSI.ItemCollections"></a>

**nota**  
Esta sección solo concierne a las tablas que tienen índices secundarios locales.

En DynamoDB, una *colección de elementos* es cualquier grupo de elementos que contiene el mismo valor de clave de partición en una tabla y todos sus índices secundarios locales. En los ejemplos utilizados en esta sección, la clave de partición de la tabla `Thread` es `ForumName` y la clave de partición de `LastPostIndex`también es `ForumName`. Todos los elementos de la tabla y del índice que tienen el mismo valor de `ForumName` forman parte de la misma colección de elementos. Por ejemplo, en la tabla `Thread` y en el índice secundario local`LastPostIndex` hay una colección de elementos para el foro `EC2` y otra colección de elementos distinta para el foro `RDS`.

En el siguiente diagrama se muestra la colección de elementos del foro `S3`.

![\[Una colección de elementos de DynamoDB con elementos de tabla e índice secundario local que tienen el mismo valor de clave de partición de S3.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/LSI_04.png)


En este diagrama, la colección de elementos consta de todos los elementos de `Thread` y `LastPostIndex` donde el valor de clave de partición `ForumName` es "S3". Si hay otros índices secundarios locales en la tabla, los elementos de esos índices cuyo valor de `ForumName` sea "S3" también formarán parte de la colección de elementos.

Puede utilizar cualquiera de las siguientes operaciones de DynamoDB para devolver información sobre las colecciones de elementos:
+ `BatchWriteItem`
+ `DeleteItem`
+ `PutItem`
+ `UpdateItem`
+ `TransactWriteItems`

Todas estas operaciones admiten el parámetro `ReturnItemCollectionMetrics`. Si establece este parámetro en `SIZE`, podrá ver información sobre el tamaño de cada colección de elementos en el índice.

**Example**  
A continuación se muestra un ejemplo del resultado de una operación `UpdateItem` en la tabla `Thread`, con `ReturnItemCollectionMetrics` configurado en `SIZE`. El elemento que se ha actualizado tenía un valor de `ForumName` de "EC2", por lo que el resultado incluye información acerca de esa colección de elementos.  

```
{
    ItemCollectionMetrics: {
        ItemCollectionKey: {
            ForumName: "EC2"
        },
        SizeEstimateRangeGB: [0.0, 1.0]
    }
}
```
El objeto `SizeEstimateRangeGB` muestra que el tamaño de esta colección de elementos está comprendido entre 0 y 1 GB. DynamoDB actualiza periódicamente este cálculo del tamaño, de modo que las cifras podrían ser distintas la próxima vez que se modifique el elemento.

### Límite del tamaño de una colección de elementos
<a name="LSI.ItemCollections.SizeLimit"></a>

El tamaño máximo de cualquier colección de elementos para una tabla que tenga uno o más índices secundarios locales es de 10 GB. Esto no se aplica a las colecciones de elementos en tablas sin índices secundarios locales, y tampoco se aplica a las colecciones de elementos en índices secundarios generales. Solo se ven afectadas las tablas que tienen uno o más índices secundarios locales.

Si una colección de elementos supera el límite de 10 GB, DynamoDB puede devolver una `ItemCollectionSizeLimitExceededException` y es posible que no pueda agregar más elementos a la colección de elementos ni incrementar los tamaños de los elementos contenidos en ella. (Las operaciones de lectura y escritura que reduzcan el tamaño de la colección de elementos sí se permitirán). También podrá agregar elementos a otras colecciones de elementos.

Para reducir el tamaño de una colección de elementos, puede elegir una de las siguientes opciones:
+ Eliminar todos los elementos innecesarios que tengan el valor de clave de partición de que se trate. Al eliminar estos elementos de la tabla base, DynamoDB también eliminará las entradas de índice cuyo valor de clave de partición sea el mismo.
+ Actualizar los elementos eliminando atributos o reduciendo el tamaño de estos últimos. Si estos atributos se han proyectado en uno o varios índices secundarios locales, DynamoDB también reducirá el tamaño de las entradas de índice correspondientes.
+ Crear una nueva tabla con las mismas clave de partición y clave de ordenación y, a continuación, mover elementos de la tabla anterior a la nueva. Esto puede ser conveniente si una tabla contiene datos históricos a los que se obtiene acceso con poca frecuencia. Puede considerar también el archivado de estos datos históricos en Amazon Simple Storage Service (Amazon S3).

Cuando el tamaño total de la colección de elementos disminuye por debajo de 10 GB, podrá volver a agregar elementos con el mismo valor de clave de partición.

Como práctica recomendada, es preferible instrumentar la aplicación de tal forma que monitorice los tamaños de las colecciones de elementos. Una forma de hacerlo es establecer el parámetro `ReturnItemCollectionMetrics` en `SIZE` siempre que utilice `BatchWriteItem`, `DeleteItem`, `PutItem` o `UpdateItem`. La aplicación debe examinar el objeto `ReturnItemCollectionMetrics` en los resultados y registrar un mensaje de error cada vez que una colección de elementos supere un límite definido por el usuario (8 GB, por ejemplo). Establecer un límite menor que 10 GB ofrece un mecanismo de advertencia precoz sistema que le permitirá saber qué colección de elementos está próxima a alcanzar el límite a tiempo para adoptar las medidas pertinentes.

### Colecciones de elementos y particiones
<a name="LSI.ItemCollections.OnePartition"></a>

En una tabla con uno o más índices secundarios locales, cada colección de elementos se almacena en una partición. El tamaño total de esa colección de elementos está limitado a la capacidad de esa partición: 10 GB. En el caso de una aplicación en la que el modelo de datos incluya colecciones de elementos cuyo tamaño no esté limitado, o en la que pueda esperar razonablemente que algunas colecciones de elementos aumenten más allá de los 10 GB en el futuro, debería considerar la posibilidad de utilizar un índice secundario general.

Debe diseñar las aplicaciones de tal forma que los datos de las tablas se distribuyan uniformemente entre los distintos valores de clave de partición. Para las tablas que tienen índices secundarios locales, las aplicaciones no deben crear "puntos calientes" de actividad de lectura y escritura dentro de una misma colección de elementos de una sola partición. 

# Trabajar con índices secundarios locales: Java
<a name="LSIJavaDocumentAPI"></a>

Puede utilizar la API de documentos AWS SDK para Java para crear una tabla Amazon DynamoDB con uno o varios índices secundarios locales, describir los índices de la tabla y utilizarlos para realizar consultas.

A continuación se indican los pasos comunes para las operaciones con tablas mediante el API de documentos del AWS SDK para Java.

1. Cree una instancia de la clase `DynamoDB`.

1. Cree los objetos de solicitud correspondientes para proporcionar los parámetros obligatorios y opcionales de la operación. 

1. Llame al método apropiado proporcionado por el cliente que ha creado en el paso anterior. 

**Topics**
+ [Creación de una tabla con un índice secundario local](#LSIJavaDocumentAPI.CreateTableWithIndex)
+ [Descripción de una tabla con un índice secundario local](#LSIJavaDocumentAPI.DescribeTableWithIndex)
+ [Consulta de un índice secundario local](#LSIJavaDocumentAPI.QueryAnIndex)
+ [Ejemplo: índices secundarios locales mediante la API de documentos de Java](LSIJavaDocumentAPI.Example.md)

## Creación de una tabla con un índice secundario local
<a name="LSIJavaDocumentAPI.CreateTableWithIndex"></a>

Los índices secundarios locales se deben crear a la vez que se crea la tabla. Para ello, utilice el método `createTable` e indique las especificaciones para uno o varios índices secundarios locales. En el ejemplo de código Java siguiente, se crea una tabla para contener información sobre las canciones de una colección de música. La clave de partición es `Artist` y la de ordenación, `SongTitle`. Un índice secundario, `AlbumTitleIndex`, facilita las consultas por título de álbum. 

A continuación se indican los pasos que hay que seguir para crear una tabla con un índice secundario local mediante la API de documentos de DynamoDB. 

1. Cree una instancia de la clase `DynamoDB`.

1. Cree una instancia de la clase `CreateTableRequest` para proporcionar la información de solicitud. 

   Debe proporcionar el nombre de la tabla, su clave principal y los valores de rendimiento aprovisionado. Para el índice secundario local, debe proporcionar el nombre de índice, el nombre y el tipo de datos de la clave de ordenación del índice, el esquema de claves del índice y la proyección de atributos.

1. Llame al método `createTable` proporcionando el objeto de solicitud como parámetro.

En el siguiente ejemplo de código Java se muestran los pasos anteriores. En el fragmento se crea una tabla (`Music`) con un índice secundario basado en el atributo `AlbumTitle`. La clave de partición y la clave de ordenación de la tabla, además de la clave de ordenación del índice, son los únicos atributos proyectados en el índice.

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

String tableName = "Music";

CreateTableRequest createTableRequest = new CreateTableRequest().withTableName(tableName);

//ProvisionedThroughput
createTableRequest.setProvisionedThroughput(new ProvisionedThroughput().withReadCapacityUnits((long)5).withWriteCapacityUnits((long)5));

//AttributeDefinitions
ArrayList<AttributeDefinition> attributeDefinitions= new ArrayList<AttributeDefinition>();
attributeDefinitions.add(new AttributeDefinition().withAttributeName("Artist").withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition().withAttributeName("SongTitle").withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition().withAttributeName("AlbumTitle").withAttributeType("S"));

createTableRequest.setAttributeDefinitions(attributeDefinitions);

//KeySchema
ArrayList<KeySchemaElement> tableKeySchema = new ArrayList<KeySchemaElement>();
tableKeySchema.add(new KeySchemaElement().withAttributeName("Artist").withKeyType(KeyType.HASH));  //Partition key
tableKeySchema.add(new KeySchemaElement().withAttributeName("SongTitle").withKeyType(KeyType.RANGE));  //Sort key

createTableRequest.setKeySchema(tableKeySchema);

ArrayList<KeySchemaElement> indexKeySchema = new ArrayList<KeySchemaElement>();
indexKeySchema.add(new KeySchemaElement().withAttributeName("Artist").withKeyType(KeyType.HASH));  //Partition key
indexKeySchema.add(new KeySchemaElement().withAttributeName("AlbumTitle").withKeyType(KeyType.RANGE));  //Sort key

Projection projection = new Projection().withProjectionType(ProjectionType.INCLUDE);
ArrayList<String> nonKeyAttributes = new ArrayList<String>();
nonKeyAttributes.add("Genre");
nonKeyAttributes.add("Year");
projection.setNonKeyAttributes(nonKeyAttributes);

LocalSecondaryIndex localSecondaryIndex = new LocalSecondaryIndex()
    .withIndexName("AlbumTitleIndex").withKeySchema(indexKeySchema).withProjection(projection);

ArrayList<LocalSecondaryIndex> localSecondaryIndexes = new ArrayList<LocalSecondaryIndex>();
localSecondaryIndexes.add(localSecondaryIndex);
createTableRequest.setLocalSecondaryIndexes(localSecondaryIndexes);

Table table = dynamoDB.createTable(createTableRequest);
System.out.println(table.getDescription());
```

Debe esperar hasta que DynamoDB cree la tabla y establezca el estado de esta última en `ACTIVE`. A partir de ese momento, puede comenzar a incluir elementos de datos en la tabla.

## Descripción de una tabla con un índice secundario local
<a name="LSIJavaDocumentAPI.DescribeTableWithIndex"></a>

Para obtener información acerca de los índices secundarios locales en una tabla, utilice el método `describeTable`. Para cada índice, puede obtener acceso a su nombre, esquema de claves y atributos proyectados.

A continuación, se muestran los pasos que hay que seguir para obtener acceso a la información de un índice secundario local de una tabla mediante la API de documentos de AWS SDK para Java.

1. Cree una instancia de la clase `DynamoDB`.

1. Cree una instancia de la clase `Table`. Debe proporcionar el nombre de la tabla.

1. Llame al método `describeTable` del objeto `Table`.

En el siguiente ejemplo de código Java se muestran los pasos anteriores.

**Example**  

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

String tableName = "Music";

Table table = dynamoDB.getTable(tableName);

TableDescription tableDescription = table.describe();

List<LocalSecondaryIndexDescription> localSecondaryIndexes 
    = tableDescription.getLocalSecondaryIndexes();

// This code snippet will work for multiple indexes, even though
// there is only one index in this example.

Iterator<LocalSecondaryIndexDescription> lsiIter = localSecondaryIndexes.iterator();
while (lsiIter.hasNext()) {

    LocalSecondaryIndexDescription lsiDescription = lsiIter.next();
    System.out.println("Info for index " + lsiDescription.getIndexName() + ":");
    Iterator<KeySchemaElement> kseIter = lsiDescription.getKeySchema().iterator();
    while (kseIter.hasNext()) {
        KeySchemaElement kse = kseIter.next();
        System.out.printf("\t%s: %s\n", kse.getAttributeName(), kse.getKeyType());
    }
    Projection projection = lsiDescription.getProjection();
    System.out.println("\tThe projection type is: " + projection.getProjectionType());
    if (projection.getProjectionType().toString().equals("INCLUDE")) {
        System.out.println("\t\tThe non-key projected attributes are: " + projection.getNonKeyAttributes());
    }
}
```

## Consulta de un índice secundario local
<a name="LSIJavaDocumentAPI.QueryAnIndex"></a>

Puede utilizar la operación `Query` en un índice secundario local de un modo bastante parecido a como `Query` se usa en una tabla. Debe especificar el nombre del índice, los criterios de consulta de la clave de ordenación del índice y los atributos que desea devolver. En este ejemplo, el índice es `AlbumTitleIndex` y la clave de ordenación del índice es `AlbumTitle`. 

Los únicos atributos devueltos son aquellos que se han proyectado en el índice. Puede modificar esta consulta de modo que también seleccione atributos sin clave, pero esto requeriría realizar actividad de recuperación en la tabla, lo que resulta relativamente costoso. Para obtener más información sobre recuperaciones de tablas, consulte [Proyecciones de atributos](LSI.md#LSI.Projections).

A continuación se indican los pasos que hay que seguir para consultar un índice secundario local con la API de documentos de AWS SDK para Java. 

1. Cree una instancia de la clase `DynamoDB`.

1. Cree una instancia de la clase `Table`. Debe proporcionar el nombre de la tabla.

1. Cree una instancia de la clase `Index`. Debe proporcionar el nombre del índice.

1. Llame al método `query` de la clase `Index`.

En el siguiente ejemplo de código Java se muestran los pasos anteriores.

**Example**  

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

String tableName = "Music";

Table table = dynamoDB.getTable(tableName);
Index index = table.getIndex("AlbumTitleIndex");

QuerySpec spec = new QuerySpec()
    .withKeyConditionExpression("Artist = :v_artist and AlbumTitle = :v_title")
    .withValueMap(new ValueMap()
        .withString(":v_artist", "Acme Band")
        .withString(":v_title", "Songs About Life"));

ItemCollection<QueryOutcome> items = index.query(spec);

Iterator<Item> itemsIter = items.iterator();

while (itemsIter.hasNext()) {
    Item item = itemsIter.next();
    System.out.println(item.toJSONPretty());
}
```

### Lecturas coherentes en un índice secundario local
<a name="LSIJavaDocumentAPI.ConsistentReads"></a>

A diferencia de los índices secundarios globales, que solo admiten lecturas coherentes posteriores, un índice secundario local admite lecturas coherentes posteriores y lecturas altamente coherentes. Una lectura de consistencia alta de un índice secundario local siempre devolverá los valores actualizados más recientemente. Si la consulta tiene que recuperar atributos adicionales de la tabla base, estos atributos recuperados también son coherentes con respecto al índice.

De forma predeterminada, `Query` usa lecturas coherentes posteriores. Para solicitar una lectura altamente coherente, establezca `ConsistentRead` en `true` en `QuerySpec`. A continuación, se muestran consultas `AlbumTitleIndex` mediante una lectura altamente coherente:

**Example**  

```
QuerySpec spec = new QuerySpec()
    .withKeyConditionExpression("Artist = :v_artist and AlbumTitle = :v_title")
    .withValueMap(new ValueMap()
        .withString(":v_artist", "Acme Band")
        .withString(":v_title", "Songs About Life"))
    .withConsistentRead(true);
```

**nota**  
una lectura altamente coherente consume una unidad de capacidad de lectura por cada 4 KB de datos devueltos (redondeados al alza), mientras que una lectura coherente posterior consume la mitad. Por ejemplo, una lectura altamente coherente que devuelva 9 KB de datos consume 3 unidades de capacidad de lectura (9 KB/ 4 KB = 2,25, redondeada a 3), mientras que la misma consulta que utilice una lectura coherente posterior consume 1,5 unidades de capacidad de lectura. Si la aplicación puede tolerar la lectura de datos que puedan estar ligeramente obsoletos, utilice lecturas coherentes posteriores para reducir el uso de la capacidad de lectura. Para obtener más información, consulte [Unidades de capacidad de lectura](LSI.md#LSI.ThroughputConsiderations.Reads).

# Ejemplo: índices secundarios locales mediante la API de documentos de Java
<a name="LSIJavaDocumentAPI.Example"></a>

En el siguiente ejemplo de código Java se muestra cómo usar índices secundarios locales en Amazon DynamoDB. En el ejemplo se crea una tabla denominada `CustomerOrders` cuya clave de partición es `CustomerId` y cuya clave de ordenación es `OrderId`. Hay dos índices secundarios locales en esta tabla:
+ `OrderCreationDateIndex`: la clave de ordenación es `OrderCreationDate` y los atributos siguientes se proyectan en el índice:
  + `ProductCategory`
  + `ProductName`
  + `OrderStatus`
  + `ShipmentTrackingId`
+ `IsOpenIndex`: la clave de ordenación es `IsOpen` y todos los atributos de la tabla se proyectan en el índice.

Después de que se crea la tabla `CustomerOrders`, el programa carga la tabla con datos que representan pedidos de clientes. A continuación, consulta los datos utilizando los índices secundarios locales. Por último, el programa elimina la tabla `CustomerOrders`.

Para obtener instrucciones paso a paso para probar el siguiente ejemplo, consulte [Ejemplos de código Java](CodeSamples.Java.md).

**Example**  

```
package com.example.dynamodb;

import software.amazon.awssdk.core.waiters.WaiterResponse;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.*;
import software.amazon.awssdk.services.dynamodb.waiters.DynamoDbWaiter;

import java.util.HashMap;
import java.util.Map;

public class DocumentAPILocalSecondaryIndexExample {

    static DynamoDbClient client = DynamoDbClient.create();
    public static String tableName = "CustomerOrders";

    public static void main(String[] args) {
        createTable();
        loadData();
        query(null);
        query("IsOpenIndex");
        query("OrderCreationDateIndex");
        deleteTable(tableName);
    }

    public static void createTable() {
        CreateTableRequest request = CreateTableRequest.builder()
            .tableName(tableName)
            .provisionedThroughput(ProvisionedThroughput.builder()
                .readCapacityUnits(1L)
                .writeCapacityUnits(1L)
                .build())
            .attributeDefinitions(
                AttributeDefinition.builder().attributeName("CustomerId").attributeType(ScalarAttributeType.S).build(),
                AttributeDefinition.builder().attributeName("OrderId").attributeType(ScalarAttributeType.N).build(),
                AttributeDefinition.builder().attributeName("OrderCreationDate").attributeType(ScalarAttributeType.N).build(),
                AttributeDefinition.builder().attributeName("IsOpen").attributeType(ScalarAttributeType.N).build())
            .keySchema(
                KeySchemaElement.builder().attributeName("CustomerId").keyType(KeyType.HASH).build(),
                KeySchemaElement.builder().attributeName("OrderId").keyType(KeyType.RANGE).build())
            .localSecondaryIndexes(
                LocalSecondaryIndex.builder()
                    .indexName("OrderCreationDateIndex")
                    .keySchema(
                        KeySchemaElement.builder().attributeName("CustomerId").keyType(KeyType.HASH).build(),
                        KeySchemaElement.builder().attributeName("OrderCreationDate").keyType(KeyType.RANGE).build())
                    .projection(Projection.builder()
                        .projectionType(ProjectionType.INCLUDE)
                        .nonKeyAttributes("ProductCategory", "ProductName")
                        .build())
                    .build(),
                LocalSecondaryIndex.builder()
                    .indexName("IsOpenIndex")
                    .keySchema(
                        KeySchemaElement.builder().attributeName("CustomerId").keyType(KeyType.HASH).build(),
                        KeySchemaElement.builder().attributeName("IsOpen").keyType(KeyType.RANGE).build())
                    .projection(Projection.builder()
                        .projectionType(ProjectionType.ALL)
                        .build())
                    .build())
            .build();

        System.out.println("Creating table " + tableName + "...");
        client.createTable(request);

        try (DynamoDbWaiter waiter = client.waiter()) {
            WaiterResponse<DescribeTableResponse> response = waiter.waitUntilTableExists(r -> r.tableName(tableName));
            response.matched().response().ifPresent(System.out::println);
        }
    }

    public static void query(String indexName) {
        System.out.println("\n***********************************************************\n");
        System.out.println("Querying table " + tableName + "...");

        if ("IsOpenIndex".equals(indexName)) {
            System.out.println("\nUsing index: '" + indexName + "': Bob's orders that are open.");
            System.out.println("Only a user-specified list of attributes are returned\n");

            Map<String, AttributeValue> values = new HashMap<>();
            values.put(":v_custid", AttributeValue.builder().s("bob@example.com").build());
            values.put(":v_isopen", AttributeValue.builder().n("1").build());

            QueryRequest request = QueryRequest.builder()
                .tableName(tableName)
                .indexName(indexName)
                .keyConditionExpression("CustomerId = :v_custid and IsOpen = :v_isopen")
                .expressionAttributeValues(values)
                .projectionExpression("OrderCreationDate, ProductCategory, ProductName, OrderStatus")
                .build();

            System.out.println("Query: printing results...");
            client.query(request).items().forEach(System.out::println);

        } else if ("OrderCreationDateIndex".equals(indexName)) {
            System.out.println("\nUsing index: '" + indexName + "': Bob's orders that were placed after 01/31/2015.");
            System.out.println("Only the projected attributes are returned\n");

            Map<String, AttributeValue> values = new HashMap<>();
            values.put(":v_custid", AttributeValue.builder().s("bob@example.com").build());
            values.put(":v_orddate", AttributeValue.builder().n("20150131").build());

            QueryRequest request = QueryRequest.builder()
                .tableName(tableName)
                .indexName(indexName)
                .keyConditionExpression("CustomerId = :v_custid and OrderCreationDate >= :v_orddate")
                .expressionAttributeValues(values)
                .select(Select.ALL_PROJECTED_ATTRIBUTES)
                .build();

            System.out.println("Query: printing results...");
            client.query(request).items().forEach(System.out::println);

        } else {
            System.out.println("\nNo index: All of Bob's orders, by OrderId:\n");

            Map<String, AttributeValue> values = new HashMap<>();
            values.put(":v_custid", AttributeValue.builder().s("bob@example.com").build());

            QueryRequest request = QueryRequest.builder()
                .tableName(tableName)
                .keyConditionExpression("CustomerId = :v_custid")
                .expressionAttributeValues(values)
                .build();

            System.out.println("Query: printing results...");
            client.query(request).items().forEach(System.out::println);
        }
    }

    public static void deleteTable(String tableName) {
        System.out.println("Deleting table " + tableName + "...");
        client.deleteTable(DeleteTableRequest.builder().tableName(tableName).build());

        try (DynamoDbWaiter waiter = client.waiter()) {
            waiter.waitUntilTableNotExists(r -> r.tableName(tableName));
        }
    }

    public static void loadData() {
        System.out.println("Loading data into table " + tableName + "...");

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("alice@example.com").build(),
            "OrderId", AttributeValue.builder().n("1").build(),
            "IsOpen", AttributeValue.builder().n("1").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150101").build(),
            "ProductCategory", AttributeValue.builder().s("Book").build(),
            "ProductName", AttributeValue.builder().s("The Great Outdoors").build(),
            "OrderStatus", AttributeValue.builder().s("PACKING ITEMS").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("alice@example.com").build(),
            "OrderId", AttributeValue.builder().n("2").build(),
            "IsOpen", AttributeValue.builder().n("1").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150221").build(),
            "ProductCategory", AttributeValue.builder().s("Bike").build(),
            "ProductName", AttributeValue.builder().s("Super Mountain").build(),
            "OrderStatus", AttributeValue.builder().s("ORDER RECEIVED").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("alice@example.com").build(),
            "OrderId", AttributeValue.builder().n("3").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150304").build(),
            "ProductCategory", AttributeValue.builder().s("Music").build(),
            "ProductName", AttributeValue.builder().s("A Quiet Interlude").build(),
            "OrderStatus", AttributeValue.builder().s("IN TRANSIT").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("176493").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("1").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150111").build(),
            "ProductCategory", AttributeValue.builder().s("Movie").build(),
            "ProductName", AttributeValue.builder().s("Calm Before The Storm").build(),
            "OrderStatus", AttributeValue.builder().s("SHIPPING DELAY").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("859323").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("2").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150124").build(),
            "ProductCategory", AttributeValue.builder().s("Music").build(),
            "ProductName", AttributeValue.builder().s("E-Z Listening").build(),
            "OrderStatus", AttributeValue.builder().s("DELIVERED").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("756943").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("3").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150221").build(),
            "ProductCategory", AttributeValue.builder().s("Music").build(),
            "ProductName", AttributeValue.builder().s("Symphony 9").build(),
            "OrderStatus", AttributeValue.builder().s("DELIVERED").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("645193").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("4").build(),
            "IsOpen", AttributeValue.builder().n("1").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150222").build(),
            "ProductCategory", AttributeValue.builder().s("Hardware").build(),
            "ProductName", AttributeValue.builder().s("Extra Heavy Hammer").build(),
            "OrderStatus", AttributeValue.builder().s("PACKING ITEMS").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("5").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150309").build(),
            "ProductCategory", AttributeValue.builder().s("Book").build(),
            "ProductName", AttributeValue.builder().s("How To Cook").build(),
            "OrderStatus", AttributeValue.builder().s("IN TRANSIT").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("440185").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("6").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150318").build(),
            "ProductCategory", AttributeValue.builder().s("Luggage").build(),
            "ProductName", AttributeValue.builder().s("Really Big Suitcase").build(),
            "OrderStatus", AttributeValue.builder().s("DELIVERED").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("893927").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("7").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150324").build(),
            "ProductCategory", AttributeValue.builder().s("Golf").build(),
            "ProductName", AttributeValue.builder().s("PGA Pro II").build(),
            "OrderStatus", AttributeValue.builder().s("OUT FOR DELIVERY").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("383283").build()));
    }

    private static void putItem(Map<String, AttributeValue> item) {
        client.putItem(PutItemRequest.builder().tableName(tableName).item(item).build());
    }
}
```

# Trabajar con índices secundarios locales: .NET
<a name="LSILowLevelDotNet"></a>

**Topics**
+ [Creación de una tabla con un índice secundario local](#LSILowLevelDotNet.CreateTableWithIndex)
+ [Descripción de una tabla con un índice secundario local](#LSILowLevelDotNet.DescribeTableWithIndex)
+ [Consulta de un índice secundario local](#LSILowLevelDotNet.QueryAnIndex)
+ [Ejemplo: índices secundarios locales que utilizan la API de bajo nivel de AWS SDK para .NET](LSILowLevelDotNet.Example.md)

Puede utilizar la API de bajo nivel AWS SDK para .NET para crear una tabla Amazon DynamoDB con uno o varios índices secundarios locales, describir los índices de la tabla y utilizarlos para realizar consultas. Estas operaciones se mapean a las acciones correspondientes del API de bajo nivel de DynamoDB. Para obtener más información, consulte [Ejemplos de código .NET](CodeSamples.DotNet.md). 

A continuación se indican los pasos comunes para las operaciones con tablas mediante el API de bajo nivel de .NET. 

1. Cree una instancia de la clase `AmazonDynamoDBClient`.

1. Cree los objetos de solicitud correspondientes para proporcionar los parámetros obligatorios y opcionales de la operación.

   Por ejemplo, cree un objeto `CreateTableRequest` para crear una tabla y un objeto `QueryRequest` para consultar una tabla o un índice. 

1. Ejecute el método apropiado proporcionado por el cliente que ha creado en el paso anterior. 

## Creación de una tabla con un índice secundario local
<a name="LSILowLevelDotNet.CreateTableWithIndex"></a>

Los índices secundarios locales se deben crear a la vez que se crea la tabla. Para ello, utilice `CreateTable` e indique las especificaciones para uno o varios índices secundarios globales. En el ejemplo de código C\$1 siguiente, se crea una tabla para contener información sobre las canciones de una colección de música. La clave de partición es `Artist` y la de ordenación, `SongTitle`. Un índice secundario, `AlbumTitleIndex`, facilita las consultas por título de álbum. 

A continuación se indican los pasos que hay que seguir para crear una tabla con un índice secundario local mediante la API de bajo nivel de .NET. 

1. Cree una instancia de la clase `AmazonDynamoDBClient`.

1. Cree una instancia de la clase `CreateTableRequest` para proporcionar la información de solicitud. 

   Debe proporcionar el nombre de la tabla, su clave principal y los valores de rendimiento aprovisionado. Para el índice secundario local, debe proporcionar el nombre de índice, el nombre y el tipo de datos de la clave de ordenación del índice, el esquema de claves del índice y la proyección de atributos.

1. Ejecute el método `CreateTable` proporcionando el objeto de solicitud como parámetro.

En el siguiente ejemplo de código C\$1 se ponen en práctica los pasos anteriores. En el fragmento se crea una tabla (`Music`) con un índice secundario basado en el atributo `AlbumTitle`. La clave de partición y la clave de ordenación de la tabla, además de la clave de ordenación del índice, son los únicos atributos proyectados en el índice.

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
string tableName = "Music";

CreateTableRequest createTableRequest = new CreateTableRequest()
{
    TableName = tableName
};

//ProvisionedThroughput
createTableRequest.ProvisionedThroughput = new ProvisionedThroughput()
{
    ReadCapacityUnits = (long)5,
    WriteCapacityUnits = (long)5
};

//AttributeDefinitions
List<AttributeDefinition> attributeDefinitions = new List<AttributeDefinition>();

attributeDefinitions.Add(new AttributeDefinition()
{
    AttributeName = "Artist",
    AttributeType = "S"
});

attributeDefinitions.Add(new AttributeDefinition()
 {
     AttributeName = "SongTitle",
     AttributeType = "S"
 });

attributeDefinitions.Add(new AttributeDefinition()
 {
     AttributeName = "AlbumTitle",
     AttributeType = "S"
 });

createTableRequest.AttributeDefinitions = attributeDefinitions;

//KeySchema
List<KeySchemaElement> tableKeySchema = new List<KeySchemaElement>();

tableKeySchema.Add(new KeySchemaElement() { AttributeName = "Artist", KeyType = "HASH" });  //Partition key
tableKeySchema.Add(new KeySchemaElement() { AttributeName = "SongTitle", KeyType = "RANGE" });  //Sort key

createTableRequest.KeySchema = tableKeySchema;

List<KeySchemaElement> indexKeySchema = new List<KeySchemaElement>();
indexKeySchema.Add(new KeySchemaElement() { AttributeName = "Artist", KeyType = "HASH" });  //Partition key
indexKeySchema.Add(new KeySchemaElement() { AttributeName = "AlbumTitle", KeyType = "RANGE" });  //Sort key

Projection projection = new Projection() { ProjectionType = "INCLUDE" };

List<string> nonKeyAttributes = new List<string>();
nonKeyAttributes.Add("Genre");
nonKeyAttributes.Add("Year");
projection.NonKeyAttributes = nonKeyAttributes;

LocalSecondaryIndex localSecondaryIndex = new LocalSecondaryIndex()
{
    IndexName = "AlbumTitleIndex",
    KeySchema = indexKeySchema,
    Projection = projection
};

List<LocalSecondaryIndex> localSecondaryIndexes = new List<LocalSecondaryIndex>();
localSecondaryIndexes.Add(localSecondaryIndex);
createTableRequest.LocalSecondaryIndexes = localSecondaryIndexes;

CreateTableResponse result = client.CreateTable(createTableRequest);
Console.WriteLine(result.CreateTableResult.TableDescription.TableName);
Console.WriteLine(result.CreateTableResult.TableDescription.TableStatus);
```

Debe esperar hasta que DynamoDB cree la tabla y establezca el estado de esta última en `ACTIVE`. A partir de ese momento, puede comenzar a incluir elementos de datos en la tabla.

## Descripción de una tabla con un índice secundario local
<a name="LSILowLevelDotNet.DescribeTableWithIndex"></a>

Para obtener información acerca de los índices secundarios locales en una tabla, utilice la API `DescribeTable`. Para cada índice, puede obtener acceso a su nombre, esquema de claves y atributos proyectados.

A continuación se indican los pasos que hay que seguir para acceder a la información de un índice secundario local de una tabla mediante el API de bajo nivel de .NET. 

1. Cree una instancia de la clase `AmazonDynamoDBClient`.

1. Cree una instancia de la clase `DescribeTableRequest` para proporcionar la información de solicitud. Debe proporcionar el nombre de la tabla.

1. Ejecute el método `describeTable` proporcionando el objeto de solicitud como parámetro.

En el siguiente ejemplo de código C\$1 se ponen en práctica los pasos anteriores.

**Example**  

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
string tableName = "Music";

DescribeTableResponse response = client.DescribeTable(new DescribeTableRequest() { TableName = tableName });
List<LocalSecondaryIndexDescription> localSecondaryIndexes =
    response.DescribeTableResult.Table.LocalSecondaryIndexes;

// This code snippet will work for multiple indexes, even though
// there is only one index in this example.
foreach (LocalSecondaryIndexDescription lsiDescription in localSecondaryIndexes)
{
    Console.WriteLine("Info for index " + lsiDescription.IndexName + ":");

    foreach (KeySchemaElement kse in lsiDescription.KeySchema)
    {
        Console.WriteLine("\t" + kse.AttributeName + ": key type is " + kse.KeyType);
    }

    Projection projection = lsiDescription.Projection;

    Console.WriteLine("\tThe projection type is: " + projection.ProjectionType);

    if (projection.ProjectionType.ToString().Equals("INCLUDE"))
    {
        Console.WriteLine("\t\tThe non-key projected attributes are:");

        foreach (String s in projection.NonKeyAttributes)
        {
            Console.WriteLine("\t\t" + s);
        }

    }
}
```

## Consulta de un índice secundario local
<a name="LSILowLevelDotNet.QueryAnIndex"></a>

Puede utilizar `Query` en un índice secundario local de un modo bastante parecido a como usa `Query` en una tabla. Debe especificar el nombre del índice, los criterios de consulta de la clave de ordenación del índice y los atributos que desea devolver. En este ejemplo, el índice es `AlbumTitleIndex` y la clave de ordenación del índice es `AlbumTitle`. 

Los únicos atributos devueltos son aquellos que se han proyectado en el índice. Puede modificar esta consulta de modo que también seleccione atributos sin clave, pero esto requeriría realizar actividad de recuperación en la tabla, lo que resulta relativamente costoso. Para obtener más información sobre recuperaciones de tablas, consulte [Proyecciones de atributos](LSI.md#LSI.Projections)

A continuación se indican los pasos que hay que seguir para consultar un índice secundario local mediante la API de bajo nivel de .NET. 

1. Cree una instancia de la clase `AmazonDynamoDBClient`.

1. Cree una instancia de la clase `QueryRequest` para proporcionar la información de solicitud.

1. Ejecute el método `query` proporcionando el objeto de solicitud como parámetro.

En el siguiente ejemplo de código C\$1 se ponen en práctica los pasos anteriores.

**Example**  

```
QueryRequest queryRequest = new QueryRequest
{
    TableName = "Music",
    IndexName = "AlbumTitleIndex",
    Select = "ALL_ATTRIBUTES",
    ScanIndexForward = true,
    KeyConditionExpression = "Artist = :v_artist and AlbumTitle = :v_title",
    ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
    {
        {":v_artist",new AttributeValue {S = "Acme Band"}},
        {":v_title",new AttributeValue {S = "Songs About Life"}}
    },
};

QueryResponse response = client.Query(queryRequest);

foreach (var attribs in response.Items)
{
    foreach (var attrib in attribs)
    {
        Console.WriteLine(attrib.Key + " ---> " + attrib.Value.S);
    }
    Console.WriteLine();
}
```

# Ejemplo: índices secundarios locales que utilizan la API de bajo nivel de AWS SDK para .NET
<a name="LSILowLevelDotNet.Example"></a>

En el siguiente ejemplo de código C\$1 se muestra cómo usar índices secundarios locales en Amazon DynamoDB. En el ejemplo se crea una tabla denominada `CustomerOrders` cuya clave de partición es `CustomerId` y cuya clave de ordenación es `OrderId`. Hay dos índices secundarios locales en esta tabla:
+ `OrderCreationDateIndex`: la clave de ordenación es `OrderCreationDate` y los atributos siguientes se proyectan en el índice:
  + `ProductCategory`
  + `ProductName`
  + `OrderStatus`
  + `ShipmentTrackingId`
+ `IsOpenIndex`: la clave de ordenación es `IsOpen` y todos los atributos de la tabla se proyectan en el índice.

Después de que se crea la tabla `CustomerOrders`, el programa carga la tabla con datos que representan pedidos de clientes. A continuación, consulta los datos utilizando los índices secundarios locales. Por último, el programa elimina la tabla `CustomerOrders`.

Para obtener instrucciones paso a paso para probar el siguiente ejemplo, consulte [Ejemplos de código .NET](CodeSamples.DotNet.md).

**Example**  

```
using System;
using System.Collections.Generic;
using System.Linq;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;
using Amazon.DynamoDBv2.DocumentModel;
using Amazon.DynamoDBv2.Model;
using Amazon.Runtime;
using Amazon.SecurityToken;

namespace com.amazonaws.codesamples
{
    class LowLevelLocalSecondaryIndexExample
    {
        private static AmazonDynamoDBClient client = new AmazonDynamoDBClient();
        private static string tableName = "CustomerOrders";

        static void Main(string[] args)
        {
            try
            {
                CreateTable();
                LoadData();

                Query(null);
                Query("IsOpenIndex");
                Query("OrderCreationDateIndex");

                DeleteTable(tableName);

                Console.WriteLine("To continue, press Enter");
                Console.ReadLine();
            }
            catch (AmazonDynamoDBException e) { Console.WriteLine(e.Message); }
            catch (AmazonServiceException e) { Console.WriteLine(e.Message); }
            catch (Exception e) { Console.WriteLine(e.Message); }
        }

        private static void CreateTable()
        {
            var createTableRequest =
                new CreateTableRequest()
                {
                    TableName = tableName,
                    ProvisionedThroughput =
                    new ProvisionedThroughput()
                    {
                        ReadCapacityUnits = (long)1,
                        WriteCapacityUnits = (long)1
                    }
                };

            var attributeDefinitions = new List<AttributeDefinition>()
        {
            // Attribute definitions for table primary key
            { new AttributeDefinition() {
                  AttributeName = "CustomerId", AttributeType = "S"
              } },
            { new AttributeDefinition() {
                  AttributeName = "OrderId", AttributeType = "N"
              } },
            // Attribute definitions for index primary key
            { new AttributeDefinition() {
                  AttributeName = "OrderCreationDate", AttributeType = "N"
              } },
            { new AttributeDefinition() {
                  AttributeName = "IsOpen", AttributeType = "N"
              }}
        };

            createTableRequest.AttributeDefinitions = attributeDefinitions;

            // Key schema for table
            var tableKeySchema = new List<KeySchemaElement>()
        {
            { new KeySchemaElement() {
                  AttributeName = "CustomerId", KeyType = "HASH"
              } },                                                  //Partition key
            { new KeySchemaElement() {
                  AttributeName = "OrderId", KeyType = "RANGE"
              } }                                                //Sort key
        };

            createTableRequest.KeySchema = tableKeySchema;

            var localSecondaryIndexes = new List<LocalSecondaryIndex>();

            // OrderCreationDateIndex
            LocalSecondaryIndex orderCreationDateIndex = new LocalSecondaryIndex()
            {
                IndexName = "OrderCreationDateIndex"
            };

            // Key schema for OrderCreationDateIndex
            var indexKeySchema = new List<KeySchemaElement>()
        {
            { new KeySchemaElement() {
                  AttributeName = "CustomerId", KeyType = "HASH"
              } },                                                    //Partition key
            { new KeySchemaElement() {
                  AttributeName = "OrderCreationDate", KeyType = "RANGE"
              } }                                                            //Sort key
        };

            orderCreationDateIndex.KeySchema = indexKeySchema;

            // Projection (with list of projected attributes) for
            // OrderCreationDateIndex
            var projection = new Projection()
            {
                ProjectionType = "INCLUDE"
            };

            var nonKeyAttributes = new List<string>()
        {
            "ProductCategory",
            "ProductName"
        };
            projection.NonKeyAttributes = nonKeyAttributes;

            orderCreationDateIndex.Projection = projection;

            localSecondaryIndexes.Add(orderCreationDateIndex);

            // IsOpenIndex
            LocalSecondaryIndex isOpenIndex
                = new LocalSecondaryIndex()
                {
                    IndexName = "IsOpenIndex"
                };

            // Key schema for IsOpenIndex
            indexKeySchema = new List<KeySchemaElement>()
        {
            { new KeySchemaElement() {
                  AttributeName = "CustomerId", KeyType = "HASH"
              }},                                                     //Partition key
            { new KeySchemaElement() {
                  AttributeName = "IsOpen", KeyType = "RANGE"
              }}                                                  //Sort key
        };

            // Projection (all attributes) for IsOpenIndex
            projection = new Projection()
            {
                ProjectionType = "ALL"
            };

            isOpenIndex.KeySchema = indexKeySchema;
            isOpenIndex.Projection = projection;

            localSecondaryIndexes.Add(isOpenIndex);

            // Add index definitions to CreateTable request
            createTableRequest.LocalSecondaryIndexes = localSecondaryIndexes;

            Console.WriteLine("Creating table " + tableName + "...");
            client.CreateTable(createTableRequest);
            WaitUntilTableReady(tableName);
        }

        public static void Query(string indexName)
        {
            Console.WriteLine("\n***********************************************************\n");
            Console.WriteLine("Querying table " + tableName + "...");

            QueryRequest queryRequest = new QueryRequest()
            {
                TableName = tableName,
                ConsistentRead = true,
                ScanIndexForward = true,
                ReturnConsumedCapacity = "TOTAL"
            };


            String keyConditionExpression = "CustomerId = :v_customerId";
            Dictionary<string, AttributeValue> expressionAttributeValues = new Dictionary<string, AttributeValue> {
            {":v_customerId", new AttributeValue {
                 S = "bob@example.com"
             }}
        };


            if (indexName == "IsOpenIndex")
            {
                Console.WriteLine("\nUsing index: '" + indexName
                          + "': Bob's orders that are open.");
                Console.WriteLine("Only a user-specified list of attributes are returned\n");
                queryRequest.IndexName = indexName;

                keyConditionExpression += " and IsOpen = :v_isOpen";
                expressionAttributeValues.Add(":v_isOpen", new AttributeValue
                {
                    N = "1"
                });

                // ProjectionExpression
                queryRequest.ProjectionExpression = "OrderCreationDate, ProductCategory, ProductName, OrderStatus";
            }
            else if (indexName == "OrderCreationDateIndex")
            {
                Console.WriteLine("\nUsing index: '" + indexName
                          + "': Bob's orders that were placed after 01/31/2013.");
                Console.WriteLine("Only the projected attributes are returned\n");
                queryRequest.IndexName = indexName;

                keyConditionExpression += " and OrderCreationDate > :v_Date";
                expressionAttributeValues.Add(":v_Date", new AttributeValue
                {
                    N = "20130131"
                });

                // Select
                queryRequest.Select = "ALL_PROJECTED_ATTRIBUTES";
            }
            else
            {
                Console.WriteLine("\nNo index: All of Bob's orders, by OrderId:\n");
            }
            queryRequest.KeyConditionExpression = keyConditionExpression;
            queryRequest.ExpressionAttributeValues = expressionAttributeValues;

            var result = client.Query(queryRequest);
            var items = result.Items;
            foreach (var currentItem in items)
            {
                foreach (string attr in currentItem.Keys)
                {
                    if (attr == "OrderId" || attr == "IsOpen"
                        || attr == "OrderCreationDate")
                    {
                        Console.WriteLine(attr + "---> " + currentItem[attr].N);
                    }
                    else
                    {
                        Console.WriteLine(attr + "---> " + currentItem[attr].S);
                    }
                }
                Console.WriteLine();
            }
            Console.WriteLine("\nConsumed capacity: " + result.ConsumedCapacity.CapacityUnits + "\n");
        }

        private static void DeleteTable(string tableName)
        {
            Console.WriteLine("Deleting table " + tableName + "...");
            client.DeleteTable(new DeleteTableRequest()
            {
                TableName = tableName
            });
            WaitForTableToBeDeleted(tableName);
        }

        public static void LoadData()
        {
            Console.WriteLine("Loading data into table " + tableName + "...");

            Dictionary<string, AttributeValue> item = new Dictionary<string, AttributeValue>();

            item["CustomerId"] = new AttributeValue
            {
                S = "alice@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "1"
            };
            item["IsOpen"] = new AttributeValue
            {
                N = "1"
            };
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130101"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Book"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "The Great Outdoors"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "PACKING ITEMS"
            };
            /* no ShipmentTrackingId attribute */
            PutItemRequest putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "alice@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "2"
            };
            item["IsOpen"] = new AttributeValue
            {
                N = "1"
            };
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130221"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Bike"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "Super Mountain"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "ORDER RECEIVED"
            };
            /* no ShipmentTrackingId attribute */
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "alice@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "3"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130304"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Music"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "A Quiet Interlude"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "IN TRANSIT"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "176493"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "1"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130111"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Movie"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "Calm Before The Storm"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "SHIPPING DELAY"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "859323"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "2"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130124"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Music"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "E-Z Listening"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "DELIVERED"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "756943"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "3"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130221"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Music"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "Symphony 9"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "DELIVERED"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "645193"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "4"
            };
            item["IsOpen"] = new AttributeValue
            {
                N = "1"
            };
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130222"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Hardware"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "Extra Heavy Hammer"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "PACKING ITEMS"
            };
            /* no ShipmentTrackingId attribute */
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "5"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130309"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Book"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "How To Cook"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "IN TRANSIT"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "440185"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "6"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130318"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Luggage"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "Really Big Suitcase"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "DELIVERED"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "893927"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "7"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130324"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Golf"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "PGA Pro II"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "OUT FOR DELIVERY"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "383283"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);
        }

        private static void WaitUntilTableReady(string tableName)
        {
            string status = null;
            // Let us wait until table is created. Call DescribeTable.
            do
            {
                System.Threading.Thread.Sleep(5000); // Wait 5 seconds.
                try
                {
                    var res = client.DescribeTable(new DescribeTableRequest
                    {
                        TableName = tableName
                    });

                    Console.WriteLine("Table name: {0}, status: {1}",
                              res.Table.TableName,
                              res.Table.TableStatus);
                    status = res.Table.TableStatus;
                }
                catch (ResourceNotFoundException)
                {
                    // DescribeTable is eventually consistent. So you might
                    // get resource not found. So we handle the potential exception.
                }
            } while (status != "ACTIVE");
        }

        private static void WaitForTableToBeDeleted(string tableName)
        {
            bool tablePresent = true;

            while (tablePresent)
            {
                System.Threading.Thread.Sleep(5000); // Wait 5 seconds.
                try
                {
                    var res = client.DescribeTable(new DescribeTableRequest
                    {
                        TableName = tableName
                    });

                    Console.WriteLine("Table name: {0}, status: {1}",
                              res.Table.TableName,
                              res.Table.TableStatus);
                }
                catch (ResourceNotFoundException)
                {
                    tablePresent = false;
                }
            }
        }
    }
}
```

# Uso de índices secundarios locales en la AWS CLI de DynamoDB
<a name="LCICli"></a>

Puede usar la AWS CLI para crear una tabla de Amazon DynamoDB con uno o más índices secundarios locales, describir los índices de la tabla y utilizarlos para realizar consultas.

**Topics**
+ [Creación de una tabla con un índice secundario local](#LCICli.CreateTableWithIndex)
+ [Descripción de una tabla con un índice secundario local](#LCICli.DescribeTableWithIndex)
+ [Consulta de un índice secundario local](#LCICli.QueryAnIndex)

## Creación de una tabla con un índice secundario local
<a name="LCICli.CreateTableWithIndex"></a>

Los índices secundarios locales se deben crear al mismo tiempo que la tabla. Para ello, use el parámetro `create-table` y proporcione las especificaciones para uno o más índices secundarios locales. En el ejemplo siguiente, se crea una tabla (`Music`) para contener información sobre las canciones de una colección de música. La clave de partición es `Artist` y la de ordenación, `SongTitle`. Un índice secundario `AlbumTitleIndex` en el atributo `AlbumTitle` facilita las consultas por título de álbum. 

```
aws dynamodb create-table \
    --table-name Music \
    --attribute-definitions AttributeName=Artist,AttributeType=S AttributeName=SongTitle,AttributeType=S \
        AttributeName=AlbumTitle,AttributeType=S  \
    --key-schema AttributeName=Artist,KeyType=HASH AttributeName=SongTitle,KeyType=RANGE \
    --provisioned-throughput \
        ReadCapacityUnits=10,WriteCapacityUnits=5 \
    --local-secondary-indexes \
        "[{\"IndexName\": \"AlbumTitleIndex\",
        \"KeySchema\":[{\"AttributeName\":\"Artist\",\"KeyType\":\"HASH\"},
                      {\"AttributeName\":\"AlbumTitle\",\"KeyType\":\"RANGE\"}],
        \"Projection\":{\"ProjectionType\":\"INCLUDE\",  \"NonKeyAttributes\":[\"Genre\", \"Year\"]}}]"
```

Debe esperar hasta que DynamoDB cree la tabla y establezca el estado de esta última en `ACTIVE`. A partir de ese momento, puede comenzar a incluir elementos de datos en la tabla. Puede utilizar [describe-table](https://docs.aws.amazon.com/cli/latest/reference/dynamodb/describe-table.html) Para determinar el estado de la creación de tablas. 

## Descripción de una tabla con un índice secundario local
<a name="LCICli.DescribeTableWithIndex"></a>

Para obtener información acerca de los índices secundarios locales en una tabla, utilice el parámetro `describe-table`. Para cada índice, puede obtener acceso a su nombre, esquema de claves y atributos proyectados.

```
aws dynamodb describe-table --table-name Music
```

## Consulta de un índice secundario local
<a name="LCICli.QueryAnIndex"></a>

Puede usar la operación `query` en un índice secundario local de un modo bastante parecido a como `query` se usa en una tabla. Debe especificar el nombre del índice, los criterios de consulta de la clave de ordenación del índice y los atributos que desea devolver. En este ejemplo, el índice es `AlbumTitleIndex` y la clave de ordenación del índice es `AlbumTitle`. 

Los únicos atributos devueltos son aquellos que se han proyectado en el índice. Puede modificar esta consulta de modo que también seleccione atributos sin clave, pero esto requeriría realizar actividad de recuperación en la tabla, lo que resulta relativamente costoso. Para obtener más información sobre recuperaciones de tablas, consulte [Proyecciones de atributos](LSI.md#LSI.Projections).

```
aws dynamodb query \
    --table-name Music \
    --index-name AlbumTitleIndex \
    --key-condition-expression "Artist = :v_artist and AlbumTitle = :v_title" \
    --expression-attribute-values  '{":v_artist":{"S":"Acme Band"},":v_title":{"S":"Songs About Life"} }'
```

# Administración de flujos de trabajo complejos con transacciones de DynamoDB
<a name="transactions"></a>

Amazon DynamoDB Transactions simplifica la experiencia de los desarrolladores que realizan cambios coordinados de "todo o nada" en varios elementos y distintas tablas. Las transacciones proporcionan atomicidad, consistencia, aislamiento y durabilidad (ACID, por sus siglas en inglés) en DynamoDB, lo que le ayuda a mantener la exactitud de los datos en sus aplicaciones.

Puede usar las API de lectura y escritura transaccional de DynamoDB para administrar flujos de trabajo empresarial complejos que requieren agregar, actualizar o eliminar varios elementos en una única operación de tipo "todo o nada". Por ejemplo, un desarrollador de videojuegos puede garantizar que los perfiles de los jugadores se actualicen correctamente cuando intercambien elementos en un juego o realicen compras dentro de este.

Con la API de escritura de transacciones, puede agrupar varias acciones `Put`, `Update`, `Delete` y `ConditionCheck`. A continuación, puede presentarlas como una sola operación `TransactWriteItems` que se realiza correctamente o da error como unidad. Lo mismo sucede con varias acciones `Get`, que se pueden agrupar y enviar como una sola operación `TransactGetItems`.

Habilitar las transacciones para las tablas de DynamoDB no conlleva ningún coste adicional. Usted paga sólo por las lecturas o escrituras que forman parte de su transacción. DynamoDB lleva a cabo dos lecturas o escrituras subyacentes de cada elemento de la transacción: una para preparar la transacción y otra para confirmarla. Estas dos operaciones de lectura/escritura subyacentes están visibles en las métricas de Amazon CloudWatch.

Para comenzar a trabajar con transacciones de DynamoDB, descargue el SDK de AWS o la AWS Command Line Interface (AWS CLI) más recientes. A continuación, siga el [Ejemplo de transacciones en DynamoDB](transaction-example.md).

En las secciones siguientes, se proporciona información general detallada sobre las API de transacciones y cómo usarlas en DynamoDB.

**Topics**
+ [Funcionamiento](transaction-apis.md)
+ [Uso de IAM con las transacciones](transaction-apis-iam.md)
+ [Código de ejemplo](transaction-example.md)

# Funcionamiento de las Transacciones de Amazon DynamoDB
<a name="transaction-apis"></a>

Con Amazon DynamoDB Transactions, puede agrupar varias acciones y enviarlas como una sola operación `TransactWriteItems` o `TransactGetItems` de tipo "todo o nada". En las secciones siguientes, se describen las operaciones de la API, la administración de la capacidad, las prácticas recomendadas y otros detalles sobre el uso de operaciones transaccionales en DynamoDB.

**Topics**
+ [API TransactWriteItems](#transaction-apis-txwriteitems)
+ [API TransactGetItems](#transaction-apis-txgetitems)
+ [Niveles de aislamiento para las transacciones de DynamoDB](#transaction-isolation)
+ [Gestión de conflictos de transacciones en DynamoDB](#transaction-conflict-handling)
+ [Uso de las API transaccionales en DynamoDB Accelerator (DAX)](#transaction-apis-dax)
+ [Administración de la capacidad para las transacciones](#transaction-capacity-handling)
+ [Prácticas recomendadas para las transacciones](#transaction-best-practices)
+ [Uso de las API transaccionales con tablas globales](#transaction-integration)
+ [Transacciones de DynamoDB en comparación con la biblioteca de transacciones del cliente de AWSLabs](#transaction-vs-library)

## API TransactWriteItems
<a name="transaction-apis-txwriteitems"></a>

`TransactWriteItems` es una operación de escritura síncrona e idempotente que agrupa hasta 100 acciones de escritura en una única operación de tipo “todo o nada”. Estas acciones pueden estar dirigidas a un máximo de 100 elementos diferenciados en una o varias tablas de DynamoDB en la misma cuenta de AWS y en la misma región. El tamaño total de los elementos de la transacción no puede superar 4 MB. Las acciones se realizan atómicamente, de tal forma que se llevan a cabo correctamente todas o ninguna de ellas.

**nota**  
 Una operación `TransactWriteItems` se diferencia de una operación `BatchWriteItem` en que todas las acciones que contiene deben completarse correctamente o, de lo contrario, no se realiza ningún cambio. Con una operación `BatchWriteItem`, es posible que solo algunas de las acciones del lote se realicen correctamente y otras, no. 
 Las transacciones no se pueden realizar mediante índices. 

No se pueden dirigir varias operaciones de la misma transacción al mismo elemento. Por ejemplo, no se puede realizar una acción `ConditionCheck` y también una acción `Update` en el mismo elemento de la misma transacción.

Puede agregar los tipos de acciones siguientes a una transacción:
+ `Put`: Inicia una operación `PutItem` para crear un nuevo elemento o sustituir uno antiguo por otro nuevo, ya sea de forma condicional o sin especificar ninguna condición.
+ `Update`: Inicia una operación `UpdateItem` para editar los atributos de un elemento existente o agregar un nuevo elemento a la tabla, si no existe ya. Esta acción se utiliza para agregar, eliminar o actualizar atributos a un elemento existente, de forma condicional o sin condiciones.
+ `Delete`: Inicia una operación `DeleteItem` para eliminar un solo elemento de una tabla identificado por su clave principal.
+ `ConditionCheck`: Comprueba la existencia de un elemento o la condición de atributos concretos del elemento.

Cuando se completa una transacción en DynamoDB, los cambios comienzan a propagarse a los índices secundarios globales (GSI), los flujos y las copias de seguridad. Esta propagación se produce gradualmente: los registros de flujo de la misma transacción pueden aparecer en momentos diferentes y pueden intercalarse con registros de otras transacciones. Los consumidores de flujos no deben asumir la atomicidad de la transacción ni las garantías de orden.

Para garantizar una instantánea atómica de los elementos modificados en una transacción, utilice la operación TransactGetItems para leer todos los elementos relevantes juntos. Esta operación proporciona una visión coherente de los datos, lo que garantiza que vea todos los cambios de una transacción completada o ninguno en absoluto.

Debido a que la propagación no es inmediata, si una tabla se restaura desde una copia de seguridad ([RestoreTableFromBackup](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_RestoreTableFromBackup.html)) o se exporta a un momento dado ([ExportTableToPointInTime](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_ExportTableToPointInTime.html)) a mitad de la propagación, puede contener solo algunos de los cambios realizados durante una transacción reciente.

### Idempotencia
<a name="transaction-apis-txwriteitems-idempotency"></a>

Opcionalmente, puede incluir un token de cliente al realizar una llamada a `TransactWriteItems` para asegurarse de que la solicitud sea *idempotente*. Hacer que las transacciones sean idempotentes ayuda a evitar errores de aplicaciones si se envía la misma operación varias veces debido a una interrupción de la conexión u otro problema de conectividad.

Si la llamada a `TransactWriteItems` original se realizó correctamente, las llamadas siguientes a `TransactWriteItems` con el mismo token de cliente también se devolverán correctamente sin hacer ningún cambio. Si se ha establecido el parámetro `ReturnConsumedCapacity`, la llamada `TransactWriteItems` inicial devolverá el número de unidades de capacidad de escritura consumidas al realizar los cambios. Las llamadas siguientes a `TransactWriteItems` con el mismo token de cliente devolverán el número de unidades de capacidad de lectura consumidas al leer el elemento.

**Información importante sobre la idempotencia**
+ Un token de cliente es válido durante 10 minutos desde que finaliza la solicitud que lo utiliza. Transcurridos esos 10 minutos, cualquier solicitud que use el mismo token de cliente se tratará como si fuera nueva. No debe reutilizar el mismo token de cliente para la misma solicitud si han pasado 10 minutos.
+ Si repite una solicitud con el mismo token de cliente dentro del periodo de idempotencia de 10 minutos, pero cambia algún otro parámetro de la solicitud, DynamoDB devolverá una excepción `IdempotentParameterMismatch`.

### Control de errores de escritura
<a name="transaction-apis-txwriteitems-errors"></a>

Las transacciones de escritura no se realizarán correctamente en las siguientes circunstancias:
+ Cuando no se cumple alguna de las condiciones de las expresiones de condición.
+ Cuando se produce un error de validación de transacción porque hay más de una acción dirigida al mismo elemento en una sola operación `TransactWriteItems`.
+ Cuando una solicitud `TransactWriteItems` está en conflicto con una operación `TransactWriteItems` en curso para uno o varios elementos de la solicitud `TransactWriteItems`. En este caso, la solicitud genera un error `TransactionCanceledException`.
+ Cuando no hay suficiente capacidad aprovisionada para completar la transacción.
+ Cuando el tamaño de un elemento es excesivo (más de 400 KB), un índice secundario local (LSI) se vuelve demasiado grande o se produce algún error de validación semejante a causa de los cambios realizados por la transacción.
+ Cuando hay algún error de usuario, como un formato de datos no válido.

 Para obtener más información acerca de cómo se manejan los conflictos con operaciones `TransactWriteItems`, consulte [Gestión de conflictos de transacciones en DynamoDB](#transaction-conflict-handling).

## API TransactGetItems
<a name="transaction-apis-txgetitems"></a>

`TransactGetItems` es una operación de lectura síncrona que agrupa hasta 100 acciones `Get`. Estas acciones pueden estar dirigidas a un máximo de 100 elementos diferenciados en una o varias tablas de DynamoDB en la misma cuenta y región de AWS. El tamaño total de los elementos de la transacción no puede superar 4 MB. 

Las acciones `Get` se realizan atómicamente, de tal forma que se llevan a cabo correctamente todas o ninguna de ellas:
+ `Get`: Inicia una operación `GetItem` para recuperar un conjunto de atributos para el elemento que tiene la clave principal especificada. Si no se encuentra ningún elemento que coincida, `Get` no devuelve ningún dato.

### Control de errores de lectura
<a name="transaction-apis-txgetitems-errors"></a>

Las transacciones de lectura no se realizarán correctamente en las siguientes circunstancias:
+ Cuando una solicitud `TransactGetItems` está en conflicto con una operación `TransactWriteItems` en curso para uno o varios elementos de la solicitud `TransactGetItems`. En este caso, la solicitud genera un error `TransactionCanceledException`.
+ Cuando no hay suficiente capacidad aprovisionada para completar la transacción.
+ Cuando hay algún error de usuario, como un formato de datos no válido.

 Para obtener más información acerca de cómo se manejan los conflictos con operaciones `TransactGetItems`, consulte [Gestión de conflictos de transacciones en DynamoDB](#transaction-conflict-handling).

## Niveles de aislamiento para las transacciones de DynamoDB
<a name="transaction-isolation"></a>

Los niveles de aislamiento de las operaciones transaccionales (`TransactWriteItems` o `TransactGetItems`) y otras operaciones son los siguientes.

### SERIALIZABLE
<a name="transaction-isolation-serializable"></a>

El aislamiento *serializable* garantiza que los resultados de varias operaciones concurrentes sean iguales que si ninguna operación se hubiera iniciado antes de finalizar la anterior.

Existe aislamiento serializable entre los siguientes tipos de operaciones:
+ Entre cualquier operación transaccional y cualquier operación de escritura estándar (`PutItem`, `UpdateItem` o `DeleteItem`).
+ Entre cualquier operación transaccional y cualquier operación de lectura estándar (`GetItem`).
+ Entre una operación `TransactWriteItems` y una operación `TransactGetItems`.

Aunque existe aislamiento serializable entre las operaciones transaccionales y cada escritura estándar individual escribe en una operación `BatchWriteItem`, no se produce aislamiento serializable entre la transacción y la operación `BatchWriteItem` como una unidad.

Del mismo modo, el nivel de aislamiento entre una operación transaccional y una operación `GetItems` individual de una operación `BatchGetItem` es serializable. Pero el nivel de aislamiento entre la transacción y la operación `BatchGetItem` como unidad es de *lectura confirmada*.

Una sola solicitud `GetItem` es serializable con respecto a una solicitud `TransactWriteItems` de una de las dos formas siguientes: antes o después de la solicitud `TransactWriteItems`. Multiple solicitudes `GetItem`, de las claves en solicitudes `TransactWriteItems` se pueden ejecutar en cualquier orden, y por lo tanto los resultados son *lectura confirmada*.

Por ejemplo, si las solicitudes `GetItem` para el elemento A y el elemento B se ejecutan simultáneamente con una solicitud `TransactWriteItems` que modifica tanto el punto A como el punto B, hay cuatro posibilidades:
+ Ambos `GetItem` se ejecutan antes de que la solicitud `TransactWriteItems`.
+ Ambos `GetItem` se ejecutan después de la solicitud `TransactWriteItems`.
+ Solicitud `GetItem` para el elemento A se ejecuta antes de la solicitud `TransactWriteItems`. Para el elemento B, `GetItem` se ejecuta después de `TransactWriteItems`.
+ Solicitud `GetItem` para el elemento B se ejecuta antes de la solicitud `TransactWriteItems`. Para el elemento A, `GetItem` se ejecuta después de `TransactWriteItems`.

Utilice `TransactGetItems`, si prefiere el nivel de aislamiento serializable para múltiples solicitudes `GetItem`.

Si se realiza una lectura no transaccional de varios elementos que formaban parte de la misma solicitud de escritura de transacción durante el vuelo, es posible que pueda leer el nuevo estado de algunos de los elementos y el estado anterior de los demás. Solo podrá leer el nuevo estado de todos los elementos que formaban parte de la solicitud de escritura de transacción cuando reciba una respuesta correcta a la escritura de la transacción, lo que indica que la transacción se ha completado.

Una vez que la transacción se completa correctamente y se recibe una respuesta, las operaciones de lectura *coherente posterior* pueden seguir devolviendo el estado anterior durante un breve periodo debido al modelo de consistencia posterior de DynamoDB. Para garantizar la lectura de los datos más actualizados inmediatamente después de una transacción, debe utilizar lecturas [*altamente coherentes*](HowItWorks.ReadConsistency.md#HowItWorks.ReadConsistency.Strongly) mediante el establecimiento de `ConsistentRead` a true.

### LECTURA CONFIRMADA
<a name="transaction-isolation-read-committed"></a>

El aislamiento de *lectura confirmada* garantiza que las operaciones de lectura siempre devuelven valores confirmados para un elemento: la lectura nunca presentará una vista al elemento que represente un estado de una escritura transaccional que no haya tenido éxito finalmente. El aislamiento de lectura confirmada no impide las modificaciones del elemento inmediatamente después de la operación de lectura.

El nivel de aislamiento es de lectura confirmada entre cualquier operación transaccional y cualquier operación de lectura que conlleve varias lecturas estándar (`BatchGetItem`, `Query` o `Scan`). Si una escritura transaccional actualiza un elemento en medio de una operación de `BatchGetItem`, `Query` o `Scan`, la parte posterior de la operación de lectura devuelve el nuevo valor confirmado (con `ConsistentRead)` o posiblemente un valor confirmado anterior [lecturas coherentes posteriores]).

### Resumen de la operación
<a name="transaction-isolation-table"></a>

A modo de resumen, en la siguiente tabla se indican los niveles de aislamiento entre una operación transaccional (`TransactWriteItems` o `TransactGetItems`) y otras operaciones.


| Operación | Nivel de aislamiento | 
| --- | --- | 
| `DeleteItem` | *Serializable* | 
| `PutItem` | *Serializable* | 
| `UpdateItem` | *Serializable* | 
| `GetItem` | *Serializable* | 
| `BatchGetItem` | *Lectura confirmada*\$1 | 
| `BatchWriteItem` | *NO serializable*\$1 | 
| `Query` | *Lectura confirmada* | 
| `Scan` | *Lectura confirmada* | 
| Otra operación transaccional | *Serializable* | 

Los niveles marcados con un asterisco (\$1) se aplican a la operación como una unidad. Sin embargo, las acciones individuales contenidas en esas operaciones tienen el nivel de aislamiento *serializable*.

## Gestión de conflictos de transacciones en DynamoDB
<a name="transaction-conflict-handling"></a>

Un conflicto de transacciones puede producirse durante solicitudes concurrentes de nivel de elemento en un elemento dentro de una transacción. Los conflictos de transacciones pueden producirse en los siguientes escenarios: 
+ Una solicitud `PutItem`, `UpdateItem` o `DeleteItem` de un elemento entra en conflicto con una solicitud `TransactWriteItems` en curso que incluye el mismo elemento.
+ Un elemento incluido en una solicitud `TransactWriteItems` forma parte de otra solicitud `TransactWriteItems` en curso.
+ Un elemento incluido en una solicitud `TransactGetItems` forma parte de otra solicitud `TransactWriteItems`, `BatchWriteItem`, `PutItem`, `UpdateItem` o `DeleteItem` en curso.

**nota**  
Cuando se rechaza una solicitud `PutItem`, `UpdateItem` o `DeleteItem`, la solicitud no se realiza correctamente y genera una excepción `TransactionConflictException`. 
Si se rechaza cualquier solicitud de nivel de elemento dentro de una operación `TransactWriteItems` o `TransactGetItems`, la solicitud no se realiza correctamente y genera una excepción `TransactionCanceledException`. Si esa solicitud falla, los SDK de AWS no vuelven a intentar la solicitud.  
Si se está utilizando el AWS SDK para Java, la excepción contiene la lista de [CancellationReasons](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_CancellationReason.html), ordenada según la lista de elementos del parámetro `TransactItems` de la solicitud. Para los demás lenguajes, se incluye una representación de cadena de la lista en el mensaje de error de la excepción. 
Si una operación `TransactWriteItems` o `TransactGetItems` está en conflicto con una solicitud `GetItem` concurrente, las dos operaciones podrían realizarse correctamente.

La [métrica TransactionConflict de CloudWatch](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/metrics-dimensions.html) se incrementa para cada solicitud de nivel de elemento que no se realiza correctamente.

## Uso de las API transaccionales en DynamoDB Accelerator (DAX)
<a name="transaction-apis-dax"></a>

Tanto `TransactWriteItems` como `TransactGetItems` se admiten en DynamoDB Accelerator (DAX) con los mismos niveles de aislamiento que en DynamoDB.

`TransactWriteItems` escribe a través de DAX. DAX pasa una llamada `TransactWriteItems` a DynamoDB y devuelve la respuesta. Para rellenar la caché después de la escritura, DAX llama a `TransactGetItems` en segundo plano para cada elemento en la operación `TransactWriteItems`, que consume unidades de capacidad de lectura adicionales. (Para obtener más información, consulte ). [Administración de la capacidad para las transacciones](#transaction-capacity-handling).) Esta funcionalidad le permite no complicar la lógica de la aplicación y utilizar DAX para operaciones transaccionales así como para operaciones no transaccionales.

`TransactGetItems`Las llamadas se pasan por DAX sin que los elementos se almacenen en caché localmente. Es el mismo comportamiento que tienen las API de lectura de consistencia alta en DAX.

## Administración de la capacidad para las transacciones
<a name="transaction-capacity-handling"></a>

Habilitar las transacciones para las tablas de DynamoDB no conlleva ningún coste adicional. Usted paga sólo por las lecturas o escrituras que forman parte de su transacción. DynamoDB lleva a cabo dos lecturas o escrituras subyacentes de cada elemento de la transacción: una para preparar la transacción y otra para confirmarla. Las dos operaciones de lectura/escritura subyacentes están visibles en las métricas de Amazon CloudWatch.

Planee las lecturas y escrituras adicionales que requieren las API transaccionales al aprovisionar la capacidad para las tablas. Por ejemplo, supongamos que su aplicación ejecuta una transacción por segundo y que cada transacción escribe tres elementos de 500 bites en la tabla. Cada elemento requiere dos unidades de capacidad de escritura (WCU): una para preparar la transacción y otra para confirmarla. Por consiguiente, tendría que aprovisionar seis WCU para la tabla. 

Si utilizó DynamoDB Accelerator (DAX) en el ejemplo anterior, también debió utilizar dos unidades de capacidad de lectura (RCU) para cada elemento de la llamada `TransactWriteItems`. Por tanto, tendría que aprovisionar seis RCU adicionales para la tabla.

Del mismo modo, si la aplicación ejecuta una transacción de lectura por segundo y cada transacción lee tres elementos de 500 bytes de la tabla, debería aprovisionar seis unidades de capacidad de lectura (RCU) para la tabla. La lectura de cada elemento requiere dos RCU: una para preparar la transacción y otra para confirmarla.

Además, el comportamiento predeterminado del SDK consiste en reintentar las transacciones si se produce alguna excepción `TransactionInProgressException`. Planee las unidades de capacidad de lectura (RCU) adicionales que consumen estos reintentos. Lo mismo sucede si reintenta transacciones de su propio código mediante un `ClientRequestToken`.

## Prácticas recomendadas para las transacciones
<a name="transaction-best-practices"></a>

Es importante tener en cuenta las prácticas recomendadas siguientes al utilizar transacciones de DynamoDB.
+ Habilite el escalado automático en las tablas o asegúrese de haber aprovisionado suficiente capacidad de rendimiento para llevar a cabo las dos operaciones de lectura o escritura para cada elemento de la transacción.
+ Si no utiliza un SDK proporcionado por AWS, incluya un atributo `ClientRequestToken` al realizar una llamada a `TransactWriteItems` para asegurarse de que la solicitud sea idempotente.
+ No agrupe las operaciones en una transacción si no es necesario. Por ejemplo, si una misma transacción compuesta de 10 operaciones se puede desglosar en varias transacciones de modo que la aplicación continúe siendo correcta, recomendamos dividir la transacción. Usar transacciones más sencillas mejora el rendimiento y aumenta la probabilidad de que se ejecuten correctamente. 
+ Si varias transacciones actualizan los mismos elementos de forma simultánea, pueden provocar conflictos que cancelen las transacciones. Recomendamos las siguientes prácticas recomendadas de DynamoDB para modelar los datos de tal forma que se reduzcan al mínimo esos conflictos.
+ Si un conjunto de atributos se actualiza a menudo para varios elementos durante una misma transacción, puede ser conveniente agrupar los atributos en un solo elemento para reducir el ámbito de la transacción.
+ Evite utilizar transacciones para la ingesta masiva de datos. Para las escrituras masivas, es preferible usar `BatchWriteItem`.

## Uso de las API transaccionales con tablas globales
<a name="transaction-integration"></a>

Las operaciones transaccionales proporcionan garantías de atomicidad, uniformidad, aislamiento y durabilidad (ACID) solo en la región de AWS en la que se invocó la API de escritura. No se admiten las transacciones entre regiones en las tablas globales. Por ejemplo, supongamos que tiene una tabla global con réplicas en las regiones EE. UU. Este (Ohio) y EE. UU. Oeste (Oregón) y realiza una operación `TransactWriteItems` en la región EE. UU. Este (Norte de Virginia). Puede observar transacciones parcialmente completadas en la región Oeste de EE. UU. (Oregón) a medida que se replican los cambios. Los cambios se replican en otras regiones solo cuando se han confirmado en la región de origen.

## Transacciones de DynamoDB en comparación con la biblioteca de transacciones del cliente de AWSLabs
<a name="transaction-vs-library"></a>

Las transacciones de DynamoDB proporcionan una forma más rentable, robusta y eficiente de sustituir la biblioteca de cliente de transacciones de [AWSLabs](https://github.com/awslabs). Sugerimos actualizar las aplicaciones de modo que utilicen las API de transacciones nativas del lado del servidor.

# Uso de IAM con las transacciones de DynamoDB
<a name="transaction-apis-iam"></a>

Puede usar AWS Identity and Access Management (IAM) para restringir las acciones que las operaciones transaccionales pueden ejecutar en Amazon DynamoDB. Para obtener más información sobre el uso de políticas de IAM en DynamoDB, consulte [Políticas basadas en identidad de DynamoDB](security_iam_service-with-iam.md#security_iam_service-with-iam-id-based-policies).

Los permisos para las acciones `Put`, `Update`, `Delete` y `Get` se rigen por los permisos usados para las operaciones `PutItem`, `UpdateItem`, `DeleteItem` y `GetItem` subyacentes. Para la acción `ConditionCheck`, puede usar el permiso `dynamodb:ConditionCheckItem` en las políticas de IAM.

A continuación, se proporcionan ejemplos de políticas de IAM que puede utilizar para configurar las transacciones de DynamoDB.

## Ejemplo 1: permitir las operaciones transaccionales
<a name="tx-policy-example-1"></a>

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:ConditionCheckItem",
                "dynamodb:PutItem",
                "dynamodb:UpdateItem",
                "dynamodb:DeleteItem",
                "dynamodb:GetItem"
            ],
            "Resource": [
                "arn:aws:dynamodb:*:*:table/table04"
            ]
        }
    ]
}
```

------

## Ejemplo 2: permitir solo las operaciones transaccionales
<a name="tx-policy-example-2"></a>

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:ConditionCheckItem",
                "dynamodb:PutItem",
                "dynamodb:UpdateItem",
                "dynamodb:DeleteItem",
                "dynamodb:GetItem"
            ],
            "Resource": [
                "arn:aws:dynamodb:*:*:table/table04"
            ],
            "Condition": {
                "ForAnyValue:StringEquals": {
                    "dynamodb:EnclosingOperation": [
                        "TransactWriteItems",
                        "TransactGetItems"
                    ]
                }
            }
        }
    ]
}
```

------

## Ejemplo 3: permitir las lecturas y escrituras no transaccionales y bloquear las lecturas y escrituras transaccionales
<a name="tx-policy-example-3"></a>

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Deny",
            "Action": [
                "dynamodb:ConditionCheckItem",
                "dynamodb:PutItem",
                "dynamodb:UpdateItem",
                "dynamodb:DeleteItem",
                "dynamodb:GetItem"
            ],
            "Resource": [
                "arn:aws:dynamodb:*:*:table/table04"
            ],
            "Condition": {
                "ForAnyValue:StringEquals": {
                    "dynamodb:EnclosingOperation": [
                        "TransactWriteItems",
                        "TransactGetItems"
                    ]
                }
            }
        },
        {
            "Effect": "Allow",
             "Action": [
                 "dynamodb:PutItem",
                 "dynamodb:DeleteItem",
                 "dynamodb:GetItem",
                 "dynamodb:UpdateItem"
             ],
             "Resource": [
                 "arn:aws:dynamodb:*:*:table/table04"
             ]
         }
    ]
}
```

------

## Ejemplo 4: evitar que se devuelva información en caso de error de comprobación de condición
<a name="tx-policy-example-4"></a>

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:ConditionCheckItem",
                "dynamodb:PutItem",
                "dynamodb:UpdateItem",
                "dynamodb:DeleteItem",
                "dynamodb:GetItem"
            ],
            "Resource": "arn:aws:dynamodb:*:*:table/table01",
            "Condition": {
                "StringEqualsIfExists": {
                    "dynamodb:ReturnValues": "NONE"
                }
            }
        }
    ]
}
```

------

# Ejemplo de transacciones en DynamoDB
<a name="transaction-example"></a>

Como ejemplo de una situación en la que Amazon DynamoDB Transactions pueden ser útiles, considere esta aplicación Java de ejemplo para un mercado en línea.

La aplicación tiene tres tablas de DynamoDB en el backend:
+ `Customers`: en esta tabla se almacenan detalles sobre los clientes del mercado. Su clave principal es un identificador único `CustomerId`.
+ `ProductCatalog`: en esta tabla se almacenan detalles tales como el precio y la disponibilidad de los productos que se venden en el mercado. Su clave principal es un identificador único `ProductId`.
+ `Orders`: en esta tabla se almacenan detalles sobre los pedidos del mercado. Su clave principal es un identificador único `OrderId`.

## Hacer un pedido
<a name="transaction-example-write-order"></a>

Los siguientes fragmentos de código ilustran cómo utilizar las transacciones de DynamoDB para coordinar los múltiples pasos necesarios para crear y procesar un pedido. El uso de una única operación todo o nada garantiza que si falla alguna parte de la transacción, no se ejecuten acciones en la transacción y no se realicen cambios.

En este ejemplo, configura un pedido de un cliente cuyo `customerId` es `09e8e9c8-ec48`. A continuación, se ejecuta como una única transacción mediante el siguiente flujo de trabajo de procesamiento de pedidos:

1. Determine que el ID del cliente sea válido.

1. Asegúrese de que el producto sea `IN_STOCK` y actualice el estado del producto a `SOLD`.

1. Asegúrese de que el pedido aún no existe y cree el pedido.

### Validar al cliente
<a name="transaction-example-order-part-a"></a>

Primero, defina una acción para verificar que un cliente con `customerId` igual a `09e8e9c8-ec48` existe en la tabla de clientes.

```
final String CUSTOMER_TABLE_NAME = "Customers";
final String CUSTOMER_PARTITION_KEY = "CustomerId";
final String customerId = "09e8e9c8-ec48";
final HashMap<String, AttributeValue> customerItemKey = new HashMap<>();
customerItemKey.put(CUSTOMER_PARTITION_KEY, new AttributeValue(customerId));

ConditionCheck checkCustomerValid = new ConditionCheck()
    .withTableName(CUSTOMER_TABLE_NAME)
    .withKey(customerItemKey)
    .withConditionExpression("attribute_exists(" + CUSTOMER_PARTITION_KEY + ")");
```

### Actualizar el estado del producto
<a name="transaction-example-order-part-b"></a>

A continuación, defina una acción para actualizar el estado del producto a `SOLD` si la condición en la que el estado del producto está establecido actualmente en `IN_STOCK` es `true`. Configuración del parámetro `ReturnValuesOnConditionCheckFailure` devuelve el elemento si el atributo de estado del producto del artículo no era igual a `IN_STOCK`.

```
final String PRODUCT_TABLE_NAME = "ProductCatalog";
final String PRODUCT_PARTITION_KEY = "ProductId";
HashMap<String, AttributeValue> productItemKey = new HashMap<>();
productItemKey.put(PRODUCT_PARTITION_KEY, new AttributeValue(productKey));

Map<String, AttributeValue> expressionAttributeValues = new HashMap<>();
expressionAttributeValues.put(":new_status", new AttributeValue("SOLD"));
expressionAttributeValues.put(":expected_status", new AttributeValue("IN_STOCK"));

Update markItemSold = new Update()
    .withTableName(PRODUCT_TABLE_NAME)
    .withKey(productItemKey)
    .withUpdateExpression("SET ProductStatus = :new_status")
    .withExpressionAttributeValues(expressionAttributeValues)
    .withConditionExpression("ProductStatus = :expected_status")
    .withReturnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD);
```

### Crear el pedido
<a name="transaction-example-order-part-c"></a>

Por último, cree el pedido siempre y cuando un pedido con ese `OrderId` no existe.

```
final String ORDER_PARTITION_KEY = "OrderId";
final String ORDER_TABLE_NAME = "Orders";

HashMap<String, AttributeValue> orderItem = new HashMap<>();
orderItem.put(ORDER_PARTITION_KEY, new AttributeValue(orderId));
orderItem.put(PRODUCT_PARTITION_KEY, new AttributeValue(productKey));
orderItem.put(CUSTOMER_PARTITION_KEY, new AttributeValue(customerId));
orderItem.put("OrderStatus", new AttributeValue("CONFIRMED"));
orderItem.put("OrderTotal", new AttributeValue("100"));

Put createOrder = new Put()
    .withTableName(ORDER_TABLE_NAME)
    .withItem(orderItem)
    .withReturnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD)
    .withConditionExpression("attribute_not_exists(" + ORDER_PARTITION_KEY + ")");
```

### Ejecutar la transacción
<a name="transaction-example-order-part-d"></a>

En el siguiente ejemplo de la se muestra cómo ejecutar las acciones definidas anteriormente como una única operación de tipo “todo o nada”.

```
    Collection<TransactWriteItem> actions = Arrays.asList(
        new TransactWriteItem().withConditionCheck(checkCustomerValid),
        new TransactWriteItem().withUpdate(markItemSold),
        new TransactWriteItem().withPut(createOrder));

    TransactWriteItemsRequest placeOrderTransaction = new TransactWriteItemsRequest()
        .withTransactItems(actions)
        .withReturnConsumedCapacity(ReturnConsumedCapacity.TOTAL);

    // Run the transaction and process the result.
    try {
        client.transactWriteItems(placeOrderTransaction);
        System.out.println("Transaction Successful");

    } catch (ResourceNotFoundException rnf) {
        System.err.println("One of the table involved in the transaction is not found" + rnf.getMessage());
    } catch (InternalServerErrorException ise) {
        System.err.println("Internal Server Error" + ise.getMessage());
    } catch (TransactionCanceledException tce) {
        System.out.println("Transaction Canceled " + tce.getMessage());
    }
```

## Leer los detalles del pedido
<a name="transaction-example-read-order"></a>

En el siguiente ejemplo se muestra cómo leer el pedido completado transaccionalmente a través de las tablas `Orders` y `ProductCatalog`.

```
HashMap<String, AttributeValue> productItemKey = new HashMap<>();
productItemKey.put(PRODUCT_PARTITION_KEY, new AttributeValue(productKey));

HashMap<String, AttributeValue> orderKey = new HashMap<>();
orderKey.put(ORDER_PARTITION_KEY, new AttributeValue(orderId));

Get readProductSold = new Get()
    .withTableName(PRODUCT_TABLE_NAME)
    .withKey(productItemKey);
Get readCreatedOrder = new Get()
    .withTableName(ORDER_TABLE_NAME)
    .withKey(orderKey);

Collection<TransactGetItem> getActions = Arrays.asList(
    new TransactGetItem().withGet(readProductSold),
    new TransactGetItem().withGet(readCreatedOrder));

TransactGetItemsRequest readCompletedOrder = new TransactGetItemsRequest()
    .withTransactItems(getActions)
    .withReturnConsumedCapacity(ReturnConsumedCapacity.TOTAL);

// Run the transaction and process the result.
try {
    TransactGetItemsResult result = client.transactGetItems(readCompletedOrder);
    System.out.println(result.getResponses());
} catch (ResourceNotFoundException rnf) {
    System.err.println("One of the table involved in the transaction is not found" + rnf.getMessage());
} catch (InternalServerErrorException ise) {
    System.err.println("Internal Server Error" + ise.getMessage());
} catch (TransactionCanceledException tce) {
    System.err.println("Transaction Canceled" + tce.getMessage());
}
```

# Captura de datos de cambio con Amazon DynamoDB
<a name="streamsmain"></a>

Muchas aplicaciones se benefician capturando los cambios en los elementos almacenados en una tabla de DynamoDB en el momento en que se producen. A continuación se muestran algunos ejemplos de casos de uso:
+ Una aplicación móvil popular modifica los datos de una tabla de DynamoDB a razón de miles de actualizaciones por segundo. Otra aplicación captura y almacena los datos sobre estas actualizaciones y ofrece métricas de uso de la aplicación para móviles prácticamente en tiempo real.
+ Una aplicación financiera modifica los datos del mercado de valores en una tabla de DynamoDB. Diferentes aplicaciones que se ejecutan en paralelo rastrean estos cambios en tiempo real, calculan el valor en riesgo y reequilibran automáticamente las carteras en función de los movimientos del precio de las acciones.
+ Los sensores en vehículos de transporte y equipos industriales envían datos a una tabla de DynamoDB. Diferentes aplicaciones monitorean el rendimiento y envían alertas de mensajería cuando se detecta un problema, predicen posibles defectos aplicando algoritmos de aprendizaje automático y comprimen y archivan datos en Amazon Simple Storage Service (Amazon S3).
+ Una aplicación envía automáticamente notificaciones a los dispositivos móviles de todos los amigos de un grupo tan pronto como uno de ellos carga una nueva imagen.
+ Un nuevo cliente agrega datos a una tabla de DynamoDB. Este evento invoca otra aplicación que envía un mensaje de correo electrónico de bienvenida al nuevo cliente.

DynamoDB admite el streaming de registros de captura de datos de cambio a nivel de elemento en tiempo casi real. Puede crear aplicaciones que consuman estas transmisiones y adopten medidas en función de su contenido.

**nota**  
No se admite agregar etiquetas a DynamoDB Streams ni utilizar el [control de acceso basado en atributos (ABAC)](access-control-resource-based.md) con DynamoDB Streams.

El siguiente vídeo le ofrece una introducción al concepto de captación de datos de cambios.

[![AWS Videos](http://img.youtube.com/vi/https://www.youtube.com/embed/VVv_-mZ5Ge8/0.jpg)](http://www.youtube.com/watch?v=https://www.youtube.com/embed/VVv_-mZ5Ge8)


**Topics**
+ [Opciones de streaming para la captura de datos de cambio](#streamsmain.choose)
+ [Uso de Kinesis Data Streams para capturar cambios en DynamoDB](kds.md)
+ [Captura de datos de cambios para DynamoDB Streams](Streams.md)

## Opciones de streaming para la captura de datos de cambio
<a name="streamsmain.choose"></a>

DynamoDB ofrece dos modelos de streaming para la captura de datos de cambios: Kinesis Data Streams para DynamoDB y DynamoDB Streams.

Para ayudarle a elegir la solución adecuada para su aplicación, la siguiente tabla resume las características de cada modelo de streaming. 

[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/streamsmain.html)

Puede habilitar ambos modelos de streaming en la misma tabla de DynamoDB.

En las siguientes charlas de vídeo, se comparan las diferencias entre las dos opciones.

[![AWS Videos](http://img.youtube.com/vi/https://www.youtube.com/embed/UgG17Wh2y0g/0.jpg)](http://www.youtube.com/watch?v=https://www.youtube.com/embed/UgG17Wh2y0g)


# Uso de Kinesis Data Streams para capturar cambios en DynamoDB
<a name="kds"></a>

Puede utilizar Amazon Kinesis Data Streams para capturar cambios en Amazon DynamoDB.

Kinesis Data Streams captura modificaciones a nivel de elemento en cualquier tabla de DynamoDB y las replica en una [secuencia de datos de Kinesis](https://docs.aws.amazon.com/streams/latest/dev/introduction.html). Sus aplicaciones pueden acceder a este flujo de datos y ver los cambios a nivel de elemento casi en tiempo real. Puede capturar y almacenar continuamente terabytes de datos por hora. Puede aprovechar el tiempo de retención de datos más prolongado y, con la capacidad de distribución ramificada mejorada, puede llegar simultáneamente a dos o más aplicaciones descendentes. Otros beneficios incluyen una mayor transparencia de auditoría y seguridad.

Kinesis Data Streams también le da acceso a [Amazon Data Firehose](https://docs.aws.amazon.com/firehose/latest/dev/what-is-this-service.html) y [Amazon Managed Service para Apache Flink](https://docs.aws.amazon.com/kinesisanalytics/latest/dev/what-is.html). Estos servicios pueden ayudarle a crear aplicaciones que alimenten paneles en tiempo real, generen alertas, apliquen precios y publicidad dinámicos y apliquen análisis de datos sofisticados y algoritmos de machine learning.

**nota**  
El uso de Kinesis Data Streams para DynamoDB está sujeto a los [Precios de Kinesis Data Streams](https://aws.amazon.com/kinesis/data-streams/pricing/) para el flujo de datos y a los [Precios de DynamoDB](https://aws.amazon.com/dynamodb/pricing/) para la tabla de origen.

Para habilitar la transmisión de Kinesis en una tabla de DynamoDB mediante la consola, la AWS CLI o el SDK de Java, consulte [Introducción a Kinesis Data Streams para Amazon DynamoDB](kds_gettingstarted.md).

**Topics**
+ [Cómo funciona Kinesis Data Streams con DynamoDB](#kds_howitworks)
+ [Introducción a Kinesis Data Streams para Amazon DynamoDB](kds_gettingstarted.md)
+ [Uso de particiones y métricas con DynamoDB Streams y Kinesis Data Streams](kds_using-shards-and-metrics.md)
+ [Uso de políticas de IAM para Amazon Kinesis Data Streams y Amazon DynamoDB](kds_iam.md)

## Cómo funciona Kinesis Data Streams con DynamoDB
<a name="kds_howitworks"></a>

Cuando un flujo de datos de Kinesis está habilitado para una tabla de DynamoDB, la tabla envía un registro de datos que captura cualquier cambio en los datos de esa tabla. Este registro de datos incluye:
+ La hora específica en la que se creó, se actualizó o se eliminó recientemente un elemento
+ Clave principal de ese elemento
+ Una instantánea del registro antes de la modificación
+ Una instantánea del registro después de la modificación 

Estos registros de datos se capturan y publican casi en tiempo real. Después de escribirlos en el flujo de datos de Kinesis, se pueden leer igual que cualquier otro registro. Puede utilizar la biblioteca de clientes de Kinesis, utilizar AWS Lambda, llamar a la API de Kinesis Data Streams y utilizar otros servicios conectados. Para obtener más información, consulte [Lectura de datos desde Amazon Kinesis Data Streams](https://docs.aws.amazon.com/streams/latest/dev/building-consumers.html) en la Guía para desarrolladores de Amazon Kinesis Data Streams.

Estos cambios en los datos también se capturan de forma asíncrona. Kinesis no tiene ningún impacto en el rendimiento de una tabla desde la que se transmite. Los registros de transmisión almacenados en la secuencia de datos de Kinesis se cifran en reposo. Para obtener más información, consulte [Protección de datos en Amazon Kinesis Data Streams](https://docs.aws.amazon.com/streams/latest/dev/server-side-encryption.html).

Los registros del flujo de datos de Kinesis podrían aparecer en un orden distinto al de cuando se produjeron los cambios en los elementos. Las mismas notificaciones de elementos también podrían aparecer más de una vez en el flujo. Puede verificar el atributo `ApproximateCreationDateTime` para identificar el orden en el que se produjeron las modificaciones de los elementos e identificar los registros duplicados. 

Al activar un flujo de datos de Kinesis como destino de transmisión de una tabla de DynamoDB, puede configurar la precisión de los valores `ApproximateCreationDateTime` en milisegundos o microsegundos. De forma predeterminada, `ApproximateCreationDateTime` indica la hora del cambio en milisegundos. Además, puede cambiar este valor en un destino de transmisión activo. Tras dicha actualización, los registros de flujo escritos en Kinesis tendrán valores `ApproximateCreationDateTime` con la precisión deseada. 

Los valores binarios escritos en DynamoDB deben estar codificados en [formato base64](HowItWorks.NamingRulesDataTypes.md). No obstante, cuando los registros de datos se escriben en un flujo de datos Kinesis, estos valores binarios codificados se codifican en base64 por segunda vez. Al leer estos registros de un flujo de datos Kinesis, para recuperar los valores binarios en bruto, las aplicaciones deben descodificar estos valores dos veces.

DynamoDB cobra por el uso de Kinesis Data Streams en unidades de captura de datos de cambio. 1 KB de cambio por elemento único cuenta como una unidad de captura de datos de cambio. El KB de cambio de cada elemento se calcula por la mayor de las imágenes “antes” y “después” del elemento escrito en el flujo, y se utiliza la misma lógica que el [consumo de unidades de capacidad para operaciones de escritura](read-write-operations.md#write-operation-consumption). De manera similar a la que funciona el modo [bajo demanda](capacity-mode.md#capacity-mode-on-demand) de DynamoDB, no es necesario aprovisionar el rendimiento de capacidad para las unidades de captura de datos de cambio.

### Activación de una instancia de flujo de datos de Kinesis para la tabla de DynamoDB
<a name="kds_howitworks.enabling"></a>

Puede habilitar o desactivar el streaming a Kinesis en su tabla de DynamoDB existente mediante la Consola de administración de AWS, el SDK de AWS o AWS Command Line Interface (AWS CLI).
+ Solo puede transmitir datos de DynamoDB a Kinesis Data Streams en la misma cuenta AWS y región AWS que su tabla. 
+ Solo puede transmitir datos desde una tabla de DynamoDB a un flujo de datos de Kinesis.

  

### Aplicación de cambios en un destino de Kinesis Data Streams de la tabla de DynamoDB
<a name="kds_howitworks.makingchanges"></a>

De forma predeterminada, todos los registros de flujos de datos de Kinesis incluyen un atributo `ApproximateCreationDateTime`. Este atributo representa una marca de tiempo en milisegundos del momento aproximado en que se creó cada registro. Puede cambiar la precisión de estos valores en [https://console.aws.amazon.com/kinesis](https://console.aws.amazon.com/kinesis), el SDK o la AWS CLI. 

# Introducción a Kinesis Data Streams para Amazon DynamoDB
<a name="kds_gettingstarted"></a>

En esta sección se describe cómo usar las tablas de Kinesis Data Streams para Amazon DynamoDB con la consola de Amazon DynamoDB, la AWS Command Line Interface (AWS CLI) y la API.

## Creación de un flujo de datos de Amazon Kinesis activo
<a name="kds_gettingstarted.making-changes"></a>

Todos estos ejemplos utilizan la tabla `Music` de DynamoDB que se creó como parte del tutorial [Introducción a DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GettingStartedDynamoDB.html).

Para obtener más información sobre cómo crear consumidores y conectar el flujo de datos de Kinesis a otros servicios de AWS, consulte [Lectura de datos de Kinesis Data Streams](https://docs.aws.amazon.com/streams/latest/dev/building-consumers.html) en la *Guía para desarrolladores de Amazon Kinesis Data Streams*.

**nota**  
 Cuando utilice particiones de KDS por primera vez, le recomendamos configurar los fragmentos para escalar verticalmente y reducir verticalmente según los patrones de uso. Cuando haya acumulado más datos sobre los patrones de uso, podrá ajustar las particiones de la transmisión para que coincidan. 

------
#### [ Console ]

1. Inicie sesión en la Consola de administración de AWS y abra la consola de Kinesis en [https://console.aws.amazon.com/kinesis/](https://console.aws.amazon.com/kinesis/).

1. Seleccionar **Creación de flujos de datos** y siga las instrucciones para crear una transmisión llamada `samplestream`. 

1. Abra la consola de DynamoDB en [https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/).

1. En el panel de navegación del lado izquierdo de la consola, elija **Tablas**.

1. Elija la tabla **Music**.

1. Elija la pestaña **Exports and streams** (Exportaciones y flujos).

1. (Opcional) En **Detalles de Amazon Kinesis Data Streams**, puede cambiar la precisión de la marca de tiempo del registro de microsegundos (predeterminado) a milisegundos. 

1. Elija **samplestream** de la lista desplegable.

1. Seleccione el botón **Activar**.

------
#### [ AWS CLI ]

1. Cree un flujo de datos de Kinesis llamado `samplestream` mediante el uso del [comando create-stream](https://docs.aws.amazon.com/cli/latest/reference/kinesis/create-stream.html).

   ```
   aws kinesis create-stream --stream-name samplestream --shard-count 3 
   ```

   Consulte [Consideraciones sobre la administración de particiones para Kinesis Data Streams](kds_using-shards-and-metrics.md#kds_using-shards-and-metrics.shardmanagment) antes de configurar el número de fragmentos para el flujo de datos de Kinesis.

1. Verifique que la transmisión de Kinesis esté activa y lista para su uso mediante el [comando describe-stream](https://docs.aws.amazon.com/cli/latest/reference/kinesis/describe-stream.html).

   ```
   aws kinesis describe-stream --stream-name samplestream
   ```

1. Habilite el streaming de Kinesis en la tabla de DynamoDB mediante el `enable-kinesis-streaming-destination` comando. Reemplace el valor de `stream-arn` por el que `describe-stream` devolvió en el paso anterior. Si lo desea, active la transmisión con una precisión más detallada (microsegundos) de los valores de marca de tiempo devueltos en cada registro.

   Active la transmisión con una precisión de marca de tiempo de microsegundos:

   ```
   aws dynamodb enable-kinesis-streaming-destination \
     --table-name Music \
     --stream-arn arn:aws:kinesis:us-west-2:12345678901:stream/samplestream
     --enable-kinesis-streaming-configuration ApproximateCreationDateTimePrecision=MICROSECOND
   ```

   O active la transmisión con la precisión de marca de tiempo predeterminada (milisegundos):

   ```
   aws dynamodb enable-kinesis-streaming-destination \
     --table-name Music \
     --stream-arn arn:aws:kinesis:us-west-2:12345678901:stream/samplestream
   ```

1. Verifique si el streaming de Kinesis está activa en la tabla mediante el comando `describe-kinesis-streaming-destination` de DynamoDB.

   ```
   aws dynamodb describe-kinesis-streaming-destination --table-name Music
   ```

1. Escribir datos en la tabla de DynamoDB utilizando el comando `put-item`, tal y como se describe en la [Guía para desarrolladores de DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/getting-started-step-2.html).

   ```
   aws dynamodb put-item \
       --table-name Music  \
       --item \
           '{"Artist": {"S": "No One You Know"}, "SongTitle": {"S": "Call Me Today"}, "AlbumTitle": {"S": "Somewhat Famous"}, "Awards": {"N": "1"}}'
   
   aws dynamodb put-item \
       --table-name Music \
       --item \
           '{"Artist": {"S": "Acme Band"}, "SongTitle": {"S": "Happy Day"}, "AlbumTitle": {"S": "Songs About Life"}, "Awards": {"N": "10"} }'
   ```

1. Uso del comando de la CLI [get-records](https://docs.aws.amazon.com/cli/latest/reference/kinesis/get-records.html) para Kinesis para recuperar el contenido del flujo de Kinesis. A continuación, utilice el siguiente fragmento de código para deserializar el contenido de la transmisión.

   ```
   /**
    * Takes as input a Record fetched from Kinesis and does arbitrary processing as an example.
    */
   public void processRecord(Record kinesisRecord) throws IOException {
       ByteBuffer kdsRecordByteBuffer = kinesisRecord.getData();
       JsonNode rootNode = OBJECT_MAPPER.readTree(kdsRecordByteBuffer.array());
       JsonNode dynamoDBRecord = rootNode.get("dynamodb");
       JsonNode oldItemImage = dynamoDBRecord.get("OldImage");
       JsonNode newItemImage = dynamoDBRecord.get("NewImage");
       Instant recordTimestamp = fetchTimestamp(dynamoDBRecord);
   
       /**
        * Say for example our record contains a String attribute named "stringName" and we want to fetch the value
        * of this attribute from the new item image. The following code fetches this value.
        */
       JsonNode attributeNode = newItemImage.get("stringName");
       JsonNode attributeValueNode = attributeNode.get("S"); // Using DynamoDB "S" type attribute
       String attributeValue = attributeValueNode.textValue();
       System.out.println(attributeValue);
   }
   
   private Instant fetchTimestamp(JsonNode dynamoDBRecord) {
       JsonNode timestampJson = dynamoDBRecord.get("ApproximateCreationDateTime");
       JsonNode timestampPrecisionJson = dynamoDBRecord.get("ApproximateCreationDateTimePrecision");
       if (timestampPrecisionJson != null && timestampPrecisionJson.equals("MICROSECOND")) {
           return Instant.EPOCH.plus(timestampJson.longValue(), ChronoUnit.MICROS);
       }
       return Instant.ofEpochMilli(timestampJson.longValue());
   }
   ```

------
#### [ Java ]

1. Siga las instrucciones de la guía para desarrolladores de Kinesis Data Streams para [crear](https://docs.aws.amazon.com/streams/latest/dev/kinesis-using-sdk-java-create-stream.html) una secuencia de datos de Kinesis llamada `samplestream` usando Java.

   Consulte [Consideraciones sobre la administración de particiones para Kinesis Data Streams](kds_using-shards-and-metrics.md#kds_using-shards-and-metrics.shardmanagment) antes de configurar el número de fragmentos para el flujo de datos de Kinesis. 

1. Use el siguiente fragmento de código para habilitar el streaming de Kinesis en la tabla de DynamoDB. Si lo desea, active la transmisión con una precisión más detallada (microsegundos) de los valores de marca de tiempo devueltos en cada registro. 

   Active la transmisión con una precisión de marca de tiempo de microsegundos:

   ```
   EnableKinesisStreamingConfiguration enableKdsConfig = EnableKinesisStreamingConfiguration.builder()
     .approximateCreationDateTimePrecision(ApproximateCreationDateTimePrecision.MICROSECOND)
     .build();
   
   EnableKinesisStreamingDestinationRequest enableKdsRequest = EnableKinesisStreamingDestinationRequest.builder()
     .tableName(tableName)
     .streamArn(kdsArn)
     .enableKinesisStreamingConfiguration(enableKdsConfig)
     .build();
   
   EnableKinesisStreamingDestinationResponse enableKdsResponse = ddbClient.enableKinesisStreamingDestination(enableKdsRequest);
   ```

   O active la transmisión con la precisión de marca de tiempo predeterminada (milisegundos):

   ```
   EnableKinesisStreamingDestinationRequest enableKdsRequest = EnableKinesisStreamingDestinationRequest.builder()
     .tableName(tableName)
     .streamArn(kdsArn)
     .build();
   
   EnableKinesisStreamingDestinationResponse enableKdsResponse = ddbClient.enableKinesisStreamingDestination(enableKdsRequest);
   ```

1. Siga las instrucciones de la *Guía para desarrolladores de Kinesis Data Streams* para [leer](https://docs.aws.amazon.com/streams/latest/dev/building-consumers.html) desde el flujo de datos creado.

1. Utilice el siguiente fragmento de código para deserializar el contenido del flujo.

   ```
   /**
    * Takes as input a Record fetched from Kinesis and does arbitrary processing as an example.
    */
   public void processRecord(Record kinesisRecord) throws IOException {
       ByteBuffer kdsRecordByteBuffer = kinesisRecord.getData();
       JsonNode rootNode = OBJECT_MAPPER.readTree(kdsRecordByteBuffer.array());
       JsonNode dynamoDBRecord = rootNode.get("dynamodb");
       JsonNode oldItemImage = dynamoDBRecord.get("OldImage");
       JsonNode newItemImage = dynamoDBRecord.get("NewImage");
       Instant recordTimestamp = fetchTimestamp(dynamoDBRecord);
   
       /**
        * Say for example our record contains a String attribute named "stringName" and we wanted to fetch the value
        * of this attribute from the new item image, the below code would fetch this.
        */
       JsonNode attributeNode = newItemImage.get("stringName");
       JsonNode attributeValueNode = attributeNode.get("S"); // Using DynamoDB "S" type attribute
       String attributeValue = attributeValueNode.textValue();
       System.out.println(attributeValue);
   }
   
   private Instant fetchTimestamp(JsonNode dynamoDBRecord) {
       JsonNode timestampJson = dynamoDBRecord.get("ApproximateCreationDateTime");
       JsonNode timestampPrecisionJson = dynamoDBRecord.get("ApproximateCreationDateTimePrecision");
       if (timestampPrecisionJson != null && timestampPrecisionJson.equals("MICROSECOND")) {
           return Instant.EPOCH.plus(timestampJson.longValue(), ChronoUnit.MICROS);
       }
       return Instant.ofEpochMilli(timestampJson.longValue());
   }
   ```

------

## Aplicación de cambios en un flujo de datos de Amazon Kinesis activo
<a name="kds_gettingstarted.making-changes"></a>

En esta sección se describe cómo realizar cambios en una configuración de Kinesis Data Streams para DynamoDB desde la consola, la AWS CLI o la API.

**Consola de administración de AWS**

1. Abra la consola de DynamoDB desde [ttps://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/).

1. Busque su tabla.

1. Elija la pestaña **Exportaciones y flujos**.

**AWS CLI**

1. Llame a `describe-kinesis-streaming-destination` para confirmar que el flujo es `ACTIVE`. 

1. Llame a `UpdateKinesisStreamingDestination`, como en este ejemplo:

   ```
   aws dynamodb update-kinesis-streaming-destination --table-name enable_test_table --stream-arn arn:aws:kinesis:us-east-1:12345678901:stream/enable_test_stream --update-kinesis-streaming-configuration ApproximateCreationDateTimePrecision=MICROSECOND
   ```

1. Llame a `describe-kinesis-streaming-destination` para confirmar que el flujo es `UPDATING`.

1. Llame a `describe-kinesis-streaming-destination` periódicamente hasta que el estado de la transmisión sea `ACTIVE` de nuevo. Las actualizaciones de precisión de la marca de tiempo suelen aplicarse al cabo de unos cinco minutos. Una vez actualizado el estado, significa que la actualización se ha completado y que el nuevo valor de precisión se aplicará a los registros futuros.

1. Escriba en la tabla con `putItem`.

1. Uso el comando `get-records` de Kinesis para recuperar el contenido del flujo.

1. Confirme que el `ApproximateCreationDateTime` de las escrituras tienen la precisión deseada.

**API de Java**

1. Proporcione un fragmento de código que construya una solicitud `UpdateKinesisStreamingDestination` y una respuesta `UpdateKinesisStreamingDestination`. 

1. Proporcione un fragmento de código que construya una solicitud `DescribeKinesisStreamingDestination` y una `DescribeKinesisStreamingDestination response`.

1. Llame a `describe-kinesis-streaming-destination` periódicamente hasta que el estado del flujo sea `ACTIVE` de nuevo, lo que significa que la actualización se ha completado y que el nuevo valor de precisión se aplicará a los registros futuros.

1. Realice escrituras en la tabla.

1.  Lea el flujo y deserialice el contenido del flujo.

1. Confirme que el `ApproximateCreationDateTime` de las escrituras tienen la precisión deseada.

# Uso de particiones y métricas con DynamoDB Streams y Kinesis Data Streams
<a name="kds_using-shards-and-metrics"></a>

## Consideraciones sobre la administración de particiones para Kinesis Data Streams
<a name="kds_using-shards-and-metrics.shardmanagment"></a>

Un flujo de datos de Kinesis cuenta su rendimiento en [particiones](https://docs.aws.amazon.com/streams/latest/dev/key-concepts.html). En Amazon Kinesis Data Streams, puede elegir entre un modo **bajo demanda** y un modo **aprovisionado** para sus flujos de datos. 

Se recomienda utilizar el modo bajo demanda para Kinesis Data Streams si la carga de trabajo de escritura de DynamoDB es muy variable e impredecible. Con el modo bajo demanda, no es necesario planificar la capacidad pues Kinesis Data Streams administra automáticamente las particiones para proporcionar el rendimiento necesario.

Para las cargas de trabajo predecibles, puede usar el modo aprovisionado para su flujo de datos de Kinesis. Con el modo aprovisionado, debe especificar el número de particiones del flujo de datos para adaptar los registros de captura de datos de cambios de DynamoDB. Para determinar el número de particiones que necesitará el flujo de datos de Kinesis para admitir la tabla de DynamoDB, necesitará los siguientes valores de entrada:
+ El tamaño promedio del registro de su tabla de DynamoDB en bytes (`average_record_size_in_bytes`).
+ El número máximo de operaciones de escritura que su tabla de DynamoDB llevará a cabo por segundo. Esto incluye las operaciones de creación, eliminación y actualización realizadas por sus aplicaciones, así como las operaciones generadas automáticamente, como las operaciones de eliminación generadas por Tiempo de vida (`write_throughput`).
+ El porcentaje de operaciones de actualización y sobrescritura que lleva a cabo en la tabla en comparación con las operaciones de creación o eliminación (`percentage_of_updates`). Tenga en cuenta que las operaciones de actualización y sobrescritura replican tanto las imágenes antiguas como las nuevas del elemento modificado en el flujo. Esto genera el doble del tamaño del elemento de DynamoDB.

Puede calcular el número de particiones (`number_of_shards`) que necesita su flujo de datos de Kinesis con los valores de entrada de la siguiente fórmula:

```
number_of_shards = ceiling( max( ((write_throughput * (4+percentage_of_updates) * average_record_size_in_bytes) / 1024 / 1024), (write_throughput/1000)), 1)
```

Por ejemplo, es posible que tenga un rendimiento máximo de 1040 operaciones de escritura por segundo (`write_throughput`) con un tamaño de registro medio de 800 bytes (`average_record_size_in_bytes)`). Si el 25 % de esas operaciones de escritura son operaciones de actualización (`percentage_of_updates`), necesitará dos particiones (`number_of_shards`) para adaptarse a su rendimiento de transmisión de DynamoDB:

```
ceiling( max( ((1040 * (4+25/100) * 800)/ 1024 / 1024), (1040/1000)), 1).
```

Tenga en cuenta lo siguiente antes de utilizar la fórmula para calcular el número de particiones necesarias con el modo aprovisionado para los flujos de datos de Kinesis:
+ Esta fórmula ayuda a realizar una estimación del número de particiones que se necesitará para acomodar los flujos de datos de cambios de DynamoDB. No representa el número total de particiones necesario en el flujo de datos de Kinesis, como el número de particiones necesario para admitir a los consumidores del flujo de datos de Kinesis.
+ En el modo aprovisionado, es posible que se produzcan excepciones en el rendimiento de lectura y escritura si no se configura el flujo de datos para manejar los picos de rendimiento. En este caso, debe escalar manualmente su flujo de datos para acomodar el tráfico de datos. 
+ Esta fórmula tiene en cuenta la sobrecarga adicional que genera DynamoDB antes de transmitir los registros de datos de los registros de cambios al flujo de datos de Kinesis.

Para obtener más información sobre los modos de capacidad de Kinesis Data Streams, consulte [Choosing the Data Stream Capacity Mode](https://docs.aws.amazon.com/streams/latest/dev/how-do-i-size-a-stream.html). Para obtener más información sobre la diferencia de precios entre los distintos modos de capacidad, consulte [Precios de Amazon Kinesis Data Streams](https://aws.amazon.com/kinesis/data-streams/pricing/).

## Monitoreo de la captura de datos de cambios con Kinesis Data Streams
<a name="kds_using-shards-and-metrics.monitoring"></a>

DynamoDB proporciona varias métricas de Amazon CloudWatch para ayudarlo a monitorear la replicación de la captura de datos de cambio en Kinesis. Para obtener una lista completa de las métricas de CloudWatch, consulte [Dimensiones y métricas de DynamoDB](metrics-dimensions.md).

Para determinar si el flujo tiene suficiente capacidad, le recomendamos que monitoree los siguientes elementos tanto durante la habilitación del flujo como en la producción:
+ `ThrottledPutRecordCount`: número de registros que el flujo de datos de Kinesis ha limitado porque la capacidad del flujo de datos de Kinesis es insuficiente. Es posible que experimente cierta limitación controlada durante picos de uso excepcionales, pero la `ThrottledPutRecordCount` debería ser lo más baja posible. DynamoDB vuelve a intentar enviar registros limitados al flujo de datos de Kinesis, pero esto podría dar lugar a una mayor latencia de replicación. 

  Si experimenta una limitación controlada excesiva y regular, es posible que tenga que aumentar el número de fragmentos del flujo de Kinesis proporcionalmente al rendimiento de escritura observado de la tabla. Para obtener más información sobre cómo determinar el tamaño de un flujo de datos de Kinesis, consulte [Determinar el tamaño inicial de un flujo de datos de Kinesis](https://docs.aws.amazon.com/streams/latest/dev/amazon-kinesis-streams.html#how-do-i-size-a-stream).
+ `AgeOfOldestUnreplicatedRecord`: el tiempo transcurrido desde el cambio de nivel de elemento más antiguo que aún no se ha replicado en el flujo de datos de Kinesis apareció en la tabla de DynamoDB. En funcionamiento normal, `AgeOfOldestUnreplicatedRecord` debe estar en el orden de milisegundos. Este número crece según los intentos de replicación fallidos cuando se deben a elecciones de configuración controladas por el cliente.

   Si la métrica `AgeOfOldestUnreplicatedRecord` supera 168 horas, la replicación de los cambios en el nivel de elemento de la tabla de DynamoDB al flujo de datos de Kinesis se desactivará automáticamente.

  Los ejemplos de las configuraciones controladas por el cliente que provocan intentos de replicación fallidos son una capacidad de flujo de datos de Kinesis no aprovisionada que produce una limitación excesiva o una actualización manual de las políticas de acceso del flujo de datos de Kinesis que evita que DynamoDB agregue datos al flujo de datos. Para mantener esta métrica lo más baja posible, es posible que tenga que garantizar el aprovisionamiento correcto de la capacidad del flujo de datos de Kinesis y asegurarse de que los permisos de DynamoDB no se modifiquen. 
+ `FailedToReplicateRecordCount`: número de registros que DynamoDB no pudo replicar en el flujo de datos de Kinesis. Algunos elementos de más de 34 KB podrían expandirse de tamaño para cambiar los registros de datos que superan el límite de tamaño del elemento de 1 MB de Kinesis Data Streams. Esta expansión de tamaño se produce cuando estos elementos mayores a 34 KB incluyen un gran número de valores de atributos booleanos o vacíos. Los valores de atributos booleanos y vacíos se almacenan como 1 byte en DynamoDB, pero se expanden hasta 5 bytes cuando se serializan mediante JSON estándar para la replicación de Kinesis Data Streams. DynamoDB no puede replicar tales registros de cambios en el flujo de datos de Kinesis. DynamoDB omite estos registros de datos de cambios y continúa replicando automáticamente los registros posteriores. 

   

Puede crear alarmas de Amazon CloudWatch que envíen un mensaje de Amazon Simple Notification Service (Amazon SNS) para notificación cuando cualquiera de las métricas anteriores supere un umbral específico. 

# Uso de políticas de IAM para Amazon Kinesis Data Streams y Amazon DynamoDB
<a name="kds_iam"></a>

La primera vez que habilita Amazon Kinesis Data Streams para Amazon DynamoDB, DynamoDB le crea automáticamente un AWS Identity and Access Management (IAM) vinculado a servicio. Esta función, `AWSServiceRoleForDynamoDBKinesisDataStreamsReplication`, permite a DynamoDB administrar la replicación de cambios a nivel de elemento en Kinesis Data Streams en su nombre. No elimine este rol vinculado a un servicio.

Para obtener más información acerca de los roles vinculados a servicios, consulte [Uso de roles vinculados a servicios](https://docs.aws.amazon.com/IAM/latest/UserGuide/using-service-linked-roles.html) en la *Guía del usuario de IAM*.

**nota**  
DynamoDB no admite condiciones basadas en etiquetas para las políticas de IAM.

Para habilitar Amazon Kinesis Data Streams para Amazon DynamoDB, debe tener los siguientes permisos en la tabla:
+ `dynamodb:EnableKinesisStreamingDestination`
+ `kinesis:ListStreams`
+ `kinesis:PutRecords`
+ `kinesis:DescribeStream`

Para describir Amazon Kinesis Data Streams para Amazon DynamoDB para una tabla de DynamoDB determinada, debe tener los siguientes permisos en la tabla.
+ `dynamodb:DescribeKinesisStreamingDestination`
+ `kinesis:DescribeStreamSummary`
+ `kinesis:DescribeStream`

Para deshabilitar Amazon Kinesis Data Streams para Amazon DynamoDB, debe tener los siguientes permisos en la tabla.
+ `dynamodb:DisableKinesisStreamingDestination`

Para actualizar Amazon Kinesis Data Streams para Amazon DynamoDB, la tabla debe tener los siguientes permisos.
+ `dynamodb:UpdateKinesisStreamingDestination`

En los siguientes ejemplos se muestra cómo utilizar las políticas de IAM para conceder permisos a Amazon Kinesis Data Streams para Amazon DynamoDB.

## Ejemplo: Habilitar Amazon Kinesis Data Streams para Amazon DynamoDB
<a name="access-policy-kds-example1"></a>

La siguiente política de IAM otorga permisos para activar Amazon Kinesis Data Streams para Amazon DynamoDB para la tabla `Music`. No concede permisos para desactivar, actualizar ni describir Kinesis Data Streams para DynamoDB para la tabla `Music`. 

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "iam:CreateServiceLinkedRole",
            "Resource": "arn:aws:iam::*:role/aws-service-role/kinesisreplication.dynamodb.amazonaws.com/AWSServiceRoleForDynamoDBKinesisDataStreamsReplication",
            "Condition": {
                "StringLike": {
                    "iam:AWSServiceName": "kinesisreplication.dynamodb.amazonaws.com"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:EnableKinesisStreamingDestination"
            ],
            "Resource": "arn:aws:dynamodb:us-west-2:111122223333:table/Music"
        }
    ]
}
```

------

## Ejemplo: Actualizar Amazon Kinesis Data Streams para Amazon DynamoDB
<a name="access-policy-kds-example2"></a>

La siguiente política de IAM otorga permisos para actualizar Amazon Kinesis Data Streams para Amazon DynamoDB para la tabla `Music`. No concede permisos para activar, desactivar ni describir Amazon Kinesis Data Streams para Amazon DynamoDB para la tabla `Music`. 

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:UpdateKinesisStreamingDestination"
            ],
            "Resource": "arn:aws:dynamodb:us-west-2:111122223333:table/Music"
        }
    ]
}
```

------

## Ejemplo: Deshabilite Amazon Kinesis Data Streams para Amazon DynamoDB
<a name="access-policy-kds-example2"></a>

La siguiente política de IAM otorga permisos para desactivar Amazon Kinesis Data Streams para Amazon DynamoDB para la tabla `Music`. No concede permisos para activar, actualizar ni describir Amazon Kinesis Data Streams para Amazon DynamoDB para la tabla `Music`. 

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:DisableKinesisStreamingDestination"
            ],
            "Resource": "arn:aws:dynamodb:us-west-2:111122223333:table/Music"
        }
    ]
}
```

------

## Ejemplo: aplicar permisos de forma selectiva para Amazon Kinesis Data Streams para Amazon DynamoDB basados en recursos
<a name="access-policy-kds-example3"></a>

La siguiente política de IAM concede permisos para activar y describir Amazon Kinesis Data Streams para Amazon DynamoDB para la tabla `Music` y deniega permisos para desactivar Amazon Kinesis Data Streams para Amazon DynamoDB para la tabla `Orders`. 

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:EnableKinesisStreamingDestination",
                "dynamodb:DescribeKinesisStreamingDestination"
            ],
            "Resource": "arn:aws:dynamodb:us-west-2:111122223333:table/Music"
        },
        {
            "Effect": "Deny",
            "Action": [
                "dynamodb:DisableKinesisStreamingDestination"
            ],
            "Resource": "arn:aws:dynamodb:us-west-2:111122223333:table/Orders"
        }
    ]
}
```

------

## Uso de roles vinculados a servicios para Kinesis Data Streams para DynamoDB
<a name="kds-service-linked-roles"></a>

Amazon Kinesis Data Streams para Amazon DynamoDB utiliza [roles vinculados a servicios](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_terms-and-concepts.html#iam-term-service-linked-role) de AWS Identity and Access Management (IAM). Un rol vinculado a un servicio es un tipo único de rol de IAM que está vinculado directamente a Kinesis Data Streams para DynamoDB. Los roles vinculados a servicios están predefinidos por Kinesis Data Streams para DynamoDB e incluyen todos los permisos que el servicio requiere para llamar a otros servicios de AWS en su nombre. 

Un rol vinculado a un servicio simplifica la configuración de Kinesis Data Streams para DynamoDB porque ya no tendrá que agregar manualmente los permisos necesarios. Kinesis Data Streams para DynamoDB define los permisos de sus roles vinculados a servicios y, a menos que esté definido de otra manera, solo Kinesis Data Streams puede asumir sus roles. Los permisos definidos incluyen las políticas de confianza y de permisos, y que la política de permisos no se pueda asociar a ninguna otra entidad de IAM.

Para obtener información acerca de otros servicios que admiten roles vinculados a servicios, consulte [Servicios de AWS que funcionan con IAM](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-services-that-work-with-iam.html) y busque los servicios que muestran **Sí** en la columna **Rol vinculado a un servicio**. Elija una opción **Sí** con un enlace para ver la documentación acerca del rol vinculado al servicio en cuestión.

### Permisos de roles vinculados a servicios para Kinesis Data Streams para DynamoDB
<a name="slr-permissions"></a>

Kinesis Data Streams para DynamoDB utiliza el rol vinculado a servicio denominado **AWSServiceRoleForDynamodBkinesisDataStreamsReplication**. El propósito del rol vinculado a servicios es permitir que Amazon DynamoDB administre la replicación de cambios a nivel de elemento en Kinesis Data Streams, en su nombre.

El rol vinculado al servicio `AWSServiceRoleForDynamoDBKinesisDataStreamsReplication` depende de los siguientes servicios para asumir el rol:
+ `kinesisreplication.dynamodb.amazonaws.com`

La política de permisos del rol permite que Kinesis Data Streams para DynamoDB realice las siguientes acciones en los recursos especificados:
+ Acción: `Put records and describe` en `Kinesis stream`
+ Acción: `Generate data keys` en `AWS KMS` para poner datos en las transmisiones de Kinesis cifrados mediante claves de AWS KMS generadas por el usuario.

Para conocer el contenido exacto del documento de política, consulte [DynamoDBKinesisReplicationServiceRolePolicy](https://console.aws.amazon.com/iam/home#policies/arn:aws:iam::aws:policy/aws-service-role/DynamoDBKinesisReplicationServiceRolePolicy).

Debe configurar permisos para permitir a una entidad de IAM (como un usuario, grupo o rol) crear, editar o eliminar un rol vinculado a servicios. Para obtener más información, consulte [Permisos de roles vinculados a servicios](https://docs.aws.amazon.com/IAM/latest/UserGuide/contributorinsights-service-linked-roles.html#service-linked-role-permissions) en la *Guía del usuario de IAM*.

### Creación de un rol vinculado a un servicio para Kinesis Data Streams para DynamoDB
<a name="create-slr"></a>

No necesita crear manualmente un rol vinculado a servicios. Cuando habilita Kinesis Data Streams para DynamoDB en la Consola de administración de AWS, la AWS CLI, o la API de AWS, Kinesis Data Streams para DynamoDB crea automáticamente el rol vinculado al servicio. 

Si elimina este rol vinculado a servicios y necesita crearlo de nuevo, puede utilizar el mismo proceso para volver a crear el rol en su cuenta. Cuando habilita Kinesis Data Streams para DynamoDB, Kinesis Data Streams para DynamoDB vuelve a crear el rol vinculado al servicio. 

### Edición de un rol vinculado a un servicio para Kinesis Data Streams para DynamoDB
<a name="edit-slr"></a>

Kinesis Data Streams para DynamoDB no permite editar el rol vinculado al servicio `AWSServiceRoleForDynamoDBKinesisDataStreamsReplication`. Después de crear un rol vinculado al servicio, no podrá cambiar el nombre del rol, ya que varias entidades podrían hacer referencia al rol. Sin embargo, sí puede editar la descripción del rol con IAM. Para obtener más información, consulte [Edición de un rol vinculado a servicios](https://docs.aws.amazon.com/IAM/latest/UserGuide/contributorinsights-service-linked-roles.html#edit-service-linked-role) en la *Guía del usuario de IAM*.

### Eliminación de un rol vinculado a un servicio para Kinesis Data Streams para DynamoDB
<a name="delete-slr"></a>

También puede utilizar la consola de IAM, la AWS CLI o la API de AWS para eliminar manualmente el rol vinculado al servicio. Para ello, primero debe limpiar manualmente los recursos del rol vinculado al servicio para poder eliminarlo después manualmente.

**nota**  
Si el servicio Kinesis Data Streams para DynamoDB está utilizando el rol cuando intenta eliminar los recursos, la eliminación podría producir un error. En tal caso, espere unos minutos e intente de nuevo la operación.

**Para eliminar manualmente el rol vinculado a servicios mediante IAM**

Puede usar la consola de IAM, la AWS CLI o la API de AWS para eliminar el rol vinculado a un servicio de `AWSServiceRoleForDynamoDBKinesisDataStreamsReplication`. Para más información, consulte [Eliminación de un rol vinculado a servicios](https://docs.aws.amazon.com/IAM/latest/UserGuide/using-service-linked-roles.html) en la *Guía del usuario de IAM*.

# Captura de datos de cambios para DynamoDB Streams
<a name="Streams"></a>

 DynamoDB Streams captura una secuencia en orden cronológico de las modificaciones de los elementos en una tabla de DynamoDB y almacena esta información en un log durante un máximo de 24 horas. Las aplicaciones pueden obtener acceso a este registro y ver los elementos de datos tal y como se encontraban antes y después de la modificación, prácticamente en tiempo real.

 El cifrado en reposo cifra los datos en DynamoDB streams. Para obtener más información, consulte [Cifrado en reposo en DynamoDB](EncryptionAtRest.md).

Una *transmisión de DynamoDB* es un flujo ordenado de información sobre los cambios que se realizan en los elementos de una tabla de DynamoDB. Cuando se habilita una transmisión en una tabla, DynamoDB obtiene información sobre cada modificación de los elementos de datos de esa tabla.

Cada vez que una aplicación crea, actualiza o elimina elementos en la tabla, DynamoDB Streams escribe un registro de transmisión con los atributos de clave principal de los elementos modificados. Un *registro de transmisión* contiene información sobre una modificación de los datos de un solo elemento de una tabla de DynamoDB. Puede configurar la secuencia de tal forma que sus registros capturen información adicional; por ejemplo, las imágenes de "antes" y "después" de los elementos modificados.

DynamoDB Streams ayuda a garantizar lo siguiente:
+ Cada registro de secuencia aparece una única vez en la secuencia.
+ Para cada elemento que se modifica de una tabla de DynamoDB, los registros de transmisión aparecen en el mismo orden en que se han realizado las modificaciones del elemento.

DynamoDB Streams escribe los registros de transmisión prácticamente en tiempo real, para que pueda crear aplicaciones que consuman estas transmisiones y adopten medidas en función de su contenido.

**Topics**
+ [Endpoints para DynamoDB Streams](#Streams.Endpoints)
+ [Habilitación de una secuencia](#Streams.Enabling)
+ [Lectura y procesamiento de un flujo](#Streams.Processing)
+ [DynamoDB Streams y período de vida](time-to-live-ttl-streams.md)
+ [Uso del adaptador Kinesis de DynamoDB Streams para procesar registros de transmisión](Streams.KCLAdapter.md)
+ [API de bajo nivel de DynamoDB Streams: ejemplo en Java](Streams.LowLevel.Walkthrough.md)
+ [DynamoDB Streams y disparadores de AWS Lambda](Streams.Lambda.md)
+ [DynamoDB Streams y Apache Flink](StreamsApacheFlink.xml.md)

## Endpoints para DynamoDB Streams
<a name="Streams.Endpoints"></a>

AWS mantiene puntos de enlace distintos para DynamoDB y DynamoDB Streams. Para usar las tablas y los índices de la base de datos, la aplicación tendrá que acceder a un punto de enlace de DynamoDB. Para leer y procesar los registros de DynamoDB Streams, la aplicación tendrá que obtener acceso a un punto de enlace de DynamoDB Streams situado en la misma región.

DynamoDB Streams ofrece dos conjuntos de puntos de conexión. Son los siguientes:
+ **Puntos de conexión exclusivos para IPv4**: puntos de conexión con la convención de nomenclatura de `streams.dynamodb.<region>.amazonaws.com`.
+ **Puntos de conexión de doble pila**: puntos de conexión nuevos que son compatibles con IPv4 e IPv6 y que siguen la convención de nomenclatura de `streams-dynamodb.<region>.api.aws`.

**nota**  
Para obtener una lista completa de las regiones y puntos de conexión de DynamoDB y DynamoDB Streams, consulte [Regiones y puntos de conexión](https://docs.aws.amazon.com/general/latest/gr/rande.html) en la *Referencia general de AWS*.

Los SDK de AWS proporcionan clientes independientes para DynamoDB y DynamoDB Streams. Según cuáles sean sus necesidades, la aplicación puede obtener acceso a un punto de enlace de DynamoDB, a un punto de enlace de DynamoDB Streams o a ambos al mismo tiempo. Para conectarse a ambos puntos de enlace, la aplicación tendrá que crear instancias de dos clientes: uno para DynamoDB y otro para DynamoDB Streams.

## Habilitación de una secuencia
<a name="Streams.Enabling"></a>

Puede habilitar una transmisión en una nueva tabla al crearla mediante la AWS CLI o uno de los SDK de AWS. También puede habilitar o deshabilitar una transmisión en una tabla existente, así como cambiar la configuración de una transmisión. DynamoDB Streams opera de forma asincrónica, por lo que el rendimiento de una tabla no se vea afectado al habilitar una transmisión.

La forma más sencilla de administrar DynamoDB Streams es usar la Consola de administración de AWS.

1. Inicie sesión en la Consola de administración de AWS y abra la consola de DynamoDB en [https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/).

1. En el panel de la consola de DynamoDB, elija **Tablas** y seleccione una tabla existente.

1. Elija la pestaña **Exports and streams** (Exportaciones y flujos).

1. En la sección **Detalles del flujo de DynamoDB**, seleccione **Activar**.

1. En la ventana **Activar el flujo de DynamoDB**, elija la información que se escribirá en el flujo cada vez que se modifiquen los datos de la tabla:
   + **Key attributes only** (Solo atributos clave): solo los atributos clave del elemento modificado.
   + **New image (Nueva imagen)**: el elemento completo tal y como aparece después de modificarlo.
   + **Old image (Imagen anterior)**: el elemento completo tal y como aparecía antes de modificarlo.
   + **New and old images (Imágenes nuevas y anteriores)**: ambas imágenes del elemento, la nueva y la anterior.

   Cuando la configuración sea la que desea, elija **Activar flujo**.

1. (Opcional) Para desactivar un flujo existente, elija **Desactivar** bajo **Detalles del flujo de DynamoDB**.

También puede usar las operaciones de la API `CreateTable` o `UpdateTable` para habilitar o modificar una secuencia, respectivamente. El parámetro `StreamSpecification` determina cómo se configura la secuencia:
+ `StreamEnabled`: especifica si una transmisión está habilitada (`true`) o deshabilitada (`false`) para la tabla.
+ `StreamViewType`: especifica la información que se escribirá en la transmisión cada vez que se modifiquen los datos de la tabla:
  + `KEYS_ONLY`: solo los atributos de clave del elemento modificado.
  + `NEW_IMAGE`: el elemento completo tal y como aparece después de modificarlo.
  + `OLD_IMAGE`: el elemento completo tal y como aparecía antes de modificarlo.
  + `NEW_AND_OLD_IMAGES`: ambas imágenes del elemento, la nueva y la anterior.

Puede habilitar o deshabilitar una secuencia en cualquier momento. Sin embargo, tenga en cuenta que recibirá una excepción `ValidationException` si intenta habilitar una secuencia en una tabla que ya tiene una. Recibirá una `ValidationException` si intenta desactivar una secuencia en una tabla que no tiene ninguna.

Cuando se establece `StreamEnabled` en `true`, DynamoDB crea una nueva transmisión con un descriptor de transmisión único asignado a ella. Si deshabilita y vuelve a habilitar una secuencia en la tabla, se crea una secuencia nueva con un descriptor de secuencia distinto.

Cada secuencia se identifica de forma exclusiva mediante un nombre de recurso de Amazon (ARN). A continuación se muestra un ejemplo de ARN de una transmisión de una tabla de DynamoDB denominada `TestTable`.

```
arn:aws:dynamodb:us-west-2:111122223333:table/TestTable/stream/2015-05-11T21:21:33.291
```

Para determinar el último descriptor de transmisión de una tabla, se emite una solicitud `DescribeTable` de DynamoDB y se busca el elemento `LatestStreamArn` en la respuesta.

**nota**  
No es posible editar un `StreamViewType` una vez que se ha configurado un flujo. Si necesita realizar cambios en un flujo después de haberlo configurado, debe desactivar el flujo actual y crear uno nuevo.

## Lectura y procesamiento de un flujo
<a name="Streams.Processing"></a>

Para leer y procesar una transmisión, la aplicación tiene que conectarse a un punto de enlace de DynamoDB Streams y emitir solicitudes de API.

Una secuencia consta de *registros de secuencia*. Cada registro de transmisión representa una única modificación de datos de la tabla de DynamoDB a la que pertenece la secuencia. A cada registro de secuencia se le asigna un número de secuencia que refleja el orden en que el registro se ha publicado en la secuencia.

Los registros de secuencia se organizan en grupos, o *fragmentos*. Cada fragmento actúa como contenedor de varios registros de secuencia y contiene la información necesaria para obtener acceso a estos registros y recorrerlos en iteración. Los registros de secuencia de un fragmento se eliminan automáticamente transcurridas de 24 horas.

Los fragmentos son efímeros: se crean y eliminan automáticamente, según sea necesario. Además, cualquier fragmento se puede dividir en varios fragmentos nuevos; esto también sucede automáticamente. (Cabe destacar que un fragmento principal puede tener un solo fragmento secundario). Un fragmento se puede dividir en respuesta a niveles de actividad de escritura elevados en la tabla principal, para que las aplicaciones puedan procesar en paralelo los registros de varios fragmentos.

Si se deshabilita una secuencia, todos fragmentos que estén abiertos se cerrarán. Los datos de la transmisión continuarán disponibles para leerlos durante 24 horas.

Como los fragmentos poseen un parentesco (principales y secundarios), las aplicaciones siempre deben procesar los fragmentos principales antes de procesar los secundarios. Esto ayuda a garantizar que los registros de secuencia se procesen también en el orden correcto. (Si utiliza DynamoDB Streams Kinesis Adapter, esto se lleva a cabo de forma automatizada: la aplicación procesará los fragmentos y registros de transmisión en el orden correcto y también administrará automáticamente los fragmentos nuevos o vencidos, así como aquellos que se dividan mientras se ejecuta la aplicación. Para obtener más información, consulte ). [Uso del adaptador Kinesis de DynamoDB Streams para procesar registros de transmisión](Streams.KCLAdapter.md).)

En el siguiente diagrama se muestra la relación entre una secuencia, sus fragmentos y los registros de secuencia contenidos en los fragmentos.

![\[Estructura de DynamoDB Streams. Los registros de secuencia que representan modificaciones de datos se organizan en particiones.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/streams-terminology.png)


**nota**  
Si lleva a cabo una operación `PutItem` o `UpdateItem` que no modifica ningún dato de un elemento, DynamoDB Streams *no* escribe ningún registro de transmisión de esa operación.

Para obtener acceso a una secuencia y procesar los registros que contiene, proceda como sigue:
+ Determine el ARN único de la secuencia a la que desea obtener acceso.
+ Determine qué fragmentos de la secuencia contienen los registros de secuencia que le interesan.
+ Obtenga acceso a los fragmentos y recupere los registros de secuencia que desee.

**nota**  
Nunca debe haber más de dos procesos leyendo la misma partición de flujo a la vez. Usar más de dos procesos de lectura por fragmento puede provocar que se aplique la limitación controlada.

La API de DynamoDB Streams ofrece las siguientes acciones para usarlas en los programas de aplicación:
+  `[ListStreams](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_streams_ListStreams.html)`: devuelve una lista de descriptores de transmisión de la cuenta y el punto de enlace actuales. Si lo desea, puede solicitar únicamente los descriptores de secuencia de un nombre de tabla concreto.
+ `[DescribeStream](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_streams_DescribeStream.html)`: devuelve información sobre un flujo, incluido el estado actual del flujo, su nombre de recurso de Amazon (ARN), la composición de sus particiones y su tabla de DynamoDB correspondiente. Puede utilizar opcionalmente el campo `ShardFilter` para recuperar la partición secundaria existente asociada a la partición principal.
+ `[GetShardIterator](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_streams_GetShardIterator.html)`: devuelve un *iterador de fragmentos* que describe una ubicación en el fragmento. Puede solicitar que el iterador proporcione acceso al punto más antiguo, al punto más reciente o a un punto concreto de la secuencia.
+ `[GetRecords](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_streams_GetRecords.html)`: devuelve los registros de secuencia de un fragmento determinado. Debe proporcionar el iterador de fragmentos devuelto por una solicitud `GetShardIterator`.

Para obtener descripciones completas de estas operaciones de la API, así como ejemplos de solicitudes y respuestas, consulte la [Referencia de API de Amazon DynamoDB Streams](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Operations_Amazon_DynamoDB_Streams.html).

### Detección de particiones
<a name="Streams.ShardDiscovery"></a>



Descubra nuevas particiones en el flujo de DynamoDB con dos métodos potentes. Como usuario de Amazon DynamoDB Streams, dispone de dos formas efectivas de rastrear e identificar nuevas particiones:

**Sondeo de toda la topología del flujo**  
Use la API de `DescribeStream` para sondear el flujo con regularidad. Esto devuelve todas las particiones de la transmisión, incluidas las nuevas particiones que se hayan creado. Al comparar los resultados a lo largo del tiempo, puede detectar las particiones recién agregadas.

**Detección de particiones secundarias**  
Use la API de `DescribeStream` con el parámetro `ShardFilter` para encontrar un subconjunto de particiones. Al especificar una partición principal en la solicitud, los flujos de DynamoDB devolverán sus particiones secundarias inmediatas. Este enfoque resulta útil cuando solo se necesita rastrear el linaje de las particiones sin escanear todo el flujo.   
Las aplicaciones que consumen datos de los flujos de DynamoDB pueden pasar de manera eficiente de leer una partición cerrada a su partición secundaria mediante este parámetro `ShardFilter`, lo que evita llamadas repetidas a la API de `DescribeStream` para recuperar y recorrer el mapa de particiones de todas las particiones cerradas y abiertas. Esto ayuda a detectar rápidamente las particiones secundarias después de cerrar la partición principal, lo que hace que las aplicaciones de procesamiento de flujos sean más receptivas y rentables.

Ambos métodos le permiten mantenerse al tanto de la estructura en evolución de los flujos de DynamoDB, lo que garantiza que nunca se pierda las actualizaciones de datos importantes ni las modificaciones de las particiones.

### Límite de retención de datos para DynamoDB Streams
<a name="Streams.DataRetention"></a>

Todos los datos de DynamoDB Streams están sujetos a una vida útil de 24 horas. Puede recuperar y analizar las últimas 24 horas de actividad de cada tabla. Sin embargo, los datos de más de 24 horas se pueden recortar (eliminar) en cualquier momento.

Si deshabilita una secuencia en una tabla, los datos de esa secuencia continuarán disponibles para leerlos durante 24 horas. Transcurrido ese tiempo, los datos vencen y los registros de secuencia se eliminan automáticamente. No existe ningún mecanismo para eliminar manualmente las secuencias. Tiene que esperar hasta que se alcance el límite de retención (24 horas) para que se eliminen los registros de secuencia.

# DynamoDB Streams y período de vida
<a name="time-to-live-ttl-streams"></a>

Puede realizar copias de seguridad o bien procesar los elementos eliminados por [período de vida](TTL.md) (TTL, por sus siglas en inglés) habilitando Amazon DynamoDB Streams en la tabla y procesando los registros de transmisión de los elementos vencidos. Para obtener más información, consulte [Lectura y procesamiento de un flujo](Streams.md#Streams.Processing).

El registro de secuencias contiene el campo de identidad del usuario `Records[<index>].userIdentity`.

Los elementos que el proceso período de vida elimina tras su vencimiento tienen los siguientes campos:
+ `Records[<index>].userIdentity.type`

  `"Service"`
+ `Records[<index>].userIdentity.principalId`

  `"dynamodb.amazonaws.com"`

**nota**  
Cuando utilice el TTL en una tabla global, el campo `userIdentity` se configurará en la región en la que se realizó el TTL. Este campo no se establecerá en otras regiones cuando se replique la eliminación.

En el código JSON siguiente se muestra la parte pertinente de un registro de secuencias único.

```
"Records": [
    {
        ...

        "userIdentity": {
            "type": "Service",
            "principalId": "dynamodb.amazonaws.com"
        }

        ...

    }
]
```

## Uso de DynamoDB Streams y Lambda para archivar elementos de TTL eliminados
<a name="streams-archive-ttl-deleted-items"></a>

La combinación de [Periodo de vida (TTL) de DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html), [DynamoDB Streams](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.html) y [AWS Lambda](https://aws.amazon.com/lambda/) puede simplificar el archivo de datos, reducir los costos de almacenamiento de DynamoDB y reducir la complejidad del código. El uso de Lambda como consumidor de flujos proporciona muchas ventajas, entre las que destaca la reducción de costos en comparación con otros consumidores como Kinesis Client Library (KCL). No se le cobran las llamadas a la API `GetRecords` en su flujo de DynamoDB cuando utiliza Lambda para consumir eventos, y Lambda puede proporcionar un filtrado de eventos mediante la identificación de patrones JSON en un evento de flujo. Con el filtrado de contenido de patrones de eventos, puede definir hasta cinco filtros diferentes para controlar qué eventos se envían a Lambda para procesarlos. De este modo, se reducen las invocaciones de sus funciones Lambda, se simplifica el código y se reduce el costo total.

Aunque DynamoDB Streams contiene todas las modificaciones de datos, como las acciones `Create`, `Modify` y `Remove`, esto puede dar lugar a invocaciones no deseadas de su función Lambda de archivo. Por ejemplo, supongamos que tiene una tabla con dos millones de modificaciones de datos por hora que se incluyen en el flujo, pero menos del 5 por ciento de estas son eliminaciones de elementos que vencerán a través del proceso TTL y se deben archivar. Con los [filtros de origen de eventos de Lambda](https://docs.aws.amazon.com/lambda/latest/dg/invocation-eventfiltering.html), la función Lambda solo se invocará 100 000 veces por hora. El resultado con el filtrado de eventos es que solo se le cobran las invocaciones necesarias en lugar de los dos millones de invocaciones que tendría sin el filtrado de eventos.

El filtrado de eventos se aplica a la [asignación del origen de eventos de Lambda](https://docs.aws.amazon.com/lambda/latest/dg/invocation-eventsourcemapping.html), que es un recurso que lee de un evento elegido (el flujo de DynamoDB) e invoca una función Lambda. En el siguiente diagrama, puede ver cómo una función Lambda consume un elemento de TTL eliminado mediante flujos y filtros de eventos.

![\[Un elemento eliminado mediante un proceso de TTL inicia una función de Lambda que utiliza flujos y filtros de eventos.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/streams-lambda-ttl.png)


### Patrón de filtros de eventos de DynamoDB Time to Live
<a name="ttl-event-filter-pattern"></a>

Si agrega el siguiente JSON a sus [criterios de filtro](https://docs.aws.amazon.com/lambda/latest/dg/API_FilterCriteria.html) de asignación de origen de eventos, podrá invocar su función Lambda solo para los elementos de TTL eliminados:

```
{
    "Filters": [
        {
            "Pattern": { "userIdentity": { "type": ["Service"], "principalId": ["dynamodb.amazonaws.com"] } }
        }
    ]
}
```

### Creación de una asignación de origen de eventos de AWS Lambda
<a name="create-event-source-mapping"></a>

Utilice los siguientes fragmentos de código para crear una asignación de origen de eventos filtrados que pueda conectar con el flujo de DynamoDB de una tabla. Cada bloque de código incluye el patrón del filtro de eventos.

------
#### [ AWS CLI ]

```
aws lambda create-event-source-mapping \
--event-source-arn 'arn:aws:dynamodb:eu-west-1:012345678910:table/test/stream/2021-12-10T00:00:00.000' \
--batch-size 10 \
--enabled \
--function-name test_func \
--starting-position LATEST \
--filter-criteria '{"Filters": [{"Pattern": "{\"userIdentity\":{\"type\":[\"Service\"],\"principalId\":[\"dynamodb.amazonaws.com\"]}}"}]}'
```

------
#### [ Java ]

```
LambdaClient client = LambdaClient.builder()
        .region(Region.EU_WEST_1)
        .build();

Filter userIdentity = Filter.builder()
        .pattern("{\"userIdentity\":{\"type\":[\"Service\"],\"principalId\":[\"dynamodb.amazonaws.com\"]}}")
        .build();

FilterCriteria filterCriteria = FilterCriteria.builder()
        .filters(userIdentity)
        .build();

CreateEventSourceMappingRequest mappingRequest = CreateEventSourceMappingRequest.builder()
        .eventSourceArn("arn:aws:dynamodb:eu-west-1:012345678910:table/test/stream/2021-12-10T00:00:00.000")
        .batchSize(10)
        .enabled(Boolean.TRUE)
        .functionName("test_func")
        .startingPosition("LATEST")
        .filterCriteria(filterCriteria)
        .build();

try{
    CreateEventSourceMappingResponse eventSourceMappingResponse = client.createEventSourceMapping(mappingRequest);
    System.out.println("The mapping ARN is "+eventSourceMappingResponse.eventSourceArn());

}catch (ServiceException e){
    System.out.println(e.getMessage());
}
```

------
#### [ Node ]

```
const client = new LambdaClient({ region: "eu-west-1" });

const input = {
    EventSourceArn: "arn:aws:dynamodb:eu-west-1:012345678910:table/test/stream/2021-12-10T00:00:00.000",
    BatchSize: 10,
    Enabled: true,
    FunctionName: "test_func",
    StartingPosition: "LATEST",
    FilterCriteria: { "Filters": [{ "Pattern": "{\"userIdentity\":{\"type\":[\"Service\"],\"principalId\":[\"dynamodb.amazonaws.com\"]}}" }] }
}

const command = new CreateEventSourceMappingCommand(input);

try {
    const results = await client.send(command);
    console.log(results);
} catch (err) {
    console.error(err);
}
```

------
#### [ Python ]

```
session = boto3.session.Session(region_name = 'eu-west-1')
client = session.client('lambda')

try:
    response = client.create_event_source_mapping(
        EventSourceArn='arn:aws:dynamodb:eu-west-1:012345678910:table/test/stream/2021-12-10T00:00:00.000',
        BatchSize=10,
        Enabled=True,
        FunctionName='test_func',
        StartingPosition='LATEST',
        FilterCriteria={
            'Filters': [
                {
                    'Pattern': "{\"userIdentity\":{\"type\":[\"Service\"],\"principalId\":[\"dynamodb.amazonaws.com\"]}}"
                },
            ]
        }
    )
    print(response)
except Exception as e:
    print(e)
```

------
#### [ JSON ]

```
{
  "userIdentity": {
     "type": ["Service"],
     "principalId": ["dynamodb.amazonaws.com"]
   }
}
```

------

# Uso del adaptador Kinesis de DynamoDB Streams para procesar registros de transmisión
<a name="Streams.KCLAdapter"></a>

Usar Amazon Kinesis Adapter es la forma recomendada de consumir secuencias de Amazon DynamoDB. La API de DynamoDB Streams es intencionadamente similar a la de Kinesis Data Streams. En ambos servicios, los data streams se componen de fragmentos, que son los contenedores de los registros de secuencia. Los API de ambos servicios contienen operaciones `ListStreams`, `DescribeStream`, `GetShards` y `GetShardIterator`. (Aunque estas acciones de DynamoDB Streams son parecidas a sus homólogas de Kinesis Data Streams, no son idénticas al 100 %).

Como usuario de DynamoDB Streams, puede sacar partido de los patrones de diseño contenidos en KCL para procesar los fragmentos de DynamoDB Streams y transmitir registros. Para ello, se utiliza DynamoDB Streams Kinesis Adapter. Kinesis Adapter implementa la interfaz de Kinesis Data Streams, de tal forma que se pueda usar KCL para consumir y procesar registros desde DynamoDB Streams. Para obtener instrucciones acerca de cómo configurar e instalar DynamoDB Streams Kinesis Adapter, consulte el [repositorio de GitHub](https://github.com/awslabs/dynamodb-streams-kinesis-adapter).

Puede escribir aplicaciones para Kinesis Data Streams mediante Kinesis Client Library (KCL). KCL simplifica la codificación porque proporciona abstracciones útiles por encima del API de bajo nivel de Kinesis Data Streams. Para obtener más información sobre KCL, consulte [Desarrollo de consumidores mediante la biblioteca Kinesis Client Library](https://docs.aws.amazon.com/kinesis/latest/dev/developing-consumers-with-kcl.html) en la *Guía para desarrolladores de Amazon Kinesis Data Streams*.

DynamoDB recomienda utilizar la versión 3.x de KCL con AWS SDK para Java v2.x. La versión actual del adaptador de Kinesis de DynamoDB Streams 1.x con AWS SDK para AWS SDK para Java v1.x se seguirá admitiendo por completo durante todo el ciclo de vida, tal y como estaba previsto durante el periodo de transición, en consonancia con la [Política de mantenimiento de AWS SDK y herramientas](https://docs.aws.amazon.com/sdkref/latest/guide/maint-policy.html).

**nota**  
Las versiones 1.x y 2.x de la biblioteca de clientes de Amazon Kinesis (KCL) están obsoletas. KCL 1.x dejará de recibir asistencia el 30 de enero de 2026. Le recomendamos que migre las aplicaciones de KCL que utilizan la versión 1.x a la última versión de KCL antes del 30 de enero de 2026. Para encontrar la versión más reciente de KCL, consulte la página de la [Biblioteca de clientes de Amazon Kinesis](https://github.com/awslabs/amazon-kinesis-client) en GitHub. Para obtener información sobre las últimas versiones de KCL, consulte [Uso de la biblioteca de clientes de Kinesis](https://docs.aws.amazon.com/streams/latest/dev/kcl.html). Para obtener información sobre cómo migrar de KCL 1.x a KCL 3.x, consulte Migración de KCL 1.x a KCL 3.x.

En el siguiente diagrama se muestra cómo interaccionan estas bibliotecas entre sí.

![\[Interacción entre DynamoDB Streams, Kinesis Data Streams y KCL para procesar registros de DynamoDB Streams.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/streams-kinesis-adapter.png)


Si DynamoDB Streams Kinesis Adapter está implementado, puede comenzar a desarrollar para la interfaz de KCL y dirigir las llamadas al API de forma transparente al punto de enlace de DynamoDB Streams.

Cuando se inicia la aplicación, llama a KCL para crear una instancia de un proceso de trabajo. Debe facilitar al proceso de trabajo información sobre la configuración de la aplicación, como el descriptor de la transmisión y las credenciales de AWS, así como el nombre de una clase de procesador de registros que usted proporcione. A medida que el proceso de trabajo ejecuta el código en el procesador de registros, lleva a cabo las siguientes tareas:
+ Se conecta a la secuencia
+ Enumera las particiones del flujo.
+ Comprueba y enumera las particiones secundarias de una partición principal cerrada dentro del flujo
+ Coordina la asociación de los fragmentos con otros procesos de trabajo (si procede)
+ Crea instancias de un procesador de registros para cada fragmento que administra
+ Extrae registros del flujo.
+ Escala la frecuencia de llamadas a la API GetRecords durante el alto rendimiento (si se configura el modo de recuperación).
+ Inserta los registros en el procesador de registros correspondiente
+ Genera puntos de comprobación para los registros procesados
+ Balancea las asociaciones entre fragmentos y procesos de trabajo cuando cambia el recuento de instancias de procesos de trabajo
+ Equilibra las asociaciones entre particiones y procesos de trabajo cuando las particiones se dividen.

El adaptador KCL admite el modo de recuperación, una característica de ajuste automático de la frecuencia de llamadas para gestionar los aumentos temporales de rendimiento. Cuando el retraso en el procesamiento de flujos supera un umbral configurable (un minuto de forma predeterminada), el modo de recuperación escala la frecuencia de llamadas a la API GetRecords en un valor configurable (tres veces de forma predeterminada) para recuperar los registros más rápido y, a continuación, vuelve a la normalidad una vez que disminuye el retraso. Esto resulta muy útil durante los periodos de alto rendimiento, en los que la actividad de escritura de DynamoDB puede saturar a los consumidores que utilizan las frecuencias de sondeo predeterminadas. El modo de recuperación se puede habilitar a través del parámetro de configuración `catchupEnabled` (falso de forma predeterminada).

**nota**  
Para obtener una descripción de los conceptos de KCL enumerados aquí, consulte [Desarrollo de consumidores mediante la biblioteca Kinesis Client Library](https://docs.aws.amazon.com/kinesis/latest/dev/developing-consumers-with-kcl.html) en la *Guía para desarrolladores de Amazon Kinesis Data Streams*.  
Para obtener más información acerca de cómo usar los flujos con AWS Lambda, consulte [DynamoDB Streams y disparadores de AWS Lambda](Streams.Lambda.md).

# Migración de KCL 1.x a KCL 3.x
<a name="streams-migrating-kcl"></a>

## Descripción general
<a name="migrating-kcl-overview"></a>

En esta guía se proporcionan instrucciones para migrar la aplicación de consumidor de KCL 1.x a KCL 3.x. Debido a las diferencias de arquitectura entre KCL 1.x y KCL 3.x, la migración requiere actualizar varios componentes para garantizar la compatibilidad.

KCL 1.x utiliza clases e interfaces diferentes en comparación con KCL 3.x. Debe migrar primero el procesador de registros, el generador de procesadores de registros y las clases de procesos de trabajo al formato compatible con KCL 3.x y, a continuación, seguir los pasos de migración de KCL 1.x a KCL 3.x.

## Pasos para realizar la migración
<a name="migration-steps"></a>

**Topics**
+ [Paso 1: migración del procesador de registros](#step1-record-processor)
+ [Paso 2: migración del generador de procesadores de registros](#step2-record-processor-factory)
+ [Paso 3: migración del proceso de trabajo](#step3-worker-migration)
+ [Paso 4: información general y recomendaciones sobre la configuración de KCL 3.x](#step4-configuration-migration)
+ [Paso 5: migración de KCL 2.x a KCL 3.x](#step5-kcl2-to-kcl3)

### Paso 1: migración del procesador de registros
<a name="step1-record-processor"></a>

En el siguiente ejemplo se muestra un procesador de registros implementado para el adaptador de Kinesis de DynamoDB Streams de KCL 1.x:

```
package com.amazonaws.kcl;

import com.amazonaws.services.kinesis.clientlibrary.exceptions.InvalidStateException;
import com.amazonaws.services.kinesis.clientlibrary.exceptions.ShutdownException;
import com.amazonaws.services.kinesis.clientlibrary.interfaces.IRecordProcessorCheckpointer;
import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessor;
import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IShutdownNotificationAware;
import com.amazonaws.services.kinesis.clientlibrary.lib.worker.ShutdownReason;
import com.amazonaws.services.kinesis.clientlibrary.types.InitializationInput;
import com.amazonaws.services.kinesis.clientlibrary.types.ProcessRecordsInput;
import com.amazonaws.services.kinesis.clientlibrary.types.ShutdownInput;

public class StreamsRecordProcessor implements IRecordProcessor, IShutdownNotificationAware {
    @Override
    public void initialize(InitializationInput initializationInput) {
        //
        // Setup record processor
        //
    }

    @Override
    public void processRecords(ProcessRecordsInput processRecordsInput) {
        for (Record record : processRecordsInput.getRecords()) {
            String data = new String(record.getData().array(), Charset.forName("UTF-8"));
            System.out.println(data);
            if (record instanceof RecordAdapter) {
                // record processing and checkpointing logic
            }
        }
    }

    @Override
    public void shutdown(ShutdownInput shutdownInput) {
        if (shutdownInput.getShutdownReason() == ShutdownReason.TERMINATE) {
            try {
                shutdownInput.getCheckpointer().checkpoint();
            } catch (ShutdownException | InvalidStateException e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    public void shutdownRequested(IRecordProcessorCheckpointer checkpointer) {
        try {
            checkpointer.checkpoint();
        } catch (ShutdownException | InvalidStateException e) {
            //
            // Swallow exception
            //
            e.printStackTrace();
        }
    }
}
```

**Migración de la clase RecordProcessor**

1. Cambie las interfaces de `com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessor` y `com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IShutdownNotificationAware` a `com.amazonaws.services.dynamodbv2.streamsadapter.processor.DynamoDBStreamsShardRecordProcessor` tal y como se indica a continuación:

   ```
   // import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessor;
   // import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IShutdownNotificationAware;
   
   import com.amazonaws.services.dynamodbv2.streamsadapter.processor.DynamoDBStreamsShardRecordProcessor;
   ```

1. Actualice las instrucciones de importación para los métodos `initialize` y `processRecords`:

   ```
   // import com.amazonaws.services.kinesis.clientlibrary.types.InitializationInput;
   import software.amazon.kinesis.lifecycle.events.InitializationInput;
   
   // import com.amazonaws.services.kinesis.clientlibrary.types.ProcessRecordsInput;
   import com.amazonaws.services.dynamodbv2.streamsadapter.model.DynamoDBStreamsProcessRecordsInput;
   ```

1. Sustituya el método `shutdownRequested` por los métodos nuevos siguientes: `leaseLost`, `shardEnded` y `shutdownRequested`.

   ```
   //    @Override
   //    public void shutdownRequested(IRecordProcessorCheckpointer checkpointer) {
   //        //
   //        // This is moved to shardEnded(...) and shutdownRequested(ShutdownReauestedInput)
   //        //
   //        try {
   //            checkpointer.checkpoint();
   //        } catch (ShutdownException | InvalidStateException e) {
   //            //
   //            // Swallow exception
   //            //
   //            e.printStackTrace();
   //        }
   //    }
   
       @Override
       public void leaseLost(LeaseLostInput leaseLostInput) {
   
       }
   
       @Override
       public void shardEnded(ShardEndedInput shardEndedInput) {
           try {
               shardEndedInput.checkpointer().checkpoint();
           } catch (ShutdownException | InvalidStateException e) {
               //
               // Swallow the exception
               //
               e.printStackTrace();
           }
       }
   
       @Override
       public void shutdownRequested(ShutdownRequestedInput shutdownRequestedInput) {
           try {
               shutdownRequestedInput.checkpointer().checkpoint();
           } catch (ShutdownException | InvalidStateException e) {
               //
               // Swallow the exception
               //
               e.printStackTrace();
           }
       }
   ```

A continuación, se muestra la versión actualizada de la clase del procesador de registros:

```
package com.amazonaws.codesamples;

import software.amazon.kinesis.exceptions.InvalidStateException;
import software.amazon.kinesis.exceptions.ShutdownException;
import software.amazon.kinesis.lifecycle.events.InitializationInput;
import software.amazon.kinesis.lifecycle.events.LeaseLostInput;
import com.amazonaws.services.dynamodbv2.streamsadapter.model.DynamoDBStreamsProcessRecordsInput;
import software.amazon.kinesis.lifecycle.events.ShardEndedInput;
import software.amazon.kinesis.lifecycle.events.ShutdownRequestedInput;
import software.amazon.dynamodb.streamsadapter.processor.DynamoDBStreamsShardRecordProcessor;
import software.amazon.dynamodb.streamsadapter.adapter.DynamoDBStreamsKinesisClientRecord;
import com.amazonaws.services.dynamodbv2.streamsadapter.processor.DynamoDBStreamsShardRecordProcessor;
import com.amazonaws.services.dynamodbv2.streamsadapter.adapter.DynamoDBStreamsClientRecord;
import software.amazon.awssdk.services.dynamodb.model.Record;

public class StreamsRecordProcessor implements DynamoDBStreamsShardRecordProcessor {

    @Override
    public void initialize(InitializationInput initializationInput) {
        
    }

    @Override
    public void processRecords(DynamoDBStreamsProcessRecordsInput processRecordsInput) {
        for (DynamoDBStreamsKinesisClientRecord record: processRecordsInput.records())
            Record ddbRecord = record.getRecord();
            // processing and checkpointing logic for the ddbRecord
        }
    }

    @Override
    public void leaseLost(LeaseLostInput leaseLostInput) {
        
    }

    @Override
    public void shardEnded(ShardEndedInput shardEndedInput) {
        try {
            shardEndedInput.checkpointer().checkpoint();
        } catch (ShutdownException | InvalidStateException e) {
            //
            // Swallow the exception
            //
            e.printStackTrace();
        }
    }

    @Override
    public void shutdownRequested(ShutdownRequestedInput shutdownRequestedInput) {
        try {
            shutdownRequestedInput.checkpointer().checkpoint();
        } catch (ShutdownException | InvalidStateException e) {
            //
            // Swallow the exception
            //
            e.printStackTrace();
        }
    }
}
```

**nota**  
El adaptador de Kinesis de DynamoDB Streams ahora usa el modelo de registro de SDKv2. En SDKv2, los objetos `AttributeValue` complejos (`BS`, `NS`, `M`, `L` y `SS`) nunca devuelven un valor nulo. Use los métodos `hasBs()`, `hasNs()`, `hasM()`, `hasL()` y `hasSs()` para verificar si estos valores existen.

### Paso 2: migración del generador de procesadores de registros
<a name="step2-record-processor-factory"></a>

El generador de procesadores de registros es responsable de la creación de procesadores de registros cuando se adquiere una asignación. A continuación, se muestra un ejemplo de un generador de la versión 1.x de KCL:

```
package com.amazonaws.codesamples;

import software.amazon.dynamodb.AmazonDynamoDB;
import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessor;
import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessorFactory;

public class StreamsRecordProcessorFactory implements IRecordProcessorFactory {
    
    @Override
    public IRecordProcessor createProcessor() {
        return new StreamsRecordProcessor(dynamoDBClient, tableName);
    }
}
```

**Migración de `RecordProcessorFactory`**
+ Cambie la interfaz implementada de `com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessorFactory` a `software.amazon.kinesis.processor.ShardRecordProcessorFactory`, tal y como se indica a continuación:

  ```
  // import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessor;
  import software.amazon.kinesis.processor.ShardRecordProcessor;
  
  // import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessorFactory;
  import software.amazon.kinesis.processor.ShardRecordProcessorFactory;
  
  // public class TestRecordProcessorFactory implements IRecordProcessorFactory {
  public class StreamsRecordProcessorFactory implements ShardRecordProcessorFactory {
  
  Change the return signature for createProcessor.
  
  // public IRecordProcessor createProcessor() {
  public ShardRecordProcessor shardRecordProcessor() {
  ```

A continuación, se muestra un ejemplo de generador de procesadores de registros de la versión 3.0:

```
package com.amazonaws.codesamples;

import software.amazon.kinesis.processor.ShardRecordProcessor;
import software.amazon.kinesis.processor.ShardRecordProcessorFactory;

public class StreamsRecordProcessorFactory implements ShardRecordProcessorFactory {

    @Override
    public ShardRecordProcessor shardRecordProcessor() {
        return new StreamsRecordProcessor();
    }
}
```

### Paso 3: migración del proceso de trabajo
<a name="step3-worker-migration"></a>

En la versión 3.0 de KCL, una nueva clase, llamada **Scheduler**, reemplaza la clase **Worker**. A continuación, se muestra un ejemplo de proceso de trabajo de la versión 1.x de KCL:

```
final KinesisClientLibConfiguration config = new KinesisClientLibConfiguration(...)
final IRecordProcessorFactory recordProcessorFactory = new RecordProcessorFactory();
final Worker worker = StreamsWorkerFactory.createDynamoDbStreamsWorker(
        recordProcessorFactory,
        workerConfig,
        adapterClient,
        amazonDynamoDB,
        amazonCloudWatchClient);
```

**Para migrar el proceso de trabajo**

1. Cambie la instrucción `import` para la clase `Worker` por las instrucciones de importación para las clases `Scheduler` y `ConfigsBuilder`.

   ```
   // import com.amazonaws.services.kinesis.clientlibrary.lib.worker.Worker;
   import software.amazon.kinesis.coordinator.Scheduler;
   import software.amazon.kinesis.common.ConfigsBuilder;
   ```

1. Importe `StreamTracker` y cambie la importación de `StreamsWorkerFactory` a `StreamsSchedulerFactory`.

   ```
   import software.amazon.kinesis.processor.StreamTracker;
   // import software.amazon.dynamodb.streamsadapter.StreamsWorkerFactory;
   import software.amazon.dynamodb.streamsadapter.StreamsSchedulerFactory;
   ```

1. Elija la posición desde la que desea iniciar la aplicación. Puede ser `TRIM_HORIZON` o `LATEST`.

   ```
   import software.amazon.kinesis.common.InitialPositionInStream;
   import software.amazon.kinesis.common.InitialPositionInStreamExtended;
   ```

1. Crear una instancia de `StreamTracker`.

   ```
   StreamTracker streamTracker = StreamsSchedulerFactory.createSingleStreamTracker(
           streamArn,
           InitialPositionInStreamExtended.newInitialPosition(InitialPositionInStream.TRIM_HORIZON)
   );
   ```

1. Cree el objeto `AmazonDynamoDBStreamsAdapterClient`.

   ```
   import software.amazon.dynamodb.streamsadapter.AmazonDynamoDBStreamsAdapterClient; 
   import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
   import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
   
   ...
   
   AwsCredentialsProvider credentialsProvider = DefaultCredentialsProvider.create();
   
   AmazonDynamoDBStreamsAdapterClient adapterClient = new AmazonDynamoDBStreamsAdapterClient(
           credentialsProvider, awsRegion);
   ```

1. Cree el objeto `ConfigsBuilder`.

   ```
   import software.amazon.kinesis.common.ConfigsBuilder;
   
   ...
   ConfigsBuilder configsBuilder = new ConfigsBuilder(
                   streamTracker,
                   applicationName,
                   adapterClient,
                   dynamoDbAsyncClient,
                   cloudWatchAsyncClient,
                   UUID.randomUUID().toString(),
                   new StreamsRecordProcessorFactory());
   ```

1. Cree `Scheduler` mediante `ConfigsBuilder` tal como se muestra en el ejemplo siguiente:

   ```
   import java.util.UUID;
   
   import software.amazon.awssdk.regions.Region;
   import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
   import software.amazon.awssdk.services.cloudwatch.CloudWatchAsyncClient;
   import software.amazon.awssdk.services.kinesis.KinesisAsyncClient;
   
   import software.amazon.kinesis.common.KinesisClientUtil;
   import software.amazon.kinesis.coordinator.Scheduler;
   
   ...
   
                   
   DynamoDbAsyncClient dynamoClient = DynamoDbAsyncClient.builder().region(region).build();
   CloudWatchAsyncClient cloudWatchClient = CloudWatchAsyncClient.builder().region(region).build();
   
                   
   DynamoDBStreamsPollingConfig pollingConfig = new DynamoDBStreamsPollingConfig(adapterClient);
   pollingConfig.idleTimeBetweenReadsInMillis(idleTimeBetweenReadsInMillis);
   
   // Use ConfigsBuilder to configure settings
   RetrievalConfig retrievalConfig = configsBuilder.retrievalConfig();
   retrievalConfig.retrievalSpecificConfig(pollingConfig);
   
   CoordinatorConfig coordinatorConfig = configsBuilder.coordinatorConfig();
   coordinatorConfig.clientVersionConfig(CoordinatorConfig.ClientVersionConfig.CLIENT_VERSION_CONFIG_COMPATIBLE_WITH_2X);
                   
   Scheduler scheduler = StreamsSchedulerFactory.createScheduler(
                   configsBuilder.checkpointConfig(),
                   coordinatorConfig,
                   configsBuilder.leaseManagementConfig(),
                   configsBuilder.lifecycleConfig(),
                   configsBuilder.metricsConfig(),
                   configsBuilder.processorConfig(),
                   retrievalConfig,
                   adapterClient
           );
   ```

**importante**  
La configuración `CLIENT_VERSION_CONFIG_COMPATIBLE_WITH_2X` mantiene la compatibilidad entre el adaptador de Kinesis de DynamoDB Streams para KCL v3 y KCL v1, pero no entre KCL v2 y v3.

### Paso 4: información general y recomendaciones sobre la configuración de KCL 3.x
<a name="step4-configuration-migration"></a>

Para obtener una descripción detallada de las configuraciones introducidas después de KCL 1.x que son relevantes en KCL 3.x, consulte las [configuraciones de KCL](https://docs.aws.amazon.com//streams/latest/dev/kcl-configuration.html) y la [configuración del cliente de migración de KCL](https://docs.aws.amazon.com//streams/latest/dev/kcl-migration.html#client-configuration).

**importante**  
En lugar de crear directamente objetos de `checkpointConfig`, `coordinatorConfig`, `leaseManagementConfig`, `metricsConfig`, `processorConfig` y `retrievalConfig`, recomendamos utilizar `ConfigsBuilder` para establecer configuraciones en KCL 3.x y versiones posteriores y así evitar problemas de inicialización del programador. `ConfigsBuilder` proporciona una forma más flexible y sostenible de configurar la aplicación de KCL.

#### Configuraciones con valor predeterminado actualizado en KCL 3.x
<a name="kcl3-configuration-overview"></a>

`billingMode`  
En la versión 1.x de KCL, el valor predeterminado de `billingMode` se establece en `PROVISIONED`. No obstante, con la versión 3.x de KCL, el valor predeterminado de `billingMode` es `PAY_PER_REQUEST` (modo bajo demanda). Le recomendamos que utilice el modo de capacidad bajo demanda para la tabla de arrendamiento a fin de ajustar automáticamente la capacidad en función del uso. Para obtener orientación sobre cómo utilizar la capacidad aprovisionada para las tablas de arrendamiento, consulte [Best practices for the lease table with provisioned capacity mode](https://docs.aws.amazon.com//streams/latest/dev/kcl-migration-lease-table.html).

`idleTimeBetweenReadsInMillis`  
En la versión 1.x de KCL, el valor predeterminado de `idleTimeBetweenReadsInMillis` se establece en 1000 (o 1 segundo). La versión 3.x de KCL establece el valor predeterminado de i`dleTimeBetweenReadsInMillis` en 1500 (o 1,5 segundos), pero el adaptador de Kinesis de Amazon DynamoDB Streams reemplaza el valor predeterminado por 1000 (o 1 segundo).

#### Nuevas configuraciones en KCL 3.x
<a name="kcl3-new-configs"></a>

`leaseAssignmentIntervalMillis`  
Esta configuración define el intervalo de tiempo antes de que las particiones recién descubiertas comiencen a procesarse y se calcula como 1,5 × `leaseAssignmentIntervalMillis`. Si este ajuste no se configura explícitamente, el intervalo de tiempo se establece de forma predeterminada en 1,5 × `failoverTimeMillis`. El procesamiento de nuevas particiones implica examinar la tabla de arrendamiento y consultar un índice secundario global (GSI) en la tabla de arrendamiento. Al reducir `leaseAssignmentIntervalMillis`, aumenta la frecuencia de estas operaciones de análisis y consulta, lo que se traduce en mayores costos de DynamoDB. Recomendamos establecer este valor en 2000 (o 2 segundos) para minimizar el retraso en el procesamiento de nuevas particiones.

`shardConsumerDispatchPollIntervalMillis`  
Esta configuración define el intervalo entre sondeos sucesivos por parte del consumidor de particiones para activar las transiciones de estado. En la versión 1.x de KCL, este comportamiento se controlaba mediante el parámetro `idleTimeInMillis`, que no se exponía como un ajuste configurable. Con la versión 3.x de KCL, recomendamos establecer esta configuración para que coincida con el valor utilizado para` idleTimeInMillis` en la configuración de la versión 1.x de KCL.

### Paso 5: migración de KCL 2.x a KCL 3.x
<a name="step5-kcl2-to-kcl3"></a>

Para garantizar una transición fluida y la compatibilidad con la última versión de la biblioteca de clientes de Kinesis (KCL), siga los pasos del 5 al 8 de las instrucciones de la guía de migración para [actualizar de KCL 2.x a KCL 3.x](https://docs.aws.amazon.com//streams/latest/dev/kcl-migration-from-2-3.html#kcl-migration-from-2-3-worker-metrics).

Para ver los problemas habituales de solución de problemas de KCL 3.x, consulte [Solución de problemas de las aplicaciones de consumidores de KCL](https://docs.aws.amazon.com//streams/latest/dev/troubleshooting-consumers.html).

# Restauración de la versión de KCL anterior
<a name="kcl-migration-rollback"></a>

En este tema se explica cómo restaurar la aplicación de consumidor a la versión de KCL anterior. El proceso de restauración consta de dos pasos:

1. Ejecución de la [herramienta de migración de KCL](https://github.com/awslabs/amazon-kinesis-client/blob/master/amazon-kinesis-client/scripts/KclMigrationTool.py).

1. Nueva implementación del código de la versión de KCL anterior.

## Paso 1: ejecución de la herramienta de migración de KCL
<a name="kcl-migration-rollback-step1"></a>

Cuando necesite restaurar la versión anterior de KCL, debe ejecutar la herramienta de migración de KCL. La herramienta realiza dos tareas importantes:
+ Elimina una tabla de metadatos llamada tabla de métricas de procesos de trabajo y el índice secundario global de la tabla de arrendamiento en DynamoDB. Estos artefactos los crea KCL 3.x, pero no son necesarios al restaurar la versión anterior.
+ Hace que todos los procesos de trabajo se ejecuten en un modo compatible con KCL 1.x y comiencen a utilizar el algoritmo de equilibrio de carga utilizado en versiones anteriores de KCL. Si tiene problemas con el nuevo algoritmo de equilibrio de carga en KCL 3.x, esto mitigará el problema inmediatamente.

**importante**  
La tabla de estados del coordinador en DynamoDB debe existir y no debe eliminarse durante el proceso de migración, restauración y avance.

**nota**  
Es importante que todos los procesos de trabajo de la aplicación de consumo utilicen el mismo algoritmo de equilibrio de carga en un momento dado. La herramienta de migración de KCL se asegura de que todos los procesos de trabajo de la aplicación de consumo KCL 3.x cambien al modo compatible con KCL 1.x, de modo que todos los procesos de trabajo ejecuten el mismo algoritmo de equilibrio de carga durante la restauración de la aplicación a la versión anterior de KCL.

Puede descargar la [herramienta de migración de KCL](https://github.com/awslabs/amazon-kinesis-client/blob/master/amazon-kinesis-client/scripts/KclMigrationTool.py) en el directorio de scripts del [repositorio de GitHub de KCL](https://github.com/awslabs/amazon-kinesis-client/tree/master). Ejecute el script desde un proceso de trabajo o host con los permisos adecuados para escribir en la tabla de estados del coordinador, la tabla de métricas de los procesos de trabajo y la tabla de arrendamiento. Asegúrese de que los [permisos de IAM](https://docs.aws.amazon.com/streams/latest/dev/kcl-iam-permissions.html) adecuados estén configurados para las aplicaciones de consumo de KCL. Ejecute el script solo una vez por aplicación de KCL mediante el comando especificado:

```
python3 ./KclMigrationTool.py --region region --mode rollback [--application_name applicationName] [--lease_table_name leaseTableName] [--coordinator_state_table_name coordinatorStateTableName] [--worker_metrics_table_name workerMetricsTableName]
```

### Parameters
<a name="kcl-migration-rollback-parameters"></a>

`--region`  
Sustituya *region* por su Región de AWS.

`--application_name`  
Este parámetro es necesario si utiliza nombres predeterminados para las tablas de metadatos de DynamoDB (tabla de arrendamiento, tabla de estados del coordinador y tabla de métricas de los proceso de trabajo). Si ha especificado nombres personalizados para estas tablas, puede omitir este parámetro. Reemplace *applicationName* por el nombre de la aplicación KCL real. La herramienta utiliza este nombre para derivar los nombres de tabla predeterminados si no se proporcionan nombres personalizados.

`--lease_table_name`  
Este parámetro es necesario cuando ha establecido un nombre personalizado para la tabla de arrendamientos en la configuración de KCL. Si utiliza el nombre de tabla predeterminado, puede omitir este parámetro. Reemplace *leaseTableName* por el nombre de tabla personalizado que especificó para la tabla de arrendamiento.

`--coordinator_state_table_name`  
Este parámetro es necesario cuando ha establecido un nombre personalizado para la tabla de estados de coordinador en la configuración de KCL. Si utiliza el nombre de tabla predeterminado, puede omitir este parámetro. Reemplace *coordinatorStateTableName* por el nombre de tabla personalizado que especificó para la tabla de estados de coordinador.

`--worker_metrics_table_name`  
Este parámetro es necesario cuando ha establecido un nombre personalizado para la tabla de métricas de proceso de trabajo en la configuración de KCL. Si utiliza el nombre de tabla predeterminado, puede omitir este parámetro. Reemplace *workerMetricsTableName* por el nombre de tabla personalizado que especificó para la tabla de métricas de proceso de trabajo.

## Paso 2: nueva implementación del código con la versión de KCL anterior
<a name="kcl-migration-rollback-step2"></a>

**importante**  
Cualquier mención a la versión 2.x en la salida generada por la herramienta de migración de KCL debe interpretarse como una referencia a la versión 1.x de KCL. La ejecución del script no realiza una recuperación completa, solo cambia el algoritmo de equilibrio de carga por el utilizado en la versión 1.x de KCL.

Tras ejecutar la herramienta de migración de KCL para realizar una recuperación, verá uno de los siguientes mensajes:

Mensaje 1  
“Rollback completed. Your application was running 2x compatible functionality. Please rollback to your previous application binaries by deploying the code with your previous KCL version”.  
**Acción requerida:** esto significa que los procesos de trabajo se estaban ejecutando en el modo compatible con KCL 1.x. Vuelva a implementar el código con la versión de KCL anterior en los procesos de trabajo.

Mensaje 2  
“Rollback completed. Your KCL Application was running 3x functionality and will rollback to 2x compatible functionality. If you don't see mitigation after a short period of time, please rollback to your previous application binaries by deploying the code with your previous KCL version”.  
**Acción requerida:** esto significa que los procesos de trabajo se estaban ejecutando en modo KCL 3.x y la herramienta de migración de KCL ha cambiado todos los procesos de trabajo al modo compatible con KCL 1.x. Vuelva a implementar el código con la versión de KCL anterior en los procesos de trabajo.

Mensaje 3  
“Application was already rolled back. Any KCLv3 resources that could be deleted were cleaned up to avoid charges until the application can be rolled forward with migration”.  
**Acción requerida:** esto significa que los procesos de trabajo ya se han restaurado para ejecutarse en el modo compatible con KCL 1.x. Vuelva a implementar el código con la versión de KCL anterior en los procesos de trabajo.

# Avance a KCL 3.x después de una restauración
<a name="kcl-migration-rollforward"></a>

En este tema se explica cómo avanzar la aplicación de consumidor a KCL 3.x después de una restauración. Cuando necesite avanzar, debe completar un proceso de dos pasos:

1. Ejecución de la [herramienta de migración de KCL](https://github.com/awslabs/amazon-kinesis-client/blob/master/amazon-kinesis-client/scripts/KclMigrationTool.py).

1. Implemente el código con KCL 3.x.

## Paso 1: ejecución de la herramienta de migración de KCL
<a name="kcl-migration-rollforward-step1"></a>

Ejecute la herramienta de migración de KCL con el siguiente comando para avanzar a KCL 3.x:

```
python3 ./KclMigrationTool.py --region region --mode rollforward [--application_name applicationName] [--coordinator_state_table_name coordinatorStateTableName]
```

### Parameters
<a name="kcl-migration-rollforward-parameters"></a>

`--region`  
Sustituya *region* por su Región de AWS.

`--application_name`  
Este parámetro es obligatorio si utiliza nombres predeterminados para la tabla de estados de coordinador. Si ha especificado nombres personalizados para la tabla de estados de coordinador, puede omitir este parámetro. Reemplace *applicationName* por el nombre de la aplicación KCL real. La herramienta utiliza este nombre para derivar los nombres de tabla predeterminados si no se proporcionan nombres personalizados.

`--coordinator_state_table_name`  
Este parámetro es necesario cuando ha establecido un nombre personalizado para la tabla de estados de coordinador en la configuración de KCL. Si utiliza el nombre de tabla predeterminado, puede omitir este parámetro. Reemplace *coordinatorStateTableName* por el nombre de tabla personalizado que especificó para la tabla de estados de coordinador.

Después de ejecutar la herramienta de migración en modo de avance, KCL crea los siguientes recursos de DynamoDB necesarios para KCL 3.x:
+ Un índice secundario global en la tabla de arrendamientos
+ Una tabla de métricas de proceso de trabajo

## Paso 2: implementación del código con KCL 3.x
<a name="kcl-migration-rollforward-step2"></a>

Después de ejecutar la herramienta de migración de KCL para una restauración, implemente el código con KCL 3.x en los procesos de trabajo. Para completar la migración, consulte [Paso 8: complete la migración](https://docs.aws.amazon.com/streams/latest/dev/kcl-migration-from-2-3.html#kcl-migration-from-2-3-finish).

# Tutorial: Adaptador Kinesis de DynamoDB Streams
<a name="Streams.KCLAdapter.Walkthrough"></a>

En esta sección se explica paso a paso una aplicación Java en la que se utiliza Amazon Kinesis Client Library y Amazon DynamoDB Streams Kinesis Adapter. En la aplicación se muestra un ejemplo de replicación de datos, donde la actividad de escritura de una tabla se aplica a una segunda tabla, de tal forma que el contenido de ambas se mantiene sincronizado. Para obtener el código fuente, consulte [Programa completo: DynamoDB Streams Kinesis Adapter](Streams.KCLAdapter.Walkthrough.CompleteProgram.md).

El programa realiza lo siguiente:

1. Crea dos tablas de DynamoDB denominadas `KCL-Demo-src` y `KCL-Demo-dst`. En cada una de estas tablas se ha habilitado una secuencia.

1. Agrega, actualiza y elimina elementos para generar actividad de actualización en la tabla de origen. Esto hace que se escriban datos en la secuencia de la tabla.

1. Lee los registros en la transmisión, los reconstruye como solicitudes de DynamoDB y aplica las solicitudes a la tabla de destino.

1. Examina las tablas de origen y destino para comprobar que sus contenidos sean idénticos.

1. Efectúa una limpieza eliminando las tablas.

Estos pasos se describen en las siguientes secciones y la aplicación completa se muestra al final del tutorial.

**Topics**
+ [Paso 1: crear tablas de DynamoDB](#Streams.KCLAdapter.Walkthrough.Step1)
+ [Paso 2: generar actividad de actualización en la tabla de origen](#Streams.KCLAdapter.Walkthrough.Step2)
+ [Paso 3: procesar la secuencia](#Streams.KCLAdapter.Walkthrough.Step3)
+ [Paso 4: comprobar que el contenido de ambas tablas es idéntico](#Streams.KCLAdapter.Walkthrough.Step4)
+ [Paso 5: Eliminar](#Streams.KCLAdapter.Walkthrough.Step5)
+ [Programa completo: DynamoDB Streams Kinesis Adapter](Streams.KCLAdapter.Walkthrough.CompleteProgram.md)

## Paso 1: crear tablas de DynamoDB
<a name="Streams.KCLAdapter.Walkthrough.Step1"></a>

El primer paso consiste en crear dos tablas de DynamoDB, una de origen y una de destino. El `StreamViewType` de la secuencia de la tabla de origen es `NEW_IMAGE`. Esto significa que cada vez que se modifica un elemento en esta tabla, su imagen de "después" se escribe en la secuencia. De esta forma, se realiza un seguimiento en la secuencia de todas las actividades de escritura en la tabla.

En el siguiente ejemplo se muestra el código utilizado para crear las dos tablas.

```
java.util.List<AttributeDefinition> attributeDefinitions = new ArrayList<AttributeDefinition>();
attributeDefinitions.add(new AttributeDefinition().withAttributeName("Id").withAttributeType("N"));

java.util.List<KeySchemaElement> keySchema = new ArrayList<KeySchemaElement>();
keySchema.add(new KeySchemaElement().withAttributeName("Id").withKeyType(KeyType.HASH)); // Partition
                                                                                         // key

ProvisionedThroughput provisionedThroughput = new ProvisionedThroughput().withReadCapacityUnits(2L)
    .withWriteCapacityUnits(2L);

StreamSpecification streamSpecification = new StreamSpecification();
streamSpecification.setStreamEnabled(true);
streamSpecification.setStreamViewType(StreamViewType.NEW_IMAGE);
CreateTableRequest createTableRequest = new CreateTableRequest().withTableName(tableName)
    .withAttributeDefinitions(attributeDefinitions).withKeySchema(keySchema)
    .withProvisionedThroughput(provisionedThroughput).withStreamSpecification(streamSpecification);
```

## Paso 2: generar actividad de actualización en la tabla de origen
<a name="Streams.KCLAdapter.Walkthrough.Step2"></a>

El siguiente paso consiste en generar actividad de escritura en la tabla de origen. Mientras tiene lugar esta actividad, la secuencia de la tabla de origen también se actualiza casi en tiempo real.

En la aplicación se define una clase auxiliar con métodos que llaman a las operaciones de API `PutItem`, `UpdateItem` y `DeleteItem` para escribir los datos. En el siguiente ejemplo se muestra cómo se utilizan estos métodos.

```
StreamsAdapterDemoHelper.putItem(dynamoDBClient, tableName, "101", "test1");
StreamsAdapterDemoHelper.updateItem(dynamoDBClient, tableName, "101", "test2");
StreamsAdapterDemoHelper.deleteItem(dynamoDBClient, tableName, "101");
StreamsAdapterDemoHelper.putItem(dynamoDBClient, tableName, "102", "demo3");
StreamsAdapterDemoHelper.updateItem(dynamoDBClient, tableName, "102", "demo4");
StreamsAdapterDemoHelper.deleteItem(dynamoDBClient, tableName, "102");
```

## Paso 3: procesar la secuencia
<a name="Streams.KCLAdapter.Walkthrough.Step3"></a>

Ahora, el programa comienza a procesar la secuencia. DynamoDB Streams Kinesis Adapter actúa como una capa transparente entre la KCL y el punto de enlace de DynamoDB Streams, para que el código pueda utilizar plenamente la KCL, en lugar de tener que realizar llamadas a DynamoDB Streams de bajo nivel. En el programa se realizan las siguientes tareas:
+ Se define una clase de procesador de registros, `StreamsRecordProcessor`, con métodos que cumplen con la definición de interfaz de KCL: `initialize`, `processRecords` y `shutdown`. El método `processRecords` contiene la lógica necesaria para leer la secuencia de la tabla de origen y escribir en la tabla de destino.
+ Define un generador de clases para la clase de procesador de registros (`StreamsRecordProcessorFactory`). Esto es necesario para los programas Java que utilizan la KCL.
+ Crea una nueva instancia del proceso de trabajo `Worker` de la KCL, asociado con el generador de clases.
+ Cierra el proceso de trabajo `Worker` cuando ha finalizado de procesar registros.

Si lo desea, habilite el modo de recuperación en la configuración del adaptador KCL de sus flujos para escalar automáticamente la tasa de llamadas a la API GetRecords tres veces (predeterminado) cuando el retraso en el procesamiento de flujos supere un minuto (predeterminado), lo que ayudará a su consumidor de flujos a gestionar los picos de alto rendimiento en su tabla.

Para obtener más información sobre la definición de la interfaz de KCL, consulte [Desarrollo de consumidores mediante la biblioteca Kinesis Client Library](https://docs.aws.amazon.com/kinesis/latest/dev/developing-consumers-with-kcl.html) en la *Guía de desarrolladores de Amazon Kinesis Data Streams*. 

En el siguiente ejemplo se muestra el bucle principal de `StreamsRecordProcessor`. La instrucción `case` determina qué acción se debe llevar a cabo, según el valor de `OperationType` que aparece en el registro de secuencia.

```
for (Record record : records) {
    String data = new String(record.getData().array(), Charset.forName("UTF-8"));
    System.out.println(data);
    if (record instanceof RecordAdapter) {
                software.amazon.dynamodb.model.Record streamRecord = ((RecordAdapter) record)
                    .getInternalObject();

                switch (streamRecord.getEventName()) {
                    case "INSERT":
                    case "MODIFY":
                        StreamsAdapterDemoHelper.putItem(dynamoDBClient, tableName,
                            streamRecord.getDynamodb().getNewImage());
                        break;
                    case "REMOVE":
                        StreamsAdapterDemoHelper.deleteItem(dynamoDBClient, tableName,
                            streamRecord.getDynamodb().getKeys().get("Id").getN());
                }
    }
    checkpointCounter += 1;
    if (checkpointCounter % 10 == 0) {
        try {
            checkpointer.checkpoint();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}
```

## Paso 4: comprobar que el contenido de ambas tablas es idéntico
<a name="Streams.KCLAdapter.Walkthrough.Step4"></a>

En este punto, el contenido de las tablas de origen y destino está sincronizado. La aplicación emite solicitudes `Scan` en las dos tablas para comprobar que su contenido sea realmente idéntico.

La clase `DemoHelper` contiene un método `ScanTable` que llama a la API de bajo nivel `Scan`. El siguiente ejemplo le muestra cómo se usa.

```
if (StreamsAdapterDemoHelper.scanTable(dynamoDBClient, srcTable).getItems()
    .equals(StreamsAdapterDemoHelper.scanTable(dynamoDBClient, destTable).getItems())) {
    System.out.println("Scan result is equal.");
}
else {
    System.out.println("Tables are different!");
}
```

## Paso 5: Eliminar
<a name="Streams.KCLAdapter.Walkthrough.Step5"></a>

La demostración ha finalizado. Por consiguiente, la aplicación elimina las tablas de origen y destino. Consulte el siguiente ejemplo de código. Incluso después de que las tablas se hayan eliminado, sus secuencias permanecerán disponibles durante un máximo de 24 horas; transcurrido este periodo se eliminan automáticamente.

```
dynamoDBClient.deleteTable(new DeleteTableRequest().withTableName(srcTable));
dynamoDBClient.deleteTable(new DeleteTableRequest().withTableName(destTable));
```

# Programa completo: DynamoDB Streams Kinesis Adapter
<a name="Streams.KCLAdapter.Walkthrough.CompleteProgram"></a>

A continuación encontrará el programa de Java completo que lleva a cabo las tareas descritas en [Tutorial: Adaptador Kinesis de DynamoDB Streams](Streams.KCLAdapter.Walkthrough.md). Cuando lo ejecute, debería ver un resultado similar al siguiente.

```
Creating table KCL-Demo-src
Creating table KCL-Demo-dest
Table is active.
Creating worker for stream: arn:aws:dynamodb:us-west-2:111122223333:table/KCL-Demo-src/stream/2015-05-19T22:48:56.601
Starting worker...
Scan result is equal.
Done.
```

**importante**  
 Para ejecutar este programa, utilice políticas con el fin de asegurarse de que la aplicación cliente tenga acceso a DynamoDB y a Amazon CloudWatch. Para obtener más información, consulte [Políticas basadas en identidad de DynamoDB](security_iam_service-with-iam.md#security_iam_service-with-iam-id-based-policies). 

El código de origen consta de cuatro archivos `.java`. Para crear este programa, agregue la siguiente dependencia, que incluye la biblioteca de clientes de Amazon Kinesis (KCL) 3.x y el AWS SDK para Java v2 como dependencias transitivas:

------
#### [ Maven ]

```
<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>dynamodb-streams-kinesis-adapter</artifactId>
    <version>2.1.0</version>
</dependency>
```

------
#### [ Gradle ]

```
implementation 'com.amazonaws:dynamodb-streams-kinesis-adapter:2.1.0'
```

------

Los archivos de origen son:
+ `StreamsAdapterDemo.java`
+ `StreamsRecordProcessor.java`
+ `StreamsRecordProcessorFactory.java`
+ `StreamsAdapterDemoHelper.java`

## StreamsAdapterDemo.java
<a name="Streams.KCLAdapter.Walkthrough.CompleteProgram.StreamsAdapterDemo"></a>

```
package com.amazonaws.codesamples;

import com.amazonaws.services.dynamodbv2.streamsadapter.AmazonDynamoDBStreamsAdapterClient;
import com.amazonaws.services.dynamodbv2.streamsadapter.StreamsSchedulerFactory;
import com.amazonaws.services.dynamodbv2.streamsadapter.polling.DynamoDBStreamsPollingConfig;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.cloudwatch.CloudWatchAsyncClient;
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest;
import software.amazon.awssdk.services.dynamodb.model.DescribeTableResponse;
import software.amazon.kinesis.common.ConfigsBuilder;
import software.amazon.kinesis.common.InitialPositionInStream;
import software.amazon.kinesis.common.InitialPositionInStreamExtended;
import software.amazon.kinesis.coordinator.Scheduler;
import software.amazon.kinesis.processor.ShardRecordProcessorFactory;
import software.amazon.kinesis.processor.StreamTracker;
import software.amazon.kinesis.retrieval.RetrievalConfig;

public class StreamsAdapterDemo {

    private static DynamoDbAsyncClient dynamoDbAsyncClient;
    private static CloudWatchAsyncClient cloudWatchAsyncClient;
    private static AmazonDynamoDBStreamsAdapterClient amazonDynamoDbStreamsAdapterClient;

    private static String tablePrefix = "KCL-Demo";
    private static String streamArn;

    private static Region region = Region.US_EAST_1;
    private static AwsCredentialsProvider credentialsProvider = DefaultCredentialsProvider.create();

    public static void main( String[] args ) throws Exception {
        System.out.println("Starting demo...");
        dynamoDbAsyncClient = DynamoDbAsyncClient.builder()
                .credentialsProvider(credentialsProvider)
                .region(region)
                .build();
        cloudWatchAsyncClient = CloudWatchAsyncClient.builder()
                .credentialsProvider(credentialsProvider)
                .region(region)
                .build();
        amazonDynamoDbStreamsAdapterClient = new AmazonDynamoDBStreamsAdapterClient(credentialsProvider, region);

        String srcTable = tablePrefix + "-src";
        String destTable = tablePrefix + "-dest";

        setUpTables();

        StreamTracker streamTracker = StreamsSchedulerFactory.createSingleStreamTracker(streamArn,
                InitialPositionInStreamExtended.newInitialPosition(InitialPositionInStream.TRIM_HORIZON));

        ShardRecordProcessorFactory shardRecordProcessorFactory =
                new StreamsAdapterDemoProcessorFactory(dynamoDbAsyncClient, destTable);

        ConfigsBuilder configsBuilder = new ConfigsBuilder(
                streamTracker,
                "streams-adapter-demo",
                amazonDynamoDbStreamsAdapterClient,
                dynamoDbAsyncClient,
                cloudWatchAsyncClient,
                "streams-demo-worker",
                shardRecordProcessorFactory
        );

        DynamoDBStreamsPollingConfig pollingConfig = new DynamoDBStreamsPollingConfig(amazonDynamoDbStreamsAdapterClient);
        RetrievalConfig retrievalConfig = configsBuilder.retrievalConfig();
        retrievalConfig.retrievalSpecificConfig(pollingConfig);

        System.out.println("Creating scheduler for stream " + streamArn);
        Scheduler scheduler = StreamsSchedulerFactory.createScheduler(
                configsBuilder.checkpointConfig(),
                configsBuilder.coordinatorConfig(),
                configsBuilder.leaseManagementConfig(),
                configsBuilder.lifecycleConfig(),
                configsBuilder.metricsConfig(),
                configsBuilder.processorConfig(),
                retrievalConfig,
                amazonDynamoDbStreamsAdapterClient
        );

        System.out.println("Starting scheduler...");
        Thread t = new Thread(scheduler);
        t.start();

        Thread.sleep(250000);

        System.out.println("Stopping scheduler...");
        scheduler.shutdown();
        t.join();

        if (StreamsAdapterDemoHelper.scanTable(dynamoDbAsyncClient, srcTable).items()
                .equals(StreamsAdapterDemoHelper.scanTable(dynamoDbAsyncClient, destTable).items())) {
            System.out.println("Scan result is equal.");
        } else {
            System.out.println("Tables are different!");
        }

        System.out.println("Done.");
        cleanupAndExit(0);
    }

    private static void setUpTables() {
        String srcTable = tablePrefix + "-src";
        String destTable = tablePrefix + "-dest";
        streamArn = StreamsAdapterDemoHelper.createTable(dynamoDbAsyncClient, srcTable);
        StreamsAdapterDemoHelper.createTable(dynamoDbAsyncClient, destTable);

        awaitTableCreation(srcTable);

        performOps(srcTable);
    }

    private static void awaitTableCreation(String tableName) {
        Integer retries = 0;
        Boolean created = false;
        while (!created && retries < 100) {
            DescribeTableResponse result = StreamsAdapterDemoHelper.describeTable(dynamoDbAsyncClient, tableName);
            created = result.table().tableStatusAsString().equals("ACTIVE");
            if (created) {
                System.out.println("Table is active.");
                return;
            } else {
                retries++;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // do nothing
                }
            }
        }
        System.out.println("Timeout after table creation. Exiting...");
        cleanupAndExit(1);
    }

    private static void performOps(String tableName) {
        StreamsAdapterDemoHelper.putItem(dynamoDbAsyncClient, tableName, "101", "test1");
        StreamsAdapterDemoHelper.updateItem(dynamoDbAsyncClient, tableName, "101", "test2");
        StreamsAdapterDemoHelper.deleteItem(dynamoDbAsyncClient, tableName, "101");
        StreamsAdapterDemoHelper.putItem(dynamoDbAsyncClient, tableName, "102", "demo3");
        StreamsAdapterDemoHelper.updateItem(dynamoDbAsyncClient, tableName, "102", "demo4");
        StreamsAdapterDemoHelper.deleteItem(dynamoDbAsyncClient, tableName, "102");
    }

    private static void cleanupAndExit(Integer returnValue) {
        String srcTable = tablePrefix + "-src";
        String destTable = tablePrefix + "-dest";
        dynamoDbAsyncClient.deleteTable(DeleteTableRequest.builder().tableName(srcTable).build());
        dynamoDbAsyncClient.deleteTable(DeleteTableRequest.builder().tableName(destTable).build());
        System.exit(returnValue);
    }
}
```

## StreamsRecordProcessor.java
<a name="Streams.KCLAdapter.Walkthrough.CompleteProgram.StreamsRecordProcessor"></a>

```
package com.amazonaws.codesamples;

import com.amazonaws.services.dynamodbv2.streamsadapter.adapter.DynamoDBStreamsClientRecord;
import com.amazonaws.services.dynamodbv2.streamsadapter.model.DynamoDBStreamsProcessRecordsInput;
import com.amazonaws.services.dynamodbv2.streamsadapter.processor.DynamoDBStreamsShardRecordProcessor;
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
import software.amazon.awssdk.services.dynamodb.model.Record;
import software.amazon.kinesis.exceptions.InvalidStateException;
import software.amazon.kinesis.exceptions.ShutdownException;
import software.amazon.kinesis.lifecycle.events.InitializationInput;
import software.amazon.kinesis.lifecycle.events.LeaseLostInput;
import software.amazon.kinesis.lifecycle.events.ShardEndedInput;
import software.amazon.kinesis.lifecycle.events.ShutdownRequestedInput;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

public class StreamsRecordProcessor implements DynamoDBStreamsShardRecordProcessor {

    private Integer checkpointCounter;

    private final DynamoDbAsyncClient dynamoDbAsyncClient;
    private final String tableName;

    public StreamsRecordProcessor(DynamoDbAsyncClient dynamoDbAsyncClient, String tableName) {
        this.dynamoDbAsyncClient = dynamoDbAsyncClient;
        this.tableName = tableName;
    }

    @Override
    public void initialize(InitializationInput initializationInput) {
        this.checkpointCounter = 0;
    }

    @Override
    public void processRecords(DynamoDBStreamsProcessRecordsInput dynamoDBStreamsProcessRecordsInput) {
        for (DynamoDBStreamsClientRecord record: dynamoDBStreamsProcessRecordsInput.records()) {
            String data = new String(record.data().array(), StandardCharsets.UTF_8);
            System.out.println(data);
            Record streamRecord = record.getRecord();

            switch (streamRecord.eventName()) {
                case INSERT:
                case MODIFY:
                    StreamsAdapterDemoHelper.putItem(dynamoDbAsyncClient, tableName,
                            streamRecord.dynamodb().newImage());
                case REMOVE:
                    StreamsAdapterDemoHelper.deleteItem(dynamoDbAsyncClient, tableName,
                            streamRecord.dynamodb().keys().get("Id").n());
            }
            checkpointCounter += 1;
            if (checkpointCounter % 10 == 0) {
                try {
                    dynamoDBStreamsProcessRecordsInput.checkpointer().checkpoint();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void leaseLost(LeaseLostInput leaseLostInput) {
        System.out.println("Lease Lost");
    }

    @Override
    public void shardEnded(ShardEndedInput shardEndedInput) {
        try {
            shardEndedInput.checkpointer().checkpoint();
        } catch (ShutdownException | InvalidStateException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void shutdownRequested(ShutdownRequestedInput shutdownRequestedInput) {
        try {
            shutdownRequestedInput.checkpointer().checkpoint();
        } catch (ShutdownException | InvalidStateException e) {
            e.printStackTrace();
        }
    }
}
```

## StreamsRecordProcessorFactory.java
<a name="Streams.KCLAdapter.Walkthrough.CompleteProgram.StreamsRecordProcessorFactory"></a>

```
package com.amazonaws.codesamples;

import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
import software.amazon.kinesis.processor.ShardRecordProcessor;
import software.amazon.kinesis.processor.ShardRecordProcessorFactory;

public class StreamsAdapterDemoProcessorFactory implements ShardRecordProcessorFactory {
    private final String tableName;
    private final DynamoDbAsyncClient dynamoDbAsyncClient;

    public StreamsAdapterDemoProcessorFactory(DynamoDbAsyncClient asyncClient, String tableName) {
        this.tableName = tableName;
        this.dynamoDbAsyncClient = asyncClient;
    }

    @Override
    public ShardRecordProcessor shardRecordProcessor() {
        return new StreamsRecordProcessor(dynamoDbAsyncClient, tableName);
    }
}
```

## StreamsAdapterDemoHelper.java
<a name="Streams.KCLAdapter.Walkthrough.CompleteProgram.StreamsAdapterDemoHelper"></a>

```
package com.amazonaws.codesamples;

import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.BillingMode;
import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest;
import software.amazon.awssdk.services.dynamodb.model.CreateTableResponse;
import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest;
import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest;
import software.amazon.awssdk.services.dynamodb.model.DescribeTableResponse;
import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement;
import software.amazon.awssdk.services.dynamodb.model.KeyType;
import software.amazon.awssdk.services.dynamodb.model.OnDemandThroughput;
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
import software.amazon.awssdk.services.dynamodb.model.ResourceInUseException;
import software.amazon.awssdk.services.dynamodb.model.ScanRequest;
import software.amazon.awssdk.services.dynamodb.model.ScanResponse;
import software.amazon.awssdk.services.dynamodb.model.StreamSpecification;
import software.amazon.awssdk.services.dynamodb.model.StreamViewType;
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class StreamsAdapterDemoHelper {

    /**
     * @return StreamArn
     */
    public static String createTable(DynamoDbAsyncClient client, String tableName) {
        List<AttributeDefinition> attributeDefinitions = new ArrayList<>();
        attributeDefinitions.add(AttributeDefinition.builder()
                .attributeName("Id")
                .attributeType("N")
                .build());

        List<KeySchemaElement> keySchema = new ArrayList<>();
        keySchema.add(KeySchemaElement.builder()
                .attributeName("Id")
                .keyType(KeyType.HASH) // Partition key
                .build());

        StreamSpecification streamSpecification = StreamSpecification.builder()
                .streamEnabled(true)
                .streamViewType(StreamViewType.NEW_IMAGE)
                .build();

        CreateTableRequest createTableRequest = CreateTableRequest.builder()
                .tableName(tableName)
                .attributeDefinitions(attributeDefinitions)
                .keySchema(keySchema)
                .billingMode(BillingMode.PAY_PER_REQUEST)
                .streamSpecification(streamSpecification)
                .build();

        try {
            System.out.println("Creating table " + tableName);
            CreateTableResponse result = client.createTable(createTableRequest).join();
            return result.tableDescription().latestStreamArn();
        } catch (Exception e) {
            if (e.getCause() instanceof ResourceInUseException) {
                System.out.println("Table already exists.");
                return describeTable(client, tableName).table().latestStreamArn();
            }
            throw e;
        }
    }

    public static DescribeTableResponse describeTable(DynamoDbAsyncClient client, String tableName) {
        return client.describeTable(DescribeTableRequest.builder()
                        .tableName(tableName)
                        .build())
                .join();
    }

    public static ScanResponse scanTable(DynamoDbAsyncClient dynamoDbClient, String tableName) {
        return dynamoDbClient.scan(ScanRequest.builder()
                        .tableName(tableName)
                        .build())
                .join();
    }

    public static void putItem(DynamoDbAsyncClient dynamoDbClient, String tableName, String id, String val) {
        Map<String, AttributeValue> item = new HashMap<>();
        item.put("Id", AttributeValue.builder().n(id).build());
        item.put("attribute-1", AttributeValue.builder().s(val).build());

        putItem(dynamoDbClient, tableName, item);
    }

    public static void putItem(DynamoDbAsyncClient dynamoDbClient, String tableName,
                               Map<String, AttributeValue> items) {
        PutItemRequest putItemRequest = PutItemRequest.builder()
                .tableName(tableName)
                .item(items)
                .build();
        dynamoDbClient.putItem(putItemRequest).join();
    }

    public static void updateItem(DynamoDbAsyncClient dynamoDbClient, String tableName, String id, String val) {
        Map<String, AttributeValue> key = new HashMap<>();
        key.put("Id", AttributeValue.builder().n(id).build());

        Map<String, String> expressionAttributeNames = new HashMap<>();
        expressionAttributeNames.put("#attr2", "attribute-2");

        Map<String, AttributeValue> expressionAttributeValues = new HashMap<>();
        expressionAttributeValues.put(":val", AttributeValue.builder().s(val).build());

        UpdateItemRequest updateItemRequest = UpdateItemRequest.builder()
                .tableName(tableName)
                .key(key)
                .updateExpression("SET #attr2 = :val")
                .expressionAttributeNames(expressionAttributeNames)
                .expressionAttributeValues(expressionAttributeValues)
                .build();

        dynamoDbClient.updateItem(updateItemRequest).join();
    }

    public static void deleteItem(DynamoDbAsyncClient dynamoDbClient, String tableName, String id) {
        Map<String, AttributeValue> key = new HashMap<>();
        key.put("Id", AttributeValue.builder().n(id).build());

        DeleteItemRequest deleteItemRequest = DeleteItemRequest.builder()
                .tableName(tableName)
                .key(key)
                .build();
        dynamoDbClient.deleteItem(deleteItemRequest).join();
    }
}
```

# API de bajo nivel de DynamoDB Streams: ejemplo en Java
<a name="Streams.LowLevel.Walkthrough"></a>

**nota**  
El código que se presenta en esta página no es exhaustivo y no contempla todos los escenarios de consumo de Amazon DynamoDB Streams. La manera recomendada de consumir registros de transmisión de DynamoDB consiste en usar Amazon Kinesis Adapter con la Kinesis Client Library (KCL), como se describe en [Uso del adaptador Kinesis de DynamoDB Streams para procesar registros de transmisión](Streams.KCLAdapter.md).

Esta sección contiene un programa de Java que muestra el funcionamiento de DynamoDB Streams. El programa realiza lo siguiente:

1. Crea una tabla de DynamoDB con una transmisión habilitada.

1. Describe los ajustes de secuencia de esta tabla.

1. Modifica los datos de la tabla.

1. Describe los fragmentos de la secuencia.

1. Lee los registros de secuencia de los fragmentos.

1. Busca las particiones secundarias y continúa leyendo los registros.

1. Elimina recursos.

Al ejecutar el programa, verá un resultado parecido al siguiente.

```
Testing Streams Demo
Creating an Amazon DynamoDB table TestTableForStreams with a simple primary key: Id
Waiting for TestTableForStreams to be created...
Current stream ARN for TestTableForStreams: arn:aws:dynamodb:us-east-2:123456789012:table/TestTableForStreams/stream/2018-03-20T16:49:55.208
Stream enabled: true
Update view type: NEW_AND_OLD_IMAGES

Performing write activities on TestTableForStreams
Processing item 1 of 100
Processing item 2 of 100
Processing item 3 of 100
...
Processing item 100 of 100
Shard: {ShardId: shardId-1234567890-...,SequenceNumberRange: {StartingSequenceNumber: 100002572486797508907,},}
    Shard iterator: EjYFEkX2a26eVTWe...
        StreamRecord(ApproximateCreationDateTime=2025-04-09T13:11:58Z, Keys={Id=AttributeValue(S=4)}, NewImage={Message=AttributeValue(S=New Item!), Id=AttributeValue(S=4)}, SequenceNumber=2000001584047545833909, SizeBytes=22, StreamViewType=NEW_AND_OLD_IMAGES)
        StreamRecord(ApproximateCreationDateTime=2025-04-09T13:11:58Z, Keys={Id=AttributeValue(S=4)}, NewImage={Message=AttributeValue(S=This is an updated item), Id=AttributeValue(S=4)}, OldImage={Message=AttributeValue(S=New Item!), Id=AttributeValue(S=4)}, SequenceNumber=2100003604869767892701, SizeBytes=55, StreamViewType=NEW_AND_OLD_IMAGES)
        StreamRecord(ApproximateCreationDateTime=2025-04-09T13:11:58Z, Keys={Id=AttributeValue(S=4)}, OldImage={Message=AttributeValue(S=This is an updated item), Id=AttributeValue(S=4)}, SequenceNumber=2200001099771112898434, SizeBytes=36, StreamViewType=NEW_AND_OLD_IMAGES)
...
Deleting the table...
Table StreamsDemoTable deleted.
Demo complete
```

**Example Ejemplo**  

```
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import software.amazon.awssdk.core.waiters.WaiterResponse;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeAction;
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.AttributeValueUpdate;
import software.amazon.awssdk.services.dynamodb.model.BillingMode;
import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest;
import software.amazon.awssdk.services.dynamodb.model.CreateTableResponse;
import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest;
import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest;
import software.amazon.awssdk.services.dynamodb.model.DescribeStreamRequest;
import software.amazon.awssdk.services.dynamodb.model.DescribeStreamResponse;
import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest;
import software.amazon.awssdk.services.dynamodb.model.DescribeTableResponse;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.GetRecordsRequest;
import software.amazon.awssdk.services.dynamodb.model.GetRecordsResponse;
import software.amazon.awssdk.services.dynamodb.model.GetShardIteratorRequest;
import software.amazon.awssdk.services.dynamodb.model.GetShardIteratorResponse;
import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement;
import software.amazon.awssdk.services.dynamodb.model.KeyType;
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
import software.amazon.awssdk.services.dynamodb.model.Record;
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
import software.amazon.awssdk.services.dynamodb.model.Shard;
import software.amazon.awssdk.services.dynamodb.model.ShardFilter;
import software.amazon.awssdk.services.dynamodb.model.ShardFilterType;
import software.amazon.awssdk.services.dynamodb.model.ShardIteratorType;
import software.amazon.awssdk.services.dynamodb.model.StreamSpecification;
import software.amazon.awssdk.services.dynamodb.model.TableDescription;
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
import software.amazon.awssdk.services.dynamodb.streams.DynamoDbStreamsClient;
import software.amazon.awssdk.services.dynamodb.waiters.DynamoDbWaiter;

public class StreamsLowLevelDemo {


    public static void main(String[] args) {
        final String usage = "Testing Streams Demo";
        try {
            System.out.println(usage);

            String tableName = "StreamsDemoTable";
            String key = "Id";
            System.out.println("Creating an Amazon DynamoDB table " + tableName + " with a simple primary key: " + key);
            Region region = Region.US_WEST_2;
            DynamoDbClient ddb = DynamoDbClient.builder()
                    .region(region)
                    .build();

            DynamoDbStreamsClient ddbStreams = DynamoDbStreamsClient.builder()
                    .region(region)
                    .build();
            DescribeTableRequest describeTableRequest = DescribeTableRequest.builder()
                    .tableName(tableName)
                    .build();
            TableDescription tableDescription = null;
            try{
                tableDescription = ddb.describeTable(describeTableRequest).table();
            }catch (Exception e){
                System.out.println("Table " + tableName + " does not exist.");
                tableDescription = createTable(ddb, tableName, key);
            }

            // Print the stream settings for the table
            String streamArn = tableDescription.latestStreamArn();
           
            StreamSpecification streamSpec = tableDescription.streamSpecification();
            System.out.println("Current stream ARN for " + tableDescription.tableName() + ": " +
                   streamArn);
            System.out.println("Stream enabled: " + streamSpec.streamEnabled());
            System.out.println("Update view type: " + streamSpec.streamViewType());
            System.out.println();
            // Generate write activity in the table
            System.out.println("Performing write activities on " + tableName);
            int maxItemCount = 100;
            for (Integer i = 1; i <= maxItemCount; i++) {
                System.out.println("Processing item " + i + " of " + maxItemCount);
                // Write a new item
                putItemInTable(key, i, tableName, ddb);
                // Update the item
                updateItemInTable(key, i, tableName, ddb);
                // Delete the item
                deleteDynamoDBItem(key, i, tableName, ddb);
            }

            // Process Stream
            processStream(streamArn, maxItemCount, ddb, ddbStreams, tableName);

            // Delete the table
            System.out.println("Deleting the table...");
            DeleteTableRequest deleteTableRequest = DeleteTableRequest.builder()
                    .tableName(tableName)
                    .build();
            ddb.deleteTable(deleteTableRequest);
            System.out.println("Table " + tableName + " deleted.");
            System.out.println("Demo complete");
            ddb.close();
        } catch (Exception e) {
            System.out.println("Error: " + e.getMessage());
        }
    }

    private static void processStream(String streamArn, int maxItemCount, DynamoDbClient ddb, DynamoDbStreamsClient ddbStreams, String tableName) {
        // Get all the shard IDs from the stream. Note that DescribeStream returns
        // the shard IDs one page at a time.
        String lastEvaluatedShardId = null;
        do {
            DescribeStreamRequest describeStreamRequest = DescribeStreamRequest.builder()
                    .streamArn(streamArn)
                    .exclusiveStartShardId(lastEvaluatedShardId).build();
            DescribeStreamResponse describeStreamResponse = ddbStreams.describeStream(describeStreamRequest);

            List<Shard> shards = describeStreamResponse.streamDescription().shards();

            // Process each shard on this page

            fetchShardsAndReadRecords(streamArn, maxItemCount, ddbStreams, shards);

            // If LastEvaluatedShardId is set, then there is
            // at least one more page of shard IDs to retrieve
            lastEvaluatedShardId = describeStreamResponse.streamDescription().lastEvaluatedShardId();

        } while (lastEvaluatedShardId != null);

    }

    private static void fetchShardsAndReadRecords(String streamArn, int maxItemCount, DynamoDbStreamsClient ddbStreams, List<Shard> shards) {
        for (Shard shard : shards) {
            String shardId = shard.shardId();
            System.out.println("Shard: " + shard);

            // Get an iterator for the current shard
            GetShardIteratorRequest shardIteratorRequest = GetShardIteratorRequest.builder()
                    .streamArn(streamArn).shardId(shardId)
                    .shardIteratorType(ShardIteratorType.TRIM_HORIZON).build();

            GetShardIteratorResponse getShardIteratorResult = ddbStreams.getShardIterator(shardIteratorRequest);

            String currentShardIter = getShardIteratorResult.shardIterator();

            // Shard iterator is not null until the Shard is sealed (marked as READ_ONLY).
            // To prevent running the loop until the Shard is sealed, we process only the
            // items that were written into DynamoDB and then exit.
            int processedRecordCount = 0;
            while (currentShardIter != null && processedRecordCount < maxItemCount) {
                // Use the shard iterator to read the stream records
                GetRecordsRequest getRecordsRequest = GetRecordsRequest.builder()
                        .shardIterator(currentShardIter).build();
                GetRecordsResponse getRecordsResult = ddbStreams.getRecords(getRecordsRequest);
                List<Record> records = getRecordsResult.records();
                for (Record record : records) {
                    System.out.println("        " + record.dynamodb());
                }
                processedRecordCount += records.size();
                currentShardIter = getRecordsResult.nextShardIterator();
            }
            if (currentShardIter == null){
                System.out.println("Shard has been fully processed. Shard iterator is null.");
                System.out.println("Fetch the child shard to continue processing instead of bulk fetching all shards");
                DescribeStreamRequest describeStreamRequestForChildShards = DescribeStreamRequest.builder()
                        .streamArn(streamArn)
                        .shardFilter(ShardFilter.builder()
                                .type(ShardFilterType.CHILD_SHARDS)
                                .shardId(shardId).build())
                        .build();
                DescribeStreamResponse describeStreamResponseChildShards = ddbStreams.describeStream(describeStreamRequestForChildShards);
                fetchShardsAndReadRecords(streamArn, maxItemCount, ddbStreams, describeStreamResponseChildShards.streamDescription().shards());
            }
        }
    }

    private static void putItemInTable(String key, Integer i, String tableName, DynamoDbClient ddb) {
        Map<String, AttributeValue> item = new HashMap<>();
        item.put(key, AttributeValue.builder()
                .s(i.toString())
                .build());
        item.put("Message", AttributeValue.builder()
                .s("New Item!")
                .build());
        PutItemRequest request = PutItemRequest.builder()
                .tableName(tableName)
                .item(item)
                .build();
        ddb.putItem(request);
    }

    private static void updateItemInTable(String key, Integer i, String tableName, DynamoDbClient ddb) {

        HashMap<String, AttributeValue> itemKey = new HashMap<>();
        itemKey.put(key, AttributeValue.builder()
                .s(i.toString())
                .build());


        HashMap<String, AttributeValueUpdate> updatedValues = new HashMap<>();
        updatedValues.put("Message", AttributeValueUpdate.builder()
                .value(AttributeValue.builder().s("This is an updated item").build())
                .action(AttributeAction.PUT)
                .build());

        UpdateItemRequest request = UpdateItemRequest.builder()
                .tableName(tableName)
                .key(itemKey)
                .attributeUpdates(updatedValues)
                .build();
        ddb.updateItem(request);
    }

    public static void deleteDynamoDBItem(String key, Integer i, String tableName, DynamoDbClient ddb) {
        HashMap<String, AttributeValue> keyToGet = new HashMap<>();
        keyToGet.put(key, AttributeValue.builder()
                .s(i.toString())
                .build());

        DeleteItemRequest deleteReq = DeleteItemRequest.builder()
                .tableName(tableName)
                .key(keyToGet)
                .build();
        ddb.deleteItem(deleteReq);
    }

    public static TableDescription createTable(DynamoDbClient ddb, String tableName, String key) {
        DynamoDbWaiter dbWaiter = ddb.waiter();
        StreamSpecification streamSpecification = StreamSpecification.builder()
                .streamEnabled(true)
                .streamViewType("NEW_AND_OLD_IMAGES")
                .build();
        CreateTableRequest request = CreateTableRequest.builder()
                .attributeDefinitions(AttributeDefinition.builder()
                        .attributeName(key)
                        .attributeType(ScalarAttributeType.S)
                        .build())
                .keySchema(KeySchemaElement.builder()
                        .attributeName(key)
                        .keyType(KeyType.HASH)
                        .build())
                .billingMode(BillingMode.PAY_PER_REQUEST) //  DynamoDB automatically scales based on traffic.
                .tableName(tableName)
                .streamSpecification(streamSpecification)
                .build();

        TableDescription newTable;
        try {
            CreateTableResponse response = ddb.createTable(request);
            DescribeTableRequest tableRequest = DescribeTableRequest.builder()
                    .tableName(tableName)
                    .build();
                    
            System.out.println("Waiting for " + tableName + " to be created...");

            // Wait until the Amazon DynamoDB table is created.
            WaiterResponse<DescribeTableResponse> waiterResponse = dbWaiter.waitUntilTableExists(tableRequest);
            waiterResponse.matched().response().ifPresent(System.out::println);
            newTable = response.tableDescription();
            return newTable;

        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
        return null;
    }



}
```

# DynamoDB Streams y disparadores de AWS Lambda
<a name="Streams.Lambda"></a>

Amazon DynamoDB se integra con AWS Lambda para que pueda crear *desencadenadores*, a saber, fragmentos de código que responden automáticamente a los eventos de DynamoDB Streams. Con los disparadores, puede crear aplicaciones que reaccionan ante las modificaciones de datos en las tablas de DynamoDB.

**Topics**
+ [Tutorial n.º 1: Uso de filtros para procesar todos los eventos con Amazon DynamoDB y AWS Lambda mediante la AWS CLI](Streams.Lambda.Tutorial.md)
+ [Tutorial n.º 2: uso de filtros para procesar algunos eventos con DynamoDB y Lambda](Streams.Lambda.Tutorial2.md)
+ [Prácticas recomendadas para usar DynamoDB Streams con Lambda](Streams.Lambda.BestPracticesWithDynamoDB.md)

Si habilita DynamoDB Streams en una tabla, puede asociar el nombre de recurso de Amazon (ARN) de la transmisión con una función de AWS Lambda que haya escrito. Todas las acciones de mutación a esa tabla de DynamoDB pueden capturarse como un elemento en el flujo. Por ejemplo, puede establecer un desencadenador para que, cuando se modifique un elemento de una tabla, aparezca inmediatamente un nuevo registro en el flujo de esa tabla. 

**nota**  
Si suscribe más de dos funciones de Lambda a un solo flujo de DynamoDB, podría aplicarse una limitación en la lectura.

El servicio [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html) sondea el flujo en busca de nuevos registros cuatro veces por segundo. Cuando hay nuevos registros de flujo disponibles, se invoca su función Lambda de forma sincrónica. Puede suscribir hasta dos funciones Lambda al mismo flujo de DynamoDB. Si suscribe más de dos funciones de Lambda al mismo flujo de DynamoDB, podría aplicarse una limitación en la lectura.

La función Lambda puede enviar una notificación, iniciar un flujo de trabajo o realizar otras muchas acciones que le especifique. Puede escribir una función Lambda para que simplemente copie cada registro de flujo a un almacenamiento persistente, como la puerta de enlace de archivo de Amazon S3 (Amazon S3), y crear un registro de auditoría permanente de la actividad de escritura en su tabla. O bien suponga que tiene una aplicación de juegos para móviles que escribe en una tabla `GameScores`. Cada vez que se actualiza el atributo `TopScore` de la tabla `GameScores`, se escribe el registro correspondiente en la secuencia de la tabla. Este evento, a su vez, puede activar una función Lambda que publique un mensaje de felicitación en una red social. Esta función también podría escribirse para ignorar cualquier registro de flujo que no sea una actualización de `GameScores` o que no modifique el atributo `TopScore`.

Sin embargo, si la función devuelve un error, Lambda vuelve a intentar ejecutar el lote hasta que se procese correctamente o los datos caduquen. También puede configurar Lambda para que lo reintente con un lote más pequeño, limitar el número de reintentos, descartar los registros cuando sean demasiado antiguos y otras opciones.

Como práctica recomendada de rendimiento, la función Lambda debe ser de corta duración. Para evitar introducir retrasos innecesarios en el procesamiento, tampoco debe ejecutar una lógica compleja. Para un flujo de alta velocidad en concreto, es mejor desencadenar un flujo de trabajo asíncrono de funciones de posprocesamiento que funciones Lambda sincrónicas de larga duración.

 Puede usar activadores de Lambda en diferentes cuentas de AWS configurando una política basada en recursos en el flujo de DynamoDB para conceder a la función de Lambda acceso de lectura entre cuentas. Para obtener información sobre cómo configurar el flujo para permitir el acceso entre cuentas, consulte [Compartir acceso con funciones de AWS Lambda](rbac-cross-account-access.md#shared-access-cross-acount-lambda) entre cuentas en la Guía para desarrolladores de DynamoDB.

Para obtener más información sobre AWS Lambda, consulte la Guía para desarrolladores de [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/).

# Tutorial n.º 1: Uso de filtros para procesar todos los eventos con Amazon DynamoDB y AWS Lambda mediante la AWS CLI
<a name="Streams.Lambda.Tutorial"></a>

 

En este tutorial, creará un desencadenador AWS Lambda para procesar una transmisión de una tabla de DynamoDB.

**Topics**
+ [Paso 1: crear una tabla de DynamoDB con un flujo habilitado](#Streams.Lambda.Tutorial.CreateTable)
+ [Paso 2: crear un rol de ejecución para Lambda](#Streams.Lambda.Tutorial.CreateRole)
+ [Paso 3: crear un tema de Amazon SNS](#Streams.Lambda.Tutorial.SNSTopic)
+ [Paso 4: crear y probar una función Lambda](#Streams.Lambda.Tutorial.LambdaFunction)
+ [Paso 5: crear y probar un disparador](#Streams.Lambda.Tutorial.CreateTrigger)

El escenario en que tiene lugar este tutorial es Woofer, una red social sencilla. Los usuarios de Woofer se comunican utilizando *ladridos* (mensajes de texto breves) que se envían a otros usuarios de Woofer. En el siguiente diagrama se muestran los componentes y el flujo de trabajo de esta aplicación.

![\[Flujo de trabajo de una aplicación Woofer de una tabla de DynamoDB, un registro de flujo, una función de Lambda y un tema de Amazon SNS.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/StreamsAndTriggers.png)


1. Un usuario escribe un elemento en una tabla de DynamoDB (`BarkTable`). Cada elemento de la tabla representa un ladrido.

1. Se escribe un nuevo registro de secuencia para reflejar que se ha agregado un elemento nuevo a `BarkTable`.

1. El nuevo registro de secuencia activa una función de AWS Lambda (`publishNewBark`).

1. Si el registro de transmisión indica que se ha agregado un nuevo elemento a `BarkTable`, la función de Lambda lee los datos del registro de transmisión y publica un mensaje en un tema de Amazon Simple Notification Service (Amazon SNS).

1. Los suscriptores a ese tema de Amazon SNS reciben el mensaje. En este tutorial, el único suscriptor es una dirección de correo electrónico.

**Antes de empezar**  
En este tutorial se utiliza la AWS Command Line Interface AWS CLI. Si aún no lo ha hecho, siga las instrucciones que figuran en la [Guía del usuario de AWS Command Line Interface](https://docs.aws.amazon.com/cli/latest/userguide/) para instalar y configurar la AWS CLI.

## Paso 1: crear una tabla de DynamoDB con un flujo habilitado
<a name="Streams.Lambda.Tutorial.CreateTable"></a>

En este paso, va a crear una tabla de DynamoDB (`BarkTable`) para almacenar todos los ladridos de los usuarios de Woofer. La clave principal consta de `Username` (clave de partición) y `Timestamp` (clave de ordenación). Ambos atributos son de tipo String.

`BarkTable` tiene una secuencia habilitada. Más adelante en este tutorial, asociará una función de AWS Lambda a la secuencia para crear un disparador.

1. Introduzca el siguiente comando para crear la tabla.

   ```
   aws dynamodb create-table \
       --table-name BarkTable \
       --attribute-definitions AttributeName=Username,AttributeType=S AttributeName=Timestamp,AttributeType=S \
       --key-schema AttributeName=Username,KeyType=HASH  AttributeName=Timestamp,KeyType=RANGE \
       --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
       --stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES
   ```

1. Busque en los resultados `LatestStreamArn`.

   ```
   ...
   "LatestStreamArn": "arn:aws:dynamodb:region:accountID:table/BarkTable/stream/timestamp
   ...
   ```

   Anote el valor de `region` y `accountID`, porque los necesitará para los demás pasos de este tutorial.

## Paso 2: crear un rol de ejecución para Lambda
<a name="Streams.Lambda.Tutorial.CreateRole"></a>

En este paso, va a crear un rol de AWS Identity and Access Management (IAM) (`WooferLambdaRole`) y a asignarle permisos. El rol lo utilizará la función de Lambda que va a crear en [Paso 4: crear y probar una función Lambda](#Streams.Lambda.Tutorial.LambdaFunction). 

Asimismo, va a crear una política para el rol. La política contendrá todos los permisos que la función de Lambda va a necesitar en tiempo de ejecución.

1. Cree un archivo denominado `trust-relationship.json` con el siguiente contenido.

------
#### [ JSON ]

****  

   ```
   {
      "Version":"2012-10-17",		 	 	 
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
            "Service": "lambda.amazonaws.com"
          },
          "Action": "sts:AssumeRole"
        }
      ]
    }
   ```

------

1. Escriba el siguiente comando para crear `WooferLambdaRole`.

   ```
   aws iam create-role --role-name WooferLambdaRole \
       --path "/service-role/" \
       --assume-role-policy-document file://trust-relationship.json
   ```

1. Cree un archivo denominado `role-policy.json` con el siguiente contenido. (Sustituya `region` y `accountID` por su región de AWSy su ID de cuenta).

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Effect": "Allow",
               "Action": [
                   "logs:CreateLogGroup",
                   "logs:CreateLogStream",
                   "logs:PutLogEvents"
               ],
               "Resource": "arn:aws:logs:us-east-1:111122223333:*"
           },
           {
               "Effect": "Allow",
               "Action": [
                   "dynamodb:DescribeStream",
                   "dynamodb:GetRecords",
                   "dynamodb:GetShardIterator",
                   "dynamodb:ListStreams"
               ],
               "Resource": "arn:aws:dynamodb:us-east-1:111122223333:table/BarkTable/stream/*"
           },
           {
               "Effect": "Allow",
               "Action": [
                   "sns:Publish"
               ],
               "Resource": [
                   "*"
               ]
           }
       ]
   }
   ```

------

   La política tiene cuatro instrucciones que permiten que `WooferLambdaRole` realice las siguientes acciones:
   + Ejecute una función Lambda (`publishNewBark`). Creará la función más adelante en este tutorial.
   + Acceder a Amazon CloudWatch Logs. La función de Lambda escribe diagnósticos en CloudWatch Logs en tiempo de ejecución.
   + Leer datos de la transmisión de DynamoDB relativos a `BarkTable`.
   + Publicar mensajes en Amazon SNS.

1. Introduzca el siguiente comando para asociar la política al `WooferLambdaRole`.

   ```
   aws iam put-role-policy --role-name WooferLambdaRole \
       --policy-name WooferLambdaRolePolicy \
       --policy-document file://role-policy.json
   ```

## Paso 3: crear un tema de Amazon SNS
<a name="Streams.Lambda.Tutorial.SNSTopic"></a>

En este paso, va a crear un tema de Amazon SNS (`wooferTopic`) y suscribe una dirección de correo electrónico a ese tema. La función de Lambda utiliza este tema para publicar los ladridos nuevos de los usuarios de Woofer.

1. Ingrese el siguiente comando para crear un nuevo tema de Amazon SNS.

   ```
   aws sns create-topic --name wooferTopic
   ```

1. Introduzca el siguiente comando para suscribir una dirección de correo electrónico a `wooferTopic`. (Sustituya `region` y `accountID` por su región de AWS y su ID de cuenta; además, sustituya `example@example.com` por una dirección de correo electrónico válida).

   ```
   aws sns subscribe \
       --topic-arn arn:aws:sns:region:accountID:wooferTopic \
       --protocol email \
       --notification-endpoint example@example.com
   ```

1. Amazon SNS envía un mensaje de confirmación a la dirección de correo electrónico indicada. Elija el enlace **Confirm subscription (Confirmar suscripción)** del mensaje para completar el proceso de suscripción.

## Paso 4: crear y probar una función Lambda
<a name="Streams.Lambda.Tutorial.LambdaFunction"></a>

En este paso, va a crear una función de AWS Lambda (`publishNewBark`) para procesar los registros de secuencia de `BarkTable`.

La función `publishNewBark` solamente procesa los eventos de la secuencia que corresponden a elementos nuevos de `BarkTable`. La función lee los datos de esos eventos y, a continuación, llama a Amazon SNS para publicarlos.

1. Cree un archivo denominado `publishNewBark.js` con el siguiente contenido. Sustituya `region` y `accountID` por su región de AWS y su ID de cuenta.

   ```
   'use strict';
   var AWS = require("aws-sdk");
   var sns = new AWS.SNS();
   
   exports.handler = (event, context, callback) => {
   
       event.Records.forEach((record) => {
           console.log('Stream record: ', JSON.stringify(record, null, 2));
   
           if (record.eventName == 'INSERT') {
               var who = JSON.stringify(record.dynamodb.NewImage.Username.S);
               var when = JSON.stringify(record.dynamodb.NewImage.Timestamp.S);
               var what = JSON.stringify(record.dynamodb.NewImage.Message.S);
               var params = {
                   Subject: 'A new bark from ' + who,
                   Message: 'Woofer user ' + who + ' barked the following at ' + when + ':\n\n ' + what,
                   TopicArn: 'arn:aws:sns:region:accountID:wooferTopic'
               };
               sns.publish(params, function(err, data) {
                   if (err) {
                       console.error("Unable to send message. Error JSON:", JSON.stringify(err, null, 2));
                   } else {
                       console.log("Results from sending message: ", JSON.stringify(data, null, 2));
                   }
               });
           }
       });
       callback(null, `Successfully processed ${event.Records.length} records.`);
   };
   ```

1. Cree un archivo zip que contenga `publishNewBark.js`. Si tiene la utilidad de línea de comandos zip, puede introducir el siguiente comando para hacerlo.

   ```
   zip publishNewBark.zip publishNewBark.js
   ```

1. Al crear la función de Lambda, debe especificar el nombre de recurso de Amazon (ARN) de `WooferLambdaRole`, que creó en [Paso 2: crear un rol de ejecución para Lambda](#Streams.Lambda.Tutorial.CreateRole). Introduzca el siguiente comando para recuperar este ARN.

   ```
   aws iam get-role --role-name WooferLambdaRole
   ```

   Busque ARN para en los resultados `WooferLambdaRole`.

   ```
   ...
   "Arn": "arn:aws:iam::region:role/service-role/WooferLambdaRole"
   ...
   ```

   Ingrese el siguiente comando para crear la función de Lambda. Sustituya *roleARN* por el ARN de `WooferLambdaRole`.

   ```
   aws lambda create-function \
       --region region \
       --function-name publishNewBark \
       --zip-file fileb://publishNewBark.zip \
       --role roleARN \
       --handler publishNewBark.handler \
       --timeout 5 \
       --runtime nodejs16.x
   ```

1. Ahora pruebe `publishNewBark` para comprobar que funciona. Para ello, le facilitamos información de entrada que parece un registro auténtico de DynamoDB Streams.

   Cree un archivo denominado `payload.json` con el siguiente contenido. Sustituya `region` y `accountID` por su Región de AWS y su ID de cuenta.

   ```
   {
       "Records": [
           {
               "eventID": "7de3041dd709b024af6f29e4fa13d34c",
               "eventName": "INSERT",
               "eventVersion": "1.1",
               "eventSource": "aws:dynamodb",
               "awsRegion": "region",
               "dynamodb": {
                   "ApproximateCreationDateTime": 1479499740,
                   "Keys": {
                       "Timestamp": {
                           "S": "2016-11-18:12:09:36"
                       },
                       "Username": {
                           "S": "John Doe"
                       }
                   },
                   "NewImage": {
                       "Timestamp": {
                           "S": "2016-11-18:12:09:36"
                       },
                       "Message": {
                           "S": "This is a bark from the Woofer social network"
                       },
                       "Username": {
                           "S": "John Doe"
                       }
                   },
                   "SequenceNumber": "13021600000000001596893679",
                   "SizeBytes": 112,
                   "StreamViewType": "NEW_IMAGE"
               },
               "eventSourceARN": "arn:aws:dynamodb:region:account ID:table/BarkTable/stream/2016-11-16T20:42:48.104"
           }
       ]
   }
   ```

   Introduzca el siguiente comando para probar la función de `publishNewBark`.

   ```
   aws lambda invoke --function-name publishNewBark --payload file://payload.json --cli-binary-format raw-in-base64-out output.txt
   ```

   Si la prueba se realiza correctamente, aparecerá el siguiente resultado.

   ```
   {
       "StatusCode": 200,
       "ExecutedVersion": "$LATEST"
   }
   ```

   Además, el archivo `output.txt` contendrá el siguiente texto.

   ```
   "Successfully processed 1 records."
   ```

   También recibirá un nuevo mensaje de correo electrónico dentro de unos minutos.
**nota**  
AWS Lambda escribe la información de diagnóstico en Amazon CloudWatch Logs. Si se produce cualquier error en la función Lambda, puede utilizar esos diagnósticos para solucionar el problema:  
Abra la consola de CloudWatch en [https://console.aws.amazon.com/cloudwatch/](https://console.aws.amazon.com/cloudwatch/).
En el panel de navegación, elija **Logs**.
Elija el grupo de log siguiente: `/aws/lambda/publishNewBark`
Elija la última secuencia de log para ver el resultado (y los errores) de la función.

## Paso 5: crear y probar un disparador
<a name="Streams.Lambda.Tutorial.CreateTrigger"></a>

En [Paso 4: crear y probar una función Lambda](#Streams.Lambda.Tutorial.LambdaFunction), hemos probado la función Lambda para asegurarnos de que se ejecutaba correctamente. En este paso, va a crear un *disparador*. Para ello, asociará la función de Lambda (`publishNewBark`) con el origen de eventos (la secuencia `BarkTable`).

1. Al crear el disparador, debe especificar el ARN de la secuencia de `BarkTable`. Introduzca el siguiente comando para recuperar este ARN.

   ```
   aws dynamodb describe-table --table-name BarkTable
   ```

   Busque en los resultados `LatestStreamArn`.

   ```
   ...
    "LatestStreamArn": "arn:aws:dynamodb:region:accountID:table/BarkTable/stream/timestamp
   ...
   ```

1. Introduzca el siguiente comando para crear el disparador. Sustituya `streamARN` por el ARN de la secuencia real.

   ```
   aws lambda create-event-source-mapping \
       --region region \
       --function-name publishNewBark \
       --event-source streamARN  \
       --batch-size 1 \
       --starting-position TRIM_HORIZON
   ```

1. Prueba el disparador. Introduzca el siguiente comando para agregar un elemento a `BarkTable`.

   ```
   aws dynamodb put-item \
       --table-name BarkTable \
       --item Username={S="Jane Doe"},Timestamp={S="2016-11-18:14:32:17"},Message={S="Testing...1...2...3"}
   ```

   Debería recibir un nuevo mensaje de correo electrónico dentro de unos minutos.

1. Abra la consola de DynamoDB y agregue algunos elementos más a `BarkTable`. Debe especificar valores para los atributos `Username` y `Timestamp`. También debe especificar un valor para `Message`, aunque no es obligatorio. Debe recibir un nuevo mensaje de correo electrónico por cada elemento que agregue a `BarkTable`.

   La función de Lambda procesa solamente los elementos nuevos que se agregan a `BarkTable`. Si actualiza o elimina un elemento de la tabla, la función no hace nada.

**nota**  
AWS Lambda escribe la información de diagnóstico en Amazon CloudWatch Logs. Si se produce cualquier error en la función Lambda, puede utilizar esos diagnósticos para solucionar el problema.  
Abra la consola de CloudWatch en [https://console.aws.amazon.com/cloudwatch/](https://console.aws.amazon.com/cloudwatch/).
En el panel de navegación, elija **Logs**.
Elija el grupo de log siguiente: `/aws/lambda/publishNewBark`
Elija la última secuencia de log para ver el resultado (y los errores) de la función.

# Tutorial n.º 2: uso de filtros para procesar algunos eventos con DynamoDB y Lambda
<a name="Streams.Lambda.Tutorial2"></a>

En este tutorial, creará un desencadenador AWS Lambda para procesar solo algunos eventos en un flujo de una tabla de DynamoDB.

**Topics**
+ [Resumen global: CloudFormation](#Streams.Lambda.Tutorial2.Cloudformation)
+ [Resumen global: CDK](#Streams.Lambda.Tutorial2.CDK)

Con el [filtrado de eventos de Lambda](https://docs.aws.amazon.com/lambda/latest/dg/invocation-eventfiltering.html) puede utilizar expresiones de filtrado para controlar qué eventos envía Lambda a su función para su procesamiento. Puede configurar hasta cinco filtros diferentes por flujos de DynamoDB. Si utiliza intervalos de lotes, Lambda aplica los criterios de filtrado a cada nuevo evento para ver si debe incluirse en el lote actual.

Los filtros se aplican mediante estructuras denominadas `FilterCriteria`. Los 3 atributos principales de `FilterCriteria` son `metadata properties`, `data properties` y `filter patterns`. 

A continuación, se muestra un ejemplo de estructura de un evento de DynamoDB Streams:

```
{
  "eventID": "c9fbe7d0261a5163fcb6940593e41797",
  "eventName": "INSERT",
  "eventVersion": "1.1",
  "eventSource": "aws:dynamodb",
  "awsRegion": "us-east-2",
  "dynamodb": {
    "ApproximateCreationDateTime": 1664559083.0,
    "Keys": {
      "SK": { "S": "PRODUCT#CHOCOLATE#DARK#1000" },
      "PK": { "S": "COMPANY#1000" }
    },
    "NewImage": {
      "quantity": { "N": "50" },
      "company_id": { "S": "1000" },
      "fabric": { "S": "Florida Chocolates" },
      "price": { "N": "15" },
      "stores": { "N": "5" },
      "product_id": { "S": "1000" },
      "SK": { "S": "PRODUCT#CHOCOLATE#DARK#1000" },
      "PK": { "S": "COMPANY#1000" },
      "state": { "S": "FL" },
      "type": { "S": "" }
    },
    "SequenceNumber": "700000000000888747038",
    "SizeBytes": 174,
    "StreamViewType": "NEW_AND_OLD_IMAGES"
  },
  "eventSourceARN": "arn:aws:dynamodb:us-east-2:111122223333:table/chocolate-table-StreamsSampleDDBTable-LUOI6UXQY7J1/stream/2022-09-30T17:05:53.209"
}
```

Las `metadata properties` son los campos del objeto de evento. En el caso de DynamoDB Streams, las `metadata properties` son campos como `dynamodb` o `eventName`. 

Las `data properties` son los campos del cuerpo de evento. Para filtrar las `data properties`, asegúrese de incluirlas en `FilterCriteria` en la clave adecuada. Para los orígenes de eventos de DynamoDB, la clave de datos es `NewImage` u `OldImage`.

Por último, las reglas de filtro definirán la expresión filtro que quiere aplicar a una propiedad específica. Estos son algunos ejemplos:


| Operador de comparación | Ejemplo | Sintaxis de reglas (parcial) | 
| --- | --- | --- | 
|  Nulo  |  El tipo de producto es nulo  |  `{ "product_type": { "S": null } } `  | 
|  Vacío  |  El nombre de producto está vacío  |  `{ "product_name": { "S": [ ""] } } `  | 
|  Igual a  |  El estado es igual a Florida  |  `{ "state": { "S": ["FL"] } } `  | 
|  Y  |  El estado del producto es igual a Florida y la categoría del producto es Chocolate  |  `{ "state": { "S": ["FL"] } , "category": { "S": [ "CHOCOLATE"] } } `  | 
|  O  |  El estado del producto es Florida o California  |  `{ "state": { "S": ["FL","CA"] } } `  | 
|  No (Negación)  |  El estado del producto no es Florida  |  `{"state": {"S": [{"anything-but": ["FL"]}]}}`  | 
|  Exists  |  El producto Homemade existe  |  `{"homemade": {"S": [{"exists": true}]}}`  | 
|  No existe  |  El producto Homemade no existe  |  `{"homemade": {"S": [{"exists": false}]}}`  | 
|  Comienza por  |  PK comienza por COMPANY  |  `{"PK": {"S": [{"prefix": "COMPANY"}]}}`  | 

Puede especificar hasta cinco patrones de filtrado de eventos para una función Lambda. Observe que cada uno de esos cinco eventos se evaluará como un O lógico. Por lo tanto, si configura dos filtros denominados `Filter_One` y `Filter_Two`, la función Lambda ejecutará `Filter_One` O `Filter_Two`.

**nota**  
En la página de [filtrado de eventos de Lambda](https://docs.aws.amazon.com/lambda/latest/dg/invocation-eventfiltering.html) hay algunas opciones para filtrar y comparar valores numéricos; sin embargo, en el caso de los eventos de filtrado de DynamoDB, esto no se aplica porque los números en DynamoDB se almacenan como cadenas. Por ejemplo ` "quantity": { "N": "50" }`, sabemos que es un número debido a la propiedad `"N"`.

## Resumen global: CloudFormation
<a name="Streams.Lambda.Tutorial2.Cloudformation"></a>

Para mostrar la funcionalidad del filtrado de eventos en la práctica, a continuación se muestra una plantilla de CloudFormation de ejemplo. Esta plantilla generará una tabla de DynamoDB Simple con una clave de partición PK y una clave de clasificación SK con Amazon DynamoDB Streams habilitado. Creará una función Lambda y un rol de ejecución Lambda simple que permitirá escribir registros en Amazon CloudWatch y leer los eventos de Amazon DynamoDB Streams. También agregará la asignación del origen de los eventos entre DynamoDB Streams y la función Lambda, para que la función pueda ejecutarse cada vez que haya un evento en Amazon DynamoDB Streams.

```
AWSTemplateFormatVersion: "2010-09-09"

Description: Sample application that presents AWS Lambda event source filtering 
with Amazon DynamoDB Streams.

Resources:
  StreamsSampleDDBTable:
    Type: AWS::DynamoDB::Table
    Properties:
      AttributeDefinitions:
        - AttributeName: "PK"
          AttributeType: "S"
        - AttributeName: "SK"
          AttributeType: "S"
      KeySchema:
        - AttributeName: "PK"
          KeyType: "HASH"
        - AttributeName: "SK"
          KeyType: "RANGE"
      StreamSpecification:
        StreamViewType: "NEW_AND_OLD_IMAGES"
      ProvisionedThroughput:
        ReadCapacityUnits: 5
        WriteCapacityUnits: 5

  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17",		 	 	 
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: "/"
      Policies:
        - PolicyName: root
          PolicyDocument:
            Version: "2012-10-17",		 	 	 
            Statement:
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: arn:aws:logs:*:*:*
              - Effect: Allow
                Action:
                  - dynamodb:DescribeStream
                  - dynamodb:GetRecords
                  - dynamodb:GetShardIterator
                  - dynamodb:ListStreams
                Resource: !GetAtt StreamsSampleDDBTable.StreamArn

  EventSourceDDBTableStream:
    Type: AWS::Lambda::EventSourceMapping
    Properties:
      BatchSize: 1
      Enabled: True
      EventSourceArn: !GetAtt StreamsSampleDDBTable.StreamArn
      FunctionName: !GetAtt ProcessEventLambda.Arn
      StartingPosition: LATEST

  ProcessEventLambda:
    Type: AWS::Lambda::Function
    Properties:
      Runtime: python3.7
      Timeout: 300
      Handler: index.handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile: |
          import logging

          LOGGER = logging.getLogger()
          LOGGER.setLevel(logging.INFO)

          def handler(event, context):
            LOGGER.info('Received Event: %s', event)
            for rec in event['Records']:
              LOGGER.info('Record: %s', rec)

Outputs:
  StreamsSampleDDBTable:
    Description: DynamoDB Table ARN created for this example
    Value: !GetAtt StreamsSampleDDBTable.Arn
  StreamARN:
    Description: DynamoDB Table ARN created for this example
    Value: !GetAtt StreamsSampleDDBTable.StreamArn
```

Después de implementar esta plantilla de CloudFormation, puede insertar el siguiente elemento de Amazon DynamoDB:

```
{
 "PK": "COMPANY#1000",
 "SK": "PRODUCT#CHOCOLATE#DARK",
 "company_id": "1000",
 "type": "",
 "state": "FL",
 "stores": 5,
 "price": 15,
 "quantity": 50,
 "fabric": "Florida Chocolates"
}
```

Gracias a la sencilla función Lambda incluida en línea en esta plantilla de CloudFormation, verá los eventos en los grupos de registro de Amazon CloudWatch para la función Lambda de la siguiente manera:

```
{
  "eventID": "c9fbe7d0261a5163fcb6940593e41797",
  "eventName": "INSERT",
  "eventVersion": "1.1",
  "eventSource": "aws:dynamodb",
  "awsRegion": "us-east-2",
  "dynamodb": {
    "ApproximateCreationDateTime": 1664559083.0,
    "Keys": {
      "SK": { "S": "PRODUCT#CHOCOLATE#DARK#1000" },
      "PK": { "S": "COMPANY#1000" }
    },
    "NewImage": {
      "quantity": { "N": "50" },
      "company_id": { "S": "1000" },
      "fabric": { "S": "Florida Chocolates" },
      "price": { "N": "15" },
      "stores": { "N": "5" },
      "product_id": { "S": "1000" },
      "SK": { "S": "PRODUCT#CHOCOLATE#DARK#1000" },
      "PK": { "S": "COMPANY#1000" },
      "state": { "S": "FL" },
      "type": { "S": "" }
    },
    "SequenceNumber": "700000000000888747038",
    "SizeBytes": 174,
    "StreamViewType": "NEW_AND_OLD_IMAGES"
  },
  "eventSourceARN": "arn:aws:dynamodb:us-east-2:111122223333:table/chocolate-table-StreamsSampleDDBTable-LUOI6UXQY7J1/stream/2022-09-30T17:05:53.209"
}
```

**Ejemplos de filtro**
+ **Solo productos que coincidan con un estado determinado**

Este ejemplo modifica la plantilla de CloudFormation para incluir un filtro que coincida con todos los productos que provienen de Florida, con la abreviatura “FL”.

```
EventSourceDDBTableStream:
    Type: AWS::Lambda::EventSourceMapping
    Properties:
      BatchSize: 1
      Enabled: True
      FilterCriteria:
        Filters:
          - Pattern: '{ "dynamodb": { "NewImage": { "state": { "S": ["FL"] } } } }'
      EventSourceArn: !GetAtt StreamsSampleDDBTable.StreamArn
      FunctionName: !GetAtt ProcessEventLambda.Arn
      StartingPosition: LATEST
```

Una vez que vuelva a implementar la pila, puede agregar el siguiente elemento de DynamoDB a la tabla. Tenga en cuenta que no aparecerá en los registros de la función Lambda, porque el producto de este ejemplo es de California.

```
{
 "PK": "COMPANY#1000",
 "SK": "PRODUCT#CHOCOLATE#DARK#1000",
 "company_id": "1000",
 "fabric": "Florida Chocolates",
 "price": 15,
 "product_id": "1000",
 "quantity": 50,
 "state": "CA",
 "stores": 5,
 "type": ""
}
```
+ **Solo los elementos que comienzan por algunos valores en PK y SK**

En este ejemplo se modifica la plantilla de CloudFormation para incluir la siguiente condición:

```
EventSourceDDBTableStream:
    Type: AWS::Lambda::EventSourceMapping
    Properties:
      BatchSize: 1
      Enabled: True
      FilterCriteria:
        Filters:
          - Pattern: '{"dynamodb": {"Keys": {"PK": { "S": [{ "prefix": "COMPANY" }] },"SK": { "S": [{ "prefix": "PRODUCT" }] }}}}'
      EventSourceArn: !GetAtt StreamsSampleDDBTable.StreamArn
      FunctionName: !GetAtt ProcessEventLambda.Arn
      StartingPosition: LATEST
```

Observe que la condición AND requiere que la condición esté en el patrón, donde las claves PK y SK están en la misma expresión separadas por una coma.

O bien empieza con algunos valores en PK y SK o es de cierto estado.

En este ejemplo se modifica la plantilla de CloudFormation para incluir las siguientes condiciones:

```
  EventSourceDDBTableStream:
    Type: AWS::Lambda::EventSourceMapping
    Properties:
      BatchSize: 1
      Enabled: True
      FilterCriteria:
        Filters:
          - Pattern: '{"dynamodb": {"Keys": {"PK": { "S": [{ "prefix": "COMPANY" }] },"SK": { "S": [{ "prefix": "PRODUCT" }] }}}}'
          - Pattern: '{ "dynamodb": { "NewImage": { "state": { "S": ["FL"] } } } }'
      EventSourceArn: !GetAtt StreamsSampleDDBTable.StreamArn
      FunctionName: !GetAtt ProcessEventLambda.Arn
      StartingPosition: LATEST
```

Observe que la condición OR se agrega con la incorporación de nuevos patrones en la sección de filtro.

## Resumen global: CDK
<a name="Streams.Lambda.Tutorial2.CDK"></a>

La siguiente plantilla de formación de proyectos CDK de ejemplo muestra la funcionalidad de filtrado de eventos. Antes de trabajar con este proyecto CDK deberá instalar los [requisitos previos](https://docs.aws.amazon.com/cdk/v2/guide/work-with.html), incluida la [ejecución de los scripts de preparación](https://docs.aws.amazon.com/cdk/v2/guide/work-with-cdk-python.html).

**Crear un proyecto CDK**

Primero cree un nuevo proyecto AWS CDK, mediante la invocación de `cdk init` en un directorio vacío.

```
mkdir ddb_filters
cd ddb_filters
cdk init app --language python
```

El comando `cdk init` utiliza el nombre de la carpeta del proyecto para asignar un nombre a varios elementos del proyecto, incluidas las clases, las subcarpetas y los archivos. Los guiones del nombre de la carpeta se convierten en guiones bajos. Por lo demás, el nombre debe seguir el formato de un identificador Python. Por ejemplo, no debe comenzar por un número ni contener espacios.

Para trabajar con el nuevo proyecto, active su entorno virtual. Esto permite que las dependencias del proyecto se instalen localmente en la carpeta del proyecto, en lugar de hacerlo globalmente.

```
source .venv/bin/activate
python -m pip install -r requirements.txt
```

**nota**  
Es posible que reconozca esto como el comando de Mac/Linux para activar un entorno virtual. Las plantillas de Python incluyen un archivo por lotes, `source.bat`, que permite utilizar el mismo comando en Windows. El comando tradicional de Windows `.venv\Scripts\activate.bat` también funciona. Si ha inicializado su proyecto AWS CDK con AWS CDK Toolkit v1.70.0 o anterior, su entorno virtual se encuentra en el directorio `.env` en lugar de `.venv`. 

**Infraestructura básica**

Abra el archivo `./ddb_filters/ddb_filters_stack.py` en el editor de texto que desee. Este archivo se generó automáticamente al crear el proyecto AWS CDK. 

A continuación, agregue las funciones `_create_ddb_table` y `_set_ddb_trigger_function`. Estas funciones crearán una tabla de DynamoDB con la clave de partición PK y la clave de clasificación SK en modo de aprovisionamiento bajo demanda, con Amazon DynamoDB Streams habilitado de forma predeterminada para mostrar las imágenes nueva y antigua.

La función Lambda se almacenará en la carpeta `lambda` debajo del archivo `app.py`. Este archivo se creará más adelante. Incluirá la variable de entorno `APP_TABLE_NAME`, que será el nombre de la tabla de Amazon DynamoDB creada por esta pila. En la misma función concederemos permisos de lectura del flujo a la función Lambda. Por último, se suscribirá a DynamoDB Streams como origen de eventos para la función Lambda. 

Al final del archivo en el método `__init__`, llamará a las construcciones respectivas para inicializarlas en la pila. Para proyectos más grandes que requieran componentes y servicios adicionales, podría ser mejor definir estas construcciones fuera de la pila base. 

```
import os
import json

import aws_cdk as cdk
from aws_cdk import (
    Stack,
    aws_lambda as _lambda,
    aws_dynamodb as dynamodb,
)
from constructs import Construct


class DdbFiltersStack(Stack):

    def _create_ddb_table(self):
        dynamodb_table = dynamodb.Table(
            self,
            "AppTable",
            partition_key=dynamodb.Attribute(
                name="PK", type=dynamodb.AttributeType.STRING
            ),
            sort_key=dynamodb.Attribute(
                name="SK", type=dynamodb.AttributeType.STRING),
            billing_mode=dynamodb.BillingMode.PAY_PER_REQUEST,
            stream=dynamodb.StreamViewType.NEW_AND_OLD_IMAGES,
            removal_policy=cdk.RemovalPolicy.DESTROY,
        )

        cdk.CfnOutput(self, "AppTableName", value=dynamodb_table.table_name)
        return dynamodb_table

    def _set_ddb_trigger_function(self, ddb_table):
        events_lambda = _lambda.Function(
            self,
            "LambdaHandler",
            runtime=_lambda.Runtime.PYTHON_3_9,
            code=_lambda.Code.from_asset("lambda"),
            handler="app.handler",
            environment={
                "APP_TABLE_NAME": ddb_table.table_name,
            },
        )

        ddb_table.grant_stream_read(events_lambda)

        event_subscription = _lambda.CfnEventSourceMapping(
            scope=self,
            id="companyInsertsOnlyEventSourceMapping",
            function_name=events_lambda.function_name,
            event_source_arn=ddb_table.table_stream_arn,
            maximum_batching_window_in_seconds=1,
            starting_position="LATEST",
            batch_size=1,
        )

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        ddb_table = self._create_ddb_table()
        self._set_ddb_trigger_function(ddb_table)
```

Ahora crearemos una función Lambda muy sencilla que imprimirá los registros en Amazon CloudWatch. Para ello, cree una carpeta nueva llamada `lambda`.

```
mkdir lambda
touch app.py
```

Con su editor de texto favorito, agregue el siguiente contenido al archivo `app.py`:

```
import logging

LOGGER = logging.getLogger()
LOGGER.setLevel(logging.INFO)


def handler(event, context):
    LOGGER.info('Received Event: %s', event)
    for rec in event['Records']:
        LOGGER.info('Record: %s', rec)
```

Asegúrese de que está en la carpeta `/ddb_filters/` y escriba el siguiente comando para crear la aplicación de muestra:

```
cdk deploy
```

En algún momento se le pedirá que confirme si desea implementar la solución. Escriba `Y` para aceptar los cambios.

```
├───┼──────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${LambdaHandler/ServiceRole} │ arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole │
└───┴──────────────────────────────┴────────────────────────────────────────────────────────────────────────────────┘

Do you wish to deploy these changes (y/n)? y

...

✨  Deployment time: 67.73s

Outputs:
DdbFiltersStack.AppTableName = DdbFiltersStack-AppTable815C50BC-1M1W7209V5YPP
Stack ARN:
arn:aws:cloudformation:us-east-2:111122223333:stack/DdbFiltersStack/66873140-40f3-11ed-8e93-0a74f296a8f6
```

Una vez implementados los cambios, abra la consola de AWS y agregue un elemento a la tabla. 

```
{
 "PK": "COMPANY#1000",
 "SK": "PRODUCT#CHOCOLATE#DARK",
 "company_id": "1000",
 "type": "",
 "state": "FL",
 "stores": 5,
 "price": 15,
 "quantity": 50,
 "fabric": "Florida Chocolates"
}
```

Los registros de CloudWatch ahora deben contener toda la información de esta entrada. 

**Ejemplos de filtro**
+ **Solo productos que coincidan con un estado determinado**

Abra el archivo `ddb_filters/ddb_filters/ddb_filters_stack.py` y modifíquelo para incluir el filtro que coincide con todos los productos que son iguales a “FL”. Esto se puede revisar justo debajo de `event_subscription` en la línea 45.

```
event_subscription.add_property_override(
    property_path="FilterCriteria",
    value={
        "Filters": [
            {
                "Pattern": json.dumps(
                    {"dynamodb": {"NewImage": {"state": {"S": ["FL"]}}}}
                )
            },
        ]
    },
)
```
+ **Solo los elementos que comienzan por algunos valores en PK y SK**

Modifique el script python para incluir la siguiente condición:

```
event_subscription.add_property_override(
    property_path="FilterCriteria",
    value={
        "Filters": [
            {
                "Pattern": json.dumps(
                    {
                        {
                            "dynamodb": {
                                "Keys": {
                                    "PK": {"S": [{"prefix": "COMPANY"}]},
                                    "SK": {"S": [{"prefix": "PRODUCT"}]},
                                }
                            }
                        }
                    }
                )
            },
        ]
    },
```
+ **O bien comience con algunos valores en PK y SK o desde un determinado estado.**

Modifique el script python para incluir las siguientes condiciones:

```
event_subscription.add_property_override(
    property_path="FilterCriteria",
    value={
        "Filters": [
            {
                "Pattern": json.dumps(
                    {
                        {
                            "dynamodb": {
                                "Keys": {
                                    "PK": {"S": [{"prefix": "COMPANY"}]},
                                    "SK": {"S": [{"prefix": "PRODUCT"}]},
                                }
                            }
                        }
                    }
                )
            },
            {
                "Pattern": json.dumps(
                    {"dynamodb": {"NewImage": {"state": {"S": ["FL"]}}}}
                )
            },
        ]
    },
)
```

Observe que la condición OR se agrega al agregar más elementos a la matriz Filters.

**Limpieza**

Localice la pila de filtros en la base de su directorio de trabajo y ejecute `cdk destroy`. Se le pedirá que confirme la eliminación del recurso:

```
cdk destroy
Are you sure you want to delete: DdbFiltersStack (y/n)? y
```

# Prácticas recomendadas para usar DynamoDB Streams con Lambda
<a name="Streams.Lambda.BestPracticesWithDynamoDB"></a>

Una función AWS Lambda se ejecuta dentro de un *contenedor*, a saber, un entorno de ejecución aislado de las demás funciones. Cuando se ejecuta una función por primera vez, AWS Lambda crea un nuevo contenedor y comienza a ejecutar el código de la función.

Una función Lambda posee un *controlador* que se ejecuta una vez en cada invocación. El controlador contiene la lógica empresarial principal de la función. Por ejemplo, la función Lambda que se muestra en [Paso 4: crear y probar una función Lambda](Streams.Lambda.Tutorial.md#Streams.Lambda.Tutorial.LambdaFunction) tiene un controlador que puede procesar los registros de una transmisión de DynamoDB. 

También puede proporcionar código de inicialización que se ejecuta una sola vez después de crear el contenedor, pero antes de que AWS Lambda ejecute el controlador por primera vez. La función Lambda que se muestra en [Paso 4: crear y probar una función Lambda](Streams.Lambda.Tutorial.md#Streams.Lambda.Tutorial.LambdaFunction) incluye código de inicialización que importa el SDK para JavaScript en Node.js y crea un cliente para Amazon SNS. Estos objetos únicamente deben definirse una vez, fuera del controlador.

Después de ejecutar la función, AWS Lambda puede optar por reutilizar el contenedor en invocaciones posteriores de la función. En este caso, el controlador de la función podría volver a utilizar los recursos que se han definido en el código de inicialización. (No se puede controlar durante cuánto tiempo AWS Lambda conservará el contenedor ni si este se reutilizará o no).

Para los desencadenadores de DynamoDB que utilizan AWS Lambda, recomendamos lo siguiente:
+ AWSLas instancias de los clientes de servicios de deben crearse en el código de inicialización, no en el controlador. Esto permite que AWS Lambda reutilice las conexiones existentes mientras dure la vida útil del contenedor.
+ En general, no es necesario administrar de forma explícita las conexiones ni implementar la agrupación de conexiones, porque AWS Lambda lo hace automáticamente.

Un consumidor de Lambda para un flujo de DynamoDB no garantiza exactamente una entrega y puede generar duplicados. Asegúrese de que el código de la función de Lambda sea idempotente para evitar que surjan problemas inesperados por la aparición de duplicados.

Para obtener más información, consulte [Prácticas recomendadas para trabajar con las funciones AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html) en la *Guía para desarrolladores de AWS Lambda*.

# DynamoDB Streams y Apache Flink
<a name="StreamsApacheFlink.xml"></a>

Puede consumir registros de Amazon DynamoDB Streams con Apache Flink. Con [Amazon Managed Service para Apache Flink](https://aws.amazon.com/managed-service-apache-flink/), puede transformar y analizar datos de streaming en tiempo real mediante Apache Flink. Apache Flink es un marco de procesamiento de flujos de código abierto para procesar datos en tiempo real. El conector de Amazon DynamoDB Streams para Apache Flink simplifica la creación y la administración de las cargas de trabajo de Apache Flink y permite integrar aplicaciones con otros Servicios de AWS.

Amazon Managed Service para Apache Flink lo ayuda a crear rápidamente aplicaciones de procesamiento de flujos de extremo a extremo para análisis de registros, análisis de flujos de clics, Internet de las cosas (IoT), tecnología publicitaria, juegos y mucho más. Los cuatro casos de uso más comunes son la extracción-transformación-carga (ETL) de streaming, las aplicaciones controladas por eventos, los análisis en tiempo real con capacidad de respuesta y la consulta interactiva de flujos de datos. Para obtener más información sobre la escritura en Apache Flink desde Amazon DynamoDB Streams, consulte [Conector de Amazon DynamoDB Streams](https://nightlies.apache.org/flink/flink-docs-master/docs/connectors/datastream/dynamodb/).