NewSQL = NoSQL+ACID

NewSQL = NoSQL+ACID
Ata hai pouco, Odnoklassniki almacenaba preto de 50 TB de datos procesados ​​en tempo real en SQL Server. Para tal volume, é case imposible proporcionar un acceso rápido e fiable e incluso tolerante a fallos no centro de datos mediante un DBMS SQL. Normalmente, nestes casos, utilízase un dos almacenamentos NoSQL, pero non todo se pode transferir a NoSQL: algunhas entidades requiren garantías de transacción ACID.

Isto levounos a utilizar o almacenamento NewSQL, é dicir, un DBMS que proporciona tolerancia a fallos, escalabilidade e rendemento dos sistemas NoSQL, pero que ao mesmo tempo mantén as garantías ACID coñecidas para os sistemas clásicos. Hai poucos sistemas industriais que funcionan desta nova clase, polo que implementamos un sistema deste tipo e puxémolo en funcionamento comercial.

Como funciona e que pasou - le debaixo do corte.

Hoxe, a audiencia mensual de Odnoklassniki é de máis de 70 millóns de visitantes únicos. Nós Estamos entre os cinco primeiros redes sociais máis grandes do mundo, e entre os vinte sitios nos que os usuarios pasan máis tempo. A infraestrutura OK manexa cargas moi elevadas: máis dun millón de solicitudes HTTP/seg por fronte. As partes dunha flota de servidores de máis de 8000 pezas están situadas preto unhas das outras, en catro centros de datos de Moscova, o que permite unha latencia de rede de menos de 1 ms entre eles.

Usamos Cassandra desde 2010, comezando coa versión 0.6. Hoxe hai varias ducias de clusters en funcionamento. O clúster máis rápido procesa máis de 4 millóns de operacións por segundo e o máis grande almacena 260 TB.

Non obstante, estes son todos os clústeres NoSQL comúns utilizados para o almacenamento débilmente coordinado datos. Queriamos substituír o almacenamento consistente principal, Microsoft SQL Server, que se utilizou desde a fundación de Odnoklassniki. O almacenamento consistía en máis de 300 máquinas SQL Server Standard Edition, que contiñan 50 TB de datos: entidades empresariais. Estes datos modifícanse como parte das transaccións con ACID e requiren alta consistencia.

Para distribuír datos entre os nodos de SQL Server, usamos tanto vertical como horizontal partición (fragmento). Historicamente, utilizamos un esquema de fragmentación de datos sinxelo: cada entidade estaba asociada cun token, unha función do ID da entidade. As entidades co mesmo token colocáronse no mesmo servidor SQL. Implementouse a relación mestre-detalle para que os tokens dos rexistros principal e fillo sempre coincidisen e estiveran situados no mesmo servidor. Nunha rede social, case todos os rexistros xéranse en nome do usuario, o que significa que todos os datos do usuario dentro dun subsistema funcional almacénanse nun servidor. É dicir, unha transacción comercial case sempre implicaba táboas dun servidor SQL, o que permitía garantir a coherencia dos datos mediante transaccións locais de ACID, sen necesidade de utilizar lento e pouco fiable transaccións distribuídas con ACID.

Grazas ao sharding e á aceleración de SQL:

  • Non usamos restricións de chave estranxeira, xa que ao dividir o ID de entidade pode estar situado noutro servidor.
  • Non utilizamos procedementos almacenados e disparadores debido á carga adicional na CPU do DBMS.
  • Non usamos JOIN por todo o anterior e moitas lecturas aleatorias do disco.
  • Fóra dunha transacción, usamos o nivel de illamento Read Uncommitted para reducir os bloqueos.
  • Realizamos só transaccións curtas (de media menos de 100 ms).
  • Non utilizamos UPDATE e DELETE de varias filas debido á gran cantidade de bloqueos; só actualizamos un rexistro á vez.
  • Sempre realizamos consultas só nos índices: unha consulta cun plan de exploración de táboas completa para nós significa sobrecargar a base de datos e facer que falle.

Estes pasos permitíronnos espremer case o máximo rendemento dos servidores SQL. Porén, os problemas foron cada vez máis numerosos. Mirámolos.

Problemas con SQL

  • Dado que utilizamos fragmentos escritos por nós mesmos, os administradores facíano manualmente para engadir novos fragmentos. Durante todo este tempo, as réplicas de datos escalables non estaban atendendo as solicitudes.
  • A medida que crece o número de rexistros na táboa, a velocidade de inserción e modificación diminúe; ao engadir índices a unha táboa existente, a velocidade cae un factor; a creación e recreación de índices prodúcese co tempo de inactividade.
  • Ter unha pequena cantidade de Windows para SQL Server en produción dificulta a xestión da infraestrutura

Pero o principal problema é

tolerancia a fallos

O servidor SQL clásico ten unha mala tolerancia a fallos. Digamos que só tes un servidor de bases de datos e falla unha vez cada tres anos. Durante este tempo, o sitio está inactivo durante 20 minutos, o que é aceptable. Se tes 64 servidores, o sitio cae unha vez cada tres semanas. E se tes 200 servidores, entón o sitio non funciona todas as semanas. Este é un problema.

Que se pode facer para mellorar a tolerancia a fallos dun servidor SQL? A Wikipedia invítanos a construír cluster altamente dispoñible: onde en caso de avaría dalgún dos compoñentes existe un backup.

Isto require unha flota de equipos caros: numerosas duplicacións, fibra óptica, almacenamento compartido e a inclusión dunha reserva non funciona de forma fiable: preto do 10% das conmutacións rematan coa falla do nodo de reserva como un tren detrás do nodo principal.

Pero a principal desvantaxe dun clúster tan dispoñible é a dispoñibilidade cero se falla o centro de datos no que se atopa. Odnoklassniki ten catro centros de datos, e temos que garantir o funcionamento en caso de falla total nun deles.

Para iso poderiamos utilizar Multi-Mestre replicación integrada en SQL Server. Esta solución é moito máis cara debido ao custo do software e sofre de problemas coñecidos coa replicación: atrasos impredicibles das transaccións coa replicación sincrónica e atrasos na aplicación de replicacións (e, como resultado, modificacións perdidas) coa replicación asíncrona. O implícito resolución manual de conflitos fai que esta opción sexa totalmente inaplicable para nós.

Todos estes problemas requirían unha solución radical, e comezamos a analizalos en detalle. Aquí necesitamos familiarizarnos co que fai principalmente SQL Server: transaccións.

Transacción sinxela

Consideremos a transacción máis sinxela, desde o punto de vista dun programador SQL aplicado: engadir unha foto a un álbum. Os álbums e as fotografías gárdanse en placas diferentes. O álbum ten un contador de fotos público. A continuación, tal transacción divídese nos seguintes pasos:

  1. Pechamos o álbum con chave.
  2. Crea unha entrada na táboa de fotos.
  3. Se a foto ten un estado público, engade un contador de fotos público ao álbum, actualiza o rexistro e confirma a transacción.

Ou en pseudocódigo:

TX.start("Albums", id);
Album album = albums.lock(id);
Photo photo = photos.create(…);

if (photo.status == PUBLIC ) {
    album.incPublicPhotosCount();
}
album.update();

TX.commit();

Vemos que o escenario máis común para unha transacción comercial é ler os datos da base de datos na memoria do servidor de aplicacións, cambiar algo e gardar os novos valores na base de datos. Normalmente, en tal transacción actualizamos varias entidades, varias táboas.

Ao executar unha transacción, pode producirse a modificación simultánea dos mesmos datos doutro sistema. Por exemplo, Antispam pode decidir que o usuario é dalgunha maneira sospeitoso e, polo tanto, todas as fotos do usuario xa non deberían ser públicas, deben ser enviadas para moderación, o que significa cambiar photo.status por algún outro valor e desactivar os contadores correspondentes. Obviamente, se esta operación se produce sen garantías de atomicidade de aplicación e illamento das modificacións competidoras, como en ÁCIDO, entón o resultado non será o necesario: ou o contador de fotos mostrará o valor incorrecto ou non todas as fotos se enviarán para a súa moderación.

Durante toda a existencia de Odnoklassniki escribiuse unha gran cantidade de códigos similares que manipulan varias entidades comerciais nunha mesma transacción. Baseado na experiencia das migracións a NoSQL desde Consistencia Eventual Sabemos que o maior reto (e o investimento de tempo) ven de desenvolver código para manter a coherencia dos datos. Polo tanto, consideramos que o principal requisito para o novo almacenamento era a provisión de transaccións ACID reais para a lóxica de aplicación.

Outros requisitos, non menos importantes, foron:

  • Se o centro de datos falla, debe estar dispoñible tanto a lectura como a escritura no novo almacenamento.
  • Manter a velocidade de desenvolvemento actual. É dicir, cando se traballa cun repositorio novo, a cantidade de código debería ser aproximadamente a mesma; non debería haber necesidade de engadir nada ao repositorio, desenvolver algoritmos para resolver conflitos, manter índices secundarios, etc.
  • A velocidade do novo almacenamento tiña que ser bastante elevada, tanto á hora de ler datos como de procesar as transaccións, o que fixo efectivamente que non fosen aplicables solucións académicamente rigorosas, universais, pero lentas, como por exemplo. compromisos en dúas fases.
  • Escalado automático sobre a marcha.
  • Usando servidores baratos regulares, sen necesidade de comprar hardware exótico.
  • Posibilidade de desenvolvemento de almacenamento por parte dos desenvolvedores da empresa. Noutras palabras, deuse prioridade ás solucións propietarias ou de código aberto, preferentemente en Java.

Decisións, decisións

Analizando as posibles solucións, chegamos a dúas opcións de arquitectura posibles:

O primeiro é tomar calquera servidor SQL e implementar a tolerancia a fallos, o mecanismo de escalado, o clúster de conmutación por fallo, a resolución de conflitos e as transaccións ACID distribuídas, fiables e rápidas. Calificamos esta opción como moi pouco trivial e con moita man de obra.

A segunda opción é levar un almacenamento NoSQL preparado con escalado implementado, un clúster de conmutación por fallo, resolución de conflitos e implementar transaccións e SQL vostede mesmo. A primeira vista, incluso a tarefa de implementar SQL, sen esquecer as transaccións ACID, parece unha tarefa que levará anos. Pero entón decatámonos de que o conxunto de funcións SQL que usamos na práctica está tan lonxe de ANSI SQL como Cassandra CQL lonxe de ANSI SQL. Facendo unha ollada aínda máis atenta a CQL, decatámonos de que estaba bastante preto do que necesitabamos.

Cassandra e CQL

Entón, que é o interesante de Cassandra, que capacidades ten?

En primeiro lugar, aquí podes crear táboas que admitan varios tipos de datos; podes facer SELECT ou UPDATE na clave principal.

CREATE TABLE photos (id bigint KEY, owner bigint,…);
SELECT * FROM photos WHERE id=?;
UPDATE photos SET … WHERE id=?;

Para garantir a coherencia dos datos de réplica, Cassandra usa enfoque de quórum. No caso máis sinxelo, isto significa que cando se colocan tres réplicas da mesma fila en distintos nodos do clúster, a escritura considérase exitosa se a maioría dos nodos (é dicir, dous de cada tres) confirmaron o éxito desta operación de escritura. . Os datos da fila considéranse consistentes se, ao ler, a maioría dos nodos foron consultados e confirmados. Así, con tres réplicas, a coherencia dos datos completa e instantánea está garantida se un nodo falla. Este enfoque permitiunos implementar un esquema aínda máis fiable: enviar sempre solicitudes ás tres réplicas, agardando unha resposta das dúas máis rápidas. A resposta tardía da terceira réplica é descartada neste caso. Un nodo que tarda en responder pode ter problemas graves: freos, recollida de lixo na JVM, recuperación directa de memoria no núcleo de Linux, fallo de hardware, desconexión da rede. Non obstante, isto non afecta de ningún xeito ás operacións nin aos datos do cliente.

Chámase o enfoque cando contactamos tres nodos e recibimos unha resposta de dous especulación: envíase unha solicitude de réplicas adicionais mesmo antes de que se "caia".

Outro beneficio de Cassandra é Batchlog, un mecanismo que garante que un lote de cambios que realizas se apliquen completamente ou non se apliquen en absoluto. Isto permítenos resolver A en ACIDO - atomicidade fóra da caixa.

O máis parecido ás transaccións en Cassandra son os chamados "transaccións lixeiras". Pero están lonxe de ser as transaccións con ACID "reais": de feito, esta é unha oportunidade para facer CAS sobre datos dun só rexistro, utilizando o consenso mediante o protocolo Paxos de peso pesado. Polo tanto, a velocidade de tales transaccións é baixa.

O que nos faltaba en Cassandra

Entón, tivemos que implementar transaccións ACID reais en Cassandra. Usando o cal poderiamos implementar facilmente outras dúas características convenientes do DBMS clásico: índices rápidos consistentes, que nos permitirían realizar seleccións de datos non só pola chave primaria, e un xerador regular de ID monótonas de incremento automático.

C*Un

Así naceu un novo DBMS C*Un, que consta de tres tipos de nodos de servidor:

  • Almacenamento: servidores Cassandra (case) estándar responsables de almacenar datos en discos locais. A medida que a carga e o volume de datos crecen, a súa cantidade pódese escalar facilmente ata decenas e centos.
  • Coordinadores de transaccións: aseguran a execución das transaccións.
  • Os clientes son servidores de aplicacións que implementan operacións comerciais e inician transaccións. Pode haber miles deste tipo de clientes.

NewSQL = NoSQL+ACID

Os servidores de todo tipo forman parte dun clúster común, utilizan o protocolo interno de mensaxes Cassandra para comunicarse entre si e rexouba para intercambiar información de cluster. Con Heartbeat, os servidores aprenden sobre fallos mutuos, manteñen un único esquema de datos: táboas, a súa estrutura e replicación; esquema de partición, topoloxía de clúster, etc.

Clientes

NewSQL = NoSQL+ACID

En lugar dos controladores estándar, úsase o modo Fat Client. Tal nodo non almacena datos, pero pode actuar como coordinador para a execución das solicitudes, é dicir, o propio Cliente actúa como coordinador das súas solicitudes: consulta réplicas de almacenamento e resolve conflitos. Isto non só é máis fiable e máis rápido que o controlador estándar, que require comunicación cun coordinador remoto, senón que tamén permite controlar a transmisión de solicitudes. Fóra dunha transacción aberta no cliente, as solicitudes envíanse aos repositorios. Se o cliente abriu unha transacción, todas as solicitudes dentro da transacción envíanse ao coordinador da transacción.
NewSQL = NoSQL+ACID

C*One Coordinador de Transaccións

O coordinador é algo que implementamos para C*One desde cero. É responsable de xestionar as transaccións, os bloqueos e a orde na que se aplican as transaccións.

Para cada transacción atendida, o coordinador xera unha marca de tempo: cada transacción posterior é maior que a transacción anterior. Dado que o sistema de resolución de conflitos de Cassandra baséase en marcas de tempo (de dous rexistros en conflito, o que ten a última marca de tempo considérase actual), o conflito sempre resolverase a favor da transacción posterior. Así implementamos Reloxo Lampport - unha forma barata de resolver conflitos nun sistema distribuído.

Pechaduras

Para garantir o illamento, decidimos utilizar o método máis sinxelo: bloqueos pesimistas baseados na clave principal do rexistro. Noutras palabras, nunha transacción, primeiro debe bloquearse un rexistro, só despois ler, modificar e gardar. Só despois dunha commit exitosa se pode desbloquear un rexistro para que as transaccións competidoras poidan usalo.

Implementar tal bloqueo é sinxelo nun ambiente non distribuído. Nun sistema distribuído, hai dúas opcións principais: ou implementar o bloqueo distribuído no clúster ou ben distribuír transaccións para que as transaccións que impliquen o mesmo rexistro sexan sempre atendidas polo mesmo coordinador.

Dado que no noso caso os datos xa están distribuídos entre grupos de transaccións locais en SQL, decidiuse asignar grupos de transaccións locais aos coordinadores: un coordinador realiza todas as transaccións con tokens de 0 a 9, o segundo - con tokens de 10 a 19, etcétera. Como resultado, cada unha das instancias do coordinador convértese no mestre do grupo de transaccións.

Entón os bloqueos pódense implementar en forma de HashMap banal na memoria do coordinador.

Fallos de coordinadores

Dado que un coordinador atende exclusivamente a un grupo de transaccións, é moi importante determinar rapidamente o feito do seu fracaso para que o segundo intento de executar a transacción se agote. Para facelo rápido e fiable, usamos un protocolo de ritmo de quórum totalmente conectado:

Cada centro de datos alberga polo menos dous nodos coordinadores. Periódicamente, cada coordinador envía unha mensaxe de latido ao resto de coordinadores e infórmaos sobre o seu funcionamento, así como que mensaxes de latido recibiu de que coordinadores do clúster a última vez.

NewSQL = NoSQL+ACID

Recibindo información similar doutros como parte das súas mensaxes de latido, cada coordinador decide por si mesmo que nodos do clúster están funcionando e cales non, guiado polo principio de quórum: se o nodo X recibiu información da maioría dos nodos do clúster sobre o normal. recepción de mensaxes do nodo Y, entón, Y funciona. E viceversa, en canto a maioría informa que faltan mensaxes do nodo Y, entón Y negouse. É curioso que se o quórum informa ao nodo X de que xa non está a recibir mensaxes del, entón o propio nodo X considerarase que fallou.

As mensaxes de latido do corazón envíanse con alta frecuencia, unhas 20 veces por segundo, cun período de 50 ms. En Java, é difícil garantir a resposta da aplicación nun prazo de 50 ms debido á duración comparable das pausas causadas polo colector de lixo. Puidemos conseguir este tempo de resposta mediante o colector de lixo G1, que nos permite especificar un obxectivo para a duración das pausas de GC. Non obstante, ás veces, moi raramente, as pausas do colector superan os 50 ms, o que pode levar a unha falsa detección de fallos. Para evitar que isto suceda, o coordinador non informa dun fallo dun nodo remoto cando desaparece a primeira mensaxe de latido do mesmo, só se desapareceron varios seguidos. Así conseguimos detectar un fallo do nodo coordinador en 200. Señorita.

Pero non é suficiente para comprender rapidamente que nodo deixou de funcionar. Necesitamos facer algo ao respecto.

Reserva

O esquema clásico implica, en caso de fracaso mestre, iniciar unhas novas eleccións utilizando unha das de moda universal algoritmos. Non obstante, tales algoritmos teñen problemas coñecidos coa converxencia temporal e coa duración do propio proceso electoral. Puidemos evitar estes atrasos adicionais mediante un esquema de substitución de coordinadores nunha rede totalmente conectada:

NewSQL = NoSQL+ACID

Digamos que queremos executar unha transacción no grupo 50. Determinemos de antemán o esquema de substitución, é dicir, que nodos executarán transaccións no grupo 50 en caso de falla do coordinador principal. O noso obxectivo é manter a funcionalidade do sistema en caso de fallo do centro de datos. Determinemos que a primeira reserva será un nodo doutro centro de datos e a segunda reserva será un nodo dun terceiro. Este esquema selecciónase unha vez e non cambia ata que cambia a topoloxía do clúster, é dicir, ata que entran nodos novos (o que ocorre moi raramente). O procedemento para seleccionar un novo mestre activo se o antigo falla sempre será o seguinte: a primeira reserva pasará a ser o mestre activo e, se deixou de funcionar, a segunda reserva pasará a ser o mestre activo.

Este esquema é máis fiable que o algoritmo universal, xa que para activar un novo mestre é suficiente para determinar o fallo do antigo.

Pero como entenderán os clientes que mestre está a traballar agora? É imposible enviar información a miles de clientes en 50 ms. É posible unha situación cando un cliente envía unha solicitude para abrir unha transacción, aínda sen saber que este mestre xa non funciona, e a solicitude esgotará o tempo de espera. Para evitar que isto suceda, os clientes envían de forma especulativa unha solicitude para abrir unha transacción ao mestre do grupo e ás súas dúas reservas á vez, pero só o que é o mestre activo neste momento responderá a esta solicitude. O cliente realizará todas as comunicacións posteriores dentro da transacción só co mestre activo.

Os mestres de copia de seguranza colocan as solicitudes recibidas de transaccións que non son súas na cola de transaccións non nacidas, onde se almacenan durante algún tempo. Se o mestre activo morre, o novo mestre procesa as solicitudes para abrir transaccións desde a súa cola e responde ao cliente. Se o cliente xa abriu unha transacción co antigo mestre, entón a segunda resposta é ignorada (e, obviamente, tal transacción non se completará e será repetida polo cliente).

Como funciona a transacción

Digamos que un cliente enviou unha solicitude ao coordinador para abrir unha transacción para tal ou cal entidade con tal ou cal clave principal. O coordinador bloquea esta entidade e colócaa na táboa de bloqueos na memoria. Se é necesario, o coordinador le esta entidade desde o almacenamento e almacena os datos resultantes nun estado de transacción na memoria do coordinador.

NewSQL = NoSQL+ACID

Cando un cliente quere cambiar os datos dunha transacción, envía unha solicitude ao coordinador para modificar a entidade, e o coordinador coloca os novos datos na táboa de estado da transacción na memoria. Isto completa a gravación; non se fai ningunha gravación no almacenamento.

NewSQL = NoSQL+ACID

Cando un cliente solicita os seus propios datos modificados como parte dunha transacción activa, o coordinador actúa do seguinte xeito:

  • se o ID xa está na transacción, entón os datos tómanse da memoria;
  • se non hai ID na memoria, entón os datos que faltan lense dos nodos de almacenamento, combinados cos que xa están na memoria, e o resultado entrégase ao cliente.

Así, o cliente pode ler os seus propios cambios, pero outros clientes non ven estes cambios, porque só se almacenan na memoria do coordinador e aínda non están nos nodos de Cassandra.

NewSQL = NoSQL+ACID

Cando o cliente envía o commit, o estado que estaba na memoria do servizo é gardado polo coordinador nun lote rexistrado e envíase como un lote rexistrado ao almacenamento de Cassandra. As tendas fan todo o necesario para garantir que este paquete se aplique atomicamente (completamente) e devolven unha resposta ao coordinador, que libera os bloqueos e confirma ao cliente o éxito da transacción.

NewSQL = NoSQL+ACID

E para retroceder, o coordinador só precisa liberar a memoria ocupada polo estado da transacción.

Como resultado das melloras anteriores, implementamos os principios de ACID:

  • Atomicidade. Esta é unha garantía de que non se rexistrará ningunha transacción parcialmente no sistema; ou se completarán todas as súas suboperacións ou non se completará ningunha. Cumprimos este principio a través do lote rexistrado en Cassandra.
  • Consistencia. Cada transacción exitosa, por definición, rexistra só resultados válidos. Se, despois de abrir unha transacción e realizar parte das operacións, se descobre que o resultado non é válido, realízase unha reversión.
  • Illamento. Cando se executa unha transacción, as transaccións concorrentes non deben afectar o seu resultado. As transaccións que compiten están illadas mediante bloqueos pesimistas no coordinador. Para as lecturas fóra dunha transacción, obsérvase o principio de illamento no nivel de lectura comprometida.
  • Sostibilidade. Independentemente dos problemas nos niveis inferiores (apagón do sistema, fallo de hardware), os cambios realizados por unha transacción completada con éxito deberían manterse cando se retomen as operacións.

Lectura por índices

Imos facer unha táboa sinxela:

CREATE TABLE photos (
id bigint primary key,
owner bigint,
modified timestamp,
…)

Ten un ID (chave principal), propietario e data de modificación. Debe facer unha solicitude moi sinxela: seleccione os datos do propietario coa data de cambio "para o último día".

SELECT *
WHERE owner=?
AND modified>?

Para que esa consulta se procese rapidamente, nun DBMS SQL clásico cómpre construír un índice por columnas (propietario, modificado). Podemos facelo con bastante facilidade, xa que agora temos garantías de ACID!

Índices en C*One

Hai unha táboa de orixe con fotografías nas que o ID do rexistro é a clave principal.

NewSQL = NoSQL+ACID

Para un índice, C*One crea unha nova táboa que é unha copia do orixinal. A clave é a mesma que a expresión índice e tamén inclúe a clave primaria do rexistro da táboa de orixe:

NewSQL = NoSQL+ACID

Agora a consulta de "propietario do último día" pódese reescribir como unha selección doutra táboa:

SELECT * FROM i1_test
WHERE owner=?
AND modified>?

O coordinador mantén automaticamente a coherencia dos datos das fotos da táboa de orixe e da táboa índice i1. Só en función do esquema de datos, cando se recibe un cambio, o coordinador xera e almacena un cambio non só na táboa principal, senón tamén en copias. Non se realizan accións adicionais na táboa de índices, os rexistros non se len e non se utilizan bloqueos. É dicir, engadir índices case non consume recursos e practicamente non ten ningún efecto na velocidade de aplicación das modificacións.

Usando ACID, puidemos implementar índices tipo SQL. Son consistentes, escalables, rápidos, compoñebles e integrados na linguaxe de consulta CQL. Non se requiren cambios no código da aplicación para admitir índices. Todo é tan sinxelo coma en SQL. E o máis importante, os índices non afectan a velocidade de execución das modificacións na táboa de transaccións orixinal.

Que pasou

Desenvolvemos C*One hai tres anos e puxémolo en funcionamento comercial.

Que conseguimos ao final? Imos avaliar isto co exemplo do subsistema de procesamento e almacenamento de fotografías, un dos tipos de datos máis importantes dunha rede social. Non estamos a falar dos propios corpos das fotografías, senón de todo tipo de metainformación. Agora Odnoklassniki ten uns 20 millóns deste tipo de rexistros, o sistema procesa 80 mil solicitudes de lectura por segundo, ata 8 mil transaccións ACID por segundo asociadas á modificación de datos.

Cando usamos SQL cun factor de replicación = 1 (pero en RAID 10), a metainformación da foto almacenábase nun clúster de 32 máquinas altamente dispoñible que executaban Microsoft SQL Server (máis de 11 copias de seguridade). Tamén se asignaron 10 servidores para almacenar copias de seguridade. Un total de 50 coches caros. Ao mesmo tempo, o sistema funcionaba a carga nominal, sen reserva.

Despois de migrar ao novo sistema, recibimos un factor de replicación = 3: unha copia en cada centro de datos. O sistema consta de 63 nodos de almacenamento Cassandra e 6 máquinas coordinadoras, para un total de 69 servidores. Pero estas máquinas son moito máis baratas, o seu custo total é de aproximadamente o 30% do custo dun sistema SQL. Ao mesmo tempo, a carga mantense ao 30%.

Coa introdución de C*One, a latencia tamén diminuíu: en SQL, unha operación de escritura tardou uns 4,5 ms. En C*One - uns 1,6 ms. A duración da transacción é de media inferior a 40 ms, a confirmación complétase en 2 ms, a duración de lectura e escritura é de media de 2 ms. O percentil 99 -só 3-3,1 ms, o número de tempo mortos diminuíu 100 veces- todo debido ao uso xeneralizado da especulación.

Ata agora, a maioría dos nodos de SQL Server foron desmantelados; só se están a desenvolver novos produtos usando C*One. Adaptamos C*One para traballar na nosa nube unha soa nube, que permitiu acelerar o despregamento de novos clústeres, simplificar a configuración e automatizar o funcionamento. Sen o código fonte, facelo sería moito máis difícil e complicado.

Agora estamos traballando na transferencia das nosas outras instalacións de almacenamento á nube, pero esa é unha historia completamente diferente.

Fonte: www.habr.com

Engadir un comentario