2015-09-08 29 views
15

Sto cercando di imparare Cassandra e trovo sempre il modo migliore per iniziare a creare un'applicazione molto semplice e piccola. Quindi sto creando un'applicazione di messaggistica di base che utilizzerà Cassandra come back-end. Vorrei fare quanto segue:Modello dati Cassandra per semplice applicazione di messaggistica

  • L'utente creerà un account con un nome utente, e-mail e password. L'e-mail e la password possono essere modificati in qualsiasi momento.
  • L'utente può aggiungere un altro utente come contatto. L'utente aggiungerebbe un contatto effettuando una ricerca nel proprio nome utente o e-mail. I contatti non necessitano di significato reciproco se aggiungo un utente sono il mio contatto, non lo devo necessario attendere per accettare/approvare qualsiasi cosa come in Facebook.
  • Un messaggio viene inviato da un utente a un altro utente. Il mittente deve essere in grado di vedere i messaggi inviati (ordinati per ora) e i messaggi che sono stati inviati a loro (ordinati per ora). Quando un utente apre l'app ho bisogno di controllare il database per eventuali nuovi messaggi per quell'utente . Devo anche segnare se il messaggio è stato letto.

Come vengo dal mondo dei database relazionali mio database relazionale sarebbe simile a questa:

UsersTable 
    username (text) 
    email (text) 
    password (text) 
    time_created (timestamp) 
    last_loggedIn (timestamp) 
------------------------------------------------ 
ContactsTable 
    user_i_added (text) 
    user_added_me (text) 
------------------------------------------------  
MessagesTable 
    from_user (text) 
    to_user (text) 
    msg_body (text) 
    metadata (text) 
    has_been_read (boolean) 
    message_sent_time (timestamp) 

lettura attraverso un paio di libri di testo Cassandra che ho un pensiero di come modellare il database. La mia preoccupazione principale è quella di modellare il database in modo molto efficiente. Quindi sto cercando di evitare le cose come ad esempio gli indici secondari ecc Questo è il mio modello finora:

CREATE TABLE users_by_username (
    username text PRIMARY KEY, 
    email text, 
    password text 
    timeCreated timestamp 
    last_loggedin timestamp 
) 

CREATE TABLE users_by_email (
    email text PRIMARY KEY, 
    username text, 
    password text 
    timeCreated timestamp 
    last_loggedin timestamp 
) 

diffondere i dati in modo uniforme e per leggere una quantità minima di partizioni (si spera solo uno) che può cercare un utente in base sul loro nome utente o e-mail rapidamente. Lo svantaggio di questo è ovviamente il fatto che sto raddoppiando i miei dati, ma il costo dello storage è piuttosto economico, quindi trovo che sia un buon compromesso invece di utilizzare gli indici secondari. Anche l'ultimo accesso dovrà essere scritto due volte, ma Cassandra è efficace nelle scritture, quindi credo che questo sia un buon compromesso.

Per i contatti non riesco a pensare a nessun altro modo di modellarlo, quindi l'ho modellato molto simile a come farei in un database relazionale. Questo è piuttosto un disegno denormalizzato che credo che dovrebbe essere buono per le prestazioni in base ai libri che ho letto?

CREATE TABLE "user_follows" (
    follower_username text, 
    followed_username text, 
    timeCreated timestamp, 
    PRIMARY KEY ("follower_username", "followed_username") 
); 

CREATE TABLE "user_followedBy" (

    followed_username text, 
    follower_username text, 
    timeCreated timestamp, 
    PRIMARY KEY ("followed_username", "follower_username") 
); 

Sono bloccato su come creare questa parte successiva. Per la messaggistica stavo pensando a questa tabella in quanto ha creato righe larghe che consentono l'ordinamento dei messaggi. Ho bisogno di messaggi per rispondere a due domande. In primo luogo deve essere in grado di mostrare all'utente tutti i messaggi che hanno e anche di essere in grado di mostrare all'utente i messaggi nuovi e non letti. Questo è un modello di base, ma non sono sicuro di come renderlo più efficace?

CREATE TABLE messages (
    message_id uuid, 
    from_user text, 
    to_user text, 
    body text, 
    hasRead boolean, 
    timeCreated timeuuid, 
    PRIMARY KEY ((to_user), timeCreated) 
) WITH CLUSTERING ORDER BY (timeCreated ASC); 

ero anche cercando di utilizzare le cose come le colonne statiche per 'colla' insieme l'utente e messaggi, così come set per le relazioni negozio di contatto, ma dalla mia comprensione stretta finora il modo in cui ho presentato è più efficiente. Chiedo se ci sono idee per migliorare l'efficienza di questo modello, se ci sono buone pratiche che fanno le cose che sto cercando di fare, o se ci sono dei problemi nascosti che posso affrontare con questo design?

In conclusione, sto cercando di modellare le query.Se fossi utilizzando database relazionali questi sarebbero essenzialmente le query sto cercando di rispondere:

To Login: 
SELECT * FROM USERS WHERE (USERNAME = [MY_USERNAME] OR EMAIL = [MY_EMAIL]) AND PASSWORD = [MY_PASSWORD]; 
------------------------------------------------------------------------------------------------------------------------ 
Update user info: 
UPDATE USERS (password) SET password = [NEW_PASSWORD] where username = [MY_USERNAME]; 
UPDATE USERS (email) SET password = [NEW_PASSWORD ] where username = [MY_USERNAME]; 
------------------------------------------------------------------------------------------------------------------------ 
To Add contact (If by username): 
INSERT INTO followings(following,follower) VALUES([USERNAME_I_WANT_TO_FOLLOW],[MY_USERNAME]); 
------------------------------------------------------------------------------------------------------------------------ 
To Add contact (If by email): 
SELECT username FROM users where email = [CONTACTS_EMAIL]; 
    Then application layer sends over another query with the username: 
INSERT INTO followings(following,follower) VALUES([USERNAME_I_WANT_TO_FOLLOW],[MY_USERNAME]); 
------------------------------------------------------------------------------------------------------------------------ 
To View contacts: 
SELECT following FROM USERS WHERE follower = [MY_USERNAME]; 
------------------------------------------------------------------------------------------------------------------------ 
To Send Message:, 
INSERT INTO MESSAGES (MSG_ID, FROM, TO, MSG, IS_MSG_NEW) VALUES (uuid, [FROM_USERNAME], [TO_USERNAME], 'MY MSG', true); 
------------------------------------------------------------------------------------------------------------------------ 
To View All Messages (Some pagination type of technique where shows me the 10 recent messages, yet shows which ones are unread): 
SELECT * FROM MESSAGES WHERE TO = [MY_USERNAME] LIMIT 10; 
------------------------------------------------------------------------------------------------------------------------ 
Once Message is read: 
UPDATE MESSAGES SET IS_MSG_NEW = false WHERE TO = [MY_USERNAME] AND MSG_ID = [MSG_ID]; 

Acclamazioni

risposta

12

Sì, è sempre una lotta per adattarsi alle limitazioni di Cassandra quando provenienti da un background database relazionale. Dal momento che non abbiamo ancora il lusso di partecipare a Cassandra, spesso si vuole stipare il più possibile in un unico tavolo. Nel tuo caso quella sarebbe la tabella users_by_username.

Ci sono alcune caratteristiche di Cassandra che dovrebbero permetterti di farlo.

Dal momento che siete nuovi a Cassandra, potreste probabilmente usare Cassandra 3.0, che è attualmente in versione beta. Nella 3.0 c'è una bella funzionalità chiamata viste materializzate. Questo ti permetterebbe di avere users_by_username come una tabella di base e creare users_by_email come una vista materializzata. Quindi Cassandra aggiornerà automaticamente la vista ogni volta che aggiorni la tabella di base.

Un'altra funzione che aiuta i tipi definiti dall'utente (in C * 2.1 e versioni successive). Invece di creare tabelle separate per follower e messaggi, è possibile creare la struttura di quelli come UDT, quindi nella tabella utente mantenere gli elenchi di tali tipi.

Quindi una vista semplificata del tuo schema potrebbe essere come questa (non sto mostrando alcuni dei campi come timestamp per mantenere questo semplice, ma quelli sono facili da aggiungere).

Innanzitutto creare il vostro UDT:

CREATE TYPE user_follows (
    followed_username text, 
    street text, 
); 

CREATE TYPE msg (
    from_user text, 
    body text 
); 

Poi creiamo la tua tabella di base:

CREATE TABLE users_by_username (
    username text PRIMARY KEY, 
    email text, 
    password text, 
    follows list<frozen<user_follows>>, 
    followed_by list<frozen<user_follows>>, 
    new_messages list<frozen<msg>>, 
    old_messages list<frozen<msg>> 
); 

Ora creiamo una vista materializzata partizionata per e-mail:

CREATE MATERIALIZED VIEW users_by_email AS 
    SELECT username, password, follows, new_messages, old_messages FROM users_by_username 
    WHERE email IS NOT NULL AND password IS NOT NULL AND follows IS NOT NULL AND new_messages IS NOT NULL 
    PRIMARY KEY (email, username); 

Ora diamo per un giro e vedere cosa può fare. Creiamo un utente:

INSERT INTO users_by_username (username , email , password) 
    VALUES ('someuser', '[email protected]', 'somepassword'); 

permettere all'utente di seguire un altro utente:

UPDATE users_by_username SET follows = [{followed_username: 'followme2', street: 'mystreet2'}] + follows 
    WHERE username = 'someuser'; 

Mandiamo all'utente un messaggio:

UPDATE users_by_username SET new_messages = [{from_user: 'auser', body: 'hi someuser!'}] + new_messages 
    WHERE username = 'someuser'; 

Ora vediamo cosa c'è nella tabella:

SELECT * FROM users_by_username ; 

username | email    | followed_by | follows             | new_messages         | old_messages | password 
----------+-------------------+-------------+---------------------------------------------------------+----------------------------------------------+--------------+-------------- 
someuser | [email protected] |  null | [{followed_username: 'followme2', street: 'mystreet2'}] | [{from_user: 'auser', body: 'hi someuser!'}] |   null | somepassword 

Ora controlliamo che la nostra vista materializzata sta lavorando:

SELECT new_messages, old_messages FROM users_by_email WHERE email='[email protected]'; 

new_messages         | old_messages 
----------------------------------------------+-------------- 
[{from_user: 'auser', body: 'hi someuser!'}] |   null 

Ora leggiamo la posta elettronica e lo mise nei vecchi messaggi:

BEGIN BATCH 
    DELETE new_messages[0] FROM users_by_username WHERE username='someuser' 
    UPDATE users_by_username SET old_messages = [{from_user: 'auser', body: 'hi someuser!'}] + old_messages where username = 'someuser' 
APPLY BATCH; 

SELECT new_messages, old_messages FROM users_by_email WHERE email='[email protected]'; 

new_messages | old_messages 
--------------+---------------------------------------------- 
     null | [{from_user: 'auser', body: 'hi someuser!'}] 

Quindi spero che ti dà alcune idee che è possibile utilizzare. Dai un'occhiata alla documentazione sugli insiemi (cioè elenchi, mappe e insiemi), poiché quelli possono davvero aiutarti a mantenere più informazioni in una tabella e sono un po 'come le tabelle all'interno di una tabella.

3

per Cassandra o NoSQL principianti modellazione dei dati, c'è un processo coinvolto nella modellazione dei dati dell'applicazione, come

1- Capire i vostri dati, la progettazione di un diagramma di concetto
2- Lista tutti i quaderni in dettaglio
3- Mappare le query utilizzando regole e pattern definiti, più adatti per cassandra
4- Creare un progetto logico, tabella con campi derivati ​​dalle query
5- Ora creare uno schema e testarne l'accettazione.

se lo modelliamo bene, quindi è facile gestire problemi come nuove query complesse, dati sul caricamento, set di coerenza dei dati.

Dopo aver preso questi dati on-line di formazione gratuito di modellazione, si otterrà maggiore chiarezza

https://academy.datastax.com/courses/ds220-data-modeling

Good Luck!