{"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\/es\/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 \/>\nHasta hace poco, en Odnoklassniki hab\u00eda alrededor de 50 TB de datos procesados en tiempo real almacenados en SQL Server. Para tal volumen, proporcionar un acceso r\u00e1pido, confiable y adem\u00e1s resistente a fallos a un centro de datos usando una base de datos SQL es pr\u00e1cticamente imposible. Por lo general, en tales casos se utiliza uno de los almacenes NoSQL, pero no todo se puede trasladar a NoSQL: algunas entidades requieren garant\u00edas de transacciones ACID. <\/p>\n<p>Esto nos llev\u00f3 a utilizar un almac\u00e9n NewSQL, es decir, una base de datos que proporciona la resistencia a fallos, escalabilidad y velocidad de los sistemas NoSQL, pero manteniendo las garant\u00edas ACID habituales de los sistemas cl\u00e1sicos. Existen pocos sistemas industriales de esta nueva clase, por lo que decidimos implementar uno nosotros mismos y ponerlo en producci\u00f3n. <\/p>\n<p>C\u00f3mo funciona y qu\u00e9 logramos, lo puedes leer a continuaci\u00f3n.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><br \/>\nHoy en d\u00eda, la audiencia mensual de \u00abOdnoklassniki\u00bb supera los 70 millones de visitantes \u00fanicos. Nosotros <noindex><a rel=\"nofollow\" href=\"https:\/\/www.similarweb.com\/top-websites\/category\/internet-and-telecom\/social-network\">estamos entre los cinco<\/a><\/noindex> principales redes sociales del mundo, y en el vig\u00e9simo lugar entre los sitios donde los usuarios pasan m\u00e1s tiempo. La infraestructura de \u00abOK\u00bb procesa cargas muy altas: m\u00e1s de un mill\u00f3n de solicitudes HTTP\/seg en los frentes. Las partes del parque de servidores, que suman m\u00e1s de 8000 unidades, est\u00e1n ubicadas cerca unas de otras, en cuatro centros de datos en Mosc\u00fa, lo que permite garantizar una latencia de red de menos de 1 ms entre ellos.<\/p>\n<p>Utilizamos Cassandra desde 2010, comenzando con la versi\u00f3n 0.6. Hoy en d\u00eda hay en operaci\u00f3n varias decenas de cl\u00fasteres. El cl\u00faster m\u00e1s r\u00e1pido procesa m\u00e1s de 4 millones de operaciones por segundo, y el m\u00e1s grande almacena 260 TB. <\/p>\n<p>Sin embargo, todos estos son cl\u00fasteres NoSQL convencionales que se utilizan para el almacenamiento <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\">de datos d\u00e9bilmente consistentes.<\/a><\/noindex> Nosotros quer\u00edamos reemplazar el almacenamiento principal consistente, Microsoft SQL Server, que se hab\u00eda utilizado desde la fundaci\u00f3n de \u00abOdnoklassniki\u00bb. El almacenamiento consist\u00eda en m\u00e1s de 300 m\u00e1quinas de SQL Server Standard Edition, que conten\u00edan 50 TB de datos: entidades de negocio. Estos datos se modifican en el marco de transacciones ACID y requieren <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/ACID\">alta consistencia.<\/a><\/noindex>.<\/p>\n<p>Para distribuir los datos entre los nodos de SQL Server utilizamos tanto particionamiento vertical como horizontal. <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Partition_(database)\">particionamiento.<\/a><\/noindex> (sharding). Hist\u00f3ricamente, hemos utilizado un esquema simple de sharding de datos: a cada entidad se le asigna un token, que es una funci\u00f3n del ID de la entidad. Las entidades con el mismo token se colocan en un mismo servidor SQL. La relaci\u00f3n tipo maestro-detalle se implementaba para que los tokens del registro principal y del registro derivado coincidieran siempre y estuvieran en el mismo servidor. En una red social, casi todos los registros se generan en nombre del usuario, lo que significa que todos los datos del usuario dentro de un mismo subsistema funcional se almacenan en un \u00fanico servidor. Es decir, en la transacci\u00f3n comercial, casi siempre estaban involucradas tablas de un mismo servidor SQL, lo que permit\u00eda garantizar la coherencia de los datos mediante transacciones ACID locales, sin necesidad de usar <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Two-phase_commit_protocol\">transacciones ACID distribuidas lentas e inseguras.<\/a><\/noindex> . Gracias al sharding y para acelerar el funcionamiento de SQL:<\/p>\n<p>No usamos restricciones de claves for\u00e1neas, ya que al hacer sharding, el ID de una entidad puede estar en otro servidor.<\/p>\n<ul>\n<li>No utilizamos procedimientos almacenados ni disparadores debido a la carga adicional en la CPU de la DBMS.<\/li>\n<li>No usamos JOINs debido a todo lo anterior y a la gran cantidad de lecturas aleatorias desde el disco.<\/li>\n<li>Fuera de las transacciones, para reducir los bloqueos mutuos, usamos el nivel de aislamiento Read Uncommitted.<\/li>\n<li>Solo ejecutamos transacciones cortas (en promedio, m\u00e1s cortas de 100 ms).<\/li>\n<li>No utilizamos UPDATE y DELETE multi-fila debido a la gran cantidad de bloqueos mutuos; actualizamos solo un registro a la vez.<\/li>\n<li>Siempre ejecutamos consultas solo por \u00edndices; una consulta con un plan de recorrido completo de la tabla para nosotros significa sobrecarga de la base de datos y su fallo.<\/li>\n<li>Estos pasos permitieron exprimir casi el m\u00e1ximo rendimiento de los servidores SQL. Sin embargo, cada vez hab\u00eda m\u00e1s problemas. Vamos a revisarlos.<\/li>\n<\/ul>\n<p>\nProblemas con SQL<\/p>\n<h2>Dado que utilizamos un sharding hecho a medida, la adici\u00f3n de nuevos shards era realizada manualmente por los administradores. Durante todo este tiempo, las r\u00e9plicas de datos escalables no atend\u00edan las consultas.<\/h2>\n<p><\/p>\n<ul>\n<li>A medida que crece el n\u00famero de registros en la tabla, disminuye la velocidad de inserci\u00f3n y modificaci\u00f3n; al agregar \u00edndices a una tabla existente, la velocidad cae dr\u00e1sticamente, la creaci\u00f3n y recreaci\u00f3n de \u00edndices se realiza con tiempo de inactividad. <\/li>\n<li>La presencia de un peque\u00f1o n\u00famero de Windows para SQL Server en producci\u00f3n dificulta la gesti\u00f3n de la infraestructura.<\/li>\n<li>Pero el principal problema es \u2014<\/li>\n<\/ul>\n<p>\nPero el principal problema es \u2014 <\/p>\n<h2>Tolerancia a fallos<\/h2>\n<p>\nUn servidor SQL cl\u00e1sico tiene una mala resistencia a fallos. Supongamos que solo tienes un servidor de base de datos y falla una vez cada tres a\u00f1os. Durante ese tiempo, el sitio no funciona durante 20 minutos, lo cual es aceptable. Si tienes 64 servidores, el sitio no funciona una vez cada tres semanas. Y si tienes 200 servidores, el sitio no funciona cada semana. Ese es el problema. <\/p>\n<p>\u00bfQu\u00e9 se puede hacer para mejorar la resistencia a fallos del servidor SQL? Wikipedia nos sugiere construir <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/High-availability_cluster\">un cl\u00faster de alta disponibilidad<\/a><\/noindex>: donde, en caso de falla de cualquiera de los componentes, hay un respaldo.<\/p>\n<p>Esto requiere un parque de costoso equipamiento: una gran duplicaci\u00f3n, fibra \u00f3ptica, almacenamiento compartido, y el activado del respaldo funciona de manera poco fiable: alrededor del 10% de las activaciones terminan en el fallo de un nodo de respaldo siguiendo al nodo principal. <\/p>\n<p>Pero la principal desventaja de un cl\u00faster de alta disponibilidad as\u00ed es la nula disponibilidad en caso de fallo del centro de datos donde est\u00e1 ubicado. \u00abOdnoklassniki\u00bb tiene cuatro centros de datos, y necesitamos asegurar su funcionamiento ante una falla completa en uno de ellos.<\/p>\n<p>Para esto, podr\u00edamos aplicar <noindex><a rel=\"nofollow\" href=\"https:\/\/technet.microsoft.com\/en-us\/library\/ms151196.aspx\">la replicaci\u00f3n Multi-Master<\/a><\/noindex> integrada en SQL Server. Esta soluci\u00f3n es considerablemente m\u00e1s cara debido al costo del software y padece de problemas bien conocidos con la replicaci\u00f3n: demoras impredecibles en las transacciones durante la replicaci\u00f3n s\u00edncrona y demoras en la aplicaci\u00f3n de replicaciones (y, como consecuencia, modificaciones perdidas) en la replicaci\u00f3n as\u00edncrona. La <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\">resoluci\u00f3n manual de conflictos<\/a><\/noindex> hace que esta opci\u00f3n sea completamente inaplicable para nosotros.<\/p>\n<p>Todos estos problemas requer\u00edan una soluci\u00f3n radical y comenzamos a analizarlos en detalle. Aqu\u00ed necesitamos familiarizarnos con lo que principalmente hace SQL Server: las transacciones.<\/p>\n<h2>Transacci\u00f3n simple<\/h2>\n<p>\nConsideremos la transacci\u00f3n m\u00e1s simple, desde el punto de vista de un programador SQL aplicado: a\u00f1adir una foto a un \u00e1lbum. Los \u00e1lbumes y las fotos se almacenan en tablas diferentes. Un \u00e1lbum tiene un contador de fotos p\u00fablicas. Entonces, esta transacci\u00f3n se divide en los siguientes pasos: <\/p>\n<ol>\n<li>Bloqueamos el \u00e1lbum por clave.<\/li>\n<li>Creamos una entrada en la tabla de fotos. <\/li>\n<li>Si la foto tiene un estado p\u00fablico, incrementamos en el \u00e1lbum el contador de fotos p\u00fablicas, actualizamos la entrada y hacemos commit de la transacci\u00f3n.<\/li>\n<\/ol>\n<p>\nO en 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>\nVemos que el escenario de transacci\u00f3n empresarial m\u00e1s com\u00fan es leer datos de la base de datos en la memoria del servidor de aplicaciones, realizar alg\u00fan cambio y guardar los nuevos valores de vuelta en la base de datos. Normalmente, en tal transacci\u00f3n actualizamos varias entidades, varias tablas. <\/p>\n<p>Al ejecutar una transacci\u00f3n, puede ocurrir la modificaci\u00f3n concurrente de los mismos datos desde otro sistema. Por ejemplo, el Antispam puede decidir que un usuario es sospechoso y, por lo tanto, todas las fotos de ese usuario ya no deben ser p\u00fablicas, deben enviarse a moderaci\u00f3n, lo que significa que hay que cambiar photo.status a otro valor y ajustar los contadores correspondientes. Es obvio que si esta operaci\u00f3n se realiza sin garant\u00edas de atomicidad en la aplicaci\u00f3n y de aislamiento de modificaciones competidoras, como en <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/ACID\">ACID<\/a><\/noindex>, el resultado no ser\u00e1 el deseado: o el contador de fotos mostrar\u00e1 un valor incorrecto, o no todas las fotos ser\u00e1n enviadas a moderaci\u00f3n. <\/p>\n<p>Se ha escrito mucho c\u00f3digo como este, que manipula diversas entidades comerciales dentro de una \u00fanica transacci\u00f3n, a lo largo de la existencia de Odnoklassniki. A partir de la experiencia de migraciones a NoSQL con <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Eventual_consistency\">Consistencia eventual<\/a><\/noindex> , sabemos que las mayores dificultades (y gastos de tiempo) surgen de la necesidad de desarrollar c\u00f3digo destinado a mantener la consistencia de los datos. Por lo tanto, el principal requisito para el nuevo almacenamiento fue asegurar a la l\u00f3gica de aplicaci\u00f3n transacciones ACID reales. <\/p>\n<p>Otros requisitos no menos importantes fueron:<\/p>\n<ul>\n<li>En caso de falla del centro de datos, tanto la lectura como la escritura deben ser posibles en el nuevo almacenamiento.<\/li>\n<li>Mantener la actual velocidad de desarrollo. Es decir, al trabajar con el nuevo almacenamiento, la cantidad de c\u00f3digo debe ser aproximadamente la misma, no debe haber necesidad de escribir algo adicional en el almacenamiento, desarrollar algoritmos de resoluci\u00f3n de conflictos, mantener \u00edndices secundarios, etc. <\/li>\n<li>La velocidad de funcionamiento del nuevo almacenamiento debe ser suficientemente alta, tanto para la lectura de datos como para el procesamiento de transacciones, lo que significa efectivamente que no se pueden aplicar soluciones estrictamente acad\u00e9micas, universales pero lentas, como por ejemplo, <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Two-phase_commit_protocol\">commits en dos fases<\/a><\/noindex>.<\/li>\n<li>Escalado autom\u00e1tico en tiempo real. <\/li>\n<li>Uso de servidores est\u00e1ndar y econ\u00f3micos, sin necesidad de comprar hardware ex\u00f3tico. <\/li>\n<li>Posibilidad de desarrollar el almacenamiento con el esfuerzo de los desarrolladores de la empresa. En otras palabras, se priorizaban soluciones internas o de c\u00f3digo abierto, preferiblemente en Java.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Soluciones, soluciones<\/h2>\n<p>\nAnalizando las posibles soluciones, llegamos a dos opciones posibles de arquitectura:<\/p>\n<p>La primera opci\u00f3n es tomar cualquier servidor SQL y realizar la redundancia necesaria, el mecanismo de escalado, un cl\u00faster tolerante a fallos, resoluci\u00f3n de conflictos y transacciones ACID distribuidas, confiables y r\u00e1pidas. Evaluamos esta opci\u00f3n como bastante no trivial y laboriosa.<\/p>\n<p>La segunda opci\u00f3n es adoptar un almac\u00e9n NoSQL listo para usar con escalabilidad implementada, un cl\u00faster tolerante a fallos, resoluci\u00f3n de conflictos y realizar transacciones y SQL por nuestra cuenta. A primera vista, incluso la tarea de implementar SQL, sin mencionar las transacciones ACID, parece un desaf\u00edo de a\u00f1os. Pero luego entendimos que el conjunto de funcionalidades SQL que utilizamos en la pr\u00e1ctica est\u00e1 tan lejos de ANSI SQL como <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Apache_Cassandra#Cassandra_Query_Language\">Cassandra CQL<\/a><\/noindex> est\u00e1 lejos de ANSI SQL. Al examinar m\u00e1s de cerca CQL, nos dimos cuenta de que se acerca bastante a lo que necesitamos.<\/p>\n<h2>Cassandra y CQL<\/h2>\n<p>\nEntonces, \u00bfqu\u00e9 hace interesante a Cassandra, qu\u00e9 capacidades tiene?<\/p>\n<p>En primer lugar, aqu\u00ed puedes crear tablas que soporten diversos tipos de datos, puedes hacer SELECT o UPDATE por clave 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>\nPara garantizar la consistencia de los datos de las r\u00e9plicas, Cassandra utiliza <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Quorum_(distributed_computing)\">un enfoque de qu\u00f3rum<\/a><\/noindex>. En el caso m\u00e1s simple, esto significa que al colocar tres r\u00e9plicas de la misma fila en diferentes nodos del cl\u00faster, la escritura se considera exitosa si la mayor\u00eda de los nodos (es decir, dos de tres) han confirmado el \u00e9xito de esta operaci\u00f3n de escritura. Los datos de la fila se consideran consistentes si, al leer, se han interrogado y confirmado la mayor\u00eda de los nodos. As\u00ed, con tres r\u00e9plicas se garantiza una consistencia total e instant\u00e1nea de los datos en caso de fallo de un nodo. Este enfoque nos ha permitido implementar un esquema a\u00fan m\u00e1s fiable: siempre enviar solicitudes a las tres r\u00e9plicas, esperando la respuesta de las dos m\u00e1s r\u00e1pidas. La respuesta retrasada de la tercera r\u00e9plica se descarta en este caso. El nodo que se retrasa en responder puede tener problemas serios: lentitud, recolecci\u00f3n de basura en la JVM, reclamaci\u00f3n de memoria directa en el n\u00facleo de Linux, fallos de hardware, desconexi\u00f3n de la red. Sin embargo, esto no afecta la operaci\u00f3n del cliente ni los datos.<\/p>\n<p>El enfoque en el que consultamos a tres nodos y recibimos respuesta de dos se llama <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Speculative_execution\">especulaci\u00f3n<\/a><\/noindex>: solicitar r\u00e9plicas adicionales se env\u00eda antes de que se \"caiga\". <\/p>\n<p>Otra de las ventajas de Cassandra es Batchlog, un mecanismo que garantiza la aplicaci\u00f3n completa o la no aplicaci\u00f3n total de un lote de cambios que usted realice. Esto nos permite resolver A en ACID: atomicidad de manera predeterminada.<\/p>\n<p>Lo m\u00e1s parecido a las transacciones en Cassandra es lo que se conoce como &#171;<noindex><a rel=\"nofollow\" href=\"https:\/\/docs.datastax.com\/en\/cql\/3.3\/cql\/cql_using\/useInsertLWT.html\">transacciones ligeras<\/a><\/noindex>&#171;. Sin embargo, est\u00e1n lejos de las transacciones ACID 'reales': en realidad, se trata de la posibilidad de hacer <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Compare-and-swap\">CAS<\/a><\/noindex> solo en los datos de un solo registro, utilizando el consenso del pesado protocolo Paxos. Por lo tanto, la velocidad de estas transacciones no es alta. <\/p>\n<h2>Lo que nos falta en Cassandra<\/h2>\n<p>\nAs\u00ed que ten\u00edamos que implementar en Cassandra verdaderas transacciones ACID. Con las que podr\u00edamos implementar f\u00e1cilmente otras dos capacidades \u00fatiles de los DBMS cl\u00e1sicos: \u00edndices r\u00e1pidos consistentes, que nos permitir\u00edan realizar consultas de datos no solo por la clave primaria, y un generador ordinario de ID autoincrementales monot\u00f3nicos.<\/p>\n<h4>C*One<\/h4>\n<p>\nAs\u00ed naci\u00f3 una nueva base de datos <b>C*One<\/b>, compuesta por tres tipos de nodos de servidor:<\/p>\n<ul>\n<li>Almacenadores \u2014 servidores Cassandra (casi) est\u00e1ndar, responsables de almacenar datos en discos locales. A medida que crece la carga y el volumen de datos, su n\u00famero se puede escalar f\u00e1cilmente a decenas y centenas.<\/li>\n<li>Los coordinadores de transacciones garantizan la ejecuci\u00f3n de las transacciones. <\/li>\n<li>Los clientes son servidores de aplicaciones que realizan operaciones comerciales e inician transacciones. Puede haber miles de tales 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 \/>\nLos servidores de todos los tipos est\u00e1n en un cl\u00faster com\u00fan, utilizan el protocolo interno de mensajes Cassandra para comunicarse entre s\u00ed y <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Gossip_protocol\">el protocolo<\/a><\/noindex> para intercambiar informaci\u00f3n del cl\u00faster. A trav\u00e9s de Heartbeat, los servidores detectan fallos mutuos, mantienen un esquema de datos \u00fanico: tablas, su estructura y replicaci\u00f3n; esquema de particionamiento, topolog\u00eda del cl\u00faster, 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 \/>\nEn lugar de controladores est\u00e1ndar, se utiliza el modo Fat Client. Este nodo no almacena datos, pero puede actuar como coordinador de ejecuci\u00f3n de solicitudes, es decir, el cliente mismo cumple la funci\u00f3n de coordinador de sus solicitudes: interroga las r\u00e9plicas del almacenamiento y resuelve los conflictos. Esto es no solo m\u00e1s fiable y r\u00e1pido que el controlador est\u00e1ndar, que requiere comunicaci\u00f3n con un coordinador remoto, sino que tambi\u00e9n permite gestionar la transmisi\u00f3n de solicitudes. Fuera de la transacci\u00f3n abierta en el cliente, las solicitudes se dirigen a los almacenes. Si el cliente ha abierto una transacci\u00f3n, entonces todas las solicitudes dentro de la transacci\u00f3n se env\u00edan al coordinador de transacciones.<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>Coordinador de transacciones C*One<\/h2>\n<p>\nEl coordinador es lo que hemos implementado para C*One desde cero. Se encarga de gestionar transacciones, bloqueos y el orden de aplicaci\u00f3n de las transacciones.<\/p>\n<p>Para cada transacci\u00f3n atendida, el coordinador genera una marca de tiempo: cada sucesiva es mayor que la de la transacci\u00f3n anterior. Dado que en Cassandra el sistema de resoluci\u00f3n de conflictos se basa en marcas de tiempo (de dos registros en conflicto, se considera v\u00e1lido el que tiene la marca de tiempo m\u00e1s reciente), el conflicto siempre se resolver\u00e1 a favor de la transacci\u00f3n posterior. As\u00ed es como hemos implementado <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\">los relojes de Lamport<\/a><\/noindex> \u2014 una forma econ\u00f3mica de resolver conflictos en un sistema distribuido.<\/p>\n<h2>Bloqueos<\/h2>\n<p>\nPara garantizar la aislamiento, decidimos utilizar el m\u00e9todo m\u00e1s simple: bloqueos pesimistas por la clave primaria del registro. En otras palabras, en la transacci\u00f3n, el registro debe ser bloqueado primero, y solo despu\u00e9s leerse, modificarse y guardarse. Solo despu\u00e9s de un commit exitoso, el registro puede ser desbloqueado para que las transacciones en competencia puedan utilizarlo.<\/p>\n<p>La implementaci\u00f3n de tal bloqueo es sencilla en un entorno no distribuido. En un sistema distribuido hay dos enfoques principales: implementar un bloqueo distribuido en el cl\u00faster o distribuir las transacciones de tal manera que las transacciones que involucren un mismo registro sean siempre atendidas por el mismo coordinador.<\/p>\n<p>Dado que en nuestro caso los datos ya est\u00e1n distribuidos en grupos de transacciones locales en SQL, se decidi\u00f3 asignar a los coordinadores de grupos de transacciones locales: un coordinador maneja todas las transacciones con un token del 0 al 9, el segundo con un token del 10 al 19, y as\u00ed sucesivamente. Como resultado, cada una de las instancias del coordinador se convierte en el maestro del grupo de transacciones. <\/p>\n<p>Entonces, los bloqueos pueden ser implementados como un simple HashMap en la memoria del coordinador.<\/p>\n<h2>Fallas de los coordinadores<\/h2>\n<p>\nDado que un coordinador atiende exclusivamente a un grupo de transacciones, es muy importante determinar r\u00e1pidamente la ocurrencia de su falla para que el reintento de ejecuci\u00f3n de la transacci\u00f3n se realice dentro del tiempo de espera. Para esto, aplicamos un protocolo de heartbeat con qu\u00f3rum totalmente conectado:<\/p>\n<p>En cada centro de datos se aloja un m\u00ednimo de dos nodos coordinadores. Peri\u00f3dicamente, cada coordinador env\u00eda un mensaje de heartbeat a los otros coordinadores y les informa sobre su funcionamiento, as\u00ed como sobre qu\u00e9 mensajes de heartbeat de qu\u00e9 coordinadores en el cl\u00faster ha recibido por \u00faltima vez. <\/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 \/>\nAl recibir informaci\u00f3n similar de los dem\u00e1s en sus mensajes de heartbeat, cada coordinador decide para s\u00ed mismo qu\u00e9 nodos del cl\u00faster est\u00e1n funcionando y cu\u00e1les no, gui\u00e1ndose por el principio de qu\u00f3rum: si el nodo X ha recibido informaci\u00f3n de la mayor\u00eda de los nodos en el cl\u00faster sobre la normal recepci\u00f3n de mensajes del nodo Y, entonces Y est\u00e1 funcionando. Y viceversa, tan pronto como la mayor\u00eda informe sobre la falta de mensajes del nodo Y, significa que Y ha fallado. Curiosamente, si el qu\u00f3rum informa al nodo X que no recibe m\u00e1s mensajes de \u00e9l, entonces el mismo nodo X se considerar\u00e1 fallido.<\/p>\n<p>Los mensajes de heartbeat se env\u00edan con gran frecuencia, alrededor de 20 veces por segundo, con un per\u00edodo de 50 ms. Es dif\u00edcil garantizar una respuesta de la aplicaci\u00f3n en Java dentro de 50 ms debido a la duraci\u00f3n comparable de las pausas causadas por el recolector de basura. Logramos alcanzar este tiempo de respuesta utilizando el recolector de basura G1, que permite especificar un objetivo para la duraci\u00f3n de las pausas de GC. Sin embargo, a veces, aunque raramente, las pausas del recolector exceden los 50 ms, lo que puede llevar a una falsa detecci\u00f3n de fallos. Para evitar esto, el coordinador no informa sobre la falla de un nodo remoto tras la p\u00e9rdida del primer mensaje de heartbeat, sino solo si se pierden varios de forma consecutiva. As\u00ed logramos detectar la falla de un nodo coordinador en 200 ms. <\/p>\n<p>Pero no basta con entender r\u00e1pidamente qu\u00e9 nodo ha dejado de funcionar. Hay que hacer algo al respecto. <\/p>\n<h2>Redundancia<\/h2>\n<p>\nEl esquema cl\u00e1sico prev\u00e9, en caso de fallo del maestro, iniciar las elecciones de uno nuevo mediante uno de los<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\"> modernos<\/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\">universales<\/a><\/noindex> algoritmos. Sin embargo, estos algoritmos tienen problemas bien conocidos de convergencia a lo largo del tiempo y duraci\u00f3n del propio proceso de elecciones. Hemos logrado evitar tales retrasos adicionales mediante un esquema de sustituci\u00f3n de coordinadores en una red completamente 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 \/>\nSupongamos que queremos ejecutar una transacci\u00f3n en el grupo 50. Definiremos de antemano el esquema de sustituci\u00f3n, es decir, qu\u00e9 nodos ejecutar\u00e1n las transacciones del grupo 50 en caso de fallo del coordinador principal. Nuestro objetivo es mantener la operatividad del sistema ante la falla de un centro de datos. Definiremos que el primer respaldo ser\u00e1 un nodo de otro centro de datos y el segundo respaldo ser\u00e1 un nodo del tercero. Este esquema se elige una vez y no cambia hasta que la topolog\u00eda del cl\u00faster cambie, es decir, hasta que entren nuevos nodos (lo cual sucede muy raramente). El orden de selecci\u00f3n de un nuevo maestro activo en caso de fallo del antiguo ser\u00e1 siempre el siguiente: el primer respaldo se convertir\u00e1 en el maestro activo, y si este tambi\u00e9n deja de funcionar, el segundo respaldo. <\/p>\n<p>Este esquema es m\u00e1s confiable que un algoritmo universal, ya que para activar un nuevo maestro solo es necesario confirmar el hecho de falla del antiguo.<\/p>\n<p>\u00bfPero c\u00f3mo podr\u00e1n los clientes entender qu\u00e9 maestro est\u00e1 trabajando en este momento? En 50 ms es imposible enviar informaci\u00f3n a miles de clientes. Puede haber una situaci\u00f3n en la que el cliente env\u00ede una solicitud para abrir una transacci\u00f3n sin saber que este maestro ya no est\u00e1 operativo, y la solicitud quedar\u00e1 pendiente por tiempo de espera. Para evitar esto, los clientes env\u00edan especulativamente la solicitud para abrir una transacci\u00f3n al maestro del grupo y a sus dos reservas, pero solo responder\u00e1 a esta solicitud aquel que sea el maestro activo en ese momento. Toda la posterior comunicaci\u00f3n en el marco de la transacci\u00f3n se realizar\u00e1 \u00fanicamente con el maestro activo.<\/p>\n<p>Los maestros de reserva colocan las solicitudes recibidas para transacciones que no les pertenecen en una cola de transacciones no nacidas, donde se almacenan durante alg\u00fan tiempo. Si el maestro activo muere, entonces un nuevo maestro procesar\u00e1 las solicitudes para abrir transacciones desde su cola y responder\u00e1 al cliente. Si el cliente ya ha logrado abrir una transacci\u00f3n con el antiguo maestro, la segunda respuesta se ignora (y, evidentemente, tal transacci\u00f3n no se completar\u00e1 y ser\u00e1 repetida por el cliente).<\/p>\n<h2>C\u00f3mo funciona la transacci\u00f3n<\/h2>\n<p>\nSupongamos que el cliente envi\u00f3 una solicitud al coordinador para abrir una transacci\u00f3n para tal entidad con tal clave primaria. El coordinador bloquea esta entidad y la coloca en la tabla de bloqueos en memoria. Si es necesario, el coordinador lee esta entidad del almacenamiento y guarda los datos obtenidos en el estado de la transacci\u00f3n en la memoria del coordinador.<\/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 \/>\nCuando el cliente quiere modificar datos en la transacci\u00f3n, env\u00eda al coordinador una solicitud para modificar la entidad, y este coloca los nuevos datos en la tabla de estado de transacciones en memoria. En este punto, la grabaci\u00f3n se completa: no se realiza ning\u00fan registro en el almacenamiento.<\/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 \/>\nCuando el cliente solicita en el marco de una transacci\u00f3n activa sus propios datos modificados, el coordinador act\u00faa de la siguiente manera: <\/p>\n<ul>\n<li>si el ID ya est\u00e1 en la transacci\u00f3n, los datos se obtienen de la memoria; <\/li>\n<li>si el ID no est\u00e1 en la memoria, los datos faltantes se leen de los nodos de almacenamiento, se combinan con los que ya est\u00e1n en memoria, y el resultado se entrega al cliente. <\/li>\n<\/ul>\n<p>\nDe este modo, el cliente puede leer sus propios cambios, mientras que otros clientes no ven esos cambios, porque se almacenan \u00fanicamente en la memoria del coordinador, y a\u00fan no est\u00e1n en los nodos de 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 \/>\nCuando un cliente env\u00eda un commit, el estado que ten\u00eda el servicio en memoria se guarda por el coordinador en un logged batch, que luego se env\u00eda a los almacenes de Cassandra. Los almacenes hacen todo lo necesario para que este paquete se aplique de manera at\u00f3mica (completamente) y devuelven una respuesta al coordinador, quien libera los bloqueos y confirma la exitosa transacci\u00f3n 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 \/>\nY para deshacer, al coordinador solo le basta con liberar la memoria ocupada por el estado de la transacci\u00f3n.<\/p>\n<p>Como resultado de las mejoras descritas, hemos implementado los principios ACID:<\/p>\n<ul>\n<li><b>Atomicidad<\/b>. Esta es la garant\u00eda de que ninguna transacci\u00f3n se registrar\u00e1 parcialmente en el sistema; se ejecutar\u00e1n todas sus suboperaciones o no se ejecutar\u00e1 ninguna. Este principio se cumple en nuestro caso gracias al logged batch en Cassandra.<\/li>\n<li><b>Consistencia<\/b>. Cada transacci\u00f3n exitosa, por definici\u00f3n, registra solo resultados v\u00e1lidos. Si despu\u00e9s de abrir una transacci\u00f3n y ejecutar parte de las operaciones se descubre que el resultado no es v\u00e1lido, se realiza un rollback.<\/li>\n<li><b>Aislamiento<\/b>. Al realizar una transacci\u00f3n, las transacciones paralelas no deben influir en su resultado. Las transacciones que compiten est\u00e1n aisladas mediante bloqueos pesimistas en el coordinador. Para lecturas fuera de la transacci\u00f3n, se cumple el principio de aislamiento a nivel de Read Committed.<\/li>\n<li><b>Sostenibilidad<\/b>. Independientemente de los problemas en los niveles inferiores \u2014 corte de energ\u00eda, fallo de hardware \u2014 los cambios realizados por una transacci\u00f3n que se haya completado con \u00e9xito deben permanecer guardados despu\u00e9s de reanudar el funcionamiento. <\/li>\n<\/ul>\n<p><\/p>\n<h2>Lectura por \u00edndices<\/h2>\n<p>\nTomemos una tabla simple: <\/p>\n<pre><code>CREATE TABLE photos (\nid bigint primary key,\nowner bigint,\nmodified timestamp,\n\u2026)<\/code><\/pre>\n<p>\nTiene un ID (clave primaria), un propietario y una fecha de modificaci\u00f3n. Necesitamos hacer una consulta muy simple: seleccionar datos por propietario en la fecha de modificaci\u00f3n \"en las \u00faltimas 24 horas\". <\/p>\n<pre><code>SELECT *\nWHERE owner=?\nAND modified&gt;?<\/code><\/pre>\n<p>\nPara que tal consulta se ejecute r\u00e1pidamente, en una base de datos SQL cl\u00e1sica se debe construir un \u00edndice sobre las columnas (owner, modified). Esto podemos hacerlo con bastante facilidad, ya que ahora tenemos garant\u00edas ACID.<\/p>\n<h2>\u00cdndices en C*One<\/h2>\n<p>\nHay una tabla original con fotograf\u00edas, donde el ID del registro es la clave 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 \/>\nPara el \u00edndice, C*One crea una nueva tabla que es una copia de la original. La clave coincide con la expresi\u00f3n del \u00edndice, incluyendo tambi\u00e9n la clave primaria del registro de la tabla 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 \/>\nAhora se puede reescribir la consulta por \"propietario en las \u00faltimas 24 horas\" como un select de otra tabla:<\/p>\n<pre><code>SELECT * FROM i1_test\nWHERE owner=?\nAND modified&gt;?<\/code><\/pre>\n<p>\nLa coherencia de los datos de la tabla original photos y la tabla de \u00edndice i1 es mantenida autom\u00e1ticamente por el coordinador. Bas\u00e1ndose solo en el esquema de datos, al recibir cambios, el coordinador genera y almacena cambios no solo de la tabla principal, sino tambi\u00e9n de las copias. No se realizan acciones adicionales en la tabla de \u00edndice, no se leen logs, no se utilizan bloqueos. Es decir, agregar \u00edndices consume casi ning\u00fan recurso y pr\u00e1cticamente no afecta la velocidad de aplicaci\u00f3n de modificaciones.<\/p>\n<p>Con ACID hemos logrado implementar \u00edndices \"como en SQL\". Tienen coherencia, pueden escalar, funcionan r\u00e1pidamente, pueden ser compuestos e integrados en el lenguaje de consultas CQL. No es necesario hacer cambios en el c\u00f3digo de aplicaci\u00f3n para soportar \u00edndices. Es tan simple como en SQL. Y lo m\u00e1s importante, los \u00edndices no afectan la velocidad de ejecuci\u00f3n de modificaciones en la tabla de transacciones original.<\/p>\n<h2>\u00bfQu\u00e9 resultados se obtuvieron?<\/h2>\n<p>\nDesarrollamos C*One hace tres a\u00f1os y lo lanzamos para uso industrial. <\/p>\n<p>\u00bfQu\u00e9 hemos logrado al final? Evaluemos esto con el ejemplo del subsistema de procesamiento y almacenamiento de fotos, uno de los tipos de datos m\u00e1s importantes en la red social. No se trata de las im\u00e1genes en s\u00ed, sino de la variada metainformaci\u00f3n. Actualmente en \"Odnoklassniki\" hay alrededor de 20 mil millones de tales registros, el sistema procesa 80 mil consultas de lectura por segundo, hasta 8 mil transacciones ACID por segundo relacionadas con la modificaci\u00f3n de datos. <\/p>\n<p>Cuando utilizamos SQL con un factor de replicaci\u00f3n = 1 (pero en RAID 10), la metainformaci\u00f3n de las fotos se almacenaba en un cl\u00faster de alta disponibilidad de 32 m\u00e1quinas con Microsoft SQL Server (m\u00e1s 11 de respaldo). Tambi\u00e9n se reservaron 10 servidores para almacenar copias de seguridad. En total, 50 m\u00e1quinas costosas. Mientras tanto, el sistema funcionaba bajo carga nominal, sin margen.<\/p>\n<p>Despu\u00e9s de migrar al nuevo sistema, obtuvimos un factor de replicaci\u00f3n = 3 \u2014 con una copia en cada centro de datos. El sistema consiste en 63 nodos de almacenamiento de Cassandra y 6 m\u00e1quinas coordinadoras, un total de 69 servidores. Pero estas m\u00e1quinas son significativamente m\u00e1s baratas, su costo total es de aproximadamente el 30 % del costo del sistema en SQL. A su vez, la carga se mantiene en un 30 %.<\/p>\n<p>Con la implementaci\u00f3n de C*One, tambi\u00e9n se redujeron los tiempos de latencia: en SQL, la operaci\u00f3n de escritura tomaba alrededor de 4,5 ms. En C*One, es de aproximadamente 1,6 ms. La duraci\u00f3n de la transacci\u00f3n es en promedio menor a 40 ms, el commit se realiza en 2 ms, y la duraci\u00f3n de lectura y escritura es en promedio 2 ms. El percentil 99 es de solo 3-3,1 ms, y el n\u00famero de timeouts se redujo en 100 veces, todo gracias a la amplia aplicaci\u00f3n de especulaciones. <\/p>\n<p>Hasta el momento, se ha retirado la mayor parte de los nodos de SQL Server de la explotaci\u00f3n, y los nuevos productos solo se desarrollan utilizando C*One. Hemos adaptado C*One para su funcionamiento en nuestra nube. <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/company\/odnoklassniki\/blog\/346868\/\">one-cloud<\/a><\/noindex>, lo que ha permitido acelerar el despliegue de nuevos cl\u00fasteres, simplificar la configuraci\u00f3n y automatizar la operaci\u00f3n. Sin el c\u00f3digo fuente, esto habr\u00eda sido significativamente m\u00e1s complicado y poco pr\u00e1ctico. <\/p>\n<p>Ahora estamos trabajando en la migraci\u00f3n de nuestros otros almacenes a la nube, pero esa ya es una historia completamente diferente.<br \/>\n<br \/>Fuente: <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.2 - 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\/es\/blog\/administrirovanie\/newsql-nosql-acid\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2\" \/>\n\t\t<meta property=\"og:locale\" content=\"es_ES\" \/>\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\/es\/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":"Hasta hace poco, hab\u00eda alrededor de 50 TB de datos procesados en Odnoklassniki.","canonical_url":"https:\/\/prohoster.info\/es\/blog\/administrirovanie\/newsql-nosql-acid","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"es_ES","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\/es\/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\/es\/wp-json\/wp\/v2\/posts\/37956","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/comments?post=37956"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/posts\/37956\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/media\/28484"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/media?parent=37956"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/categories?post=37956"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/tags?post=37956"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}