Onde nasceu ? Dados do Linkedln e análise da equipe pesquisa, análise e hadoop e dados gasoduto pesquisar gráfico social Caltrain chefe muito branda
dois vivas para o modelo de dados relacional A visão relacional é um triunfo da ciência da computação, computação, mas ... colar juntos cordas para chegar a seus dados é bobagem duro para construir estruturas de dados reutilizáveis não escondem a hierarquia de memória! bom: arquivos de sistemas API ruim: SQL, alguns RPCs RPCs por que é tão difícil?
Falhas em um sistema distribuído é muito mais complicada Um pode falar com B não implica B pode conversar com um
Voldemort é um repositório de dados distribuído que é concebido como umarmazenamento umarmazenamento de
chave-valor utilizado pelo LinkedIn para armazenamento de alta escalabilidade. É nomeado chave-valor utilizado após o fictício Harry Potter vilão Lord Voldemort . Voldemort ainda está em desenvolvimento. Não é nem banco de dados de um objeto, nem um banco de dados relacional. Ele não tentar satisfazer relações arbitrárias e as ACID propriedades, mas sim é um grande, distribuído, tolerante a falhas, tabela persistente hash.
Vantagens Voldemort oferece uma série de vantagens em relação a outras bases de dados:
Combina na memória cache com o sistema de armazenamento de modo que uma camada de armazenamento em cache separado não é necessário (em vez do sistema de armazenamento em si é apenas rápido) É possível emular a camada de armazenagem, uma vez que é completamente mockable. Isso faz com que o desenvolvimento e o teste de unidade simples, em que pode ser feito contra um sistema de armazenamento de usar e deitar fora de memória sem a necessidade de um cluster real ou sistema de armazenamento real Lê e escreve escala horizontal Simple API: A API decide replicação de dados e colocação e acomoda uma ampla gama de aplicações específicas estratégias Dados transparentes porcionamento: Este permite a expansão cluster sem reequilíbrio todos os dados
Propriedades O Voldemort armazenamento de dados distribuídos tem as seguintes propriedades: [ 1 ]
Colocação de dados: Suporte para conectáveis estratégias de posicionamento de dados existe para apoiar coisas como distribuição em centros de dados que estão distantes. A replicação de dados: Os dados são automaticamente replicadas ao longo de um grande número de servidores. Dados particionamento: Os dados são automaticamente dividida de modo que o servidor contém apenas um subconjunto dos dados totais Desempenho do nó único bem: 10-20k de operações por segundo pode ocorrer dependendo das máquinas, a rede, o sistema de disco, eo fator de replicação de dados Independência nó: Cada nó é independente de outros nós sem ponto central de falha ou coordenação Serialização Pluggable: Este permite que as chaves r icos e valores, incluindo listas e tuplas com campos nomeados, bem como a integração com frameworks de serialização comuns. Exemplos dessas estruturas são Avro, serialização de Java, Protocol Buffers, e Thrift Falhas transparentes: falhas do servidor são manipuladas de forma transparente, de modo que o usuário não ver tais problemas Controle de versão: Os itens de dados são versões para maximizar a integridade dos dados em caso de falha, sem comprometer a disponibilidade do sistema
Voldemort é um repositório de dados distribuído que é concebido como umarmazenamento umarmazenamento de
chave-valor utilizado pelo LinkedIn para armazenamento de alta escalabilidade. É nomeado chave-valor utilizado após o fictício Harry Potter vilão Lord Voldemort . Voldemort ainda está em desenvolvimento. Não é nem banco de dados de um objeto, nem um banco de dados relacional. Ele não tentar satisfazer relações arbitrárias e as ACID propriedades, mas sim é um grande, distribuído, tolerante a falhas, tabela persistente hash.
Vantagens Voldemort oferece uma série de vantagens em relação a outras bases de dados:
Combina na memória cache com o sistema de armazenamento de modo que uma camada de armazenamento em cache separado não é necessário (em vez do sistema de armazenamento em si é apenas rápido) É possível emular a camada de armazenagem, uma vez que é completamente mockable. Isso faz com que o desenvolvimento e o teste de unidade simples, em que pode ser feito contra um sistema de armazenamento de usar e deitar fora de memória sem a necessidade de um cluster real ou sistema de armazenamento real Lê e escreve escala horizontal Simple API: A API decide replicação de dados e colocação e acomoda uma ampla gama de aplicações específicas estratégias Dados transparentes porcionamento: Este permite a expansão cluster sem reequilíbrio todos os dados
Propriedades O Voldemort armazenamento de dados distribuídos tem as seguintes propriedades: [ 1 ]
Colocação de dados: Suporte para conectáveis estratégias de posicionamento de dados existe para apoiar coisas como distribuição em centros de dados que estão distantes. A replicação de dados: Os dados são automaticamente replicadas ao longo de um grande número de servidores. Dados particionamento: Os dados são automaticamente dividida de modo que o servidor contém apenas um subconjunto dos dados totais Desempenho do nó único bem: 10-20k de operações por segundo pode ocorrer dependendo das máquinas, a rede, o sistema de disco, eo fator de replicação de dados Independência nó: Cada nó é independente de outros nós sem ponto central de falha ou coordenação Serialização Pluggable: Este permite que as chaves r icos e valores, incluindo listas e tuplas com campos nomeados, bem como a integração com frameworks de serialização comuns. Exemplos dessas estruturas são Avro, serialização de Java, Protocol Buffers, e Thrift Falhas transparentes: falhas do servidor são manipuladas de forma transparente, de modo que o usuário não ver tais problemas Controle de versão: Os itens de dados são versões para maximizar a integridade dos dados em caso de falha, sem comprometer a disponibilidade do sistema
Valor-chave de armazenamento Para habilitar alto desempenho e disponibilidade que permitem apenas muito simples de valor-chave de acesso de dados. Ambas as chaves e os valores podem ser objetos compostos complexos, incluindo listas ou mapas, mas nada-a-menos as consultas apenas suportados são efetivamente o seguinte: valor = store.get (chave) store.put (valor, chave) store.delete (chave)
Este não é de forma f orma suficientemente boa para todos os problemas de armazenamento, há uma variedade de trade-offs: Contras
sem complexos filtros de consulta tudo se junta deve ser feita no código sem restrições de chave estrangeira não desencadeia
Prós
apenas consultas eficientes são o desempenho possível, muito previsível fácil de distribuir através de um cluster de orientação a serviços muitas vezes não permite restrições de chaves estrangeiras e forças de junta a ser feito f eito em código de qualquer maneira (porque chave se refere a dados mantidos por outro serviço) utilizando um banco de dados relacional que você precisa de uma camada de cache para escalar lê, a camada de cache normalmente obriga em valores-chave de armazenamento de qualquer maneira m aneira muitas vezes acabam com XML ou outros blobs desnormalizada para o desempenho de qualquer maneira separação limpa de armazenamento e lógica (SQL incentiva lógica buisiness mistura com as operações de armazenamento para maior eficiência) nenhum objeto-relacional perder-jogo
Mais discussão dos detalhes do modelo de dados, será dada a seguir.
Arquitetura do Sistema
Cada camada no código implementa uma interface de armazenamento simples que não colocar, obter e excluir. Cada uma dessas camadas é responsável por executar uma função, como a comunicação TCP / IP, serialização, reconciliação versão, entre os nós de roteamento, etc Por exemplo, a camada de roteamento é responsável por tomar uma operação, digamos, um PUT, e delegando-a todos os N réplicas de armazenamento em paralelo, ao manusear quaisquer falhas.
Mantendo cada uma destas camadas separadas significa que eles podem ser misturados e combinados no tempo de execução para atender diferentes necessidades. Por exemplo, podemos adicionar uma camada de compressão que comprime valores de byte em qualquer nível abaixo do nível de serialização.Da mesma forma que temos a flexibilidade de onde o roteamento inteligente de dados para partições é feito. Isso pode ser feito no lado do cliente para "inteligentes" clientes, ou pode ser feito no lado do servidor para permitir mudos, hardware clientes com balanceamento de carga (http dizer escrita em Ruby). O que fazemos é simplesmente uma questão de se a camada de rede fica acima ou abaixo da camada de roteamento.
No diagrama acima, "Carregar Bal". indica um balanceador de carga de hardware ou round-robin balanceador de carga de software e "Partition roteamentoconsciente" é o armazenamento de sistemas de roteamento interno. Obviamente menos lúpulo é bom do ponto de vista de latência (desde, bem, há menos saltos), agradável do ponto de vista de transferência (já que há menos potenciais gargalos), mas requer a inteligência de roteamento para subir na pilha (por exemplo, o cliente deve ser Java e usar a nossa biblioteca). Na final, a imagem mais à direita os pedidos http-RPC para o serviço estão a ser encaminhados para as máquinas que contêm os dados corretos (quando possível), para que, no caso simples de um único replicado ler a necessidade máquina pode buscar diretamente do local, bdb-processo. Essa flexibilidade torna configurações de alto desempenho possível. O acesso ao disco é o único grande desempenho atingido no armazenamento, o segundo é o lúpulo de rede. O acesso ao disco pode ser evitado através do particionamento do conjunto de dados e armazenamento em cache, tanto quanto possível. Lúpulo rede exigem flexibilidade de arquitetura para eliminar. Note que, no diagrama acima, podemos implementar 3-hop, 2-hop, e 1-hop serviços remotos usando
configurações diferentes. Esta permite um desempenho muito elevado para ser alcançado quando é possível encaminhar chamadas directamente serviço para o servidor apropriado. Dados particionamento e replicação
Dados precisa ser particionado em um cluster de servidores de modo que nenhum único servidor precisa manter o conjunto de dados completo. Mesmo quando os dados podem caber em um único disco, o acesso ao disco para valores pequenos é dominado por tempo de busca para particionamento tem o efeito de melhorar a eficiência do cache, dividindo o conjunto "quente" de dados em pequenos pedaços que podem (espero) ser totalmente em memória no servidor que armazena essa partição. Isto significa que os servidores no cluster não são intercambiáveis, e os pedidos devem ser encaminhados para um servidor que contém os dados solicitados, e não apenas a qualquer servidor disponível de forma aleatória. Da mesma forma os servidores regularmente não, tornar-se sobrecarregado, ou são levadas em manutenção. Se existirem S servidores e cada servidor é assumido falhar independentemente com probabilidade p em um determinado dia, então a probabilidade de perda de pelo menos um servidor de um dia, será de 1 (1 - p ) s . É óbvio que este fato, não se pode armazenar dados em um único servidor ou a probabilidade de perda de dados será inversamente proporcional ao tamanho do cluster. A maneira mais simples possível para alcançar este objetivo seria cortar os dados em S partições (um por servidor) e armazene cópias de uma determinada chave K em R servidores. Uma maneira de associar os R servidores com chave K seria tomar um = K mod S e armazenar o valor em servidores um , um , um ...,um + r . Assim, para qualquer probabilidade p você pode escolher um fator apropriado replicação R para alcançar uma probabilidade aceitavelmente baixo de perda de dados. Este sistema tem a propriedade interessante que qualquer pessoa pode calcular a localização de um valor apenas por saber de sua chave, o que nos permite fazer look-ups de uma forma peer-to-peer sem contato com um servidor central de metadados que tem um mapeamento de todas as chaves aos servidores. A desvantagem da abordagem acima ocorre quando um servidor é adicionado ou retirado do cluster (dizer, porque nós compramos um novo hardware ou um servidor está temporariamente fora do ar). Neste caso d podem mudar e todos os dados irão deslocar entre os servidores. Evento se d não muda carga não vai distribuir uniformemente a partir de um único servidor removido / falha para o resto do agrupamento.
Hashing consistente é uma técnica que evita esses problemas, e podemos usá-lo para calcular a localização de cada tecla no cluster. Usando esta técnica voldemort tem a propriedade de que, quando um servidor falhar carga irá distribuir igualmente sobre todos os servidores restantes no cluster. Da mesma forma, quando um novo servidor é adicionado a um conjunto de S servidores, apenas 1 / ( S +1) valores devem ser movidos para a nova máquina. Para visualizar o método de hash consistente, podemos ver as possíveis inteiros valores de hash como um anel começando com 0 e circulando em torno de 2 ^ 311. Este anel é dividido em Q partições de tamanho igual, com Q >> S , e cada um dos S servidores é atribuído Q / S destes. Uma chave é mapeada para o anel usando uma função arbitrária hash, e então calcular uma lista de R servidores responsáveis por esta chave, tendo os primeiros R nós únicos quando se mover sobre as partições no sentido horário. O diagrama abaixo fotos um anel de hash para servidores A , B , C , D . As setas indicam as teclas mapeadas no anel de hash ea lista resultante de servidores que irá armazenar o valor para essa chave seR = 3.
Formato de dados e consultas Em um banco de dados relacional de dados é dividido em mesas 2D. O equivalente aqui é uma "loja", não usamos a palavra tabela, pois os dados não é necessariamente tabular (um valor pode conter listas e mapeamentos que não são consideradas em um mapeamento rigoroso relacional). Cada chave é única para uma loja, e cada chave pode ter, no m áximo, um valor.
Consultas
Voldemort suporta semântica hashtable, para um único valor pode ser modificado em um tempo e de recuperação é de chave primária. Isso faz com que a distribuição através de máquinas particularmente fácil, já que tudo pode ser dividido pela chave primária. Note-se que, apesar de não apoiar um-muitos relações, fazemos listas de apoio como valores que realiza a mesma coisa - por isso, é possível armazenar um número razoável de valores associados a uma única tecla. Isto corresponde a um java.util.Map onde o valor é um java.util.List. Na maioria dos casos desnormalização é uma melhoria de desempenho enorme uma vez que existe apenas um único conjunto de pesquisas em disco, mas para grandes relacionamentos um-para-muitos (digamos que uns mapas chave para dezenas de milhões de valores), que deve ser mantido no servidor e t ransmitido através de um cursor preguiçosamente esta abordagem não é prático. Este caso (raro) deve ser dividido em sub-consultas ou não tratadas a nível de aplicação. A simplicidade dos procedimentos podem ser uma vantagem, uma vez que cada uma tem um desempenho muito previsível, é fácil de quebrar o desempenho de um serviço para o número de operações de armazenagem executa rapidamente e estimar a carga. Em contraste consultas SQL são muitas vezes opaco, e os planos de execução pode ser dependente dos dados, por isso pode ser muito difícil de estimar se uma determinada consulta terá um bom desempenho com dados realistas sob carga (especialmente para um novo recurso que não tem nem dados, nem de carga). Além disso, ter uma interface de operação de três permite transparentemente zombar fora da camada de armazenamento de todo e teste de unidade usando uma implementação de simulação-de armazenamento que é um pouco mais do que um HashMap. Isso faz com que o teste de unidade exterior de um recipiente ou ambiente muito mais prática.
Modelo de Dados e serialização Serialização em Voldemort é conectável assim você pode usar um dos cozido em serializers ou facilmente escrever a sua própria. No nível mais baixo do formato de dados para Voldemort é apenas matrizes de bytes para ambas as chaves e valores. Formatos de nível mais alto de dados são uma opção de configuração que são definidos para cada loja - qualquer formato pode ser apoiado através da implementação de uma classe Serializer que lida com a tradução entre bytes e objetos. Isso garante que o cliente serializa os bytes corr etamente. Os seguintes tipos são suportados fora da caixa, inserindo o tipo apropriado na configuração da loja:
json - Um binário, digitado JSON modelo de dados que suporta listas,
mapas, datas, valores booleanos e números de precisão diferentes. Este é o tipo de serialização só que tem um mapeamento completo de bytes <-> objetos e de cordas <-> objetos. Isto significa que pode ser interagido com como SQL (por exemplo, através do cliente de linha de comando). Nossa utilização da produção atual utiliza uma digitado, compacto, esquema check formato JSON-like, mas isso não tem nenhum estatuto especial, e para outras aplicações de outros mecanismos de serialização pode ser melhor. corda - Apenas armazena cordas primas. Útil para blobs XML. java-serialização - Nosso velho amigo de serialização Java. Certifique-se de entender a compatibilidade garante serialização java fornece antes de armazenar muitos objetos java. protobuf - buffers Protocolo é um formato de serialização de geração de código do Google. Esta pode ser a melhor maneira de ir, se você não precisa de acesso à linha de comando. thrift - Thrift é outro formato de serialização geração de código. avro-generic / avro específica / avro-reflexivo - Avro é outro sistema de serialização de dados rich. identidade - Isso efetivamente desativa serialialization, apenas dando-lhe de volta o byte exata [].
String e serialização de identidade são bastante auto-explicativo. Documentação / Tutorial para os formatos de serialização outros podem ser facilmente encontrados na internet. Assim, o restante desta seção descreve a motivação por trás do tipo json. Tipo de serialização JSON Detalhes
Existem três estados que os dados podem residir, e nós queremos ser capazes de traduzir entre todos eles:
Em estruturas de dados de memória: por exemplo, um objeto Usuário Bytes para transmissão de persistência e de rede Representação de texto: É essencial ser capaz de ter um DBA verificar certos valores e fazer atualizações on-line sem escrever novo código
SQL, basicamente, em torno de padroniza o formato de consulta de texto e programas de lidar com o mapeamento entre essas cordas e as estruturas de dados internos do programa usa. Este é o problema de mapeamento objetorelacional clássico. JSON é um excelente modelo de dados para armazenamento, pois suporta os tipos comuns que são usados em todas as linguagens de programação (strings,
números, listas / matrizes e objetos / tabelas de hash), o problema é que ele é inerentemente sem esquema. O uso mais comum para todo o problema é f azer com que o armazenamento de N linhas, todos com o m esmo formato exacto (ou seja, contendo as mesmas colunas), neste caso JSON é um desperdício, uma vez que armazena o formato de dados em cada linha. Da mesma forma que queremos ser capazes de verificar afirmações sobre a forma de os dados para evitar que erros de digitação uma coluna para armazenar dados corrompidos. Para evitar isso, devemos ser capazes de atribuir um esquema para a chave eo valor de cada loja que descreve o que é permitido para armazenar lá e como traduzi-lo para e de bytes. O esquema pode-se ser especificado em JSON, bem como, utilizando os seguintes tipos: int8, int16, int32, int64, float32, float64, objeto de data, string, objeto, bytes, boolean, array,
Por exemplo, se eu esperar uma loja para conter cadeias então eu posso especificar o tipo de tabela já que "String"
Note que esta definição de tipo é, em si JSON válido O código java que busca dados irá retornar uma String Se eu esperar a loja para conter uma lista de números inteiros, digamos, ids membros, posso especificar o tipo ["Int32"]
O código java irá retornar uma List
. Se eu esperar a loja para conter um objeto de usuário simples que eu poderia definir o tipo {"Fname": "string", "sobrenome": "string", "id": "int32", "e-mails": ["string"]}
Aqui, o código Java retornará uma mapa contendo cada uma das teclas de dados, e o valor associado. Aqui está uma lista completa de tipos permitidos:
tipo
subestilos bytes usados armazenáveis
Tipo Java
exemplo JSON
definição do tipo de exemplo
int8, int16, int32, Byte, Short, 8, 16, 32, 64, número int64, float32, Integer, Long 1 32, 64, 32 float64 data, Float, duplo, data
"Int32"
2+ comprimento String, Byte [] de corda ou bytes
"Olá"
boolean boolean
1
verdadeiro "Boolean"
objeto objeto
{"Key1": 1, 1 tamanho + Mapa "Key2": "2", de conteúdos "key3": false}
{"Nome": "string", "height": "int16"}
ordem ordem
tamanho * sizeof (tipo)
["Int32"]
corda
cordas, bytes
Boolean
List <>
[1, 2, 3]
"String"
Neste sentido, a definição de tipo é um conjunto de restrições de padrão json que fazem serialização eficiente (por meio da distribuição de fora campos repetidos e armazenar números compacta) e permitir a checagem de dados básicos de correção. Note-se que mesmo que um valor pode ter todos esses campos diferentes que só suportam consultas por chave definida da loja. Para ajudar com a evolução do esquema esta implementação JSON inclui a capacidade de versão do esquema para permitir a migração gradual dos dados. Os dados serão sempre gravados usando o esquema mais recente, mas será sempre ler usando qualquer esquema foi usado para escrever. Isso permite que a migração de esquema para ter lugar sem derrubar o serviço que recebe os dados.
Consistência e Versioning Ao tomar simultânea múltipla escreve distribuídos em vários servidores (e talvez vários centros de dados) de consistência de dados torna-se um problema difícil. A solução tradicional para este problema é distribuído transações, mas estes são ambos. Lenta (devido a muitas idas) e frágil como eles exigem que todos os servidores de estar disponível para processar uma transacção Em particular, qualquer algoritmo que deve falar com> 50% de servidores para garantir a consistência torna-se bastante problemática se o aplicativo é executado em vários centros de dados e, portanto, a latência para o cruzamento de dados do centro de operações será extremamente alto.
Uma solução alternativa é a tolerar a possibilidade de inconsistência, e resolver inconsistências em tempo de ler. Esse é o método utilizado aqui. Aplicações costumam fazer uma leitura-modificação-atualizar seqüência ao modificar dados. Por exemplo, se um usuário adiciona um endereço de e-m ail à sua conta, pode carregar o objeto de usuário, adicionar o e-m ail, e em seguida, escrever os novos valores de volta para o banco de dados. As transações em bancos de dados são uma solução para este problema, mas não são uma opção real quando a transação deve abranger cargas de várias páginas (que pode ou não pode ser concluída, e que pode terminar em qualquer período de t empo específico) O valor para uma determinada chave é consistente se, na ausência de alterações, todas as leituras de retorno que o mesmo valor de chave. No mundo só de leitura de dados é criado de uma forma consistente e não mudou. Quando acrescentamos tanto escreve, e replicação, encontramos problemas: agora precisamos atualizar vários valores em várias máquinas e deixar as coisas em um estado consistente. Na presença de falhas no servidor isso é muito difícil, na presença de partições de rede é comprovadamente impossível (a partição é quando, por exemplo, A e B podem alcançar um ao outro e C e D podem alcançar um ao outro, mas A e B pode 't alcance C e D). Existem vários métodos para alcançar a consistência com diferentes garantias e compensações de desempenho.
Duas fases - Este é um protocolo de bloqueio que envolve duas rodadas de
coordenação entre as máquinas. É perfeitamente consistente falha, mas não tolerantes, e muito lenta. Paxos estilo de consenso - Este é um protocolo para chegar a um acordo sobre um valor que é mais tolerante fracasso. Leia-reparação - As duas primeiras abordagens evitar inconsistência permanente. Esta abordagem envolve a escrita de todas as versões inconsistentes, e depois para ler o tempo de detecção do conflito, e resolver os problemas. Isso envolve pouca coordenação e é completamente falha tolerante, mas pode exigir a lógica de aplicação adicional para resolver conflitos.
Nós usamos versões e leitura de reparação. Isto tem uma das melhores garantias de disponibilidade e maior eficiência (apenas W escreve ida e volta de rede são necessários para réplicas N onde W pode ser configurado para ser inferior a N). 2PC normalmente requer 2N roundtrips bloqueio. Variações Paxos variar um pouco, mas são comparáveis aos 2PC.
Outra abordagem para atingir a consistência é usando Handoff sugeriu . Neste método durante as gravações, se acharmos que os nós de destino são para baixo nós armazenamos uma "dica" do valor atualizado em um dos nós vivos. Então, quando esses nós para baixo voltar as "dicas" são empurrados para eles, tornando os dados consistente. Muitos dos detalhes são emprestados do papel da Amazônia abaixo Aqui estão alguns bons textos sobre este assunto:
Consistência na Dynamo da Amazon Paxos Made Simple Duas fases O significado é de consistência eventual (por CTO da Amazon, Werner Vogels)
Controle de versão em um sistema distribuído Um sistema de controle de versão simples é otimista bloqueio armazenamos um balcão único ou "relógio" valor com cada pedaço de dados e só permitir atualizações quando a atualização especifica o valor do relógio correta. Isso funciona bem em um banco de dados centralizado, mas cai em um sistema distribuído onde os servidores aparecem e desaparecem e replicação pode levar tempo. Para isso o uso de um único valor não irá conter o suficiente da história escrita para nos permitir jogar fora versões antigas. Considere a seguinte seqüência de ações: # Dois servidores ao mesmo tempo buscar o mesmo valor [Cliente 1] chegar (1234) => {"nome": "Jay", "email": "jay.kreps @ linkedin.com"} [Cliente 2] se (1234) => {"nome": "Jay", "email": "jay.kreps @ linkedin.com"} # Cliente uma modifica o nome e faz um put [Cliente 1] colocar (1234, {"nome": "jay Kreps", "e-mail": "jay.kreps @ linkedin.com"}) # 2 modifica o cliente de e-mail e faz um put [Cliente 2] colocado (1234, {"nome": "Jay", "email": "jay.kreps @ yahoo.com"}) # Agora temos as seguintes versões conflitantes: {"Nome": "Jay", "email": "jay.kreps @ linkedin.com"} {"Nome": "jay Kreps", "e-mail": "jay.kreps @ linkedin.com"} {"Nome": "Jay", "email": "jay.kreps @ yahoo.com"}
Neste modelo os dois últimos escreve tornar irrelevante o valor original (desde que acontecerá depois da original). No entanto não há nenhuma regra que irá dizer ao servidor que ele pode jogar fora ou se muda para o e-mail ou a mudança de nome. Então, nós queremos um sistema de controle de versão que permite detectar substitui e jogar fora a versão antiga, mas também nos permite detectar conflitos e deixar o cliente conciliar estes. Uma resposta a isso é uma assim chamada versão vetor de relógio. Um relógio vetor mantém um contador para cada servidor escrito, e nos permite calcular quando duas versões estão em conflito, e quando uma versão bem-sucedida ou precede outro. Um relógio vetor é uma lista de servidor: pares de versão: [1:45,2:3,5:55]
A versão indica que o servidor era o "mestre" para que o número de gravações. Uma versão v1 consegue uma versão v2 se para todo i , v1 i > v2 i . Se nem v1 > v2 nem v1 < v2 , entãov1 e v2 co-ocorrer, e estão em conflito. Aqui está um exemplo simples de duas versões conflitantes: [1:2,2:1] [1:1,2:2]
Assim, nosso esquema de versões define uma ordem parcial sobre os valores que simples esquemas de bloqueio otimista definem uma ordem total .
Parâmetros de roteamento Qualquer sistema persistente precisa responder à pergunta "onde está o meu material?". Esta é uma pergunta muito fácil se tivermos um banco de dados centralizado, pois a resposta é sempre "em algum lugar no servidor de banco de dados". Num sistema de chave dividida existem várias máquinas que podem ter os dados. Quando fazemos uma leitura precisamos ler a partir de pelo menos um servidor para obter a resposta, quando fazemos uma gravação que precisamos (finalmente) escrever a todos N das réplicas. Existem, portanto, três parâmetros que importam:
N - O número de réplicas R - O número de máquinas de ler a partir de W - O número escreve bloquear para
Note que se R + W > N então temos a garantia de "ler nosso escreve". Se W = 0, em seguida, as gravações são não-bloqueio e não há garantia de que quer o sucesso. Puts e exclusões não são consistentes nem seja imediatamente isolado. A semântica é esta: se uma operação de colocar / apagar sucede, sem exceção, então é garantido que pelo menos W nós realizou a operação, no entanto, se a gravação falhar (digamos, porque nós muito poucos conseguem realizar a operação), então o Estado é indeterminado. Se pelo menos um posto / apagar consegue, então o valor será eventualmente o novo valor, no entanto, se nenhum conseguiu então o valor é perdido. Se o cliente quer garantir o estado depois de uma operação de escrita não devem emitir outra gravação.
Camada de persistência Apoiamos uma API simples para persistência e uso BDB edição Java como padrão. Outros mecanismos de armazenamento suportados são MySQL, o armazenamento em memória (utilizado para o teste de unidade) e nosso mecanismo de armazenamento próprio costume de somente leitura (gerados offline como um processo de lote em Hadoop). Para adicionar uma nova implementação de persistência você precisa implementa colocar, obter, excluir e, além de fornecer um iterador sobre os valores na loja local.
Suporte para lote computadorizada de dados read-only lojas Uma das necessidades mais intensivas de dados de armazenamento é armazenar os dados do lote calculados sobre os membros e conteúdo do nosso sistema. Estes postos de trabalho, muitas vezes lidar com as relações entre as entidades (por exemplo, os usuários relacionados, ou artigos de notícias relacionadas) e assim por N entidades podem produzir até N 2 relacionamentos. Um exmaple no LinkedIn são as redes membros, que estão na faixa 12TB se armazenado explicitamente para todos os membros.Processamento em lote de dados é geralmente muito mais eficiente do que o acesso aleatório, o que significa que se pode facilmente produzir mais dados em lote computadorizada que podem ser facilmente acessados pelo sistema vivo. Hadoop expande essa capacidade. Estamos no processo de open-sourcing um. Voldemort persistência backend que suporta muito eficiente acesso somente leitura que ajuda a ter um monte de dor de nossos criação, implantação e gestão de grandes, somente leitura lote conjuntos de dados computadorizada Grande parte da dor de lidar com computação lote vem do processo de "push", que transfere dados de um data warehouse ou instância hadoop ao sistema vivo. Em um db tradicional este, muitas vezes, significa a reconstrução do índice
no sistema ao vivo com os novos dados. Fazendo milhões de instruções SQL INSERT ou UPDATE, geralmente não é de todo eficiente, e, normalmente, em um db SQL os dados serão implantados como uma nova tabela e, em seguida, trocou para substituir os dados atuais quando a nova tabela é completamente construído. Isso é melhor do que fazer milhões de atualizações individuais, mas isso ainda significa que o sistema vivo está agora construindo um índice GB de muitos, para o novo conjunto de dados (ou Performa), ao m esmo tempo servindo de tráfego ao vivo. Isso por si só pode levar horas ou dias, e pode destruir o desempenho em consultas ao vivo. Algumas pessoas têm fixado este trocando no nível de banco de dados (por exemplo, ter uma db online e offline, e depois troca), mas isso requer esforço e significa apenas metade hardware do seu está sendo utilizada. Voldemort corrige esse processo, tornando possível a Prebuild o próprio índice offline (em Hadoop ou onde), e simplesmente empurrá-lo para fora aos servidores ao vivo e transparente trocar. Para mais detalhes sobre estas lojas lote computadorizada (chamado somente leitura lojas) ler esta .
Referências
Dynamo: altamente disponível Amazônia loja Key-Value - Este é o original! Tempo, Clocks, ea Ordenação de Eventos em um Sistema Distribuído Este é o modelo para o sistema de controle de versões Consistência eventual Revisited discussão muito interessante no Blog Werner Vogels 'na interação com os desenvolvedores do sistema de armazenamento e quais as vantagens e desvantagens significa em termos práticos. Conjectura de cerveja e da viabilidade de consistentes, disponíveis, serviços partição tolerantes web - Consistência, Disponibilidade, Partição tolerância escolher dois. Berkeley DB desempenho - Uma visão um tanto tendenciosa do desempenho bdb. Google Bigtable - Para efeito de comparação, uma abordagem muito diferente. Um Fit Tamanho é tudo: uma idéia cujo tempo chegou e saiu - Papel muito interessante pelo criador de Ingres, Postgres e Vertica Tamanho único? - Parte 2, resultados comparativos - Benchmarks para ir com o papel acima Consistência na Dynamo da Amazon - Um bom post sobre Dynamo Paxos Made Simple Duas fases - Wikipédia descrição.
Configuração Há três arquivos de configuração que a operação do servidor de controle:
cluster.xml - Este contém as informações sobre todos os nós (servidores,
por exemplo) no cluster, o que eles estão em nome de host, as portas que eles usam, etc É exatamente o mesmo para todos os nós de Voldemort. Não guarda parâmetros de ajuste ou diretórios de dados para os nós, já que não é informação pública ao cluster, mas é específico para que a configuração de nós em particular. stores.xml - Este contém as informações sobre todas as lojas (tabelas, por exemplo) no cluster. Isso inclui informações sobre o número necessário de sucesso lê para manter a consistência, o número necessário de gravações, bem como a forma como chaves e valores são serializados em bytes. É o mesmo em todos os nós do cluster. server.properties - Este contém os parâmetros de ajuste que controlam um determinado nó. Isto inclui o ID do nó local para que ele saiba o que a entrada em cluster.xml corresponde a si mesma, também o tamanho do pool de threads, bem como qualquer configuração necessária para o mecanismo de persistência local, tais como BDB ou m ysql. Este arquivo é diferente em cada nó.
Finalmente, há uma variável de ambiente, VOLDEMORT_HOME, que controla o diretório no qual dados e configuração residem. Você pode ver um exemplo de como a configuração é Definiu no. Config / subdiretório do projeto Isto inclui configurações de exemplo que você pode modificar com suas especificidades próprias.
Configuração de cluster Aqui está um exemplo cluster.xml para um cluster de 2 nós com 8 partições de dados. Nós também temos opcionais "zona" campos que permitem mapear os nós para determinados grupos lógicos (datacenter, rack, etc) chamadas zonas: <-! O nome é apenas para ajudar os usuários a identificar este cluster do gui -> mycluster name> 0 zona-id> 1 proximidade lista-> 1 zona-id>
0 proximidade lista-> <-! O ID de nó é único, um início ID seqüencial com 0 que identifica cada servidor no cluster -> 0 id> vldmt1.prod.linkedin.com host> 8081 http-port> 6666 socket porta-> 6667 admin porta-> <-! Uma lista de partições de dados atribuídos a este servidor -> 0,1,2,3 partições> 0 zona-id> Server> 1 id> vldmt2.prod.linkedin.com host> 8081 http-port> 6666 socket porta-> 6667 admin porta-> 4,5,6,7 partições> 1 zona-id> Server>
Uma coisa que é importante entender é que as partições não são partições estáticas de servidores, mas eles são um mecanismo para particionar o espaço da chave de tal forma que cada tecla é estaticamente mapeado para uma partição de dados em particular. O que isto significa é que um determinado cluster pode suportar muitas lojas cada uma com replicação diferentes fatores, o fator de replicação não é codificado no projeto do cluster. Isto é importante, uma vez que alguns dados é mais importante do que os outros dados, e o correcto equilíbrio entre o desempenho e consistência de uma loja pode ser diferente da outra loja. Outro ponto importante a lembrar é que o número de partições de dados não podem ser alterados.Fazemos apoiar uma redistribuição online ( reequilíbrio) de partições. Em outras palavras, a inclusão de novos resultados em gânglios movendo propriedade de divisórias, mas o número total de partições irá sempre permanecer o mesmo, assim como o mapeamento de chave para partição. Isto significa que é importante dar um bom número de partições para começar. O script aqui vai gerar esta parte da configuração para você. Note-se que a configuração é atualmente arquivos simples por isso é importante que os dados em cluster.xml e stores.xml ser exatamente o mesmo em cada servidor, e que as IDs de nós e partições não pode ser alterado, uma vez que pode significar que os clientes vão pensar seus dados devem estar no nó X , quando na
verdade ele foi armazenado no nó Y . Esta limitação será removida como a configuração é movida em voldemort si.
Configuração da loja Aqui é um stores.xml exemplos de uma loja chamada de teste, que requer apenas uma única leitura e escrita e usa bdb para persistência: teste nome> 2 replicação fator-> 2 preferencial lê-> 1 requerido lê-> 2 preferencial-escreve> 1 requerido-escreve> bdb persistence> cliente routing> consistente de roteamento rota estratégia> corda tipo > utf8 esquema info-> Key serializador-> json type> [{"id": "int32", "nome": "string"}] esquema info-> gzip Value serializador-> Loja> Lojas>
Cada um desses parâmetros merece uma discussão rápida:
Nome - O nome da loja. Esta é a seqüência em que os clientes serão
capazes de conectar e operar sobre esta loja. É equivalente ao nome da tabela no SQL. replicação factor - Este é o número total de vezes que os dados são armazenados. Cada colocar ou apagar operação deve, eventualmente, acertar esta muitos nós. Um factor de replicação de n significa que pode ser possível tolerar até n - 1 falhas de nó sem perda de dados. preferido-lê (opcional)-O número de sucesso lê o cliente irá tentar fazer antes de retornar um valor para a aplicação. O padrão a ser igual a leituras necessária
necessário-lê -O número mínimo de leituras que podem ter sucesso sem
lançar uma exceção.Considere um caso em que o factor de replicação é 5, leituras preferida é 4, e lê-se necessário é 2.Se três dos cinco nós são operacionais, então o cliente pode experimentar todos os nós para tentar chegar a 4 preferido lê, mas uma vez que apenas três são sensíveis permitirá a leitura para completar. Havia apenas um sido sensível teria jogado uma exceção, já que era menor do que a garantia de consistência solicitado para esta tabela (e que poderia significar retornar dados obsoletos). preferido-escreve (opcional)-O número de sucesso escreve o cliente tenta bloquear antes de voltar para o sucesso. O padrão é exigido-escreve necessário-escreve - O menor número de gravações que podem ter sucesso sem o cliente voltar uma exceção. persistência - O backend de persistência utilizada pela loja. Atualmente, esse poderia ser um dos bdb , mysql , memória , readonly , e de cache . A diferença entre o cache e memória é que a memória vai jogar e OutOfMemory exceção se torna maior do que a pilha da JVM enquanto o cache irá descartar dados. roteamento - Determina a política de roteamento. Apoiamos tanto cliente (Client lado de roteamento) e servidor (encaminhamento do lado do servidor). encaminhamento estratégia - Determina como armazenamos as réplicas. Atualmente nós suportamos três roteamento-estratégias consistente de roteamento (padrão), zona de roteamento etodo- roteamento . serializador-chave - O tipo de serialização usado para ler e gravar chaves . O tipo pode ser json ,java- serialização , corda , protobuff , parcimônia , ou identidade (ou seja, bytesprimas). O esquema info-dá informações para o serializador sobre como realizar o mapeamento (por exemplo, o esquema descrito no JSON aqui ). valor serializador- - O tipo de serialização usado para ler e escrever valores . Os tipos suportados são os mesmos que para as chaves. No exemplo acima, destacam-se ainda "compressão" o subelemento que atualmente suporta 'gzip' e 'lzf "compressão. Os subelementos são os mesmos que para a chave serializador, exceto que o serializador o valor pode ter vários esquemas-infos com versões diferentes. A versão mais recente é a usada para gravar dados, mas os dados é sempre lido com a versão que foi escrito com. Isto permite a evolução do esquema gradual. Controle de versão é suportada apenas pelo serializador JSON como formatos de serialização outros têm os seus sistemas de controle de versão próprios. Aqui estão algumas serializers exemplo:
<-! Uma serializador que serializa cordas simples na codificação UTF8 -> corda tipo > utf8 esquema info-> Value serializador->
<-! Uma serializador que serializa dados formato binário JSON com o esquema dado. Cada valor é um List