Spanner: TrueTime e consistência externa

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 a transações. Especificamente, cada transação recebe um carimbo de data/hora que reflete o momento em que o Spanner considera que ela ocorreu. 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 executem leituras consistentes em um banco de dados inteiro (mesmo em várias regiões do Cloud) sem bloquear as gravações.

Consistência externa

O Spanner oferece aos clientes as garantias de controle de simultaneidade mais rigorosas para transações, o que é chamado de consistência externa2. Sob 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 ele forneça garantias tão fortes, o Spanner permite que os aplicativos tenham um desempenho comparável a bancos de dados que fornecem garantias mais fracas (em troca, um desempenho maior). Por exemplo, como bancos de dados compatíveis com isolamento de snapshots, 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 de seus clientes comece com US $50 na conta corrente e US $50 na 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 imutáveis de dados, geralmente 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. Conquistar isso em um sistema amplamente distribuído, como o Spanner, em que servidores em todo o mundo precisam atribuir carimbos de data/hora, é muito mais difícil de fazer de maneira eficiente.

O Spanner depende do TrueTime para gerar carimbos de data/hora monotonicamente crescentes. 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 propriedade de consistência mais rigorosa para sistemas de processamento de transações. Todas as transações no Spanner satisfazem essa propriedade de consistência, não apenas aquelas dentro de uma partição. A consistência externa indica que o Spanner executa transações de uma maneira 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 em que as 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, que é uma propriedade mais rigorosa 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 ser observadas para confirmação.

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 mais forte 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 receba 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 posterior (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. O Spanner fornece leituras desatualizadas, que oferecem benefícios de desempenho semelhantes à consistência posterior, mas com garantias de consistência muito mais fortes. Uma leitura desatualizada retorna dados de um carimbo de data/hora "antigo", que não pode bloquear gravações porque as versões anteriores dos dados são imutáveis.

Sugestões de leitura

Observações

  • 1J. C. Corbett, J. Dean, M. Epstein, A. Fikes, C. Frost, J. Furman, S. Ghemawat, A. Gubarev, C. Heiser, P. Hochschild, W. Hsieh, S. Kanthak, E. Kogan, H. Li, A. Lloyd, S. Melnik, D. Mwaura, D. Nagle, S. Quinlan, R. Rao, L. Rolig, Y. Saito, M. Szymaniak, C. Taylor, R. Wang, and D. Woodford. Spanner: Google's Globally-Distributed Database. In Tenth USENIX Symposium on Operating Systems Design and Implementation (OSDI 12), pp. 261–264, Hollywood, CA, outubro de 2012.
  • 2Gifford, D. K. Information Storage in a Decentralized Computer System. Tese de doutorado, Universidade de Stanford, 1981.