La Sara porta divuit mesos executant el mateix informe. Obre el seu client SQL a mig matí, llança la consulta del tiquet mitjà per ciutat i se'n va a fer un cafè, perquè sap que triga entre quatre i sis minuts. El que no sabia fins al mòdul 5 —fins que el tauler mercadofresco-produccion ho va ensenyar en un gràfic— és que durant aquests minuts la latència de la botiga puja, i que el divendres que el va llançar a les 18:30 no va ser casualitat que atenció al client rebés queixes.

La migració a Aurora ha millorat les coses: un punt d'enllaç personalitzat aïlla les consultes de la Sara a aurora-mf-lector-lotes i la botiga ja no les nota. Però els informes continuen trigant el mateix, perquè el problema mai va ser d'aïllament: és que un motor orientat a files llegeix 48 milions de files completes de 40 columnes per fer-ne servir 4, sense comprimir i sense paral·lelitzar. Amazon Redshift és el magatzem de dades d'AWS: un motor columnar, comprimit i massivament paral·lel, dissenyat exactament per a la pregunta que la Sara fa cada dia. Aquí els informes surten definitivament de la base de dades transaccional, es modelen en estrella i passen de minuts a segons.

Avís de cost. Redshift és dels serveis que més ràpid generen factura si es descuiden: un clúster aprovisionat oblidat costa centenars de dòlars al mes. En acabar qualsevol prova, elimina l'espai de noms i el grup de treball.

Avís de compliment. Un magatzem analític concentra en un sol lloc l'historial complet de compra de tots els clients: és, en termes de RGPD, l'actiu més sensible de MercadoFresco. L'anonimització, la base legal del tractament analític i la política de retenció han d'estar documentades i revisades pel responsable de protecció de dades abans de la primera càrrega.

Contingut

  1. OLAP davant d'OLTP: per què un informe mata una base transaccional
  2. Emmagatzematge columnar, compressió i execució massivament paral·lela
  3. Arquitectura: node líder, nodes de còmput i talls
  4. RA3, DC2 i Redshift Serverless
  5. El model en estrella de MercadoFresco
  6. Claus de distribució i d'ordenació
  7. Càrrega de dades amb COPY i la canalització nocturna
  8. Integració sense ETL des d'Aurora
  9. Redshift Spectrum, Athena i AWS Glue
  10. Les consultes de negoci de la Sara
  11. Vistes materialitzades
  12. Concurrència, cues WLM i escalat de concurrència
  13. Seguretat, permisos i anonimització
  14. Costos, pausa i represa
  15. Errors habituals i consells
  16. Exercicis
  17. Conclusió

OLAP davant d'OLTP: per què un informe mata una base transaccional

A 06-01 vam veure la taula que separa tots dos mons. Ara toca veure el mecanisme concret, amb la consulta real de la Sara:

-- L'informe del tiquet mitja per ciutat, tal com esta avui a Aurora.
SELECT c.ciudad,
       COUNT(*)          AS comandes,
       AVG(p.importe)    AS tiquet_mitja,
       SUM(p.importe)    AS facturacio
FROM   pedidos p
JOIN   clientes c ON c.id_cliente = p.id_cliente
WHERE  p.fecha_pedido >= CURRENT_DATE - INTERVAL '18 months'
GROUP  BY c.ciudad
ORDER  BY facturacio DESC;

La taula pedidos té 40 columnes i 48 milions de files, amb una mida mitjana de fila de 380 bytes. La consulta en necessita tres: id_cliente, importe i fecha_pedido, uns 28 bytes.

En un motor orientat a files, les dades es guarden fila completa rere fila completa. Per llegir 28 bytes cal llegir-ne els 380, perquè el disc es llegeix per blocs i a cada bloc hi ha files senceres:

Aurora (files) Redshift (columnes)
Dades llegides 48 M × 380 B = 18,2 GB 48 M × 28 B = 1,34 GB
Compressió típica Cap sobre les dades 3-4× → ≈380 MB
Paral·lelisme 1 procés Tots els talls alhora
Durada mesurada 4-6 min 2-6 s

La reducció no és cap truc: és llegir 48 vegades menys dades i repartir-les entre desenes de processos.

Emmagatzematge columnar, compressió i execució massivament paral·lela

graph TB
    subgraph FILES["Orientat a files · Aurora"]
        F1["Bloc 1: (84213, 4471, 2026-08-02, 34.20, Valencia, ...37 columnes mes)"]
        F2["Bloc 2: (84214, 8802, 2026-08-02, 51.90, Bilbao, ...37 columnes mes)"]
    end
    subgraph COLS["Orientat a columnes · Redshift"]
        C1["Bloc A · id_pedido: 84213, 84214, 84215, 84216, ..."]
        C2["Bloc B · importe: 34.20, 51.90, 12.75, 88.40, ..."]
        C3["Bloc C · fecha: 2026-08-02, 2026-08-02, 2026-08-02, ..."]
    end
    Q["SELECT AVG(importe) ..."] -->|llegeix blocs sencers<br/>i descarta 37 columnes| FILES
    Q -->|llegeix nomes el bloc B| COLS

Guardar per columnes té un segon avantatge que multiplica el primer: tots els valors d'un bloc són del mateix tipus i molt semblants entre si, així que comprimeixen extraordinàriament bé.

Codificació Com funciona Bona per a Exemple a MercadoFresco
AZ64 Compressió pròpia per a tipus numèrics i de data Números, dates importe, fecha_pedido
ZSTD Compressió general d'alta relació Text variable nombre_producto
BYTEDICT Diccionari de fins a 256 valors Baixa cardinalitat estado, franja_reparto
RUNLENGTH Guarda valor i repeticions Valors repetits i ordenats id_ciudad si és clau d'ordenació
RAW Sense comprimir Claus d'ordenació petites La primera columna de la clau

Redshift tria la codificació sola: amb ENCODE AUTO —el comportament per defecte— analitza les dades carregades i aplica el que correspongui. La recomanació pràctica és deixar-ho en automàtic llevat que es tingui una raó mesurada per fer el contrari; ANALYZE COMPRESSION mostra què recomanaria sobre dades ja carregades.

La tercera peça és l'MPP (massively parallel processing): la taula no viu en un sol lloc, està repartida entre tots els nodes i cadascun processa la seva part simultàniament. Amb 4 nodes de 4 talls, la consulta de la Sara es divideix en 16 feines que llegeixen i agreguen 3 milions de files cadascuna alhora, i els resultats parcials es combinen al final. D'aquí la diferència entre minuts i segons.

Això també explica per què Redshift és dolent en el contrari: recuperar una comanda concreta pel seu identificador exigeix coordinar tots els nodes per retornar una fila. Aurora ho fa en 2 ms amb un índex; Redshift triga centenars de mil·lisegons. Redshift no substitueix Aurora: la complementa.

Arquitectura: node líder, nodes de còmput i talls

graph TD
    CLI["Client SQL de la Sara"] --> L["Node lider<br/>analitza, planifica, distribueix i combina"]
    L --> N1["Node de comput 1"]
    L --> N2["Node de comput 2"]
    N1 --> S1["Tall 1"]
    N1 --> S2["Tall 2"]
    N2 --> S3["Tall 3"]
    N2 --> S4["Tall 4"]
    S1 --> RMS["Emmagatzematge gestionat a S3 · RA3"]
    S2 --> RMS
    S3 --> RMS
    S4 --> RMS

El node líder rep la consulta, l'analitza, genera el pla, compila el codi i el reparteix; no guarda dades d'usuari i és l'únic punt de connexió. Els nodes de còmput executen la seva porció i retornen resultats parcials. Els talls (slices) són les divisions de cada node, una per vCPU: la unitat real de paral·lelisme i el motiu que la clau de distribució importi tant, perquè si les dades no es reparteixen bé entre talls, la meitat del maquinari està aturada.

RA3 davant de DC2, i Redshift Serverless

DC2 RA3 Serverless
Emmagatzematge Local al node (SSD) Gestionat a S3, amb memòria cau local Gestionat
Escalar còmput i dades Acoblats Independents Automàtic
Unitat de facturació Node-hora Node-hora RPU-hora
Quan està encès Sempre Sempre (o pausat) Només quan hi ha consultes
Mínim pràctic 2 nodes 2 nodes 8 RPU
Administració Alta Mitjana Cap

DC2 és la generació antiga: ràpida però amb emmagatzematge local, de manera que créixer en dades obliga a afegir nodes que no calen per còmput. RA3 separa totes dues coses amb emmagatzematge gestionat sobre S3 i memòria cau local automàtica. Redshift Serverless elimina el concepte de node: es defineix un grup de treball amb capacitat base en RPU (Redshift Processing Units, mínim 8), es paga per RPU-segon mentre hi ha consultes executant-se, i quan no hi ha activitat no es paga còmput, només l'emmagatzematge.

Per què Serverless és l'opció per a MercadoFresco

El perfil d'ús analític de MercadoFresco, mesurat:

Dada Valor
Informes al dia 3-8, amb pics a final de mes
Durada objectiu per informe 5-30 s
Temps total de còmput al dia ≈4 minuts
Usuaris analítics 2 (la Sara i una becària)
Volum del magatzem 340 GB avui, +12 GB/mes

Quatre minuts de còmput al dia sobre 1.440 possibles: un clúster aprovisionat estaria encès el 99,7 % del temps sense fer res.

RA3 aprovisionat (2 × ra3.xlplus) Serverless (8 RPU base)
Còmput 730 h × 2 × 1,086 = 1.586 USD/mes ~2 h/mes × 8 RPU × 0,36 = ≈6 USD
Emmagatzematge Inclòs fins a 32 TB per node 340 GB × 0,024 = 8 USD
Administració Dimensionar, pausar, vigilar Cap
Total ≈1.586 USD ≈14 USD

Dos ordres de magnitud, sense discussió. Un clúster aprovisionat es justifica quan hi ha consultes pràcticament tot el dia, desenes d'analistes i càrrega predictible: serà el cas de MercadoFresco d'aquí a uns anys, no avui.

# Serverless es compon d'un espai de noms (dades, xifratge, permisos)
# i d'un grup de treball (capacitat, xarxa). Es creen per separat.
aws redshift-serverless create-namespace \
  --namespace-name mercadofresco-analitica \
  --admin-username analitica_admin --manage-admin-password \
  --kms-key-id alias/mercadofresco-datos \
  --default-iam-role-arn arn:aws:iam::111122223333:role/rol-redshift-mercadofresco \
  --iam-roles arn:aws:iam::111122223333:role/rol-redshift-mercadofresco \
  --tags Key=Proyecto,Value=mercadofresco Key=Entorno,Value=produccion \
         Key=Componente,Value=analitica Key=Propietario,Value=sara \
         Key=CentroCoste,Value=negocio \
  --region eu-west-1 --profile mercadofresco-dev

aws redshift-serverless create-workgroup \
  --workgroup-name wg-mercadofresco-analitica \
  --namespace-name mercadofresco-analitica \
  --base-capacity 8 --max-capacity 64 \
  --subnet-ids snet-mercadofresco-datos-a snet-mercadofresco-datos-b \
  --security-group-ids sg-mercadofresco-basedatos \
  --no-publicly-accessible \
  --region eu-west-1 --profile mercadofresco-dev

--max-capacity 64 és el límit de despesa: sense ell, una consulta mal escrita escala i ho cobra. I --no-publicly-accessible amb subxarxes privades és innegociable: el magatzem conté l'historial de compra de tots els clients.

El model en estrella de MercadoFresco

A OLTP es normalitza per evitar duplicació; a OLAP es desnormalitza en estrella: una taula de fets gran amb les mesures numèriques, envoltada de dimensions petites amb els atributs descriptius. Menys unions, i les que queden són contra taules petites.

graph TD
    DP["dim_producto<br/>sk_producto · sku · nombre<br/>categoria · proveedor · alergenos"] --> H
    DC["dim_cliente<br/>sk_cliente · segmento<br/>antiguedad · franja_preferida"] --> H
    DT["dim_tiempo<br/>sk_tiempo · fecha · dia_semana<br/>es_festivo · semana · mes"] --> H
    DU["dim_ciudad<br/>sk_ciudad · ciudad · provincia<br/>comunidad · almacen_asignado"] --> H
    H["hechos_pedidos<br/>sk_tiempo · sk_producto · sk_cliente · sk_ciudad<br/>unidades · importe · descuento · coste_reparto"]
-- Taula de fets: una fila per linia de comanda. Es la taula gran.
CREATE TABLE hechos_pedidos (
    sk_tiempo        INTEGER   NOT NULL,
    sk_producto      INTEGER   NOT NULL,
    sk_cliente       INTEGER   NOT NULL,
    sk_ciudad        SMALLINT  NOT NULL,
    id_pedido        BIGINT    NOT NULL,   -- referencia al sistema origen
    unidades         SMALLINT  NOT NULL,
    importe          DECIMAL(10,2) NOT NULL,
    descuento        DECIMAL(10,2) NOT NULL DEFAULT 0,
    coste_reparto    DECIMAL(10,2) NOT NULL DEFAULT 0
)
DISTSTYLE KEY
DISTKEY (sk_cliente)          -- s'explica a la seccio seguent
SORTKEY (sk_tiempo, sk_ciudad);

-- Dimensio petita: es replica sencera a cada node amb DISTSTYLE ALL.
CREATE TABLE dim_ciudad (
    sk_ciudad        SMALLINT NOT NULL,
    ciudad           VARCHAR(80)  NOT NULL,
    provincia        VARCHAR(80)  NOT NULL,
    comunidad        VARCHAR(80)  NOT NULL,
    almacen_asignado VARCHAR(40)  NOT NULL
)
DISTSTYLE ALL
SORTKEY (sk_ciudad);

Dos detalls que sorprenen venint de PostgreSQL. Redshift no imposa claus primàries ni foranes: es poden declarar, i convé fer-ho perquè el planificador les fa servir per optimitzar, però no les verifica —la integritat la garanteix el procés de càrrega—; i no hi ha índexs, perquè la clau d'ordenació fa aquest paper. A més, les dimensions han de tenir claus substitutes (sk_*, enters petits) i no els identificadors de l'origen: ocupen menys, comprimeixen millor i permeten guardar història quan un atribut canvia —si un client es muda de ciutat, les comandes antigues han de continuar comptant a la ciutat on es van lliurar—.

Claus de distribució

La clau de distribució decideix a quin tall cau cada fila, i és la decisió que més afecta el rendiment.

Estil Com reparteix Quan fer-lo servir Risc
KEY Per hash d'una columna Taula gran que s'uneix sempre per aquesta columna Biaix si la columna està mal repartida
ALL Còpia completa a cada node Dimensions petites (< 2-3 M files) Multiplica l'emmagatzematge i les escriptures
EVEN Per torns, sense criteri Taules que no s'uneixen o sense columna clara Redistribució a cada unió
AUTO Redshift decideix i canvia Punt de partida per defecte Menys control

El que passa quan es tria malament és concret i mesurable. Si hechos_pedidos es distribueix per sk_producto i la consulta uneix per sk_cliente, Redshift ha de redistribuir la taula de fets per la xarxa a cada consulta: és el pas DS_DIST_BOTH del pla d'execució, i pot multiplicar per deu la durada. Pitjor encara és el biaix: si es distribueix per sk_ciudad i el 45 % de les comandes són de Madrid, aquest 45 % cau en un sol tall i un procés treballa mentre quinze esperen. És la clau calenta de 06-02, amb un altre nom.

A MercadoFresco es tria DISTKEY (sk_cliente) perquè hi ha desenes de milers de clients amb repartiment raonablement uniforme i perquè les consultes de cohorts i de tiquet mitjà s'agrupen per client. I les quatre dimensions van amb DISTSTYLE ALL perquè són minúscules: replicar-les costa poc i elimina tota redistribució a les unions. Per diagnosticar biaix es compara el nombre de files per tall a les vistes del sistema: si el màxim i el mínim difereixen molt, la clau està mal triada.

Claus d'ordenació

La clau d'ordenació determina l'ordre físic de les files al disc. Redshift guarda el valor mínim i el màxim de cada bloc d'un megabyte, així que, si la consulta filtra per la clau d'ordenació, el motor descarta blocs sencers sense ni llegir-los. És l'equivalent conceptual a un índex agrupat.

SORTKEY (sk_tiempo, sk_ciudad) a hechos_pedidos respon al fet que totes les consultes de la Sara filtren per rang de dates: amb 18 mesos d'història i una consulta de l'últim trimestre, Redshift descarta el 83 % dels blocs abans de llegir res. L'ordre importa: la primera columna ha de ser la que més es fa servir per filtrar per rang, i posar sk_ciudad al davant malbarataria gairebé tot el benefici perquè el filtre per ciutat és d'igualtat i apareix amb menys freqüència.

Tres avisos. Les càrregues noves arriben sense ordenar i queden en una regió no ordenada que VACUUM SORT ONLY reorganitza; amb clau de data i càrrega incremental per data amb prou feines cal, perquè les dades arriben ja gairebé en ordre. ANALYZE actualitza les estadístiques del planificador, sense les quals el pla pot ser dolentíssim —Serverless executa totes dues coses sol, en segon pla—. I les claus intercalades (INTERLEAVED) donen pes igual a diverses columnes, però només compensen en casos concrets i costen molt manteniment: la recomanació per defecte és la composta.

Càrrega de dades amb COPY

COPY és l'única manera sensata de carregar volum a Redshift: llegeix de S3 en paral·lel des de tots els talls alhora. Un INSERT fila a fila és entre cent i mil vegades més lent.

COPY hechos_pedidos
FROM 's3://mercadofresco-informes-analitica/hechos/pedidos/2026/08/02/'
IAM_ROLE 'arn:aws:iam::111122223333:role/rol-redshift-mercadofresco'
FORMAT AS PARQUET;

Quatre decisions darrere d'aquestes quatre línies. Parquet, no CSV: és columnar i comprimit en origen, així que la càrrega és més ràpida, no cal declarar l'esquema i els tipus vénen definits —no hi ha ambigüitat entre 12,40 i 12.40—. Un prefix, no un fitxer: COPY carrega tots els objectes del prefix en paral·lel, i la regla pràctica és generar un múltiple del nombre de talls en fitxers d'1 a 128 MB comprimits, perquè un sol fitxer gegant deixa aturats tots els talls menys un. IAM_ROLE, mai claus d'accés al SQL: el rol l'assumeix el clúster i l'audita trail-mercadofresco. I manifest quan cal exactitud —un JSON que llista explícitament els objectes amb "mandatory": true—, que és el que garanteix que la càrrega nocturna processa exactament els fitxers que l'exportació va generar, ni un més ni un menys.

{"entries": [
  {"url": "s3://mercadofresco-informes-analitica/hechos/pedidos/2026/08/02/part-000.parquet",
   "mandatory": true},
  {"url": "s3://mercadofresco-informes-analitica/hechos/pedidos/2026/08/02/part-001.parquet",
   "mandatory": true}
]}

Després de cada càrrega convé revisar STL_LOAD_ERRORS, que explica fila a fila què ha fallat. És la primera taula que cal mirar quan un COPY es queixa.

La canalització nocturna

graph LR
    A["Aurora · aurora-mf-lector-lotes<br/>02:00"] --> B["Exportacio incremental<br/>comandes del dia"]
    B --> C["S3 · mercadofresco-informes-analitica<br/>Parquet particionat per data"]
    C --> D["COPY a taules de preparacio"]
    D --> E["Transformar i carregar<br/>dimensions i fets"]
    E --> F["Refrescar vistes materialitzades"]
    F --> G["Metrica d'exit a CloudWatch<br/>i avis a alertas-mercadofresco si falla"]

Quatre regles la fan fiable. Incremental, no completa: s'exporten només les comandes amb fecha_modificacion posterior a l'última marca d'aigua, no els 48 milions cada nit. Idempotent: si el procés es reintenta, el resultat ha de ser el mateix, i això s'aconsegueix esborrant la partició del dia abans de carregar-la dins d'una transacció. Des de la rèplica, mai des de l'escriptor: per això existeix aurora-mf-lector-lotes. I amb alarma, perquè una fallada silenciosa significa que la Sara mira dades velles sense saber-ho, que és pitjor que no tenir informe.

L'orquestració d'aquests passos —amb reintents, dependències i gestió de fallades— és feina de Step Functions i EventBridge, que es veuen a 07-03 i 07-04. Aquí n'hi ha prou de saber que la canalització existeix i quines garanties ha de complir.

Integració sense ETL des d'Aurora

La integració sense ETL (zero-ETL) replica de manera contínua les taules d'Aurora PostgreSQL a Redshift, amb un retard de segons i sense escriure ni una línia de canalització. Es configura una integració entre el clúster origen i l'espai de noms destí, i AWS manté la còpia.

Canalització pròpia Integració sense ETL
Retard Hores (nocturna) Segons
Transformacions Qualsevol Cap: arriben les taules tal qual
Model resultant En estrella, optimitzat Rèplica de l'esquema OLTP
Manteniment L'equip AWS
Cost Còmput del procés E/S de la replicació

No són excloents, i la combinació és el que MercadoFresco acaba fent servir: la integració sense ETL porta les taules normalitzades en temps gairebé real i un procés dins de Redshift les transforma en el model en estrella. Es guanya frescor i s'elimina la part més fràgil de la canalització conservant el model optimitzat.

Redshift Spectrum

Spectrum permet consultar dades que són a S3 sense carregar-les, mitjançant taules externes definides al catàleg de dades d'AWS Glue.

CREATE EXTERNAL SCHEMA historico_s3
FROM DATA CATALOG DATABASE 'mercadofresco_analitica'
IAM_ROLE 'arn:aws:iam::111122223333:role/rol-redshift-mercadofresco';

-- Uneix dades calentes (a Redshift) amb dades fredes (a S3) en una sola consulta.
SELECT h.sk_ciudad, SUM(h.importe) AS actual, SUM(a.importe) AS historic
FROM   hechos_pedidos h
LEFT   JOIN historico_s3.pedidos_archivo a ON a.sk_ciudad = h.sk_ciudad
GROUP  BY h.sk_ciudad;

El cas de MercadoFresco: mantenir a Redshift els últims 24 mesos i deixar a S3, en classes d'accés poc freqüent (02-03), tot l'anterior més les cistelles abandonades que vam exportar a 06-02. Es factura per TB escanejats, així que particionar per data i fer servir Parquet és el que separa una consulta de cèntims d'una de desenes de dòlars.

Redshift davant d'Athena, i AWS Glue com a catàleg

Athena (05-03, on vam consultar trail-mercadofresco) també executa SQL sobre S3. La pregunta honesta és quan n'hi ha prou.

Amazon Athena Amazon Redshift
Model de cost 5 USD per TB escanejat RPU-hora o node-hora
Infraestructura Cap Espai de noms o clúster
Latència típica 5-60 s 1-10 s (dades carregades)
Optimització Només format i particions Distribució, ordenació, vistes materialitzades
Concurrència Bona, amb quotes Molt bona, amb WLM
Unions complexes Acceptables Molt bones
Vistes materialitzades No
Quan Consultes esporàdiques sobre dades a S3 Consultes repetides, quadres de comandament, model dimensional

Athena n'hi ha prou per a exploració ocasional, anàlisi de registres i qualsevol cas de poques consultes al mes. Redshift cal quan les mateixes consultes es repeteixen cada dia, quan hi ha quadres de comandament que es refresquen sols, quan hi ha unions de diverses taules grans o quan l'escaneig repetit a Athena comença a costar més que el magatzem. Per a MercadoFresco, amb 3-8 informes diaris sobre les mateixes taules i un model dimensional per mantenir, Redshift Serverless és l'elecció; Athena continua sent l'eina per als registres de CloudTrail i per a exploracions puntuals sobre S3.

AWS Glue aporta el catàleg de dades: el registre central de quines taules existeixen, on són i quin esquema tenen. Athena, Spectrum i les feines de Glue el comparteixen, així que definir una taula un cop la fa visible des dels tres, i els rastrejadors (crawlers) poden deduir l'esquema recorrent un prefix de S3.

Les consultes de negoci de la Sara

-- 1. Tiquet mitja i facturacio per ciutat, ultims 18 mesos.
-- El filtre per sk_tiempo aprofita la clau d'ordenacio i descarta blocs.
SELECT ci.ciudad,
       COUNT(DISTINCT h.id_pedido)             AS comandes,
       ROUND(SUM(h.importe) / COUNT(DISTINCT h.id_pedido), 2) AS tiquet_mitja,
       ROUND(SUM(h.importe), 2)                AS facturacio
FROM   hechos_pedidos h
JOIN   dim_ciudad ci ON ci.sk_ciudad = h.sk_ciudad
JOIN   dim_tiempo t  ON t.sk_tiempo  = h.sk_tiempo
WHERE  t.fecha >= DATEADD(month, -18, CURRENT_DATE)
GROUP  BY ci.ciudad
ORDER  BY facturacio DESC;
-- 2. Productes que mes s'esgoten els divendres a la franja de pic.
-- Compara les unitats venudes els divendres 17-21h amb la mitjana de la resta de dies.
WITH divendres AS (
    SELECT h.sk_producto, SUM(h.unidades) AS uds_divendres
    FROM   hechos_pedidos h
    JOIN   dim_tiempo t ON t.sk_tiempo = h.sk_tiempo
    WHERE  t.dia_semana = 5
      AND  t.fecha >= DATEADD(month, -6, CURRENT_DATE)
    GROUP  BY h.sk_producto
),
resta AS (
    SELECT h.sk_producto, SUM(h.unidades) / 6.0 AS uds_mitjana_dia
    FROM   hechos_pedidos h
    JOIN   dim_tiempo t ON t.sk_tiempo = h.sk_tiempo
    WHERE  t.dia_semana <> 5
      AND  t.fecha >= DATEADD(month, -6, CURRENT_DATE)
    GROUP  BY h.sk_producto
)
SELECT p.nombre, p.categoria,
       d.uds_divendres,
       ROUND(d.uds_divendres / NULLIF(r.uds_mitjana_dia, 0), 2) AS factor_divendres
FROM   divendres d
JOIN   resta r      ON r.sk_producto = d.sk_producto
JOIN   dim_producto p ON p.sk_producto = d.sk_producto
WHERE  d.uds_divendres > 200
ORDER  BY factor_divendres DESC
LIMIT  25;

És la consulta que permet a la Marta decidir quant producte fresc encarregar a la llotja el dijous al vespre: no el més venut en absolut, sinó el que es dispara específicament el divendres.

-- 3. Cohorts: quin percentatge de clients continua comprant N mesos despres de la
--    seva primera comanda, agrupats pel mes en que es van donar d'alta.
WITH primera_comanda AS (
    SELECT h.sk_cliente, DATE_TRUNC('month', MIN(t.fecha)) AS mes_alta
    FROM   hechos_pedidos h JOIN dim_tiempo t ON t.sk_tiempo = h.sk_tiempo
    GROUP  BY h.sk_cliente
),
activitat AS (
    SELECT p.mes_alta,
           DATEDIFF(month, p.mes_alta, DATE_TRUNC('month', t.fecha)) AS mes_relatiu,
           COUNT(DISTINCT h.sk_cliente) AS clients_actius
    FROM   hechos_pedidos h
    JOIN   dim_tiempo t    ON t.sk_tiempo  = h.sk_tiempo
    JOIN   primera_comanda p ON p.sk_cliente = h.sk_cliente
    GROUP  BY 1, 2
)
SELECT mes_alta, mes_relatiu, clients_actius,
       ROUND(100.0 * clients_actius /
             FIRST_VALUE(clients_actius)
               OVER (PARTITION BY mes_alta ORDER BY mes_relatiu), 1) AS retencio_pct
FROM   activitat
WHERE  mes_relatiu BETWEEN 0 AND 12
ORDER  BY mes_alta, mes_relatiu;

Les tres trigaven minuts a Aurora i triguen segons a Redshift. I les tres són impossibles a DynamoDB: aquest és el criteri de «consultes exploratòries» de 06-01 fet SQL.

Vistes materialitzades

Una vista materialitzada guarda el resultat precalculat d'una consulta. Redshift pot refrescar-la de manera incremental, processant només les dades noves, i reescriu automàticament les consultes perquè facin servir la vista encara que l'usuari no la mencioni.

CREATE MATERIALIZED VIEW mv_ventas_diarias_ciudad
AUTO REFRESH YES
AS
SELECT t.fecha, h.sk_ciudad,
       COUNT(DISTINCT h.id_pedido) AS comandes,
       SUM(h.importe)              AS facturacio,
       SUM(h.unidades)             AS unitats
FROM   hechos_pedidos h
JOIN   dim_tiempo t ON t.sk_tiempo = h.sk_tiempo
GROUP  BY t.fecha, h.sk_ciudad;

El quadre de comandament de negoci passa d'agregar 48 milions de files a llegir-ne uns quants milers. AUTO REFRESH YES deixa que Redshift decideixi quan refrescar segons l'activitat; amb canalització nocturna també val un REFRESH MATERIALIZED VIEW explícit al final de la càrrega, que dona control exacte sobre quan canvien els números que veu la Sara.

Concurrència, cues WLM i escalat de concurrència

La gestió de càrregues de treball (WLM) organitza les consultes en cues amb memòria i prioritat pròpies, perquè un informe pesat no en bloquegi els altres. El WLM manual exigeix configurar cues, memòria i concurrència a mà i no s'adapta; el WLM automàtic només demana prioritats i s'ajusta sol, i és l'opció per defecte llevat de casos molt específics.

Amb WLM automàtic es defineixen prioritats per grup d'usuaris: els quadres de comandament a HIGHEST perquè són ràpids i molts ulls els miren, les consultes exploratòries de la Sara a NORMAL, i la càrrega nocturna a LOW perquè a ningú li importa que trigui deu minuts més de matinada.

Dos mecanismes complementaris: l'escalat de concurrència, que afegeix capacitat temporal quan s'acumula cua —a Serverless està integrat a l'escalat per RPU—, i les regles de monitoratge de consultes (QMR), que avorten automàticament el que es descontrola (per exemple, qualsevol consulta que superi 15 minuts o escanegi més de 500 GB). És la manera més eficaç que un SELECT * accidental no es mengi el pressupost.

Seguretat, permisos i anonimització

Xarxa. El grup de treball viu a les subxarxes privades snet-mercadofresco-datos-a i -b, amb sg-mercadofresco-basedatos i sense accés públic; la Sara s'hi connecta per l'editor de consultes v2 de la consola, que no exigeix obrir res. Xifratge en repòs amb alias/mercadofresco-datos i en trànsit amb TLS obligatori. I permisos de només lectura per al grup mercadofresco-analitica:

CREATE GROUP mercadofresco_analitica;
CREATE USER sara PASSWORD DISABLE IN GROUP mercadofresco_analitica;  -- federada

GRANT USAGE  ON SCHEMA analitica TO GROUP mercadofresco_analitica;
GRANT SELECT ON ALL TABLES IN SCHEMA analitica TO GROUP mercadofresco_analitica;
ALTER DEFAULT PRIVILEGES IN SCHEMA analitica
  GRANT SELECT ON TABLES TO GROUP mercadofresco_analitica;

-- Vista que exposa el necessari sense dades identificatives.
CREATE VIEW analitica.v_clientes_segmento AS
SELECT sk_cliente, segmento, antiguedad_meses, franja_preferida, sk_ciudad
FROM   analitica.dim_cliente;
REVOKE SELECT ON analitica.dim_cliente     FROM GROUP mercadofresco_analitica;
GRANT  SELECT ON analitica.v_clientes_segmento TO GROUP mercadofresco_analitica;

ALTER DEFAULT PRIVILEGES és la línia que la gent oblida: sense ella, les taules creades demà no seran llegibles i algú acabarà concedint permisos de més per sortir del pas.

Anonimització i RGPD. El magatzem no ha de contenir nom, adreça postal, telèfon, correu ni document d'identitat. Cap pregunta de negoci els necessita: el tiquet mitjà per ciutat necessita la ciutat, no el carrer. La canalització substitueix l'identificador de client per una clau substituta sense correspondència reversible fora d'un magatzem controlat, agrega l'adreça a nivell de ciutat o codi postal, i descarta la resta. A més, l'anàlisi de dades és una finalitat diferent de la gestió de la comanda i necessita la seva pròpia base legal, la seva menció a la política de privacitat i una política de retenció documentada. Redshift ofereix control d'accés per files i emmascarament dinàmic de columnes per a casos en què alguna dada sensible hagi de romandre. Tot aquest disseny l'ha de revisar el responsable de protecció de dades abans de la primera càrrega: és molt més barat que redissenyar el magatzem després.

Costos, pausa i represa

Concepte Preu orientatiu eu-west-1 MercadoFresco
Serverless, RPU-hora ~0,36 USD ~2 h/mes d'activitat = 6 USD
Emmagatzematge gestionat 0,024 USD/GB-mes 340 GB = 8 USD
Spectrum 5 USD/TB escanejat Ocasional, < 2 USD
Instantànies més enllà de la mida Preu de S3 Menyspreable
Total ≈16 USD/mes

Amb Serverless, la «pausa» és automàtica: sense consultes no hi ha RPU i no hi ha càrrec de còmput. Tot i així convé posar un límit d'ús que avisi o talli en superar un llindar mensual:

aws redshift-serverless create-usage-limit \
  --resource-arn arn:aws:redshift-serverless:eu-west-1:111122223333:workgroup/wg-mercadofresco-analitica \
  --usage-type serverless-compute --amount 200 --period monthly \
  --breach-action deactivate \
  --region eu-west-1 --profile mercadofresco-dev

En clústers aprovisionats, pause-cluster i resume-cluster aturen el cobrament de còmput mantenint les dades, i es poden programar: és la palanca que converteix un entorn de desenvolupament de 1.500 USD/mes en un de 300.

Neteja. En acabar qualsevol prova: elimina el grup de treball i l'espai de noms —amb instantània final si les dades importen—, esborra les taules externes de Glue i revisa els objectes que hagis deixat a mercadofresco-informes-analitica. Comprova amb Cost Explorer que l'etiqueta Componente=analitica torna a zero el mes següent.

Errors Habituals i Consells

Fer servir Redshift com a base de dades transaccional. És l'error conceptual greu. Redshift no té índexs per a cerques puntuals, i els INSERT fila a fila són lentíssims. Carregar per lots amb COPY, consultar en agregat; el transaccional es queda a Aurora.

Carregar amb INSERT. Entre cent i mil vegades més lent que COPY, i a més fragmenta la taula. Si les dades arriben d'una en una, s'acumulen a S3 i es carreguen per lots.

Triar la clau de distribució per intuïció. Distribuir per una columna amb pocs valors diferents produeix biaix i malbarata el paral·lelisme. Distribuir per una columna diferent de la que es fa servir a les unions produeix redistribució a cada consulta. Davant del dubte, AUTO, i revisar després amb EXPLAIN buscant DS_DIST_BOTH.

Posar a la clau d'ordenació una columna per la qual mai es filtra. No aporta res. La primera columna ha de ser aquella per la qual es filtra per rang, que gairebé sempre és la data.

Oblidar ANALYZE i VACUUM en clústers aprovisionats. Sense estadístiques el planificador pren decisions dolentes i sense VACUUM la taula es degrada. Serverless els executa sol, però convé saber-ho.

Carregar dades personals sense anonimitzar «per no perdre detall». S'acaba amb l'actiu més sensible de l'empresa replicat en un sistema al qual accedeix més gent, sense base legal clara. L'anonimització es dissenya abans de la primera càrrega.

Un sol fitxer enorme al COPY. Deixa aturats tots els talls menys un. Divideix en un múltiple del nombre de talls, en trossos d'1 a 128 MB comprimits.

Deixar un clúster aprovisionat encès per «provar». És el descuit més car d'aquest mòdul: 1.500 USD al mes per un clúster que ningú fa servir. Serverless o pausa programada.

Consell: mesura l'abans i el després. Guarda la durada dels tres informes de la Sara a Aurora i compara-la amb Redshift. És el número que justifica el projecte davant de direcció, i el tens gratis a SVL_QUERY_METRICS i als taulers del mòdul 5.

Consell: comença petit i amb Spectrum. Abans de dissenyar el model en estrella complet, exporta a Parquet i consulta amb Spectrum o Athena. Si amb això n'hi ha prou, t'has estalviat un magatzem. La complexitat s'afegeix quan es demostra que cal.

Exercicis

Exercici 1: triar claus de distribució i d'ordenació

MercadoFresco afegeix la taula de fets hechos_reparto, amb una fila per lliurament: 12 milions de files, columnes sk_tiempo, sk_ciudad, sk_repartidor (40 valors diferents), sk_pedido, minutos_entrega, km_recorridos i incidencia (booleà, cert en el 3 % dels casos). Les consultes habituals són: retard mitjà per ciutat i mes; incidències per repartidor de l'última setmana; i un creuament amb hechos_pedidos per sk_pedido per relacionar retard amb import.

Decideix DISTSTYLE, DISTKEY i SORTKEY, justifica cada elecció, i indica què passaria si es distribuís per sk_repartidor i quin senyal del pla d'execució ho delataria.

Exercici 2: Athena o Redshift

Una empresa germana de MercadoFresco, dedicada a la venda de material d'hostaleria, té 40 GB d'històric de comandes a S3 en format CSV. El seu analista llança entre tres i cinc consultes al mes, sempre exploratòries i diferents entre si, i no hi ha quadres de comandament. Està valorant muntar Redshift Serverless «perquè és el que fan els grans».

Respon: (a) què recomanes i per què, amb números; (b) quins dos canvis a les dades farien que la seva opció fos molt més barata i ràpida sense canviar de servei; (c) quins tres senyals concrets indicarien, d'aquí a un any, que ha arribat el moment de passar a Redshift.

Exercici 3: dissenyar la canalització amb garanties

Escriu el disseny de la canalització nocturna que porta les comandes del dia des de aurora-mercadofresco-pedidos fins a hechos_pedidos. Ha de cobrir: de quin punt d'enllaç es llegeix i per què; com se seleccionen només els registres nous o modificats; format, particionat i mida dels fitxers a S3; com es garanteix la idempotència si el procés es reintenta; com es carreguen les dimensions quan un atribut canvia (per exemple, un client que es muda de ciutat); què es fa amb les dades personals; i com es detecta i notifica una fallada. Indica també quina part d'aquest disseny desapareixeria si es fes servir integració sense ETL.

Solucions

Solució 1

DISTSTYLE KEY amb DISTKEY (sk_pedido) i SORTKEY (sk_tiempo, sk_ciudad).

Distribució: la consulta més cara de les tres és el creuament amb hechos_pedidos, que és la taula gran. Si totes dues taules es distribueixen per la mateixa columna —sk_pedido—, les files que cal unir són al mateix tall i la unió es resol localment, sense moure res per la xarxa. És el patró de coubicació, i és la raó principal per triar KEY. sk_pedido té a més cardinalitat alta i repartiment uniforme, així que no hi ha biaix. Nota: hechos_pedidos està distribuïda per sk_cliente, així que per aprofitar plenament la coubicació caldria valorar unificar el criteri o acceptar la redistribució de la taula menor, que amb 12 milions de files és assumible; la decisió es pren mesurant amb EXPLAIN.

Ordenació: les dues primeres consultes filtren per rang temporal («per mes», «última setmana»), així que sk_tiempo ha d'anar primer per descartar blocs. sk_ciudad en segona posició ajuda el primer informe.

Si es distribuís per sk_repartidor: amb 40 valors diferents i un clúster de 16 talls, com a molt 16 talls rebrien dades i de manera molt desigual —els repartidors de Madrid tenen moltes més entregues—. El resultat és biaix greu: uns pocs talls amb milions de files i la resta gairebé buits, amb el paral·lelisme malbaratat. El senyal al pla d'execució seria DS_DIST_BOTH o DS_BCAST_INNER a la unió amb hechos_pedidos, i a les vistes del sistema es veuria una diferència enorme entre el tall amb més files i el que en té menys. La regla general: mai distribuir per una columna de baixa cardinalitat.

Solució 2

(a) Athena, sens dubte. Amb 40 GB i cinc consultes al mes, encara que cadascuna escanegés l'històric complet serien 0,04 TB × 5 × 5 USD = 1 USD al mes. Redshift Serverless costaria l'emmagatzematge més el còmput de cada sessió, més la feina de dissenyar i mantenir un model dimensional que ningú explotarà. I la característica que decideix: les consultes són sempre diferents i exploratòries, així que no hi ha res per precalcular ni cap vista materialitzada per amortitzar. Tot el valor de Redshift —model optimitzat, vistes materialitzades, concurrència— es recolza en la repetició, i aquí no n'hi ha.

(b) Dos canvis: convertir a Parquet i particionar per data. Parquet amb compressió redueix 40 GB a uns 6-8 GB i, en ser columnar, una consulta que faci servir 4 columnes de 30 n'escaneja una fracció. El particionat per any i mes al prefix de S3 permet que Athena descarti particions senceres quan la consulta filtra per data. Combinats, és habitual baixar de desenes de gigabytes escanejats a centenars de megabytes: la factura passa a cèntims i la consulta, d'un minut a uns segons. Tots dos canvis són una feina de Glue d'una tarda i no canvien de servei.

(c) Tres senyals per migrar a Redshift. Primer, la repetició: apareixen quadres de comandament o informes recurrents diaris, i llavors les vistes materialitzades i el model dimensional comencen a amortitzar-se. Segon, la concurrència: passen d'un analista a un equip amb consultes simultànies i comencen a xocar amb les quotes d'Athena. Tercer, el cost creuat: la factura mensual d'Athena s'acosta al cost d'un grup de treball Serverless —15-20 USD al mes amb aquest perfil—, moment en què Redshift dona més pel mateix diner. Un quart indici qualitatiu: quan les consultes comencen a unir tres o més taules grans, Athena es degrada molt abans que Redshift.

Solució 3

Origen i punt d'enllaç. Es llegeix del punt d'enllaç personalitzat que apunta a aurora-mf-lector-lotes, la rèplica aïllada de 06-03. Mai de l'escriptor —l'exportació competeix amb les comandes— i mai del punt d'enllaç de lectura general, perquè ompliria la memòria cau de la rèplica que serveix l'historial als clients amb pàgines de 18 mesos d'antiguitat.

Selecció incremental. Una marca d'aigua a Parameter Store (/mercadofresco/produccion/analitica/marca-agua) amb l'última fecha_modificacion processada. La consulta selecciona WHERE fecha_modificacion > :marca AND fecha_modificacion <= :corte, amb :corte fixat a l'inici del procés perquè les escriptures ocorregudes durant l'exportació quedin per a la nit següent i no es perdin ni es dupliquin. Requereix fecha_modificacion indexada i mantinguda per disparador.

Format a S3. Parquet amb compressió Snappy, a s3://mercadofresco-informes-analitica/hechos/pedidos/anio=2026/mes=08/dia=02/, amb fitxers de 64-128 MB en nombre múltiple del paral·lelisme. El particionat per data al prefix és el que fa barates les consultes de Spectrum i permet reprocessar un dia concret.

Idempotència. Dins d'una transacció: DELETE FROM hechos_pedidos WHERE sk_tiempo = :dia; i tot seguit el COPY de la partició, amb manifest que enumera explícitament els fitxers i "mandatory": true. Si el procés es reintenta, el resultat és idèntic. El manifest a més evita carregar fitxers parcials d'una execució avortada.

Dimensions que canvien. S'aplica dimensió de variació lenta de tipus 2: quan un client es muda de ciutat, no s'actualitza la seva fila, es tanca la vigent (fecha_fin, es_actual = false) i se n'insereix una de nova amb clau substituta diferent. Així les comandes antigues continuen apuntant a la fila que era vigent quan es van lliurar i l'històric per ciutat no es reescriu sol. Si se sobreescrivís (tipus 1), la facturació de València de l'any passat canviaria perquè un client es va mudar a Bilbao, que és exactament el tipus d'error que destrueix la confiança en un magatzem de dades.

Dades personals. Nom, adreça, telèfon i correu no surten d'Aurora. L'exportació projecta només les columnes necessàries, substitueix id_cliente per la clau substituta sk_cliente i agrega l'adreça a ciutat i codi postal. La correspondència entre sk_cliente i id_cliente viu a Aurora, amb accés restringit, no al magatzem.

Detecció de fallades. Cada execució publica una mètrica personalitzada MercadoFresco/Analitica/CargaCorrecta (1 o 0) i FilasCargadas a l'espai MercadoFresco/Tienda. Dues alarmes cap a alertas-mercadofresco: una si no hi ha cap càrrega correcta abans de les 06:00 —el cas perillós és el silenci, no l'error— i una altra si FilasCargadas es desvia més d'un 50 % de la mitjana dels últims 7 dies, que detecta una exportació truncada. Els errors de càrrega es consulten a STL_LOAD_ERRORS.

Què desapareix amb la integració sense ETL. Tota l'extracció i l'aterratge: consulta incremental, marca d'aigua, Parquet, particionat, manifest i COPY; AWS manté les taules replicades en segons. El que no desapareix és la transformació al model en estrella, la lògica de dimensions de tipus 2, l'anonimització —crític: la replicació porta les taules tal qual, incloses les dades personals, així que la projecció i l'emmascarament s'han de fer ja dins de Redshift amb vistes i permisos— i la vigilància de la frescor. Se simplifica la meitat fràgil, no tot.

Conclusió

Els informes de la Sara han sortit de la base de dades transaccional. Entens per què n'havien de sortir: una consulta que agrega 18 mesos llegeix 18,2 GB en un motor orientat a files per fer servir 1,34 GB de dades útils, sense comprimir i amb un sol procés, mentre que l'emmagatzematge columnar llegeix només les columnes necessàries, les comprimeix de 3 a 4 vegades perquè els valors contigus s'assemblen, i les processa en paral·lel a tots els talls alhora. De 4-6 minuts a 2-6 segons, i amb la simetria que convé recordar: Redshift és igual de dolent retornant una comanda concreta pel seu identificador. No substitueix Aurora, la complementa.

Coneixes l'arquitectura —node líder que planifica i combina, nodes de còmput, talls com a unitat real de paral·lelisme—, la diferència entre DC2, RA3 i Serverless, i l'aritmètica que decideix per a MercadoFresco: quatre minuts de còmput al dia signifiquen que un clúster aprovisionat estaria encès el 99,7 % del temps sense fer res, 1.586 USD davant de 14 USD al mes. Amb mercadofresco-analitica com a espai de noms, wg-mercadofresco-analitica com a grup de treball en subxarxes privades, i --max-capacity com a límit de despesa real.

Saps dissenyar l'esquema analític: el model en estrella amb hechos_pedidos i les dimensions dim_producto, dim_cliente, dim_tiempo i dim_ciudad, amb claus substitutes i sense fiar-te d'unes claus foranes que Redshift declara però no verifica. I sobretot saps triar les dues claus que decideixen el rendiment: la de distribució, amb KEY per coubicar les taules grans que s'uneixen, ALL per a dimensions petites i l'avís que una columna de baixa cardinalitat produeix biaix i deixa la meitat del maquinari aturada; i la d'ordenació, amb la data primer, que descarta blocs sencers gràcies als mínims i màxims per bloc.

Manejes la càrrega amb COPY des de mercadofresco-informes-analitica —Parquet, un prefix amb molts fitxers d'1 a 128 MB, IAM_ROLE en comptes de claus, manifest amb mandatory quan l'exactitud importa— i la canalització nocturna incremental, idempotent, des de la rèplica i amb alarma, perquè la fallada perillosa és la silenciosa. Coneixes la integració sense ETL des d'Aurora i quan combinar-la amb transformació pròpia; Spectrum per consultar S3 sense carregar; i la comparació honesta amb Athena, que n'hi ha prou quan les consultes són esporàdiques i diferents, amb Glue com a catàleg compartit. Més les vistes materialitzades, el WLM automàtic amb prioritats per grup i les regles que avorten la consulta descontrolada.

I saps que aquest és l'actiu més sensible de l'empresa: subxarxes privades, xifratge amb alias/mercadofresco-datos, només lectura per al grup mercadofresco-analitica amb ALTER DEFAULT PRIVILEGES perquè les taules de demà també quedin cobertes, i anonimització dissenyada abans de la primera càrrega —cap pregunta de negoci necessita el carrer del client— amb revisió del responsable de protecció de dades. Per uns 16 USD al mes, amb límit d'ús configurat i la instrucció de netejar el que es creï per practicar.

Queda l'última càrrega de les quatre que vam diagnosticar a 06-01, i és la més absurda de totes. El catàleg es consulta 4.100 vegades per minut en hora punta per retornar, el 94 % de les vegades, exactament el mateix que va retornar la vegada anterior: preus i descripcions que canvien un cop al dia, a les 06:00, quan arriba la càrrega de la llotja. Aurora resol cadascuna d'aquestes consultes correctament en uns mil·lisegons, però fer-ho 4.100 vegades per minut per no aportar cap informació nova és feina malgastada que es paga en latència, en capacitat i en factura. A 06-05, «Amazon ElastiCache», el catàleg se servirà des de memòria en microsegons: veurem Redis davant de Memcached, els patrons de memòria cau amb el seu codi, les estructures de dades aplicades al rànquing del divendres i a la cistella ràpida, les sessions que per fi faran la botiga veritablement sense estat, i el tancament del mòdul amb la capa de dades completa de MercadoFresco.

Curs d'AWS

Mòdul 1: Introducció a AWS

Mòdul 2: Serveis principals d'AWS

Mòdul 3: Xarxes i lliurament de contingut

Mòdul 4: Seguretat i identitat

Mòdul 5: Monitoratge i gestió

Mòdul 6: Bases de dades

Mòdul 7: Integració d'aplicacions

Mòdul 8: Eines per a desenvolupadors

Mòdul 9: Infraestructura com a codi i govern de comptes

Mòdul 10: Contenidors a AWS

Mòdul 11: Millors pràctiques i gestió de costos

© Copyright 2026. Tots els drets reservats