2012-10-31 11 views
5

Ho una domanda relativa a SQLAlchemy, sharding del database e UUID per voi gente buona.SQLAlchemy, UUIDs, Sharding e AUTO_INCREMENT chiave primaria ... come farli lavorare insieme?

Attualmente sto usando MySQL in cui ho una tabella di forma:

CREATE TABLE foo (
    added_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
    id BINARY(16) NOT NULL, 
    ... other stuff ... 
    UNIQUE KEY(id) 
); 

Un po 'di storia su questo tavolo. Non mi interessa mai il 'added_id', sto solo usando per assicurarmi che gli elementi inseriti siano raggruppati insieme su disco (dato che l'albero B utilizzato per indicizzare la tabella in MySQL utilizza la chiave primaria come indice del cluster). La colonna 'id' contiene la rappresentazione binaria di un UUID - questa è la colonna a cui tengo veramente e tutte le altre cose fanno riferimento a questo ID. Ancora una volta, non voglio che l'UUID sia la chiave primaria, dal momento che l'UUID è casuale e quindi il B-Tree creato per indicizzare la tabella ha caratteristiche IO orribili (almeno questo è ciò che è stato detto altrove). Inoltre, sebbene l'UUID1 includa il timestamp per garantire che gli ID siano generati in ordine "sequenziale", l'inclusione dell'indirizzo MAC nell'ID lo rende qualcosa che preferirei evitare. Quindi, mi piacerebbe usare UUID4s.

Ok, ora passiamo alla parte SQLAlchemy. In SQLAlchemy si può definire un modello usando il loro ORM per la tabella di cui sopra facendo qualcosa di simile:

# The SQL Alchemy ORM base class 
Base = declerative_base() 

# The model for table 'foo' 
class Foo(Base): 
    __table__ = 'foo' 
    add_id = Column(Integer, primary_key=True, nullable=False) 
    id = Column(Binary, index=True, unique=True, nullable=False) 
    ... 

Ancora una volta, questo è fondamentalmente lo stesso del SQL sopra.

E ora alla domanda. Diciamo che questo database sta per essere diviso (partizionato orizzontalmente) in 2 (o più) database separati. Ora, (supponendo che non ci siano cancellazioni) ognuno di questi database avrà i record con added_id di 1, 2, 3, ecc. Nella tabella foo. Poiché SQLAlchemy utilizza una sessione per gestire gli oggetti su cui si sta lavorando in modo che ogni oggetto sia identificato solo dalla sua chiave primaria, sembra che sarebbe possibile avere la situazione in cui potrei finire tentando di accedere a due oggetti Foo dai due frammenti con lo stesso added_id risultante in alcuni conflitti nella sessione gestita.

Qualcuno si è imbattuto in questo problema? Che cosa hai fatto per risolverlo? O, più che probabile, mi manca qualcosa dalla documentazione SQLAlchemy che garantisce che questo non possa accadere. Tuttavia, esaminando l'esempio di sharding fornito con il download di SQLAlchemy (esempi/sharding/attribute_shard.py) sembra che facciano un passo in questa direzione designando uno dei frammenti del database come un generatore di ID ... creando un collo di bottiglia implicito come tutto INSERTI devono andare contro quel singolo database per ottenere un ID. (Si menziona anche l'uso degli UUID, ma a quanto pare causa il problema di prestazioni degli indici.)

In alternativa, esiste un modo per impostare l'UUID come chiave primaria e avere i dati in cluster su disco utilizzando l'add_id? Se non è possibile in MySQL è possibile in un altro DB come Postgres?

Grazie in anticipo per qualsiasi input!

--- UPDATE ---- Voglio solo aggiungere una risposta fuori banda che ho ricevuto a questa domanda. Il seguente testo non è qualcosa che ho scritto, voglio solo includerlo qui nel caso qualcuno lo trovasse utile.

Il modo più semplice per evitare tale situazione con MySQL e le chiavi di incremento automatico è utilizzare diversi offset di incremento automatico per ciascun database, ad es.:

ALTER TABLE pippo AUTO_INCREMENT = 100000;

Lo svantaggio è che è necessario prestare attenzione in termini di come si configura ciascun frammento e si deve pianificare un po 'il numero totale di frammenti che si utilizzano.

Non c'è alcun modo per convincere MySQL a utilizzare una chiave non primaria per l'indice cluster. Se non ti interessa usare SQLAlchemy per gestire lo schema del tuo database (anche se, probabilmente, dovresti), puoi semplicemente impostare l'UUID come chiave primaria nello schema SQLAlchemy e lasciare l'add_id come pk nella tabella effettiva.

Ho anche visto soluzioni alternative che utilizzano semplicemente un server esterno (ad es. Redis) per mantenere l'ID di riga.

risposta

5

sì, è possibile specificare qualsiasi delle colonne della tabella come chiave primaria ai fini della mappatura utilizzando l'argomento "primary_key" mapper, che è un elenco di oggetti di colonna o di una singola colonna:

Base = declarative_base() 

# The model for table 'foo' 
class Foo(Base): 
    __table__ = 'foo' 
    add_id = Column(Integer, primary_key=True, nullable=False) 
    id = Column(Binary, index=True, unique=True, nullable=False) 

    __mapper_args__ = {'primary_key': id} 

Sopra, mentre lo SQLAlchemy Core tratterà "add_id" come colonna "autoincrement", il mapper sarà per lo più non interessato, usando invece "id" come la colonna a cui tiene conto quando si considera l'"identità" dell'oggetto .

Vedere documentation for mapper() per ulteriori descrizione.

+0

Grazie mille. – prschmid

Problemi correlati