Diseño de esquemas de perfiles de juego en DynamoDB
Caso de uso empresarial del perfil de juego
En este caso de uso se habla del uso de DynamoDB para almacenar los perfiles de los jugadores de un sistema de juego. Los usuarios (en este caso, los jugadores) necesitan crear perfiles antes de poder interactuar con muchos juegos modernos, especialmente los que son en línea. Los perfiles de juego suelen incluir lo siguiente:
-
Información básica, como el nombre de usuario
-
Datos del juego, como elementos y equipos
-
Registros de juegos, como tareas y actividades
-
Información social, como listas de amigos
Para cumplir con los requisitos detallados de acceso a las consultas de datos para esta aplicación, las claves principales (clave de partición y clave de clasificación) utilizarán nombres genéricos (PK y SK) para sobrecargarlas con varios tipos de valores, como veremos a continuación.
Los patrones de acceso para este diseño de esquema son:
-
Obtener la lista de amigos de un usuario
-
Obtener toda la información de un jugador
-
Obtener la lista de elementos de un usuario
-
Obtener un elemento específico de la lista de elementos del usuario
-
Actualizar el personaje de un usuario
-
Actualizar el recuento de elementos de un usuario
El tamaño del perfil de juego variará en los diferentes juegos. Si los valores de atributo grandes se comprimen, es posible que se ajusten a los límites de los elementos de DynamoDB y se reduzcan los costos. La estrategia de gestión del rendimiento dependería de varios factores, como el número de jugadores, el número de partidos jugados por segundo y la estacionalidad de la carga de trabajo. Normalmente, en el caso de un juego recién lanzado, se desconoce el número de jugadores y el nivel de popularidad, por lo que empezaremos con el modo de rendimiento bajo demanda.
Diagrama de relación entre entidades del perfil de juego
Este es el diagrama de relaciones entre entidades (ERD) que utilizaremos para el diseño del esquema del perfil de juego.
Patrones de acceso a los perfiles de juego
Estos son los patrones de acceso que consideraremos para el diseño del esquema de redes sociales.
-
getPlayerFriends
-
getPlayerAllProfile
-
getPlayerAllItems
-
getPlayerSpecificItem
-
updateCharacterAttributes
-
updateItemCount
Evolución del diseño del esquema de perfiles de juego
En el ERD anterior, podemos ver que este es un tipo de modelado de datos de relación de uno a varios. En DynamoDB, los modelos de datos de uno a varios se pueden organizar en recopilaciones de elementos, lo que es diferente de las bases de datos relacionales tradicionales, en las que se crean varias tablas y se vinculan mediante claves externas. Una recopilación de elementos es un grupo de elementos que comparten el mismo valor de clave de partición, pero tienen valores de clave de clasificación diferentes. Dentro de una recopilación de elementos, cada elemento tiene un valor de clave de clasificación único que lo distingue de otros elementos. Con esto en mente, vamos a usar el siguiente patrón para los valores HASH
y RANGE
para cada tipo de entidad.
Para empezar, utilizamos nombres genéricos como PK
y SK
para almacenar diferentes tipos de entidades en la misma tabla a fin de preparar el modelo para el futuro. Para una mejor legibilidad, podemos incluir prefijos para indicar el tipo de datos o incluir un atributo arbitrario llamado Entity_type
o Type
. En el ejemplo actual, utilizamos una cadena que comienza con player
para almacenar player_ID
como PK
; usar entity name#
como prefijo de SK
y añadir un atributo Type
para indicar de qué tipo de entidad es este dato. Esto nos permitirá almacenar más tipos de entidades en el futuro y utilizar tecnologías avanzadas como sobrecarga de GSI y GSI disperso para cumplir con más patrones de acceso.
Empecemos a implementar los patrones de acceso. Los patrones de acceso, como agregar jugadores y agregar equipo, se pueden realizar a través de la operación PutItem
, por lo que podemos ignorarlos. En este documento, nos centraremos en los patrones de acceso típicos mostrados anteriormente.
Paso 1: Abordar el patrón de acceso 1 (getPlayerFriends
)
Abordamos el patrón de acceso 1 (getPlayerFriends
) con este paso. En nuestro diseño actual, la amistad es simple y el número de amigos en el juego es pequeño. Para simplificar, utilizamos un tipo de datos de lista para almacenar las listas de amigos (modelo 1:1). En este diseño, utilizamos GetItem
para satisfacer este patrón de acceso. En la operación GetItem
, proporcionamos explícitamente la clave de partición y el valor de la clave de clasificación para obtener un elemento específico.
Sin embargo, si un juego tiene un gran número de amigos y las relaciones entre ellos son complejas (por ejemplo, las amistades son bidireccionales con un componente de invitación y de aceptación), sería necesario utilizar una relación de varios a varios para almacenar a cada amigo de forma individual, a fin de escalar hasta un tamaño ilimitado de la lista de amigos. Y si el cambio de amistad implica operar con varios elementos al mismo tiempo, las transacciones de DynamoDB se pueden usar para agrupar varias acciones y enviarlas como una sola operación de todo o nada TransactWriteItems
o TransactGetItems
.
Paso 2: Abordar los patrones de acceso 2 (getPlayerAllProfile
), 3 (getPlayerAllItems
) y 4 (getPlayerSpecificItem
)
Mediante este paso, abordamos los patrones de acceso 2 (getPlayerAllProfile
), 3 (getPlayerAllItems
) y 4 (getPlayerSpecificItem
). Lo que estos tres patrones de acceso tienen en común es una consulta de rango, que utiliza la operación Query. En función del alcance de la consulta, se utilizan Claves de condición y Expresiones de filtro, que se utilizan normalmente en el desarrollo práctico.
En la operación Query, proporcionamos un valor único para la clave de partición y obtenemos todos los elementos con ese valor de clave de partición. El patrón de acceso 2 (getPlayerAllProfile
) se implementa de esta manera. De forma opcional, podemos agregar una expresión de condición de clave de clasificación, una cadena que determina los elementos que se van a leer de la tabla. El patrón de acceso 3 (getPlayerAllItems
) se implementa al agregar la condición clave de clave de clasificación begins_with ITEMS#
. Además, para simplificar el desarrollo del lado de la aplicación, podemos usar expresiones de filtro para implementar el patrón de acceso 4 (getPlayerSpecificItem
).
Este es un ejemplo de pseudocódigo que utiliza una expresión de filtro que filtra los elementos de la categoría Weapon
:
filterExpression: "ItemType = :itemType" expressionAttributeValues: {":itemType": "Weapon"}
nota
Una expresión de filtro se aplica después de que Query finalice, pero antes de devolver los resultados al cliente. Por consiguiente, una Query consume la misma cantidad de capacidad de lectura aunque se especifique una expresión de filtro.
Si el patrón de acceso consiste en consultar un conjunto de datos grande y filtrar una gran cantidad de datos para conservar solo un pequeño subconjunto de datos, el enfoque adecuado consiste en diseñar la clave de partición y la clave de clasificación de DynamoDB de forma más eficaz. Por ejemplo, en el ejemplo anterior para obtener un determinado ItemType
, si hay muchos elementos para cada jugador y la consulta de un determinado ItemType
es un patrón de acceso típico, sería más eficiente incluir ItemType
en SK
como una clave compuesta. El modelo de datos tendría este aspecto: ITEMS#ItemType#ItemId
.
Paso 3: Abordar patrones de acceso 5 (updateCharacterAttributes
) y 6 (updateItemCount
)
Mediante este paso, abordamos los patrones de acceso 5 (updateCharacterAttributes
) y 6 (updateItemCount
). Cuando el jugador necesite modificar el personaje, por ejemplo, reducir la moneda o modificar la cantidad de un arma determinada en los elementos, use UpdateItem
para implementar estos patrones de acceso. Para actualizar la moneda de un jugador y asegurarnos de que nunca baje de la cantidad mínima, podemos agregar una Ejemplo de la CLI de expresión de condición de DynamoDB para reducir el saldo solo si es superior o igual a la cantidad mínima. Este es un ejemplo de pseudocódigo:
UpdateExpression: "SET currency = currency - :amount" ConditionExpression: "currency >= :minAmount"
Al desarrollar con DynamoDB y utilizar Contadores atómicos para reducir el inventario, podemos garantizar la idempotencia mediante el uso de un bloqueo positivo. Este es un ejemplo de pseudocódigo para contadores atómicos:
UpdateExpression: "SET ItemCount = ItemCount - :incr" expression-attribute-values: '{":incr":{"N":"1"}}'
Además, en un escenario en el que el jugador compra un elemento con moneda, todo el proceso debe deducir la moneda y agregar un elemento al mismo tiempo. Podemos usar las transacciones de DynamoDB para agrupar varias acciones y enviarlas como una sola operación de todo o nada TransactWriteItems
o TransactGetItems
. TransactWriteItems
es una operación de escritura sincrónica e idempotente que agrupa hasta 100 acciones de escritura en una sola operación de todo o nada. Las acciones se realizan atómicamente, de tal forma que se llevan a cabo correctamente todas o ninguna de ellas. Las transacciones ayudan a eliminar el riesgo de duplicación o desaparición de divisas. Para obtener más información acerca de las transacciones, consulte Ejemplo de transacciones en DynamoDB.
En la tabla siguiente se resumen todos los patrones de acceso y cómo los aborda el diseño del esquema:
Patrón de acceso | Tabla base/GSI/LSI | Operación | Valor de clave de partición | Valor de la clave de clasificación | Otras condiciones/filtros |
---|---|---|---|---|---|
getPlayerFriends | Tabla de base | GetItem | PK=PlayerID | SK=“FRIENDS#playerID” | |
getPlayerAllProfile | Tabla de base | Consultar | PK=PlayerID | ||
getPlayerAllItems | Tabla de base | Consultar | PK=PlayerID | SK begins_with “ITEMS#” | |
getPlayerSpecificItem | Tabla de base | Consultar | PK=PlayerID | SK begins_with “ITEMS#” | filterExpression: "ItemType = :itemType" expressionAttributeValues: { ":itemType": "Weapon" } |
updateCharacterAttributes | Tabla de base | UpdateItem | PK=PlayerID | SK=“#METADATA#playerID” | UpdateExpression: "SET currency = currency - :amount" ConditionExpression: "currency >= :minAmount" |
updateItemCount | Tabla de base | UpdateItem | PK=PlayerID | SK =“ITEMS#ItemID” | update-expression: "SET ItemCount = ItemCount - :incr" expression-attribute-values: '{":incr":{"N":"1"}}' |
Esquema final del perfil de juego
Este es el diseño final del esquema. Para descargar este diseño de esquema como un archivo JSON, consulte los ejemplos de DynamoDB
Tabla base:
Uso de NoSQL Workbench con este diseño de esquema
Puede importar este esquema final en NoSQL Workbench, una herramienta visual que proporciona características de modelado de datos, visualización de datos y desarrollo de consultas para DynamoDB, a fin de explorar y editar más a fondo el nuevo proyecto. Para comenzar, siga estos pasos:
-
Descargue NoSQL Workbench. Para obtener más información, consulte Descargar NoSQL Workbench para DynamoDB.
-
Descargue el archivo de esquema JSON que se muestra anteriormente, que ya está en el formato de modelo NoSQL Workbench.
-
Importe el archivo de esquema JSON en NoSQL Workbench. Para obtener más información, consulte Importación de un modelo de datos existente.
-
Una vez que haya importado en NOSQL Workbench, podrá editar el modelo de datos. Para obtener más información, consulte Edición de un modelo de datos existente.
-
Para visualizar el modelo de datos, agregar datos de ejemplo o importar datos de ejemplo de un archivo CSV, utilice la característica Visualizador de datos de NoSQL Workbench.