{"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\/pt\/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 \/>\nAt\u00e9 recentemente, no Odnoklassniki, cerca de 50 TB de dados processados em tempo real eram armazenados no SQL Server. Para tal volume, garantir acesso r\u00e1pido, confi\u00e1vel e tamb\u00e9m \u00e0 prova de falhas ao datacenter usando um SGBD SQL \u00e9 praticamente imposs\u00edvel. Normalmente, em tais casos, utiliza-se um dos armazenamentos NoSQL, mas nem tudo pode ser transferido para NoSQL: algumas entidades exigem garantias de transa\u00e7\u00f5es ACID. <\/p>\n<p>Isso nos levou ao uso de um armazenamento NewSQL, ou seja, um SGBD que oferece resist\u00eancia a falhas, escalabilidade e desempenho de sistemas NoSQL, mas mantendo as garantias ACID familiares dos sistemas cl\u00e1ssicos. H\u00e1 poucas solu\u00e7\u00f5es industriais em funcionamento dessa nova classe, portanto, n\u00f3s mesmos implementamos tal sistema e o colocamos em opera\u00e7\u00e3o industrial. <\/p>\n<p>Como isso funciona e o que conseguimos \u2014 leia mais abaixo.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><br \/>\nHoje, a audi\u00eancia mensal do 'Odnoklassniki' \u00e9 de mais de 70 milh\u00f5es de visitantes \u00fanicos. N\u00f3s <noindex><a rel=\"nofollow\" href=\"https:\/\/www.similarweb.com\/top-websites\/category\/internet-and-telecom\/social-network\">estamos entre os cinco<\/a><\/noindex> maiores redes sociais do mundo, e no vig\u00e9simo lugar entre os sites onde os usu\u00e1rios passam mais tempo. A infraestrutura do 'OK' lida com cargas muito altas: mais de um milh\u00e3o de solicita\u00e7\u00f5es HTTP por segundo nas frentes. Partes do parque de servidores, totalizando mais de 8000 unidades, est\u00e3o localizadas pr\u00f3ximas umas das outras \u2014 em quatro data centers em Moscovo, o que permite garantir uma lat\u00eancia de rede inferior a 1 ms entre eles.<\/p>\n<p>Utilizamos Cassandra desde 2010, come\u00e7ando pela vers\u00e3o 0.6. Hoje, est\u00e3o em opera\u00e7\u00e3o v\u00e1rias dezenas de clusters. O cluster mais r\u00e1pido processa mais de 4 milh\u00f5es de opera\u00e7\u00f5es por segundo, e o maior armazena 260 TB. <\/p>\n<p>No entanto, todos s\u00e3o clusters NoSQL comuns, usados para armazenar <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\">dados fracamente consistentes.<\/a><\/noindex> Quer\u00edamos substituir o principal armazenamento consistente, Microsoft SQL Server, que era utilizado desde a funda\u00e7\u00e3o do 'Odnoklassniki'. O armazenamento consistia em mais de 300 m\u00e1quinas SQL Server Standard Edition, que continham 50 TB de dados \u2014 entidades de neg\u00f3cios. Esses dados s\u00e3o modificados no contexto de transa\u00e7\u00f5es ACID e exigem <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/ACID\">alta consist\u00eancia.<\/a><\/noindex>.<\/p>\n<p>Para distribuir dados entre os n\u00f3s do SQL Server, utilizamos tanto a parti\u00e7\u00e3o vertical quanto a horizontal. <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Partition_(database)\">particionamento.<\/a><\/noindex> (sharding). Historicamente, utilizamos um esquema simples de sharding de dados: a cada entidade foi atribu\u00eddo um token - uma fun\u00e7\u00e3o baseada na ID da entidade. Entidades com o mesmo token eram colocadas no mesmo servidor SQL. A rela\u00e7\u00e3o do tipo mestre-detalhe era implementada de forma que os tokens do registro principal e do registro gerado sempre coincidissem e estivessem no mesmo servidor. Em uma rede social, quase todos os registros s\u00e3o gerados em nome do usu\u00e1rio - isso significa que todos os dados do usu\u00e1rio dentro de um subsistema funcional s\u00e3o armazenados em um \u00fanico servidor. Ou seja, em uma transa\u00e7\u00e3o de neg\u00f3cios, quase sempre estavam envolvidos apenas tabelas de um \u00fanico servidor SQL, o que permitia garantir a consist\u00eancia dos dados por meio de transa\u00e7\u00f5es ACID locais, sem necessidade de utilizar <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Two-phase_commit_protocol\">transa\u00e7\u00f5es ACID lentas e n\u00e3o confi\u00e1veis.<\/a><\/noindex> Gra\u00e7as ao sharding e para acelerar o trabalho do SQL:<\/p>\n<p>N\u00e3o utilizamos restri\u00e7\u00f5es de chave estrangeira, pois no sharding a ID da entidade pode estar em outro servidor.<\/p>\n<ul>\n<li>N\u00e3o utilizamos procedimentos armazenados e gatilhos devido \u00e0 carga adicional na CPU do SGBD.<\/li>\n<li>N\u00e3o utilizamos JOINs devido ao que foi mencionado acima e \u00e0 grande quantidade de leituras aleat\u00f3rias do disco.<\/li>\n<li>Fora das transa\u00e7\u00f5es, para reduzir bloqueios, usamos o n\u00edvel de isolamento Read Uncommitted.<\/li>\n<li>Executamos apenas transa\u00e7\u00f5es curtas (em m\u00e9dia, de menos de 100 ms).<\/li>\n<li>N\u00e3o utilizamos UPDATE e DELETE em m\u00faltiplas linhas devido ao alto n\u00famero de bloqueios - atualizamos apenas um registro de cada vez.<\/li>\n<li>As consultas sempre s\u00e3o executadas apenas por \u00edndices - uma consulta com um plano de visualiza\u00e7\u00e3o completa da tabela significa sobrecarga do banco de dados e sua falha.<\/li>\n<li>Essas etapas permitiram extrair quase o m\u00e1ximo de desempenho dos servidores SQL. No entanto, os problemas estavam se tornando cada vez mais frequentes. Vamos analis\u00e1-los.<\/li>\n<\/ul>\n<p>\nProblemas com SQL<\/p>\n<h2>Como utilizamos sharding feito \u00e0 m\u00e3o, a adi\u00e7\u00e3o de novos shards era feita manualmente pelos administradores. Durante todo esse tempo, c\u00f3pias escal\u00e1veis de dados n\u00e3o atendiam \u00e0s solicita\u00e7\u00f5es.<\/h2>\n<p><\/p>\n<ul>\n<li>\u00c0 medida que o n\u00famero de registros na tabela crescia, a velocidade de inser\u00e7\u00e3o e modifica\u00e7\u00e3o diminu\u00eda; ao adicionar \u00edndices a uma tabela existente, a velocidade ca\u00eda drasticamente, e a cria\u00e7\u00e3o e recrea\u00e7\u00e3o de \u00edndices aconteciam com tempo de inatividade. <\/li>\n<li>A presen\u00e7a de um n\u00famero limitado de Windows para SQL Server na produ\u00e7\u00e3o dificulta a gest\u00e3o da infraestrutura.<\/li>\n<li>Mas o principal problema \u00e9 -<\/li>\n<\/ul>\n<p>\nMas o principal problema \u00e9 <\/p>\n<h2>Resili\u00eancia<\/h2>\n<p>\nUm um servidor SQL cl\u00e1ssico tem uma baixa disponibilidade. Suponha que voc\u00ea tenha apenas um servidor de banco de dados, e ele falhe a cada tr\u00eas anos. Nesse per\u00edodo, o site fica fora do ar por 20 minutos, o que \u00e9 aceit\u00e1vel. Se voc\u00ea tiver 64 servidores, o site fica fora do ar a cada tr\u00eas semanas. E se voc\u00ea tiver 200 servidores, o site fica fora do ar toda semana. Esse \u00e9 um problema. <\/p>\n<p>O que pode ser feito para aumentar a disponibilidade do servidor SQL? A Wikip\u00e9dia nos sugere construir <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/High-availability_cluster\">um cluster de alta disponibilidade<\/a><\/noindex>: onde, em caso de falha de qualquer um dos componentes, h\u00e1 um substituto.<\/p>\n<p>Isso exige um parque de equipamentos caros: muita duplica\u00e7\u00e3o, fibra \u00f3ptica, armazenamento compartilhado, e a ativa\u00e7\u00e3o de um backup \u00e9 pouco confi\u00e1vel: cerca de 10% das ativa\u00e7\u00f5es resultam na falha da nodem reserva em cascata em rela\u00e7\u00e3o \u00e0 nodem principal. <\/p>\n<p>Mas a principal desvantagem de um cluster de alta disponibilidade \u00e9 a aus\u00eancia total de disponibilidade em caso de falha do data center no qual ele est\u00e1 localizado. O 'Od\u043d\u043e\u043a\u043b\u0430\u0441\u0441\u043d\u0438\u043a\u0438' tem quatro data centers, e precisamos garantir opera\u00e7\u00e3o em caso de falha total em um deles.<\/p>\n<p>Para isso, poder\u00edamos aplicar <noindex><a rel=\"nofollow\" href=\"https:\/\/technet.microsoft.com\/en-us\/library\/ms151196.aspx\">replica\u00e7\u00e3o Multi-Master<\/a><\/noindex> embutida no SQL Server. Esta solu\u00e7\u00e3o \u00e9 significativamente mais cara devido ao custo do software e sofre de problemas bem conhecidos de replica\u00e7\u00e3o \u2014 atrasos imprevis\u00edveis nas transa\u00e7\u00f5es durante replica\u00e7\u00e3o s\u00edncrona e atrasos na aplica\u00e7\u00e3o das replica\u00e7\u00f5es (e, como consequ\u00eancia, modifica\u00e7\u00f5es perdidas) durante a replica\u00e7\u00e3o ass\u00edncrona. O que implica <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\">resolu\u00e7\u00e3o de conflitos manual<\/a><\/noindex> torna essa op\u00e7\u00e3o completamente invi\u00e1vel para n\u00f3s.<\/p>\n<p>Todos esses problemas exigiam uma solu\u00e7\u00e3o radical e come\u00e7amos uma an\u00e1lise detalhada. Aqui, precisamos nos familiarizar com o que o SQL Server faz principalmente \u2014 transa\u00e7\u00f5es.<\/p>\n<h2>Transa\u00e7\u00e3o simples<\/h2>\n<p>\nConsideremos a mais simples, do ponto de vista de um programador SQL pr\u00e1tico: adicionar uma foto a um \u00e1lbum. \u00c1lbum e fotos s\u00e3o armazenados em tabelas diferentes. O \u00e1lbum possui um contador de fotos p\u00fablicas. Ent\u00e3o, essa transa\u00e7\u00e3o se divide nos seguintes passos: <\/p>\n<ol>\n<li>Bloqueamos o \u00e1lbum pela chave.<\/li>\n<li>Criamos um registro na tabela de fotos. <\/li>\n<li>Se a foto tiver status p\u00fablico, ent\u00e3o incrementamos o contador de fotos p\u00fablicas no \u00e1lbum, atualizamos o registro e comitamos a transa\u00e7\u00e3o.<\/li>\n<\/ol>\n<p>\nOu na forma de pseudoc\u00f3digo:<\/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>\nObservamos que o cen\u00e1rio de transa\u00e7\u00e3o de neg\u00f3cios mais comum \u00e9 ler dados do banco de dados na mem\u00f3ria do servidor de aplicativos, fazer algumas altera\u00e7\u00f5es e salvar os novos valores de volta no banco de dados. Normalmente, em tal transa\u00e7\u00e3o, atualizamos v\u00e1rias entidades, v\u00e1rias tabelas. <\/p>\n<p>Durante a execu\u00e7\u00e3o da transa\u00e7\u00e3o, pode ocorrer a modifica\u00e7\u00e3o concorrente dos mesmos dados a partir de outro sistema. Por exemplo, um sistema anti-spam pode decidir que um usu\u00e1rio \u00e9 suspeito e, portanto, todas as fotos do usu\u00e1rio n\u00e3o devem mais ser p\u00fablicas, elas precisam ser enviadas para modera\u00e7\u00e3o, o que significa alterar photo.status para algum outro valor e reverter os contadores correspondentes. \u00c9 evidente que, se essa opera\u00e7\u00e3o ocorrer sem garantias de atomicidade e isolamento das modifica\u00e7\u00f5es concorrentes, como em <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/ACID\">ACID<\/a><\/noindex>, o resultado n\u00e3o ser\u00e1 o necess\u00e1rio \u2014 ou o contador de fotos mostrar\u00e1 um valor incorreto, ou nem todas as fotos ser\u00e3o enviadas para modera\u00e7\u00e3o. <\/p>\n<p>Tal c\u00f3digo, manipulando v\u00e1rias entidades de neg\u00f3cios dentro de uma \u00fanica transa\u00e7\u00e3o, foi escrito em grande quantidade durante toda a exist\u00eancia da plataforma. Por meio de experi\u00eancias de migra\u00e7\u00e3o para NoSQL com <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Eventual_consistency\">Consist\u00eancia Eventual<\/a><\/noindex> , sabemos que as maiores dificuldades (e gastos de tempo) est\u00e3o relacionadas \u00e0 necessidade de desenvolver c\u00f3digo que mantenha a consist\u00eancia dos dados. Portanto, a principal exig\u00eancia para o novo armazenamento foi garantir que a l\u00f3gica de aplica\u00e7\u00e3o suporte verdadeiras transa\u00e7\u00f5es ACID. <\/p>\n<p>Outras exig\u00eancias igualmente importantes foram:<\/p>\n<ul>\n<li>Em caso de falha do data center, a leitura e a grava\u00e7\u00e3o no novo armazenamento devem estar dispon\u00edveis.<\/li>\n<li>Manter a velocidade atual de desenvolvimento. Ou seja, ao trabalhar com o novo armazenamento, a quantidade de c\u00f3digo deve ser aproximadamente a mesma, n\u00e3o deve haver necessidade de adicionar nada ao armazenamento, desenvolver algoritmos de resolu\u00e7\u00e3o de conflitos, manter \u00edndices secund\u00e1rios, etc. <\/li>\n<li>A velocidade de opera\u00e7\u00e3o do novo armazenamento deve ser suficientemente alta, tanto na leitura de dados quanto no processamento de transa\u00e7\u00f5es, o que significa efetivamente que solu\u00e7\u00f5es acad\u00eamicas rigorosas, universais, mas lentas, como, por exemplo, <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Two-phase_commit_protocol\">compromissos de duas fases<\/a><\/noindex>.<\/li>\n<li>Escalabilidade autom\u00e1tica em tempo real. <\/li>\n<li>Uso de servidores comuns e acess\u00edveis, sem a necessidade de comprar equipamentos ex\u00f3ticos. <\/li>\n<li>Capacidade de expandir o armazenamento com os esfor\u00e7os dos desenvolvedores da empresa. Em outras palavras, a prioridade era dada a solu\u00e7\u00f5es pr\u00f3prias ou baseadas em c\u00f3digo aberto, preferencialmente em Java.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Solu\u00e7\u00f5es, solu\u00e7\u00f5es<\/h2>\n<p>\nAnalisando as poss\u00edveis solu\u00e7\u00f5es, chegamos a duas op\u00e7\u00f5es de arquitetura:<\/p>\n<p>Primeira \u2014 utilizar qualquer servidor SQL e implementar a resili\u00eancia necess\u00e1ria, o mecanismo de escalabilidade, cluster resistente a falhas, resolu\u00e7\u00e3o de conflitos e transa\u00e7\u00f5es ACID distribu\u00eddas, confi\u00e1veis e r\u00e1pidas. Avaliamos essa op\u00e7\u00e3o como bastante n\u00e3o trivial e trabalhosa.<\/p>\n<p>A segunda op\u00e7\u00e3o \u2014 adotar um reposit\u00f3rio NoSQL pronto, com escalabilidade implementada, cluster resistente a falhas, resolu\u00e7\u00e3o de conflitos e implementar transa\u00e7\u00f5es e SQL por conta pr\u00f3pria. \u00c0 primeira vista, a tarefa de implementar SQL, sem mencionar as transa\u00e7\u00f5es ACID, parece um desafio que levar\u00e1 anos. Mas depois percebemos que o conjunto de funcionalidades SQL que usamos na pr\u00e1tica est\u00e1 longe do ANSI SQL, assim como <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Apache_Cassandra#Cassandra_Query_Language\">Cassandra CQL<\/a><\/noindex> est\u00e1 longe do ANSI SQL. Ao olhar com mais aten\u00e7\u00e3o para o CQL, percebemos que ele se aproxima bastante do que precisamos.<\/p>\n<h2>Cassandra e CQL<\/h2>\n<p>\nEnt\u00e3o, o que torna o Cassandra interessante e quais recursos ele possui?<\/p>\n<p>Em primeiro lugar, \u00e9 poss\u00edvel criar tabelas com suporte a diversos tipos de dados, e \u00e9 poss\u00edvel fazer SELECT ou UPDATE pela chave prim\u00e1ria.<\/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>\nPara garantir a consist\u00eancia dos dados das r\u00e9plicas, o Cassandra utiliza <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Quorum_(distributed_computing)\">uma abordagem de qu\u00f3rum<\/a><\/noindex>. No caso mais simples, isso significa que, ao colocar tr\u00eas r\u00e9plicas da mesma linha em diferentes n\u00f3s do cluster, a grava\u00e7\u00e3o \u00e9 considerada bem-sucedida se a maioria dos n\u00f3s (ou seja, duas de tr\u00eas) confirmou o sucesso dessa opera\u00e7\u00e3o de grava\u00e7\u00e3o. Os dados da linha s\u00e3o considerados consistentes se, ao serem lidos, a maioria dos n\u00f3s foram questionados e confirmaram isso. Assim, com tr\u00eas r\u00e9plicas, garante-se uma consist\u00eancia total e instant\u00e2nea dos dados em caso de falha de um n\u00f3. Essa abordagem nos permitiu implementar um esquema ainda mais confi\u00e1vel: sempre enviar solicita\u00e7\u00f5es a todas as tr\u00eas r\u00e9plicas, aguardando a resposta das duas mais r\u00e1pidas. A resposta atrasada da terceira r\u00e9plica \u00e9, nesse caso, descartada. O n\u00f3 que atrasou a resposta pode ter s\u00e9rios problemas \u2014 lentid\u00e3o, coleta de lixo na JVM, recupera\u00e7\u00e3o de mem\u00f3ria direta no kernel do Linux, falhas de hardware, desconex\u00e3o da rede. No entanto, isso n\u00e3o afeta a opera\u00e7\u00e3o do cliente nem os dados.<\/p>\n<p>A abordagem em que consultamos tr\u00eas n\u00f3s, recebendo a resposta de dois, \u00e9 chamada <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Speculative_execution\">de especula\u00e7\u00e3o<\/a><\/noindex>: a solicita\u00e7\u00e3o para r\u00e9plicas extras \u00e9 enviada antes de 'cair'. <\/p>\n<p>Outra das vantagens do Cassandra \u00e9 o Batchlog \u2014 um mecanismo que garante a aplica\u00e7\u00e3o completa ou a n\u00e3o aplica\u00e7\u00e3o total do pacote de altera\u00e7\u00f5es que voc\u00ea faz. Isso nos permite resolver o A em ACID \u2014 atomicidade de forma nativa.<\/p>\n<p>O mais pr\u00f3ximo das transa\u00e7\u00f5es no Cassandra \u00e9 o que se chama de &#171;<noindex><a rel=\"nofollow\" href=\"https:\/\/docs.datastax.com\/en\/cql\/3.3\/cql\/cql_using\/useInsertLWT.html\">transa\u00e7\u00f5es leves<\/a><\/noindex>&#171;. Mas, em compara\u00e7\u00e3o com as 'verdadeiras' transa\u00e7\u00f5es ACID, elas est\u00e3o longe disso: na verdade, \u00e9 a possibilidade de fazer <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Compare-and-swap\">CAS<\/a><\/noindex> nos dados apenas de um \u00fanico registro, utilizando um consenso pelo pesado protocolo Paxos. Portanto, a velocidade dessas transa\u00e7\u00f5es n\u00e3o \u00e9 alta. <\/p>\n<h2>O que nos faltou no Cassandra<\/h2>\n<p>\nAssim, precis\u00e1vamos implementar transa\u00e7\u00f5es ACID reais no Cassandra. Com as quais poder\u00edamos facilmente implementar duas outras funcionalidades \u00fateis dos DBMS cl\u00e1ssicos: \u00edndices r\u00e1pidos consistentes, o que nos permitiria realizar consultas de dados n\u00e3o apenas pela chave prim\u00e1ria e um gerador comum de IDs auto-incrementais monot\u00f4nicos.<\/p>\n<h4>C*One<\/h4>\n<p>\nAssim nasceu um novo SGBD <b>C*One<\/b>, composto por tr\u00eas tipos de n\u00f3s de servidor:<\/p>\n<ul>\n<li>Armazenadores \u2014 servidores Cassandra (quase) padr\u00e3o, respons\u00e1veis pela armazenamento de dados em discos locais. \u00c0 medida que a carga e o volume de dados aumentam, seu n\u00famero pode ser facilmente escalonado para dezenas e centenas.<\/li>\n<li>Os coordenadores de transa\u00e7\u00f5es garantem a execu\u00e7\u00e3o das transa\u00e7\u00f5es. <\/li>\n<li>Os clientes s\u00e3o servidores de aplicativos que realizam opera\u00e7\u00f5es comerciais e iniciam transa\u00e7\u00f5es. Pode haver milhares desses clientes.<\/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 \/>\nServidores de todos os tipos fazem parte de um cluster comum, utilizam o protocolo de mensagens interno Cassandra para se comunicarem entre si e <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Gossip_protocol\">gossip<\/a><\/noindex> para trocar informa\u00e7\u00f5es de cluster. Atrav\u00e9s do Heartbeat, os servidores detectam falhas m\u00fatuas, mant\u00eam um esquema de dados unificado \u2014 tabelas, sua estrutura e replica\u00e7\u00e3o; esquema de particionamento, topologia do cluster, etc.<\/p>\n<h4>Clientes<\/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 \/>\nEm vez de drivers padr\u00e3o, utiliza-se o modo Fat Client. Esse n\u00f3 n\u00e3o armazena dados, mas pode atuar como coordenador da execu\u00e7\u00e3o de solicita\u00e7\u00f5es, ou seja, o Cliente mesmo desempenha a fun\u00e7\u00e3o de coordenador de suas solicita\u00e7\u00f5es: interroga as r\u00e9plicas do armazenamento e resolve conflitos. Isso n\u00e3o apenas \u00e9 mais confi\u00e1vel e r\u00e1pido do que o driver padr\u00e3o, que exige comunica\u00e7\u00e3o com um coordenador remoto, mas tamb\u00e9m permite gerenciar a transmiss\u00e3o de solicita\u00e7\u00f5es. Fora de uma transa\u00e7\u00e3o aberta pelo cliente, as solicita\u00e7\u00f5es s\u00e3o direcionadas para os armazenamentos. Se o cliente abriu uma transa\u00e7\u00e3o, todas as solicita\u00e7\u00f5es dentro da transa\u00e7\u00e3o s\u00e3o direcionadas para o coordenador de transa\u00e7\u00f5es.<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>Coordenador de transa\u00e7\u00f5es C*One<\/h2>\n<p>\nCoordenador \u00e9 o que implementamos para o C*One do zero. Ele \u00e9 respons\u00e1vel pela gest\u00e3o de transa\u00e7\u00f5es, bloqueios e ordem de aplica\u00e7\u00e3o das transa\u00e7\u00f5es.<\/p>\n<p>Para cada transa\u00e7\u00e3o atendida, o coordenador gera um timestamp: cada um subsequente \u00e9 maior do que o anterior. Como no Cassandra o sistema de resolu\u00e7\u00e3o de conflitos \u00e9 baseado em timestamps (das duas entradas conflitantes, considera-se a de timestamp mais recente como v\u00e1lida), o conflito sempre ser\u00e1 resolvido a favor da transa\u00e7\u00e3o subsequente. Assim, implementamos <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\">os rel\u00f3gios de Lamport<\/a><\/noindex> \u2014 uma forma econ\u00f4mica de resolu\u00e7\u00e3o de conflitos em um sistema distribu\u00eddo.<\/p>\n<h2>Bloqueios<\/h2>\n<p>\nPara garantir isolamento, decidimos usar a maneira mais simples \u2014 bloqueios pessimistas pela chave prim\u00e1ria do registro. Em outras palavras, em uma transa\u00e7\u00e3o, o registro deve ser bloqueado primeiro, e s\u00f3 ent\u00e3o pode ser lido, modificado e salvo. Apenas ap\u00f3s um commit bem-sucedido o registro pode ser desbloqueado, permitindo que transa\u00e7\u00f5es concorrentes possam utiliz\u00e1-lo.<\/p>\n<p>A implementa\u00e7\u00e3o desse bloqueio \u00e9 simples em um ambiente n\u00e3o distribu\u00eddo. Em um sistema distribu\u00eddo, existem dois caminhos principais: implementar um bloqueio distribu\u00eddo no cluster ou distribuir as transa\u00e7\u00f5es de modo que as transa\u00e7\u00f5es envolvendo um \u00fanico registro sejam sempre atendidas pelo mesmo coordenador.<\/p>\n<p>Como, no nosso caso, os dados j\u00e1 est\u00e3o distribu\u00eddos em grupos de transa\u00e7\u00f5es locais no SQL, decidiu-se atribuir a coordenadores grupos de transa\u00e7\u00f5es locais: um coordenador gerencia todas as transa\u00e7\u00f5es com token de 0 a 9, o segundo, com token de 10 a 19, e assim por diante. Com isso, cada uma das inst\u00e2ncias de coordenador se torna o mestre do grupo de transa\u00e7\u00f5es. <\/p>\n<p>Assim, os bloqueios podem ser implementados como um simples HashMap na mem\u00f3ria do coordenador.<\/p>\n<h2>Falhas dos coordenadores<\/h2>\n<p>\nComo um coordenador atende exclusivamente a um grupo de transa\u00e7\u00f5es, \u00e9 muito importante identificar rapidamente a ocorr\u00eancia de sua falha, para que a nova tentativa de executar a transa\u00e7\u00e3o esteja dentro do tempo limite. Para isso, aplicamos um protocolo de heartbeat totalmente conectado de qu\u00f3rum:<\/p>\n<p>Em cada data center, h\u00e1 pelo menos duas n\u00f3s de coordenador. Periodicamente, cada coordenador envia uma mensagem de heartbeat para os demais coordenadores, informando sobre seu funcionamento, bem como sobre quais mensagens de heartbeat recebeu dos outros coordenadores no cluster por \u00faltimo. <\/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 \/>\nAo receber informa\u00e7\u00f5es semelhantes dos demais em suas mensagens de heartbeat, cada coordenador decide quais n\u00f3s do cluster est\u00e3o funcionando e quais n\u00e3o est\u00e3o, baseando-se no princ\u00edpio de qu\u00f3rum: se o n\u00f3 X recebeu da maioria dos n\u00f3s no cluster a informa\u00e7\u00e3o sobre a recep\u00e7\u00e3o normal das mensagens do n\u00f3 Y, ent\u00e3o Y est\u00e1 ativo. E vice-versa, assim que a maioria informar a falta de mensagens do n\u00f3 Y, significa que Y falhou. Curiosamente, se o qu\u00f3rum informar ao n\u00f3 X que n\u00e3o est\u00e1 mais recebendo mensagens dele, ent\u00e3o o pr\u00f3prio n\u00f3 X se considerar\u00e1 como tendo falhado.<\/p>\n<p>As mensagens de Heartbeat s\u00e3o enviadas com alta frequ\u00eancia, cerca de 20 vezes por segundo, com um intervalo de 50 ms. Em Java, \u00e9 dif\u00edcil garantir uma resposta do aplicativo dentro de 50 ms devido \u00e0 dura\u00e7\u00e3o compar\u00e1vel das pausas causadas pelo coletor de lixo. Conseguimos alcan\u00e7ar esse tempo de resposta usando o coletor de lixo G1, que permite especificar um objetivo para a dura\u00e7\u00e3o das pausas do GC. No entanto, \u00e0s vezes, raramente, as pausas do coletor ultrapassam 50 ms, o que pode levar a uma detec\u00e7\u00e3o falsa de falha. Para evitar isso, o coordenador n\u00e3o reporta a falha de um n\u00f3 remoto ao perder a primeira mensagem heartbeat dele, mas apenas se v\u00e1rias mensagens consecutivas forem perdidas. Assim, conseguimos detectar a falha do n\u00f3 coordenador em 200 ms. <\/p>\n<p>Mas \u00e9 insuficiente entender rapidamente qual n\u00f3 deixou de funcionar. Precisamos fazer algo a respeito. <\/p>\n<h2>Redund\u00e2ncia<\/h2>\n<p>\nO esquema cl\u00e1ssico prev\u00ea, em caso de falha do mestre, a realiza\u00e7\u00e3o de elei\u00e7\u00f5es para um novo com um dos<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\"> modestos<\/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\">universais<\/a><\/noindex> algoritmos. No entanto, esses algoritmos t\u00eam problemas bem conhecidos com a converg\u00eancia em tempo e a dura\u00e7\u00e3o do pr\u00f3prio processo eleitoral. Conseguimos evitar essas delongas adicionais utilizando o esquema de substitui\u00e7\u00e3o de coordenadores em uma rede totalmente conectada:<\/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 \/>\nSuponha que queiramos realizar uma transa\u00e7\u00e3o em um grupo 50. Vamos definir antecipadamente um esquema de substitui\u00e7\u00e3o, ou seja, quais n\u00f3s executar\u00e3o as transa\u00e7\u00f5es do grupo 50 em caso de falha do coordenador principal. Nosso objetivo \u00e9 manter a funcionalidade do sistema em caso de falha do data center. Vamos definir que o primeiro reserva ser\u00e1 um n\u00f3 de outro data center, e o segundo reserva ser\u00e1 um n\u00f3 do terceiro. Esse esquema \u00e9 escolhido uma vez e n\u00e3o muda at\u00e9 que a topologia do cluster mude, ou seja, at\u00e9 que novos n\u00f3s entrem nele (o que acontece muito raramente). A ordem de escolha do novo mestre ativo em caso de falha do antigo ser\u00e1 sempre: o primeiro reserva se tornar\u00e1 o mestre ativo, e se ele tamb\u00e9m deixar de funcionar \u2014 o segundo reserva. <\/p>\n<p>Esse esquema \u00e9 mais confi\u00e1vel que um algoritmo universal, pois para ativar um novo mestre \u00e9 suficiente apenas confirmar a falha do antigo.<\/p>\n<p>Mas como os clientes saber\u00e3o qual dos mestres est\u00e1 trabalhando no momento? \u00c9 imposs\u00edvel enviar informa\u00e7\u00f5es para milhares de clientes em 50 ms. Pode ocorrer uma situa\u00e7\u00e3o em que o cliente envia um pedido para abrir uma transa\u00e7\u00e3o, ainda sem saber que esse mestre j\u00e1 n\u00e3o est\u00e1 funcionando, e o pedido ficar\u00e1 pendente at\u00e9 o tempo limite. Para evitar que isso aconte\u00e7a, os clientes enviam especulativamente um pedido para abrir a transa\u00e7\u00e3o diretamente ao mestre do grupo e a ambas as suas reservas, mas apenas aquele que \u00e9 o mestre ativo no momento responder\u00e1 a esse pedido. Toda a comunica\u00e7\u00e3o subsequente dentro da transa\u00e7\u00e3o ser\u00e1 realizada apenas com o mestre ativo.<\/p>\n<p>Os mestres de reserva colocam em uma fila de transa\u00e7\u00f5es n\u00e3o nas suas pr\u00f3prias os pedidos recebidos, onde eles permanecem por um per\u00edodo de tempo. Se o mestre ativo falhar, um novo mestre processa os pedidos de abertura de transa\u00e7\u00f5es de sua fila e responde ao cliente. Se o cliente j\u00e1 tiver aberto uma transa\u00e7\u00e3o com o mestre antigo, a segunda resposta \u00e9 ignorada (e, obviamente, essa transa\u00e7\u00e3o n\u00e3o ser\u00e1 finalizada e ser\u00e1 repetida pelo cliente).<\/p>\n<h2>Como funciona a transa\u00e7\u00e3o<\/h2>\n<p>\nSuponha que o cliente enviou ao coordenador um pedido para abrir uma transa\u00e7\u00e3o para uma determinada entidade com uma determinada chave prim\u00e1ria. O coordenador bloqueia essa entidade e a coloca na tabela de bloqueios na mem\u00f3ria. Se necess\u00e1rio, o coordenador l\u00ea essa entidade do armazenamento e salva os dados obtidos no estado da transa\u00e7\u00e3o na mem\u00f3ria do coordenador.<\/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 o cliente deseja alterar os dados na transa\u00e7\u00e3o, ele envia ao coordenador um pedido para modificar a entidade, e este coloca os novos dados na tabela de estado das transa\u00e7\u00f5es na mem\u00f3ria. Neste ponto, a grava\u00e7\u00e3o \u00e9 conclu\u00edda \u2014 a grava\u00e7\u00e3o no armazenamento n\u00e3o \u00e9 realizada.<\/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 o cliente solicita dados que ele mesmo alterou dentro de uma transa\u00e7\u00e3o ativa, o coordenador procede da seguinte forma: <\/p>\n<ul>\n<li>se o ID j\u00e1 estiver na transa\u00e7\u00e3o, os dados s\u00e3o extra\u00eddos da mem\u00f3ria; <\/li>\n<li>se o ID n\u00e3o estiver na mem\u00f3ria, os dados ausentes s\u00e3o lidos dos n\u00f3s de armazenamento, combinados com os que j\u00e1 est\u00e3o na mem\u00f3ria, e o resultado \u00e9 entregue ao cliente. <\/li>\n<\/ul>\n<p>\nAssim, o cliente pode ler suas pr\u00f3prias altera\u00e7\u00f5es, enquanto outros clientes n\u00e3o veem essas altera\u00e7\u00f5es, porque elas est\u00e3o armazenadas apenas na mem\u00f3ria do coordenador e ainda n\u00e3o est\u00e3o nos n\u00f3s do 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 o cliente envia um commit, o estado armazenado na mem\u00f3ria do servi\u00e7o \u00e9 salvo pelo coordenador em um logged batch, que \u00e9 ent\u00e3o enviado aos reposit\u00f3rios Cassandra. Os reposit\u00f3rios fazem tudo o que \u00e9 necess\u00e1rio para que este pacote seja aplicado de forma at\u00f4mica (completa) e retornam uma resposta ao coordenador, que libera os bloqueios e confirma o sucesso da transa\u00e7\u00e3o ao 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 para o rollback, o coordenador s\u00f3 precisa liberar a mem\u00f3ria ocupada pelo estado da transa\u00e7\u00e3o.<\/p>\n<p>Como resultado das melhorias descritas acima, implementamos os princ\u00edpios ACID:<\/p>\n<ul>\n<li><b>Atomicidade<\/b>. Isso garante que nenhuma transa\u00e7\u00e3o seja registrada no sistema parcialmente; ser\u00e3o executadas todas as suas subopera\u00e7\u00f5es ou nenhuma delas. Este princ\u00edpio \u00e9 respeitado por meio do logged batch no Cassandra.<\/li>\n<li><b>Consist\u00eancia<\/b>. Cada transa\u00e7\u00e3o bem-sucedida, por defini\u00e7\u00e3o, registra apenas resultados v\u00e1lidos. Se, ap\u00f3s a abertura da transa\u00e7\u00e3o e a execu\u00e7\u00e3o de algumas opera\u00e7\u00f5es, perceber-se que o resultado \u00e9 inv\u00e1lido, um rollback \u00e9 realizado.<\/li>\n<li><b>Isolamento<\/b>. Ao executar uma transa\u00e7\u00e3o, transa\u00e7\u00f5es paralelas n\u00e3o devem influenciar seu resultado. Transa\u00e7\u00f5es concorrentes s\u00e3o isoladas por meio de bloqueios pessimistas no coordenador. Para leituras fora da transa\u00e7\u00e3o, o princ\u00edpio de isolamento \u00e9 mantido no n\u00edvel Read Committed.<\/li>\n<li><b>Resili\u00eancia<\/b>. Independentemente dos problemas em n\u00edveis inferiores \u2014 desligamento do sistema, falha de hardware \u2014 altera\u00e7\u00f5es realizadas por uma transa\u00e7\u00e3o conclu\u00edda com sucesso devem permanecer salvas ap\u00f3s a retomada do funcionamento. <\/li>\n<\/ul>\n<p><\/p>\n<h2>Leitura por \u00edndices<\/h2>\n<p>\nVamos pegar uma tabela simples: <\/p>\n<pre><code>CREATE TABLE photos (\nid bigint primary key,\nowner bigint,\nmodified timestamp,\n\u2026)<\/code><\/pre>\n<p>\nEla possui ID (chave prim\u00e1ria), propriet\u00e1rio e data de modifica\u00e7\u00e3o. Precisamos fazer uma consulta muito simples \u2014 selecionar dados pelo propriet\u00e1rio com a data de modifica\u00e7\u00e3o \"nas \u00faltimas 24 horas\". <\/p>\n<pre><code>SELECT *\nWHERE owner=?\nAND modified&gt;?<\/code><\/pre>\n<p>\nPara que tal consulta seja executada rapidamente, em um SGBD SQL cl\u00e1ssico, \u00e9 necess\u00e1rio construir um \u00edndice nas colunas (owner, modified). Podemos fazer isso de forma bastante simples, j\u00e1 que agora temos garantias ACID!<\/p>\n<h2>\u00cdndices no C*One<\/h2>\n<p>\nH\u00e1 uma tabela original com fotos, na qual o ID do registro \u00e9 a chave prim\u00e1ria. <\/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 \/>\nPara o \u00edndice, o C*One cria uma nova tabela que \u00e9 uma c\u00f3pia da original. A chave coincide com a express\u00e3o do \u00edndice, e inclui tamb\u00e9m a chave prim\u00e1ria do registro da tabela original:<\/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 \/>\nAgora, a consulta por \"propriet\u00e1rio nas \u00faltimas 24 horas\" pode ser reescrita como um select de outra tabela:<\/p>\n<pre><code>SELECT * FROM i1_test\nWHERE owner=?\nAND modified&gt;?<\/code><\/pre>\n<p>\nA consist\u00eancia dos dados da tabela original photos e do \u00edndice i1 \u00e9 mantida automaticamente pelo coordenador. Com base apenas no esquema de dados, ao receber uma altera\u00e7\u00e3o, o coordenador gera e armazena a altera\u00e7\u00e3o n\u00e3o apenas da tabela principal, mas tamb\u00e9m das c\u00f3pias. Nenhuma a\u00e7\u00e3o adicional \u00e9 realizada na tabela de \u00edndice, os logs n\u00e3o s\u00e3o lidos, bloqueios n\u00e3o s\u00e3o utilizados. Ou seja, adicionar \u00edndices consome quase nenhum recurso e praticamente n\u00e3o influencia na velocidade das modifica\u00e7\u00f5es.<\/p>\n<p>Com o ACID, conseguimos implementar \u00edndices \"como no SQL\". Eles t\u00eam consist\u00eancia, podem ser escal\u00e1veis, funcionam rapidamente, podem ser compostos e integrados na linguagem de consulta CQL. N\u00e3o \u00e9 necess\u00e1rio fazer altera\u00e7\u00f5es no c\u00f3digo do aplicativo para suportar os \u00edndices. \u00c9 tudo t\u00e3o simples quanto no SQL. E o mais importante, os \u00edndices n\u00e3o afetam a velocidade de execu\u00e7\u00e3o das modifica\u00e7\u00f5es na tabela de transa\u00e7\u00f5es original.<\/p>\n<h2>O que foi produzido<\/h2>\n<p>\nDesenvolvemos o C*One h\u00e1 tr\u00eas anos e o colocamos em opera\u00e7\u00e3o industrial. <\/p>\n<p>O que conseguimos no final? Vamos avaliar isso com base no subsistema de processamento e armazenamento de fotos, um dos tipos de dados mais importantes em uma rede social. N\u00e3o estamos falando apenas dos arquivos das fotos, mas de toda a metainforma\u00e7\u00e3o. Atualmente, no \"Od\u043d\u043e\u043a\u043b\u0430\u0441\u0441\u043d\u0438\u043a\u0438\", h\u00e1 cerca de 20 bilh\u00f5es desses registros, o sistema processa 80 mil consultas de leitura por segundo, at\u00e9 8 mil transa\u00e7\u00f5es ACID por segundo relacionadas \u00e0 modifica\u00e7\u00e3o de dados. <\/p>\n<p>Quando usamos SQL com fator de replica\u00e7\u00e3o = 1 (mas em RAID 10), a metainforma\u00e7\u00e3o das fotos era armazenada em um cluster altamente dispon\u00edvel de 32 m\u00e1quinas com Microsoft SQL Server (mais 11 reservas). Tamb\u00e9m foram designados 10 servidores para armazenamento de backups. No total, eram 50 m\u00e1quinas caras. Nesse cen\u00e1rio, o sistema funcionou com carga nominal, sem margem.<\/p>\n<p>Ap\u00f3s a migra\u00e7\u00e3o para o novo sistema, obtivemos um fator de replica\u00e7\u00e3o = 3 \u2014 com uma c\u00f3pia em cada data center. O sistema consiste em 63 n\u00f3s de armazenamento Cassandra e 6 m\u00e1quinas coordenadoras, totalizando 69 servidores. No entanto, essas m\u00e1quinas s\u00e3o significativamente mais baratas, e seu custo total representa cerca de 30% do custo do sistema em SQL. A carga se mant\u00e9m em torno de 30%.<\/p>\n<p>Com a implementa\u00e7\u00e3o do C*One, as lat\u00eancias tamb\u00e9m diminu\u00edram: em SQL, a opera\u00e7\u00e3o de grava\u00e7\u00e3o levava cerca de 4,5 ms. No C*One, cerca de 1,6 ms. A dura\u00e7\u00e3o da transa\u00e7\u00e3o, em m\u00e9dia, \u00e9 inferior a 40 ms, o commit \u00e9 realizado em 2 ms, e a dura\u00e7\u00e3o de leitura e escrita \u00e9, em m\u00e9dia, de 2 ms. O percentil 99 \u00e9 de apenas 3-3,1 ms, e o n\u00famero de timeouts diminuiu em 100 vezes \u2014 tudo isso gra\u00e7as ao amplo uso de especula\u00e7\u00f5es. <\/p>\n<p>At\u00e9 o momento, a maior parte dos n\u00f3s do SQL Server foi desativada, e novos produtos est\u00e3o sendo desenvolvidos apenas com a utiliza\u00e7\u00e3o do C*One. Adaptamos o C*One para funcionar em nossa nuvem <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/company\/odnoklassniki\/blog\/346868\/\">one-cloud<\/a><\/noindex>, o que permitiu acelerar a implanta\u00e7\u00e3o de novos clusters, simplificar a configura\u00e7\u00e3o e automatizar a opera\u00e7\u00e3o. Sem o c\u00f3digo-fonte, isso teria sido significativamente mais complicado e improvisado. <\/p>\n<p>Atualmente, estamos trabalhando na migra\u00e7\u00e3o de outros nossos armaz\u00e9ns para a nuvem \u2014 mas isso \u00e9 uma hist\u00f3ria completamente diferente.<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 5.0.3 - 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.\" \/>\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\/pt\/blog\/administrirovanie\/newsql-nosql-acid\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"pt_PT\" \/>\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.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/pt\/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":"At\u00e9 recentemente, havia cerca de 50 TB de dados processados no Odnoklassniki.","canonical_url":"https:\/\/prohoster.info\/pt\/blog\/administrirovanie\/newsql-nosql-acid","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"pt_PT","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.","og:url":"https:\/\/prohoster.info\/pt\/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","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts\/37956","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/comments?post=37956"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts\/37956\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/media\/28484"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/media?parent=37956"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/categories?post=37956"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/tags?post=37956"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}