O TrueTime é um relógio de distribuição altamente disponível fornecido para aplicativos em todos os servidores do Google1. Ele permite que os aplicativos gerem carimbos de data/hora crescentes monotonicamente: um aplicativo pode calcular um carimbo de data/hora T que tem garantia de que seja maior que qualquer carimbo de data/hora T' se T' for gerado antes de T começar a ser gerado. Essa garantia é válida em todos os servidores e para todos os carimbos de data/hora.
Esse recurso do TrueTime é usado pelo Spanner para atribuir carimbos de data/hora aos transações. Especificamente, cada transação recebe um carimbo de data/hora reflete o instante em que o Spanner considera que ela tem o incidente. Como o Spanner usa o controle de simultaneidade de várias versões, a garantia de ordenação nos carimbos de data/hora permite que os clientes do Spanner realizem leituras consistentes em um banco de dados inteiro (mesmo em várias regiões do Cloud) sem bloquear gravações.
Consistência externa
O Spanner oferece aos clientes o controle de simultaneidade mais rigoroso garantias para transações, chamadas de consistência2. Com a consistência externa, o sistema se comporta como se todas as transações fossem executadas sequencialmente, mesmo que o Spanner as execute em vários servidores (e possivelmente em vários data centers) para maior desempenho e disponibilidade. Além disso, se uma transação é concluída antes de outra começar a confirmação, o sistema garante que os clientes nunca possam ver um estado que inclua o efeito da segunda transação, mas não da primeira. De maneira intuitiva, o Spanner é semanticamente indistinguível de um banco de dados de uma única máquina. Mesmo que oferece garantias tão sólidas, o Spanner permite que os aplicativos alcancem desempenho comparável ao de bancos de dados que oferecem garantias mais fracas (em troca para um desempenho melhor). Por exemplo, como os bancos de dados compatíveis com isolamento de instantâneos, o Spanner permite que as gravações prossigam sem serem bloqueadas por transações somente leitura, mas sem exibir as anomalias que o isolamento de snapshots permite.
A consistência externa simplifica muito o desenvolvimento de aplicativos. Por exemplo: suponha que você tenha criado um aplicativo bancário no Spanner e um dos seus clientes começam com R $50,00 na conta corrente e R $50,00 em conta-poupança. Seu aplicativo então começa um fluxo de trabalho no qual ele primeiro confirma uma transação T1 para depositar US$ 200 na conta poupança e, em seguida, emite uma segunda transação T2 para debitar US$ 150 da conta corrente. Além disso, imagine que, no fim do dia, os saldos negativos em uma conta sejam cobertos automaticamente por outras contas, e um cliente sofre uma penalidade se o saldo total em todas as contas for negativo a qualquer momento durante esse dia. A consistência externa garante que, como a T2 começa a confirmar após a T1 terminar, todos os leitores do banco de dados observarão que a T1 de depósito ocorreu antes da T2 de débito. Dito de outra forma, a consistência externa garante que ninguém jamais veja um estado em que a T2 tenha ocorrido antes da T1. Em outras palavras, o débito nunca causará uma penalidade por fundos insuficientes.
Um banco de dados tradicional que usa armazenamento de versão única e bloqueio rígido de duas fases oferece consistência externa. Infelizmente, nesse sistema, toda vez que o aplicativo quer ler os dados mais atuais (o que chamamos de "leitura forte"), o sistema adquire um bloqueio de leitura nos dados, que bloqueia as gravações nos dados que estão sendo lidos.
Carimbos de data/hora e controle de simultaneidade de várias versões (MVCC)
Para ler sem bloquear gravações, o Spanner e muitos outros sistemas de banco de dados mantêm várias versões de dados imutáveis (muitas vezes chamadas de controle de simultaneidade de várias versões). Uma gravação cria uma nova versão imutável, cujo carimbo de data/hora é o da transação da gravação. Uma "leitura de instantâneo" em um carimbo de data/hora retorna o valor da versão mais recente antes desse carimbo de data/hora e não precisa bloquear as gravações. Portanto, é importante que os carimbos de data/hora atribuídos às versões sejam consistentes com a ordem em que as transações podem ser observadas para confirmar. Chamamos essa propriedade de "processo do carimbo de data/hora apropriado". Observe que a existência de um processo do carimbo de data/hora apropriado é equivalente à consistência externa.
Para ver por que o processo do carimbo de data/hora apropriado é importante, pense no exemplo bancário da seção anterior. Sem o processo do carimbo de data/hora apropriado, a T2 poderia ser atribuída a um carimbo de data/hora anterior ao atribuído à T1. Por exemplo: se um sistema hipotético usasse horários locais em vez do TrueTime e o horário do servidor que processa a T2 estivesse ligeiramente atrasado. Uma leitura de instantâneo poderia refletir o débito da T2, mas não o depósito da T1, mesmo que o cliente tenha visto o depósito finalizado antes de iniciar o débito.
Conseguir o carimbo de data/hora apropriado é trivial para um banco de dados de uma única máquina. Por exemplo: você pode apenas atribuir carimbos de data/hora de um contador global e crescente monotonicamente. Conseguir isso em um sistema amplamente distribuído, como o Spanner, quais servidores em todo o mundo precisam atribuir carimbos de data/hora, é muito mais difícil de fazer com eficiência.
O Spanner depende do TrueTime para gerar valores crescentes carimbos de data/hora. O Spanner usa esses carimbos de data/hora de duas maneiras. Primeiro, ele os usa como carimbos de data/hora apropriados para transações de gravação sem a necessidade de comunicação global. Em segundo lugar, ele os usa como carimbos de data/hora para leituras fortes, o que permite que elas sejam executadas em uma rodada de comunicação e até mesmo que elas abranjam vários servidores.
Perguntas frequentes
Quais garantias de consistência o Spanner oferece?
O Spanner oferece consistência externa, que é a mais rigorosa para sistemas de processamento de transações. Todas as transações no Spanner atendem a essa propriedade de consistência, não apenas aquelas dentro de uma partição. Externo estados de consistência são: o Spanner executa transações de é indistinguível de um sistema em que as transações são executadas em série, e, além disso, que a ordem serial é consistente com a ordem na qual transações podem ser observadas para confirmação. Como os carimbos de data/hora gerados para as transações correspondem à ordem serial, se qualquer cliente que vir uma transação T2 começar a confirmar após outra transação T1 terminar, o sistema atribuirá um carimbo de data/hora para T2 que é maior do que o carimbo de data/hora de T1.
O Spanner oferece capacidade de linearização?
Sim. Na verdade, o Spanner oferece consistência externa, que é uma propriedade mais forte do que a capacidade de linearização, porque ela não diz nada sobre o comportamento das transações. A capacidade de linearização é uma propriedade de objetos simultâneos que são compatíveis com operações de leitura e gravação atômicas. Em um banco de dados, um "objeto" normalmente seria uma única linha ou mesmo uma única célula. A consistência externa é uma propriedade de sistemas de processamento de transações em que os clientes sintetizam dinamicamente transações que contêm várias operações de leitura e gravação em objetos arbitrários. A capacidade de linearização pode ser vista como um caso especial de consistência externa em que uma transação só pode conter uma única operação de leitura ou gravação em um único objeto.
O Spanner oferece capacidade de serialização?
Sim. Na verdade, o Spanner oferece consistência externa, uma abordagem do que a capacidade de serialização. Um sistema de processamento de transações é serializável se executa transações de maneira indistinguível de um sistema em que as transações são executadas em série. O Spanner também garante que a ordem serial seja consistente com a ordem em que as transações podem a serem observados para se comprometer.
Pense novamente no exemplo bancário usado anteriormente. Em um sistema que oferece capacidade de serialização, mas não consistência externa, mesmo que o cliente tenha realizado a T1 e a T2 sequencialmente, o sistema poderá reordená-los, o que poderia fazer com que o débito resultasse em uma penalidade por fundos insuficientes.
O Spanner oferece consistência forte?
Sim. Na verdade, o Spanner oferece consistência externa, que é uma propriedade do que a consistência forte. O modo padrão para leituras no Spanner é "forte", o que garante que elas observem os efeitos de todas as transações que foram confirmadas antes do início da operação, independentemente de qual réplica recebe a leitura.
Qual a diferença entre consistência forte e consistência externa?
Um protocolo de replicação exibe "consistência forte" se os objetos replicados são linearizáveis. Como a capacidade de linearização, a "forte consistência" é mais fraca que a "consistência externa", porque não diz nada sobre o comportamento das transações.
O Spanner oferece consistência eventual (ou lenta)?
O Spanner oferece consistência externa, que é uma propriedade muito mais forte do que a consistência posterior. A consistência eventual tem garantias mais fracas em troca de maior desempenho. Ela é problemática porque os leitores podem observar o banco de dados em um estado em que nunca foi verdadeiro. Por exemplo: uma leitura pode observar um estado em que a transação B foi confirmada, mas na transação A não foi, mesmo que a A tenha acontecido antes da B. Spanner. fornece leituras desatualizadas, que oferecem o desempenho melhora como a consistência posterior, mas com consistência muito mais forte. garantias. Uma leitura desatualizada retorna dados de uma "antiga" o carimbo de data/hora, o que não pode gravações em blocos porque as versões anteriores dos dados são imutáveis.