{"id":37956,"date":"2019-10-31T22:20:47","date_gmt":"2019-10-31T19:20:47","guid":{"rendered":"https:\/\/prohoster.info\/blog\/newsql-nosql-acid\/"},"modified":"2019-10-31T22:20:47","modified_gmt":"2019-10-31T19:20:47","slug":"newsql-nosql-acid","status":"publish","type":"post","link":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/newsql-nosql-acid","title":{"rendered":"NewSQL = NoSQL + ACID","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"NewSQL = NoSQL + ACID\" src=\"\/wp-content\/uploads\/2019\/09\/973145efa566bca10ffe7ba95733a1d5.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nFino a poco tempo fa, circa 50 TB di dati trattati in tempo reale erano conservati in SQL Server su Odnoklassniki. Per gestire un volume simile, garantire un accesso veloce, affidabile e anche resistente ai guasti a un centro dati utilizzando un database SQL \u00e8 praticamente impossibile. Di solito, in questi casi, si utilizza uno dei database NoSQL, ma non tutto pu\u00f2 essere trasferito in NoSQL: alcune entit\u00e0 richiedono garanzie di transazioni ACID. <\/p>\n<p>Questo ci ha portato all'uso di un database NewSQL, ovvero un sistema di gestione di database che offre resilienza, scalabilit\u00e0 e velocit\u00e0 simili a quelle dei sistemi NoSQL, ma preserva le garanzie ACID tipiche dei sistemi classici. Ci sono pochi sistemi industriali funzionanti di questa nuova classe, quindi abbiamo realizzato un tale sistema noi stessi e l'abbiamo messo in produzione. <\/p>\n<p>Come funziona e quali risultati abbiamo ottenuto \u2014 leggi di seguito.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><br \/>\nOggi, l'audience mensile di Odnoklassniki supera i 70 milioni di visitatori unici. Noi <noindex><a rel=\"nofollow\" href=\"https:\/\/www.similarweb.com\/top-websites\/category\/internet-and-telecom\/social-network\">siamo tra i primi cinque<\/a><\/noindex> una delle pi\u00f9 grandi reti sociali al mondo e tra i primi venti siti dove gli utenti trascorrono pi\u00f9 tempo. L'infrastruttura di 'OK' gestisce carichi molto elevati: oltre un milione di richieste HTTP al secondo. Le parti del parco server, che superano le 8000 unit\u00e0, sono posizionate vicine l'una all'altra \u2014 in quattro data center a Mosca, permettendo di garantire una latenza di rete inferiore a 1 ms tra di loro.<\/p>\n<p>Utilizziamo Cassandra dal 2010, a partire dalla versione 0.6. Oggi sono in funzione diverse decine di cluster. Il cluster pi\u00f9 veloce gestisce oltre 4 milioni di operazioni al secondo, mentre il pi\u00f9 grande conserva 260 TB di dati. <\/p>\n<p>Tuttavia, tutti questi sono cluster NoSQL comuni, utilizzati per lo storage <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%A1%D0%BE%D0%B3%D0%BB%D0%B0%D1%81%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D1%81%D1%82%D1%8C_%D0%B2_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D0%BE%D0%BC_%D1%81%D1%87%D1%91%D1%82%D0%B5\">di dati<\/a><\/noindex> debilmente consistenti. Volevamo invece sostituire il principale storage consistente, Microsoft SQL Server, utilizzato sin dalla fondazione di 'Odnoklassniki'. Questo storage era composto da oltre 300 macchine SQL Server Standard Edition, con 50 TB di dati \u2014 entit\u00e0 aziendali. Questi dati vengono modificati nell'ambito di transazioni ACID e richiedono <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/ACID\">alta coerenza<\/a><\/noindex>.<\/p>\n<p>Per distribuire i dati tra i nodi SQL Server, abbiamo utilizzato sia la partizione verticale che quella orizzontale. <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Partition_(database)\">partizionamento<\/a><\/noindex> (sharding). Storicamente, abbiamo utilizzato uno schema semplice di sharding dei dati: ciascuna entit\u00e0 veniva associata a un token, una funzione dell'ID dell'entit\u00e0. Le entit\u00e0 con lo stesso token venivano collocate su un singolo SQL Server. La relazione di tipo master-detail era implementata in modo che i token del record principale e di quello derivato coincidessero sempre e si trovassero sullo stesso server. Nella rete sociale, quasi tutte le registrazioni vengono create a nome dell'utente, il che significa che tutti i dati dell'utente all'interno di un sotto-sistema funzionale sono memorizzati su un unico server. Ci\u00f2 implica che nella transazione aziendale quasi sempre erano coinvolte tabelle di un solo SQL Server, consentendo di garantire la coerenza dei dati tramite transazioni ACID locali, senza necessit\u00e0 di utilizzare <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Two-phase_commit_protocol\">transazioni ACID distribuite<\/a><\/noindex> , lenti e inaffidabili.<\/p>\n<p>Grazie allo sharding e per ottimizzare le prestazioni di SQL:<\/p>\n<ul>\n<li>Non utilizziamo i vincoli di chiave esterna, poich\u00e9 durante lo sharding l'ID dell'entit\u00e0 pu\u00f2 trovarsi su un altro server.<\/li>\n<li>Non utilizziamo le stored procedure e i trigger a causa del carico aggiuntivo sulla CPU del DBMS.<\/li>\n<li>Non utilizziamo i JOIN a causa di quanto sopra e per il gran numero di letture casuali dal disco.<\/li>\n<li>Al di fuori delle transazioni, per ridurre i deadlock, utilizziamo il livello di isolamento Read Uncommitted.<\/li>\n<li>Eseguiamo solo transazioni brevi (in media pi\u00f9 brevi di 100 ms).<\/li>\n<li>Non utilizziamo UPDATE e DELETE multipli a causa del gran numero di deadlock: aggiorniamo solo un record alla volta.<\/li>\n<li>Eseguiamo sempre le query solo sugli indici: una query con un piano di scansione completa della tabella per noi significa un sovraccarico del database e il suo fallimento.<\/li>\n<\/ul>\n<p>\nQuesti passaggi ci hanno permesso di estrarre quasi il massimo delle prestazioni dai server SQL. Tuttavia, i problemi stavano diventando sempre pi\u00f9 numerosi. Esaminiamoli.<\/p>\n<h2>Problemi con SQL<\/h2>\n<p><\/p>\n<ul>\n<li>Poich\u00e9 utilizzavamo uno sharding personalizzato, l'aggiunta di nuovi shard veniva eseguita manualmente dagli amministratori. Per tutto questo tempo, le repliche di dati scalabili non gestivano le query. <\/li>\n<li>Con l'aumento del numero di record nella tabella, la velocit\u00e0 di inserimento e modifica diminuisce; aggiungendo indici a una tabella esistente, la velocit\u00e0 diminuisce drasticamente, e la creazione e ricreazione degli indici avviene con un downtime.<\/li>\n<li>La presenza di un numero limitato di Windows per SQL Server in produzione complica la gestione dell'infrastruttura.<\/li>\n<\/ul>\n<p>\nMa il problema principale \u00e8 \u2014 <\/p>\n<h2>Affidabilit\u00e0<\/h2>\n<p>\nUn classico SQL Server ha una scarsa affidabilit\u00e0. Supponiamo che tu abbia solo un server di database e si guasti ogni tre anni. Durante quel periodo, il sito non funziona per 20 minuti, il che \u00e8 accettabile. Se hai 64 server, il sito non funziona ogni tre settimane. E se hai 200 server, il sito non funziona ogni settimana. Questo \u00e8 un problema. <\/p>\n<p>Cosa si pu\u00f2 fare per migliorare l'affidabilit\u00e0 del SQL Server? Wikipedia ci suggerisce di costruire un <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/High-availability_cluster\">cluster ad alta disponibilit\u00e0<\/a><\/noindex>: dove, in caso di guasto di uno qualsiasi dei componenti, c'\u00e8 una duplicazione.<\/p>\n<p>Questo richiede un parco di costosa attrezzatura: molteplici ridondanze, fibra ottica, storage condiviso, e anche l'inclusione di riserve funziona in modo inaffidabile: circa il 10% delle attivazioni termina con il fallimento del nodo di riserva trainato dal nodo principale. <\/p>\n<p>Ma il principale svantaggio di un cluster cos\u00ec altamente disponibile \u00e8 la totale indisponibilit\u00e0 in caso di guasto del data center in cui \u00e8 collocato. \"Odnoklassniki\" ha quattro data center, e dobbiamo garantire il funzionamento anche in caso di un'interruzione totale in uno di essi.<\/p>\n<p>Per questo si potrebbe utilizzare <noindex><a rel=\"nofollow\" href=\"https:\/\/technet.microsoft.com\/en-us\/library\/ms151196.aspx\">Multi-Master<\/a><\/noindex> replication, integrata in SQL Server. Questa soluzione \u00e8 molto pi\u00f9 costosa a causa del costo del software e presenta i ben noti problemi con la replicazione: ritardi imprevedibili delle transazioni nella replicazione sincrona e ritardi nell'applicazione delle replicazioni (e, di conseguenza, modifiche perse) nella replicazione asincrona. Ci\u00f2 implicando <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/replication\/transactional\/peer-to-peer-conflict-detection-in-peer-to-peer-replication\">la risoluzione manuale dei conflitti<\/a><\/noindex> rende questa opzione completamente inapplicabile per noi.<\/p>\n<p>Tutti questi problemi richiedevano una soluzione radicale e abbiamo iniziato ad analizzarli in dettaglio. Qui dobbiamo familiarizzare con ci\u00f2 che fa principalmente SQL Server: le transazioni.<\/p>\n<h2>Transazione semplice<\/h2>\n<p>\nConsideriamo una transazione molto semplice, dal punto di vista di un programmatore SQL applicativo: l'aggiunta di una foto in un album. Gli album e le foto sono memorizzati in tabelle diverse. Ogni album ha un contatore per le foto pubbliche. Quindi, questa transazione si suddivide nei seguenti passaggi: <\/p>\n<ol>\n<li>Blocchiamo l'album tramite la chiave.<\/li>\n<li>Creiamo una voce nella tabella delle foto. <\/li>\n<li>Se la foto ha lo stato pubblico, incrementiamo il contatore delle foto pubbliche nell'album, aggiorniamo la voce e comit diamo la transazione.<\/li>\n<\/ol>\n<p>\nO in forma di pseudocodice:<\/p>\n<pre><code>TX.start(\"Albums\", id);\nAlbum album = albums.lock(id);\nPhoto photo = photos.create(\u2026);\n\nif (photo.status == PUBLIC ) {\n    album.incPublicPhotosCount();\n}\nalbum.update();\n\nTX.commit();<\/code><\/pre>\n<p>\nVediamo che lo scenario pi\u00f9 comune di una transazione aziendale \u00e8 leggere i dati dal database in memoria del server applicativo, modificare qualcosa e salvare i nuovi valori di nuovo nel database. Di solito, in una tale transazione, aggiorniamo pi\u00f9 entit\u00e0, ovvero pi\u00f9 tabelle. <\/p>\n<p>Durante l'esecuzione di una transazione, potrebbe avvenire una modifica competitiva dei medesimi dati da un altro sistema. Ad esempio, il sistema anti-spam potrebbe decidere che un utente \u00e8 sospetto e, quindi, tutte le foto dell'utente non dovrebbero pi\u00f9 essere pubbliche, necessitando di essere inviate per la moderazione, il che implica cambiare photo.status in un altro valore e ripristinare i contatori corrispondenti. \u00c8 evidente che se questa operazione avviene senza garanzie di atomicit\u00e0 e isolamento delle modifiche concorrenti, come in <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/ACID\">ACID<\/a><\/noindex>, il risultato non sar\u00e0 quello necessario: o il contatore delle foto mostrer\u00e0 un valore errato, o non tutte le foto verranno inviate per la moderazione. <\/p>\n<p>Un codice simile, che manipola diverse entit\u00e0 aziendali all'interno di una singola transazione, \u00e8 stato scritto in quantit\u00e0 enormi nel corso dell'esistenza di Odnoklassniki. Tuttavia, dalle esperienze di migrazione a NoSQL con <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Eventual_consistency\">Eventual Consistency<\/a><\/noindex> Sappiamo che le maggiori difficolt\u00e0 (e i maggiori investimenti di tempo) derivano dalla necessit\u00e0 di sviluppare codice per mantenere la coerenza dei dati. Pertanto, il requisito principale per il nuovo storage era assicurare alla logica applicativa vere transazioni ACID. <\/p>\n<p>Altrettanto importanti erano i requisiti:<\/p>\n<ul>\n<li>In caso di guasto del data center, sia la lettura che la scrittura nel nuovo storage devono essere disponibili.<\/li>\n<li>Mantenere l'attuale velocit\u00e0 di sviluppo. Ci\u00f2 significa che, lavorando con il nuovo storage, la quantit\u00e0 di codice dovrebbe rimanere sostanzialmente la stessa, senza la necessit\u00e0 di scrivere ulteriori funzioni per lo storage, sviluppare algoritmi di risoluzione dei conflitti, mantenere indici secondari, ecc. <\/li>\n<li>La velocit\u00e0 del nuovo storage deve essere sufficientemente elevata sia per la lettura dei dati che per l'elaborazione delle transazioni, il che implica l'inapplicabilit\u00e0 di soluzioni rigorose e universali, ma lente, come ad esempio: <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Two-phase_commit_protocol\">i commit a due fasi<\/a><\/noindex>.<\/li>\n<li>Scalabilit\u00e0 automatica in tempo reale. <\/li>\n<li>Utilizzo di normali server economici, senza la necessit\u00e0 di acquistare hardware esotico. <\/li>\n<li>L'opzione di sviluppare il sistema di archiviazione internamente dai nostri sviluppatori. In altre parole, si dava priorit\u00e0 a soluzioni proprietarie o basate su codice aperto, preferibilmente in Java.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Soluzioni, soluzioni<\/h2>\n<p>\nAnalizzando le possibili soluzioni, siamo giunti a due possibili scelte architettoniche:<\/p>\n<p>La prima \u00e8 prendere un qualsiasi server SQL e implementare la necessaria resilienza, meccanismi di scalabilit\u00e0, cluster fault-tolerant, risoluzione dei conflitti e transazioni ACID distribuite, affidabili e veloci. Abbiamo valutato questa opzione come piuttosto complessa e laboriosa.<\/p>\n<p>La seconda opzione \u00e8 utilizzare un archivio NoSQL pronto all'uso con scalabilit\u00e0 implementata, cluster fault-tolerant, risoluzione dei conflitti e implementare le transazioni e SQL noi stessi. A prima vista, anche l'implementazione di SQL, per non parlare delle transazioni ACID, sembra un compito che richieder\u00e0 anni. Ma poi abbiamo capito che il set di funzionalit\u00e0 SQL che utilizziamo nella pratica \u00e8 lontano dall'ANSI SQL altrettanto quanto <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Apache_Cassandra#Cassandra_Query_Language\">Cassandra CQL<\/a><\/noindex> \u00e8 lontano dall'ANSI SQL. Esaminando pi\u00f9 da vicino CQL, ci siamo resi conto che \u00e8 abbastanza vicino a ci\u00f2 di cui abbiamo bisogno.<\/p>\n<h2>Cassandra e CQL<\/h2>\n<p>\nQuindi, cosa rende interessante Cassandra e quali possibilit\u00e0 offre?<\/p>\n<p>In primo luogo, \u00e8 possibile creare tabelle che supportano vari tipi di dati, \u00e8 possibile eseguire SELECT o UPDATE utilizzando la chiave primaria.<\/p>\n<pre><code>CREATE TABLE photos (id bigint KEY, owner bigint,\u2026);\nSELECT * FROM photos WHERE id=?;\nUPDATE photos SET \u2026 WHERE id=?;<\/code><\/pre>\n<p>\nPer garantire la coerenza dei dati delle repliche, Cassandra utilizza <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Quorum_(distributed_computing)\">un approccio basato sul quorum<\/a><\/noindex>. In the simplest case, this means that when placing three replicas of the same row on different nodes of the cluster, a write is considered successful if the majority of nodes (i.e., two out of three) confirm the success of this write operation. The data of the row is considered consistent if, during reading, the majority of nodes were queried and confirmed it. Thus, with three replicas, full and immediate data consistency is guaranteed in the event of one node failure. This approach has allowed us to implement an even more reliable scheme: always sending requests to all three replicas, waiting for a response from the two fastest. The delayed response from the third replica is discarded in this case. The node that responds late may have serious issues \u2014 lag, garbage collection in JVM, direct memory reclaim in the Linux kernel, hardware failure, or network disconnection. However, this does not affect client operations or the data.<\/p>\n<p>The approach where we query three nodes and receive a response from two is called <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Speculative_execution\">speculation<\/a><\/noindex>: a request for excess replicas is sent even before it 'drops out'. <\/p>\n<p>Un altro vantaggio di Cassandra \u00e8 il Batchlog, un meccanismo che garantisce l'applicazione completa o il rifiuto totale delle modifiche apportate. Ci\u00f2 ci consente di affrontare A in ACID \u2014 l'atomicit\u00e0 fuori dalla scatola.<\/p>\n<p>La cosa pi\u00f9 vicina alle transazioni in Cassandra \u00e8 quella che viene chiamata &#171;<noindex><a rel=\"nofollow\" href=\"https:\/\/docs.datastax.com\/en\/cql\/3.3\/cql\/cql_using\/useInsertLWT.html\">lightweight transactions<\/a><\/noindex>&#171;. Tuttavia, sono lontane dalle \"vere\" transazioni ACID: in realt\u00e0, si tratta della possibilit\u00e0 di fare <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Compare-and-swap\">CAS<\/a><\/noindex> su un'unica riga di dati, utilizzando il consenso del protocollo pesante Paxos. Pertanto, la velocit\u00e0 di queste transazioni \u00e8 limitata. <\/p>\n<h2>Cosa ci mancava in Cassandra<\/h2>\n<p>\nQuindi, dovevamo implementare vere transazioni ACID in Cassandra. Con cui avremmo potuto facilmente realizzare altre due funzionalit\u00e0 utili delle classiche DBMS: indici rapidi e consistenti, che ci avrebbero permesso di effettuare query non solo per chiave primaria e un generatore standard di ID auto-incrementali monotoni.<\/p>\n<h4>C*One<\/h4>\n<p>\nCos\u00ec \u00e8 nata una nuova DBMS <b>C*One<\/b>, composta da tre tipi di nodi server:<\/p>\n<ul>\n<li>I nodi di archiviazione sono server Cassandra (quasi) standard che svolgono il compito di memorizzare i dati sui dischi locali. Con l'aumentare del carico e del volume dei dati, il loro numero pu\u00f2 essere facilmente scalato fino a decine e centinaia.<\/li>\n<li>I coordinatori delle transazioni garantiscono l'esecuzione delle transazioni. <\/li>\n<li>I client sono server delle applicazioni che implementano operazioni di business e avviano transazioni. Possono esserci migliaia di tali client.<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"NewSQL = NoSQL + ACID\" src=\"\/wp-content\/uploads\/2019\/09\/f3809d89404ae596c62b642dbdb9e431.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nI server di tutti i tipi fanno parte di un cluster comune, utilizzano il protocollo di messaggistica interna di Cassandra per comunicare tra di loro e <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Gossip_protocol\">gossip<\/a><\/noindex> per scambiare informazioni sul cluster. Con l'ausilio del Heartbeat, i server sono in grado di conoscere i guasti reciproci, mantenere un'unica struttura dati \u2014 tabelle, la loro struttura e replica; schema di partizionamento, topologia del cluster, ecc.<\/p>\n<h4>Client<\/h4>\n<p>\n<img decoding=\"async\" alt=\"NewSQL = NoSQL + ACID\" src=\"\/wp-content\/uploads\/2019\/09\/80f83358f215cf59db3b657ba1083420.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nInvece dei normali driver, viene utilizzata la modalit\u00e0 Fat Client. Questo nodo non memorizza dati, ma pu\u00f2 fungere da coordinatore per l'esecuzione delle richieste, cio\u00e8 il Cliente svolge autonomamente la funzione di coordinamento delle proprie richieste: interroga le repliche dello storage e risolve i conflitti. Questo \u00e8 non solo pi\u00f9 affidabile e veloce rispetto al driver standard, che richiede comunicazione con un coordinatore remoto, ma consente anche di gestire la trasmissione delle richieste. Fuori da una transazione aperta sul client, le richieste vengono inviate agli storage. Se il cliente ha aperto una transazione, tutte le richieste all'interno della transazione vengono indirizzate al coordinatore delle transazioni.<br \/>\n<img decoding=\"async\" alt=\"NewSQL = NoSQL + ACID\" src=\"\/wp-content\/uploads\/2019\/09\/709bf227eeb8f71abeff703a72780efd.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<h2>Coordinatore delle transazioni C*One<\/h2>\n<p>\nIl coordinatore \u00e8 ci\u00f2 che abbiamo implementato da zero per C*One. \u00c8 responsabile della gestione delle transazioni, dei blocchi e dell'ordine di applicazione delle transazioni.<\/p>\n<p>Per ogni transazione gestita, il coordinatore genera un timestamp: ogni successivo \u00e8 maggiore di quello della transazione precedente. Poich\u00e9 in Cassandra il sistema di risoluzione dei conflitti si basa sui timestamp (tra due record in conflitto, quello con il timestamp pi\u00f9 recente \u00e8 considerato valido), il conflitto sar\u00e0 sempre risolto a favore della transazione successiva. In questo modo abbiamo implementato <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%A7%D0%B0%D1%81%D1%8B_%D0%9B%D1%8D%D0%BC%D0%BF%D0%BE%D1%80%D1%82%D0%B0\">orologi di Lamport<\/a><\/noindex> \u2014 un modo economico per risolvere i conflitti in un sistema distribuito.<\/p>\n<h2>Blocchi<\/h2>\n<p>\nPer garantire l'isolamento, abbiamo deciso di utilizzare il metodo pi\u00f9 semplice: blocchi pessimisti sulla chiave primaria del record. In altre parole, nella transazione, il record deve prima essere bloccato, poi letto, modificato e salvato. Solo dopo un commit avvenuto con successo, il record pu\u00f2 essere sbloccato, permettendo alle transazioni concorrenti di utilizzarlo.<\/p>\n<p>Implementare un tale meccanismo di blocco \u00e8 semplice in un ambiente non distribuito. In un sistema distribuito ci sono due approcci principali: o implementare un blocco distribuito sul cluster, oppure distribuire le transazioni in modo che le transazioni che coinvolgono un singolo record siano sempre gestite dallo stesso coordinatore.<\/p>\n<p>Poich\u00e9 nel nostro caso i dati sono gi\u00e0 distribuiti in gruppi di transazioni locali in SQL, \u00e8 stato deciso di assegnare ai coordinatori dei gruppi di transazioni locali: un coordinatore gestisce tutte le transazioni con token da 0 a 9, il secondo \u2014 con token da 10 a 19, e cos\u00ec via. Come risultato, ciascuno degli istanze del coordinatore diventa il master del gruppo di transazioni. <\/p>\n<p>In tal modo, i blocchi possono essere implementati come una semplice HashMap nella memoria del coordinatore.<\/p>\n<h2>Guasti dei coordinatori<\/h2>\n<p>\nPoich\u00e9 un coordinatore gestisce esclusivamente un gruppo di transazioni, \u00e8 molto importante identificare rapidamente il suo guasto, affinch\u00e9 il tentativo di esecuzione della transazione sia completato entro il timeout. Per garantire che questo avvenga in modo veloce e affidabile, abbiamo applicato un protocollo di heartbeat a quorum pieno e connesso:<\/p>\n<p>In ogni centro dati sono presenti almeno due nodi coordinatori. Periodicamente, ogni coordinatore invia un messaggio heartbeat agli altri coordinatori per segnalare il proprio funzionamento e informare quali messaggi heartbeat ha ricevuto dai coordinatori nel cluster l'ultima volta. <\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL + ACID\" src=\"\/wp-content\/uploads\/2019\/09\/5c337e53815cad0a2071b160d98d42fa.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nRicevendo informazioni simili dagli altri tramite i loro messaggi heartbeat, ogni coordinatore determina quali nodi del cluster stanno funzionando e quali no, seguendo il principio del quorum: se il nodo X ha ricevuto informazioni dalla maggior parte dei nodi del cluster riguardo al corretto ricevimento dei messaggi dal nodo Y, allora Y \u00e8 operativo. Viceversa, non appena la maggior parte dei nodi segnala la mancata ricezione dei messaggi dal nodo Y, significa che Y ha subito un guasto. \u00c8 interessante notare che se il quorum informa il nodo X di non ricevere pi\u00f9 messaggi da esso, il nodo X stesso si considerer\u00e0 guasto.<\/p>\n<p>I messaggi Heartbeat vengono inviati ad alta frequenza, circa 20 volte al secondo, con un intervallo di 50 ms. In Java \u00e8 difficile garantire una risposta dell'applicazione entro 50 ms a causa della durata comparabile delle pause causate dal garbage collector. Siamo riusciti a ottenere questo tempo di risposta utilizzando il garbage collector G1, che consente di specificare un obiettivo per la durata delle pause GC. Tuttavia, a volte, abbastanza raramente, le pause del garbage collector superano i 50 ms, il che pu\u00f2 portare a falsi allarmi di guasto. Per evitare ci\u00f2, il coordinatore non segnala il guasto di un nodo remoto alla prima mancanza di un messaggio heartbeat, ma solo se ne mancano diversi consecutivi. In questo modo siamo riusciti a rilevare il guasto del nodo del coordinatore in 200 ms. <\/p>\n<p>Ma non basta capire rapidamente quale nodo ha smesso di funzionare. \u00c8 necessario fare qualcosa al riguardo. <\/p>\n<h2>Riserva<\/h2>\n<p>\nLo schema classico prevede, in caso di guasto del master, di avviare l'elezione di un nuovo master tramite uno dei<noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_Raft\"> moderni<\/a><\/noindex> <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9F%D0%B0%D0%BA%D1%81%D0%BE%D1%81\">universal<\/a><\/noindex> algoritmi. Tuttavia, questi algoritmi presentano problemi ben noti relativi alla convergenza nel tempo e alla durata del processo di voto stesso. Siamo riusciti a evitare ritardi aggiuntivi grazie allo schema di sostituzione dei coordinatori in una rete completamente connessa:<\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL + ACID\" src=\"\/wp-content\/uploads\/2019\/09\/5e2db258fa2200026390a73a414dfe81.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nSupponiamo di voler eseguire una transazione nel gruppo 50. Definiamo in anticipo lo schema di sostituzione, ovvero quali nodi eseguiranno le transazioni del gruppo 50 in caso di guasto del coordinatore principale. Il nostro obiettivo \u00e8 mantenere la funzionalit\u00e0 del sistema in caso di guasto di un data center. Definiamo che il primo riserva sar\u00e0 un nodo di un altro data center, e il secondo riserva sar\u00e0 un nodo di un terzo. Questo schema viene scelto una sola volta e non cambia finch\u00e9 non cambia la topologia del cluster, ovvero finch\u00e9 non entrano nuovi nodi (cosa che accade molto raramente). L'ordine per la scelta di un nuovo master attivo in caso di guasto del precedente sar\u00e0 sempre il seguente: il primo riserva diventer\u00e0 master attivo, e se anche lui smette di funzionare, il secondo riserva. <\/p>\n<p>Questo schema \u00e8 pi\u00f9 affidabile rispetto a un algoritmo universale, poich\u00e9 per attivare un nuovo master \u00e8 sufficiente verificare il guasto del precedente.<\/p>\n<p>Ma come possono i clienti sapere quale maestro \u00e8 attivo? Sono necessari 50 ms per inviare informazioni a migliaia di clienti. Pu\u00f2 succedere che un cliente invii una richiesta per aprire una transazione senza sapere che quel maestro non \u00e8 pi\u00f9 attivo, e la richiesta si bloccano per timeout. Per evitare questo, i clienti inviano speculative richieste di apertura della transazione sia al maestro del gruppo che a entrambi i suoi riservi, ma solo il maestro attivo in quel momento risponder\u00e0. Tutta la successiva comunicazione nell'ambito della transazione avverr\u00e0 solo con il maestro attivo.<\/p>\n<p>I maestri di riserva collocano le richieste per transazioni non proprie in coda delle transazioni non ancora nate, dove rimangono per un certo tempo. Se il maestro attivo viene a mancare, un nuovo maestro gestisce le richieste di apertura delle transazioni dalla propria coda e risponde al cliente. Se il cliente ha gi\u00e0 aperto una transazione con il vecchio maestro, la seconda risposta viene ignorata (e, ovviamente, tale transazione non verr\u00e0 completata e sar\u00e0 ripetuta dal cliente).<\/p>\n<h2>Come funziona una transazione<\/h2>\n<p>\nSupponiamo che un cliente abbia inviato al coordinatore una richiesta per aprire una transazione per una certa entit\u00e0 con una certa chiave primaria. Il coordinatore blocca questa entit\u00e0 e la inserisce nella tabella dei blocchi in memoria. Se necessario, il coordinatore legge questa entit\u00e0 dal repository e salva i dati ottenuti nello stato della transazione in memoria del coordinatore.<\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL + ACID\" src=\"\/wp-content\/uploads\/2019\/09\/246b4a5c68a4cc886b87af784c16b9dc.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nQuando un cliente desidera modificare i dati in una transazione, invia al coordinatore una richiesta di modifica dell'entit\u00e0, e quest'ultimo inserisce i nuovi dati nella tabella degli stati delle transazioni in memoria. A questo punto, la registrazione \u00e8 completata \u2014 non viene effettuata alcuna registrazione nel repository.<\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL + ACID\" src=\"\/wp-content\/uploads\/2019\/09\/4400a91af30eb71134978ff27474f745.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nQuando un cliente richiede i propri dati modificati all'interno di una transazione attiva, il coordinatore agisce in questo modo: <\/p>\n<ul>\n<li>se l'ID \u00e8 gi\u00e0 presente nella transazione, i dati vengono prelevati dalla memoria; <\/li>\n<li>se l'ID non \u00e8 presente in memoria, i dati mancanti vengono letti dai nodi di storage, uniti con quelli gi\u00e0 presenti in memoria, e il risultato viene fornito al cliente. <\/li>\n<\/ul>\n<p>\nIn questo modo, il cliente pu\u00f2 leggere le proprie modifiche, mentre gli altri clienti non vedono queste modifiche, poich\u00e9 sono conservate solo in memoria del coordinatore e non sono ancora presenti nei nodi di Cassandra.<\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL + ACID\" src=\"\/wp-content\/uploads\/2019\/09\/c93fd7e8393f431a976f1f56b95e227c.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nQuando un cliente invia un commit, lo stato presente in memoria del servizio viene salvato dal coordinatore in un batch registrato, che viene poi inviato ai dati di Cassandra. I dati compiono tutto il necessario affinch\u00e9 questo pacchetto venga applicato in modo atomico (completo) e restituiscono una risposta al coordinatore, che rilascia i blocchi e conferma il successo della transazione al cliente.<\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL + ACID\" src=\"\/wp-content\/uploads\/2019\/09\/ab20487ba53ed88e2f560a2249b5d6f9.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nE per l'annullamento, \u00e8 sufficiente che il coordinatore rilasci semplicemente la memoria occupata dallo stato della transazione.<\/p>\n<p>Come risultato delle ottimizzazioni sopra descritte, abbiamo implementato i principi ACID:<\/p>\n<ul>\n<li><b>Atomicit\u00e0<\/b>. Questa \u00e8 la garanzia che nessuna transazione sar\u00e0 registrata nel sistema in modo parziale; tutte le sue sotto-operazioni verranno eseguite o nessuna di esse verr\u00e0 eseguita. Noi rispettiamo questo principio grazie al batch registrato in Cassandra.<\/li>\n<li><b>Coerenza<\/b>. Ogni transazione riuscita registra per definizione solo risultati validi. Se, dopo l'apertura di una transazione e l'esecuzione di alcune operazioni, si scopre che il risultato non \u00e8 valido, viene eseguito un annullamento.<\/li>\n<li><b>Isolamento<\/b>. Durante l'esecuzione di una transazione, le transazioni parallele non devono influenzare il suo risultato. Le transazioni concorrenti sono isolate tramite blocchi pessimistici sul coordinatore. Per le letture fuori dalla transazione, si osserva il principio di isolamento a livello di Read Committed.<\/li>\n<li><b>Affidabilit\u00e0<\/b>. Indipendentemente dai problemi nei livelli inferiori \u2014 interruzione dell'alimentazione, guasto hardware \u2014 le modifiche effettuate da una transazione completata con successo devono rimanere salvate dopo il ripristino del funzionamento. <\/li>\n<\/ul>\n<p><\/p>\n<h2>Lettura tramite indici<\/h2>\n<p>\nConsideriamo una semplice tabella: <\/p>\n<pre><code>CREATE TABLE photos (\nid bigint primary key,\nowner bigint,\nmodified timestamp,\n\u2026)<\/code><\/pre>\n<p>\nEssa ha un ID (chiave primaria), proprietario e data di modifica. Dobbiamo eseguire una richiesta molto semplice \u2014 selezionare i dati in base al proprietario con la data di modifica \"nelle ultime 24 ore\". <\/p>\n<pre><code>SELECT *\nWHERE owner=?\nAND modified&gt;?<\/code><\/pre>\n<p>\nAffinch\u00e9 una richiesta del genere venga eseguita rapidamente, in un classico DBMS SQL \u00e8 necessario creare un indice sulle colonne (owner, modified). Possiamo farlo in modo abbastanza semplice, poich\u00e9 ora abbiamo garanzie ACID!<\/p>\n<h2>Indici in C*One<\/h2>\n<p>\nC'\u00e8 una tabella di origine con le foto, in cui l'ID della registrazione \u00e8 la chiave primaria. <\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL + ACID\" src=\"\/wp-content\/uploads\/2019\/09\/9f5d7c5f66882666a7ae8219f710beeb.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nPer l'indice C*One viene creata una nuova tabella che \u00e8 una copia della tabella originale. La chiave corrisponde all'espressione dell'indice e include anche la chiave primaria della registrazione della tabella originale:<\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL + ACID\" src=\"\/wp-content\/uploads\/2019\/09\/ce6df1c8407bca7ddc61fd88141455e6.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nOra la query per \u00abil proprietario negli ultimi giorni\u00bb pu\u00f2 essere riscritta come select da un'altra tabella:<\/p>\n<pre><code>SELECT * FROM i1_test\nWHERE owner=?\nAND modified&gt;?<\/code><\/pre>\n<p>\nLa coerenza dei dati della tabella originale photos e dell'indice i1 \u00e8 mantenuta automaticamente dal coordinatore. Sulla base solo dello schema dei dati, quando vengono ricevute delle modifiche, il coordinatore genera e memorizza le modifiche non solo della tabella principale, ma anche delle copie. Non vengono eseguite azioni aggiuntive sulla tabella dell'indice, i log non vengono letti e non vengono utilizzati blocchi. In altre parole, l'aggiunta di indici consuma quasi nessuna risorsa e influisce praticamente sulla velocit\u00e0 di applicazione delle modifiche.<\/p>\n<p>Grazie ad ACID, siamo riusciti a implementare indici \u00abcome in SQL\u00bb. Questi offrono coerenza, possono scalare, funzionano rapidamente, possono essere composti e sono integrati nel linguaggio di query CQL. Non \u00e8 necessario apportare modifiche al codice applicativo per supportare gli indici. \u00c8 tutto semplice, come in SQL. E cosa pi\u00f9 importante, gli indici non influenzano la velocit\u00e0 di esecuzione delle modifiche alla tabella delle transazioni.<\/p>\n<h2>Cosa abbiamo ottenuto<\/h2>\n<p>\nAbbiamo sviluppato C*One tre anni fa e l'abbiamo messo in produzione. <\/p>\n<p>Qual \u00e8 il risultato finale? Valutiamo questo esaminando il sottosistema di elaborazione e archiviazione delle foto, uno dei tipi di dati pi\u00f9 importanti in un social network. Non stiamo parlando solo delle immagini stesse, ma di tutte le varie informazioni sui metadati. Attualmente, su \u00abOdnoklassniki\u00bb ci sono circa 20 miliardi di tali registrazioni, il sistema gestisce 80.000 richieste di lettura al secondo e fino a 8.000 transazioni ACID al secondo, relative alla modifica dei dati. <\/p>\n<p>Quando abbiamo utilizzato SQL con un fattore di replica = 1 (ma in RAID 10), i metadati delle immagini venivano conservati su un cluster altamente disponibile di 32 macchine con Microsoft SQL Server (pi\u00f9 11 di riserva). Sono stati anche riservati 10 server per la memorizzazione dei backup. In totale, 50 macchine costose. In questo modo, il sistema operava a carico nominale, senza margine.<\/p>\n<p>Dopo la migrazione al nuovo sistema, abbiamo ottenuto un fattore di replica = 3 \u2014 una copia in ogni data center. Il sistema \u00e8 composto da 63 nodi di archiviazione Cassandra e 6 macchine coordinatrici, per un totale di 69 server. Queste macchine costano significativamente meno, il loro costo totale \u00e8 di circa il 30% rispetto al sistema SQL. Il carico rimane al livello del 30%.<\/p>\n<p>Con l'implementazione di C*One sono diminuite anche le latenze: in SQL, l'operazione di scrittura richiedeva circa 4,5 ms. In C*One \u2014 circa 1,6 ms. La durata della transazione \u00e8 mediamente inferiore a 40 ms, il commit avviene in 2 ms, la durata di lettura e scrittura \u00e8 mediamente di 2 ms. Il 99\u00b0 percentile \u00e8 di soli 3-3,1 ms, il numero di timeout \u00e8 diminuito di 100 volte \u2014 tutto grazie all'ampio utilizzo di speculazioni. <\/p>\n<p>Attualmente, la maggior parte dei nodi SQL Server \u00e8 stata dismessa, e i nuovi prodotti vengono sviluppati esclusivamente utilizzando C*One. Abbiamo adattato C*One per funzionare nel nostro cloud. <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/company\/odnoklassniki\/blog\/346868\/\">one-cloud<\/a><\/noindex>, il che ha permesso di accelerare il dispiegamento di nuovi cluster, semplificare la configurazione e automatizzare l'operativit\u00e0. Senza il codice sorgente, ci\u00f2 sarebbe stato notevolmente pi\u00f9 complicato e macchinoso. <\/p>\n<p>Ora stiamo lavorando al trasferimento di altri nostri archivi nel cloud, ma questa \u00e8 tutta un'altra storia.<br \/>\n<br \/>Fonte: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/odnoklassniki\/blog\/417593\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0414\u043e \u043d\u0435\u0434\u0430\u0432\u043d\u0435\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0432 \u041e\u0434\u043d\u043e\u043a\u043b\u0430\u0441\u0441\u043d\u0438\u043a\u0430\u0445 \u043e\u043a\u043e\u043b\u043e 50 \u0422\u0411 \u0434\u0430\u043d\u043d\u044b\u0445, \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c\u044b\u0445 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u0445\u0440\u0430\u043d\u0438\u043b\u043e\u0441\u044c \u0432 SQL Server. \u0414\u043b\u044f \u0442\u0430\u043a\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043c\u0430 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442\u044c \u0431\u044b\u0441\u0442\u0440\u044b\u0439 \u0438 \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439, \u0434\u0430 \u0435\u0449\u0435 \u0438 \u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u044b\u0439 \u043a \u043e\u0442\u043a\u0430\u0437\u0443 \u0426\u041e\u0414 \u0434\u043e\u0441\u0442\u0443\u043f, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f SQL \u0421\u0423\u0411\u0414, \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e. \u041e\u0431\u044b\u0447\u043d\u043e \u0432 \u0442\u0430\u043a\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u043e\u0434\u043d\u043e \u0438\u0437 NoSQL-\u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449, \u043d\u043e \u043d\u0435 \u0432\u0441\u0451 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u0432 NoSQL: \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u0442\u0440\u0435\u0431\u0443\u044e\u0442 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":28484,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-37956","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 4.9.10 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0414\u043e \u043d\u0435\u0434\u0430\u0432\u043d\u0435\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0432 \u041e\u0434\u043d\u043e\u043a\u043b\u0430\u0441\u0441\u043d\u0438\u043a\u0430\u0445 \u043e\u043a\u043e\u043b\u043e 50 \u0422\u0411 \u0434\u0430\u043d\u043d\u044b\u0445, \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c\u044b\u0445 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u0445\u0440\u0430\u043d\u0438\u043b\u043e\u0441\u044c \u0432 SQL Server. \u0414\u043b\u044f \u0442\u0430\u043a\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043c\u0430 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442\u044c \u0431\u044b\u0441\u0442\u0440\u044b\u0439 \u0438 \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439, \u0434\u0430 \u0435\u0449\u0435 \u0438 \u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u044b\u0439 \u043a \u043e\u0442\u043a\u0430\u0437\u0443 \u0426\u041e\u0414 \u0434\u043e\u0441\u0442\u0443\u043f, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f SQL \u0421\u0423\u0411\u0414, \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e. \u041e\u0431\u044b\u0447\u043d\u043e \u0432 \u0442\u0430\u043a\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u043e\u0434\u043d\u043e \u0438\u0437 NoSQL-\u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449, \u043d\u043e \u043d\u0435 \u0432\u0441\u0451 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u0432 NoSQL: \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u0442\u0440\u0435\u0431\u0443\u044e\u0442\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/newsql-nosql-acid\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 4.9.10\" \/>\n\t\t<meta property=\"og:locale\" content=\"it_IT\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47NewSQL = NoSQL+ACID | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0414\u043e \u043d\u0435\u0434\u0430\u0432\u043d\u0435\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0432 \u041e\u0434\u043d\u043e\u043a\u043b\u0430\u0441\u0441\u043d\u0438\u043a\u0430\u0445 \u043e\u043a\u043e\u043b\u043e 50 \u0422\u0411 \u0434\u0430\u043d\u043d\u044b\u0445, \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c\u044b\u0445 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u0445\u0440\u0430\u043d\u0438\u043b\u043e\u0441\u044c \u0432 SQL Server. \u0414\u043b\u044f \u0442\u0430\u043a\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043c\u0430 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442\u044c \u0431\u044b\u0441\u0442\u0440\u044b\u0439 \u0438 \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439, \u0434\u0430 \u0435\u0449\u0435 \u0438 \u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u044b\u0439 \u043a \u043e\u0442\u043a\u0430\u0437\u0443 \u0426\u041e\u0414 \u0434\u043e\u0441\u0442\u0443\u043f, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f SQL \u0421\u0423\u0411\u0414, \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e. \u041e\u0431\u044b\u0447\u043d\u043e \u0432 \u0442\u0430\u043a\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u043e\u0434\u043d\u043e \u0438\u0437 NoSQL-\u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449, \u043d\u043e \u043d\u0435 \u0432\u0441\u0451 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u0432 NoSQL: \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u0442\u0440\u0435\u0431\u0443\u044e\u0442\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/newsql-nosql-acid\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T19:20:47+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T19:20:47+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47NewSQL = NoSQL+ACID | ProHoster","description":"Fino a tempi recenti, in Odnoklassniki erano circa 50 TB di dati, elaborati in tempo reale, memorizzati in SQL Server. Per un tale volume, garantire un accesso veloce e affidabile, oltre a essere resistente ai guasti, utilizzando SQL DBMS \u00e8 praticamente impossibile. Di solito si utilizzano uno dei database NoSQL, ma non tutto pu\u00f2 essere trasferito a NoSQL: alcune entit\u00e0 richiedono","canonical_url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/newsql-nosql-acid","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"it_IT","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47NewSQL = NoSQL+ACID | ProHoster","og:description":"\u0414\u043e \u043d\u0435\u0434\u0430\u0432\u043d\u0435\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0432 \u041e\u0434\u043d\u043e\u043a\u043b\u0430\u0441\u0441\u043d\u0438\u043a\u0430\u0445 \u043e\u043a\u043e\u043b\u043e 50 \u0422\u0411 \u0434\u0430\u043d\u043d\u044b\u0445, \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c\u044b\u0445 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u0445\u0440\u0430\u043d\u0438\u043b\u043e\u0441\u044c \u0432 SQL Server. \u0414\u043b\u044f \u0442\u0430\u043a\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043c\u0430 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442\u044c \u0431\u044b\u0441\u0442\u0440\u044b\u0439 \u0438 \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439, \u0434\u0430 \u0435\u0449\u0435 \u0438 \u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u044b\u0439 \u043a \u043e\u0442\u043a\u0430\u0437\u0443 \u0426\u041e\u0414 \u0434\u043e\u0441\u0442\u0443\u043f, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f SQL \u0421\u0423\u0411\u0414, \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e. \u041e\u0431\u044b\u0447\u043d\u043e \u0432 \u0442\u0430\u043a\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u043e\u0434\u043d\u043e \u0438\u0437 NoSQL-\u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449, \u043d\u043e \u043d\u0435 \u0432\u0441\u0451 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u0432 NoSQL: \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u0442\u0440\u0435\u0431\u0443\u044e\u0442","og:url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/newsql-nosql-acid","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T19:20:47+00:00","article:modified_time":"2019-10-31T19:20:47+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"37956","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-23 19:55:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 01:17:23","updated":"2026-01-23 19:55:19"},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/37956","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/comments?post=37956"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/37956\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media\/28484"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media?parent=37956"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/categories?post=37956"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/tags?post=37956"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}