Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Conceptes de persistència

Localització de les dades

Una aplicació conté codi i dades. El codi interactua amb les dades que hi ha a la memòria principal, que és volàtil, el tipus de memòria més ràpida que existeix i la més senzilla de llegir i escriure. A POO, les dades són als objectes.

Un problema de la memòria principal és que perdem el seu contingut si l’aplicació finalitza, ja sigui de forma correcta o per un problema. El context necessari per a l’execució d’una aplicació és el seu estat, i cal recuperar-lo cada cop que les executem. Això es fa utilitzant memòria no volàtil basada en un o diversos arxius físics.

Quan parlem de recuperar el context d’una aplicació per a la seva execució, no sempre és possible fer-ho amb totes les dades que es gestionen. Pot ser perquè la mida sigui superior a la de la memòria principal, però també perquè no calgui i sigui millor ser mandrós en la seva càrrega per raó de rendiment.

Model de dades

El disseny del model de dades ha de tenir en compte el millor rendiment possible quan s’utilitza memòria no volàtil, molt més lenta, i facilitar la futura escalabilitat de les nostres dades.

Un mecanisme per a minimitzar temps és la memòria cache: mantenim un nombre limitat d’objectes a la memòria principal per evitar haver-los de carregar des de la memòria no volàtil cada cop, i els invalidem si es modifiquen.

També els SGBD poden proporcionar-nos mecanismes per a processar les dades sense necessitat de carregar-les en memòria, gràcies a llenguatges declaratius com SQL: demanem quines dades necessitem, no com processar-les.

Si parlem de disseny, és convenient crear una capa de persistència que encapsuli el procés. Això ens permetrà substituir-la sense afectar la resta de l’aplicació. Si parlem d’usabilitat, la persistència hauria de ser transparent per a l’usuari.

Què, quan i com persistir

Tenim dos mètodes de persistència: d’estat, el més habitual, i basat en esdeveniments.

Persistència d’estat

Parlant de POO, ens podríem fer la pregunta de si allò que volem persistir és un objecte o una estructura de dades. Els objectes tenen comportament i encapsulen les dades. Les estructures de dades exposen les dades i no tenen comportament. Aquesta distinció ens ajudarà a decidir la nostra estratègia de persistència.

Per definir la persistència, cal repassar els camps de l’objecte i decidir quins necessitarem per tal de tornar a instanciar l’objecte en memòria principal. Alguns camps innecessaris poden ser calculats o tenir una funció temporal. Podem ajudar-nos de l’estudi dels constructors i setters de l’objecte.

A més, ens caldrà fer referències entre els objectes. Quan dissenyem mecanismes ad-hoc, podem utilitzar un identificador d’objecte en el moment de la persistència. En el cas dels SGBD, els objectes es relacionen amb claus foranes.

Tenim diversos àmbits de persistència:

  • Context general de l’aplicació: configuracions generals, per usuari, etc.
  • Documents, basats en serialització. Persistim en base a un document que pot intercanviar-se.
  • Registres, habitualment relacionats i associats a SGBD. No se sol restaurar completament al començament, només quan cal.

Persistència d’esdeveniments

La persistència per esdeveniments (Event Sourcing) és un mecanisme que permet restaurar l’estat d’una aplicació a partir de tots els esdeveniments que s’han produït. Es tracta d’una mena de log on es guarden els esdeveniments per ordre d’ocurrència, i que permet reconstruir l’estat en qualsevol moment del temps.

Quan l’aplicació s’inicia, reconstrueix l’estat actual aplicant tots els esdeveniments en l’ordre en què es van produir. Com que podria haver molts d’esdeveniments, aquest mecanisme se sol combinar amb altres, com per exemple emmagatzemar un estat intermedi (snapshot) i aplicar els esdeveniments a partir d’aquell instant.

Quan i com

Hem de persistir sempre que hi hagi un canvi en alguna dada? La resposta general és que sí. Així evitem perdre dades si el nostre programa falla abans d’haver persistit. Però de vegades no seria òptim, parlant de rendiment, haver de persistir tot l’estat d’una aplicació. Alternativament, es podria endarrerir el moment, o fer-ho periòdicament de forma automàtica. També podem deixar aquesta responsabilitat en mans de l’usuari (File > Save).

La persistència per esdeveniments permet només guardar l’esdeveniment que es produeix en lloc de totes les dades, i també pot ser una solució per millorar el rendiment.

Quin tipus de persistència?

El format i organització dependrà de les nostres necessitats.

Criteris per decidir la forma de persistència:

  • És una aplicació monousuari o multiusuari?
  • Es comparteix informació a la xarxa amb altres clients o aplicacions?
  • Hi ha un volum molt alt de dades?
  • Hi ha un esquema estable (ben estructurat)?
  • Quins requisits qualitatius tenim: disponibilitat, escalabilitat, latència, rendiment, consistència…?

Si la resposta està a prop d’una aplicació monousuari, sense connexió amb altres clients, i poques dades, segurament podem gestionar-ho mitjançant persistència en fitxer. Caldria, en tot cas, decidir quin és el format d’aquest fitxer, ja que podem utilitzar solucions existents sense necessitar inventar-nos un format a mida. En POO, utilitzem el concepte de serialització d’objectes: generem una representació del graf d’objectes que permet restaurar-los quan l’aplicació torna a executar-se. El format podria ser a mida, utilitzant JSON, un arxiu de preferències clau-valor, etc.

Si la resposta s’assembla a aplicació multiusuari amb informació compartida per la xarxa i grans volums de dades, estem a prop de necessitar un sistema de gestió de base de dades (SGBD). Des d’una aplicació orientada a objectes, una opció habitual per accedir a un SGBD relacional és el mapatge d’objectes relacional (ORM), tot i que no és l’única (també podem utilitzar SQL directament o query builders). Un ORM interposa una capa entre la lògica i la persistència, de tal forma que podem persistir utilitzant el paradigma d’orientació a objecte en lloc del llenguatge SQL. És un procés complex i no exempte de problemes. A Java es pot implementar a mida (JDBC) o bé utilitzar llibreries de mapatge (JPA).

Els SGBD resolen problemes habituals que ens trobem en el desenvolupament d’aplicacions. Entre ells:

  • Definició, creació, manteniment i control d’accés a una base de dades.
  • Gestió de transaccions i concurrència (segons el model).
  • Facilitats per recuperar dades en cas de danys.
  • Gestió d’autoritzacions i accés remot.
  • Regles de comportament de les dades en funció de la seva estructura.

Base de dades d’aplicació o d’integració

Una base de dades d’integració és una base de dades que actua com a magatzem de dades de diverses aplicacions i, per tant, integra dades d’aquestes aplicacions.

Una base de dades d’aplicació es controla i accedeix des d’una sola aplicació. Per a compartir dades amb altres aplicacions, l’aplicació que controla la base de dades hauria de proporcionar serveis.

La recomanació general és la d’evitar bases de dades d’integració. En general, les bases de dades d’integració comporten problemes greus perquè la base de dades esdevé un punt d’acoblament entre les aplicacions que hi accedeixen. Generalment es tracta d’un acoblament profund que augmenta significativament el risc que suposa canviar aquestes aplicacions i dificulta la seva evolució.

Model relacional vs NoSQL

Les bases de dades actuals responen majoritàriament al model relacional, que ha triomfat gràcies a l’establiment de l’SQL com a estàndard. En paral·lel han guanyat pes les bases de dades NoSQL (key-value, document, column-family i graf), afavorides pel Big Data i les aplicacions en temps real. Sovint són sistemes sense esquema, i poden conviure amb solucions relacionals en models de persistència poliglota.

Dins el model relacional també tenim extensions sense esquema (camps JSON, taules d’atributs) que permeten encabir dades no estructurades dins d’un esquema, tot i que aquestes no són tan accessibles des de consultes SQL.

A l’hora de triar entre un model i l’altre, els criteris principals són:

  • Esquema: les dades tenen una estructura estable i coneguda, o cal flexibilitat per fer-la evolucionar ràpidament?
  • Relacions: hi ha relacions entre entitats que haurem de navegar sovint? Les relacionals les modelen de forma natural; als NoSQL solen resoldre’s a l’aplicació o amb models de graf.
  • Integritat: qui garanteix que les dades siguin coherents, el SGBD o l’aplicació? (veure Integritat de les dades).
  • Escalabilitat: el volum de dades i de peticions requereix escalat horitzontal en clúster? Les NoSQL hi estan més preparades.
  • Consistència vs disponibilitat: el sistema pot tolerar lectures lleugerament desactualitzades a canvi de més disponibilitat? Les relacionals tendeixen a ACID, les NoSQL a BASE, tot i que la frontera s’ha difuminat (molts NoSQL ofereixen ACID).
  • Rendiment: les relacionals poden patir amb escriptures massives o joins grans degut a les garanties ACID, però la normalització en si sovint millora el rendiment de lectura.

Les dimensions tècniques es detallen a Model Relacional i Model NoSQL.

Integritat de les dades

Una de les diferències pràctiques més importants entre el model relacional i el NoSQL és qui garanteix que les dades siguin correctes.

Les BBDD relacionals ofereixen mecanismes declaratius per a expressar regles d’integritat directament a l’esquema:

  • Claus primàries i restriccions UNIQUE per evitar duplicats.
  • Claus externes amb accions referencials (CASCADE, SET NULL, RESTRICT) per mantenir la coherència entre taules.
  • NOT NULL per garantir que certs camps sempre tinguin valor.
  • CHECK per expressar regles de domini (age >= 0, status IN (...)).

El SGBD rebutja qualsevol escriptura que violi aquestes regles, independentment de qui sigui l’autor: l’aplicació principal, un script de manteniment, una migració o un nou servei afegit més tard.

A la majoria de BBDD NoSQL aquestes garanties no existeixen a nivell d’emmagatzematge. La responsabilitat d’assegurar la integritat recau a l’aplicació, i això té conseqüències:

  • És una propietat de correctesa, no de rendiment: si només l’aplicació valida les dades, qualsevol bug, script ad-hoc o servei nou pot introduir dades incoherents silenciosament.
  • Els problemes s’acumulen: les violacions d’integritat en un NoSQL solen passar desapercebudes fins que una query falla o un informe mostra dades impossibles. Llavors els danys estan distribuïts en mesos o anys d’escriptures.
  • És difícil retrofitar-ho: afegir restriccions a un SGBD relacional existent és laboriós però ben definit. Afegir-les a un NoSQL implica auditar tots els escriptors i netejar les dades històriques.
  • Es perd documentació: una restricció SQL és autodocumentada, qualsevol que miri l’esquema la veu. Les regles implementades a codi estan disperses i són invisibles per a qui no llegeix l’aplicació.
  • Sovint es reconstrueix en codi: molts equips acaben adoptant validadors (JSON Schema, Mongoose, Pydantic) que recreen una capa d’esquema sobre el NoSQL, el que en part desfà l’argument de la flexibilitat.

Això no vol dir que el NoSQL sigui una mala opció, però sí que la flexibilitat d’esquema té un cost que cal assumir conscientment: la responsabilitat de la integritat es desplaça del SGBD a l’aplicació.

Característiques ACID

En el context de bases de dades, ACID (acrònim anglès de Atomicity, Consistency, Isolation, Durability) són tot un seguit de propietats que ha de complir tot sistema de gestió de bases de dades per tal de garantir que les transaccions (unitats d’execució que agrupen una o més operacions sobre les dades) siguin fiables.

Concretament, l’acrònim ACID significa:

  • Atomicitat: Una transacció o bé finalitza correctament i confirma o bé no deixa cap rastre de la seva execució.
  • Consistència: Una transacció porta la base de dades d’un estat vàlid a un altre, respectant totes les restriccions definides (claus, checks, integritat referencial).
  • llament (o Isolament): Cada transacció del sistema s’ha d’executar com si fos l’única que s’executa en aquell moment en el sistema. Aquesta és la propietat que regula el comportament davant de la concurrència.
  • Durabilitat: Si es confirma una transacció, el resultat d’aquesta ha de ser definitiu i no es pot perdre.

Característiques BASE

Les característiques BASE estan associades a les BBDD NoSQL. Es basen en el teorema CAP (Consistency-Availability-Partition Tolerance), que afirma que en presència d’una partició de xarxa un sistema distribuït només pot garantir dues d’aquestes tres propietats. Com que les particions són inevitables a la pràctica, la decisió real és entre consistència i disponibilitat quan n’hi ha una (veure Model NoSQL).

Són l’acrònim de:

  • Basic Availability: la base de dades funciona la majoria del temps.
  • Soft-State: no cal tenir consistència a l’escriptura, ni les rèpliques han de ser consistents.
  • Eventual consistency: la consistència es pot tenir més tard en el temps (funcionament mandrós).
Last change: , commit: ceadfba