0% encontró este documento útil (0 votos)
33 vistas27 páginas

Migraciones y Modelación de Bases de Datos

Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
33 vistas27 páginas

Migraciones y Modelación de Bases de Datos

Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

Bases de Datos

Clase 14: Bases de Datos y desarrollo de software


Migraciones
Digamos que estamos haciendo un scrapper de
precios y stocks de cervezas en distintas tiendas.
Tenemos el siguiente esquema que hasta ahora nos
ha funcionado bien:

• Cervezas(id, nombre, ibu, abv, id_marca,


precio)

• Marcas(id, nombre, país)

• Tiendas(id, nombre)

• Stocks(id_tienda, id_cerveza, cantidad)


Nuestra jefa nos comenta que los precios de una
misma cerveza no necesariamente son los mismos
para tiendas distintas!

¿Cómo lo solucionamos?
CREATE TABLE Precios(
id_cerveza INT NOT NULL,
id_tienda INT NOT NULL,
precio DECIMAL(10, 2),
FOREIGN KEY id_cerveza REFERENCES Cervezas(id),
FOREIGN KEY id_tienda REFERENCES Tiendas(id)
)

<copio los precios de la tabla Cervezas a la nueva


tabla Precios para no perder información>

ALTER TABLE Cervezas


DROP COLUMN precio;
¿Habrá una forma mejor de hacer esto?

🤔
Migraciones

• El esquema de una base de datos define las tablas y sus


relaciones.

• En la vida de un proyecto los requerimientos van cambiando, y el


esquema de la base de datos necesita cambiar.

• Usamos migraciones para modificar el esquema de la base de


datos en la medida que las necesidades van cambiando.
Ejemplo: yoyo-migrations
# file: migrations/0002.create-prices.py
from yoyo import step

__depends__ = {"0001.initial-schema"}

steps = [
step(
"CREATE TABLE Precios(id_cerveza INT NOT NULL, ...)",
"DROP TABLE Precios"
),
step(
"ALTER TABLE Cervezas DROP COLUMN precio",
"ALTER TABLE Cervezas ADD precio DECIMAL(10, 2)”
)
]
Migraciones
• Las creamos y probamos en nuestra copia local de la base de datos.

• Si no queda bien, deshacemos la migración (rollback), la arreglamos y


la corremos de nuevo.

• Cuando está todo bien subimos nuestros cambios (a git por ejemplo).

• Al lanzar versiones nuevas se corren (automáticamente) las


migraciones pendientes.

• Si el lanzamiento tiene problemas se puede hacer redeploy de la


version anterior del servicio junto con un rollback de las migraciones
relacionadas a ese lanzamiento.
Veamos un ejemplo en vivo.

👀
Migraciones delicadas
• Las migraciones de la base de datos corren después del deploy,
en la etapa del release de la aplicación (antes de que la versión
nueva empieze a funcionar).

• Los cambios que se hagan deben ser compatibles la versión


anterior de la aplicación.

• Para hacer cambios grandes a veces es necesario hacer varias


migraciones en varios lanzamientos de la aplicación para evitar
problemas.
Migraciones delicadas:
Renombrar una tabla o columna
Al renombrar una tabla o una columna la versión anterior de la
aplicación no va a poder leer la tabla, para hacer el cambio es
necesario hacerlo en varios pasos:

1. Crear una tabla o columna nueva con el nombre correcto.

2. Escribir los datos nuevos en ambas tablas/columnas.

3. Copiar los datos de la tabla/columna antigua a la nueva.

4. Cambiar las lecturas de la tabla/columna antigua a la nueva.

5. Cuando todo esté bien dejar de escribir a la tabla/columna nueva


y borrarla.
Rediseñar una base de
datos
Hacer todo de nuevo generalmente es la peor decisión. Si es posible
arreglar el modelo de datos sin partir de 0, suele ser preferible.

Pero a veces es necesario.

¿Cómo migramos una base de datos mal diseñada a otra nueva


bien hecha?

El proceso es similar a renombrar una tabla, pero es un proceso


mucho más largo y engorroso.
Rediseñar una base de
datos
1. Partimos de a poco modelando correctamente algunas tablas en
la base de datos nueva.

2. Escribimos datos nuevos a ambas tablas. Hay que tener cuidado


con referencias entre distintas bases de datos. PostgreSQL
soporta llaves foráneas enter distintas bases de datos, pero las
ORMs en general no).

3. Copiamos los datos de la base de datos vieja a la nueva.

4. Eventualmente estamos seguros de que la tabla en la base de


datos nueva funciona correctamente y dejamos de usar la tabla
de la base de datos vieja.
Modelación de datos
¡Lo más importante al desarrollar una aplicación!


Modelación de datos

Más allá del uso que nuestra aplicación le dé a los datos,


eventualmente los vamos a querer para hacer análisis, reportes,
campañas, entre otros.

Si el esquema es engorroso y dificil de entender, va a ser difícil


trabajar con los datos. Arreglar una base de datos mal modelada es
generalmente una tarea poco abarcable.

Eventualmente queremos poder tomar decisiones a partir de los


datos.
Modelación de procesos

A veces queremos modelar procesos que van cambiando de


estados:

• Una compra (pagada, en preparación, despachada)

• Una revisión manual (en espera de revisión, distintos resultados


de la revisión: aprobada, rechazada, u otros)
Modelación de procesos
Podríamos querer modelarlo como un campo de la tabla:

Order(id, state, comment, created_at, updated_at)

O también:

Order(id, comment, ordered_at, paid_at, prepared_at,


shipped_at)

¿Estamos seguro que no pierdemos información así? ¿Qué pasa si


queremos guardar información adicional sobre las etapas del
proceso?
Modelación de procesos

Order(id, comment, created_at)

PaidOrder(id, order_id, created_at, payment_method,


payment_receipt)

PreparedOrder(id, order_id, created_at, comment)

ShippedOrder(id, order_id, created_at, tracking_number,


shipping_company, expected_delivery_date)
Rendimiento y ORMs
Rendimiento y ORMs

• Generalmente en una aplicación web lo más lento es la base de


datos (cuando no hay mucha lógica implementada en el código).

• Muchas veces la aplicación no funciona lento porque los datos


estén mal modelados, o porque la consulta sea muy pesada.

• ¡Podríamos estar haciendo muchas consultas chicas!

• (También nos podría estar haciendo falta un índice.)


Rendimiento y ORMs
def my_view(request, article_id):
article = Article.objects.get(article_id)
return render(
request,
"my_view.html",
{"article": article, "author": article.author}
)

Este pedazo de código hace (al menos) dos consultas a la base de


datos:
• La primera para traer el articulo que venía como argumento de la
vista.
• La segunda para buscar el autor del articulo cuando lo pedimos
para el dict que va en lo que retorna la vista.
Rendimiento y ORMs
def my_view(request, article_id):
article = Article.objects.get(article_id)
.select_related("author")

return render(
request,
"my_view.html",
{"article": article, "author": article.author}
)

Podemos hacer las consultas juntas (con un join) usando


select_related en Django.

Cuando queremos traer varias instancias de modelos relacionados


podemos usar prefetch_related. Se usa para relaciones N-N.
Rendimiento y ORMs
• No podemos optimizar nuestras aplicaciones si no sabemos
dónde funcionan lento.

• Por lo que necesitamos herramientas que nos muestren esa


información.

• En los entornos de desarrollo enDjango se usa django-debug-


toolbar, para Ruby on Rails existe Rails Mini Profiler.

• En producción generalmente queremos más información por lo


que integramos herramientas de monitoreo (APM) como Scout o
NewRelic.
Veamos un ejemplo en vivo.

👀
Rendimiento y ORMs
Ya vimos que cuando queremos
aproximar el tiempo que va a
tomar una consulta revisamos
cuantas páginas del disco se
leen.

Para efectos de lecturas a una


base de datos a través de una
red, la cantidad de consultas que
hacemos suele impactar más que
la eficiencia de las consultas.

Eso no quita que tenemos que


hacer las consultas bien!

También podría gustarte