Neste artigo, tentarei mostrar um exemplo simples do meu próprio sistema de correção de erros baseado em RAID-6. Em particular, considere uma situação em que você precisa fornecer redundância de sistema para que ele possa resistir à falha de uma ou duas unidades.
Como bônus, informações sobre como a correção de erros funciona no RAID-5, porque o RAID-6 é uma versão aprimorada do RAID-5.
visão global
Digamos que você tenha três discos com alguns dados. Vamos chamá-los de D1, D2 e D3. Para usar um sistema RAID-6, você precisa de duas unidades adicionais: PD e RS. Em alguns minutos, descreverei o que PD e RS representam. Portanto, um total de cinco drives: D1, D2, D3, PD e RS.
Então a situação:
- D1, D2 e D3 contêm dados arbitrários . Digamos fotos de gatos.
- Um disco especial PD (Parity Drive, às vezes P na documentação) contém dados ampliados gerados automaticamente a partir de D1, D2 e D3.
- Segundo disco especial RS (códigos Reed-Solomon, às vezes chamados de Q) para os mesmos dados que PD.
Vamos ver como realizar operações básicas em tal array.
Como funciona a recuperação
Se você calcular PD e RS corretamente, poderá sobreviver sem dor a uma falha de até dois discos. O procedimento de recuperação depende de quais unidades específicas falham. Normalmente são consideradas sete situações. Abaixo, eles são classificados de fáceis a complexos.
- Perda de PD (apenas um disco).
Um caso muito simples. A unidade PD contém apenas dados gerados automaticamente, portanto, podem ser recuperados usando os dados originais nas unidades D1, D2 e D3.
- : D1, D2 D3 ( ).
, , , RAID-5: PD . PD, . RS ( ).
- RS ( ).
1: , RS, — .
- PD RS ( ).
1 3. , PD, RS.
- RS ( ).
, . PD, , № 2. , RS.
- PD ( ).
. ( D3), PD, . RS (D1 D2), D3. PD. , — .
- ( ).
. PD, RS . — .
Nas próximas seções, exploraremos esses casos com mais detalhes e veremos o código-fonte (em Python) que realiza a recuperação de dados real.
Lembre-se de que os arrays RAID-6 reais não alocam uma unidade inteira para PD ou RS. Esses dados estão espalhados por todas as unidades. Controladores diferentes usam métodos diferentes: assíncrono esquerdo ou síncrono direito, pode haver uma mudança em relação aos dados RAID, latência, etc. Vamos deixar de lado a discussão sobre por que isso acontece e como são as listras reais. RAID-6. Vamos nos concentrar especificamente nos códigos Reed-Solomon.
Dados de teste
Vamos definir "dados do usuário". Para simplificar, vamos definir o tamanho de cada "disco" para 5 bytes.
| Disco | Dados ASCII | Dados em HEX |
|---|---|---|
D1
|
primeiro | 0x66, 0x69, 0x72, 0x73, 0x74 |
D2
|
segundo | 0x73, 0x65, 0x63, 0x6e, 0x64 |
D3
|
terceiro | 0x74, 0x68, 0x69, 0x72, 0x64 |
Agora, vamos examinar mais de perto os cenários mencionados.
Situação 1. Perda de disco PD
Para gerar PD, apenas discos de dados do usuário são necessários. No nosso caso, são D1, D2 e D3. O disco PD é simplesmente XORed de todos os dados do usuário.
Para gerar o deslocamento 0 para PD, todos os bytes do deslocamento 0 devem ser compactados em todos os discos. Idem para o deslocamento 1 e assim por diante:
PD [0] = D1 [0] xou D2 [0] xou D3 [0] PD [1] = D1 [1] xou D2 [1] xou D3 [1] PD [2] = D1 [2] xou D2 [2] xou D3 [2] PD [3] = D1 [3] xou D2 [3] xou D3 [3] PD [4] = D1 [4] xou D2 [4] xou D3 [4]
Exemplo:
PD [0] = 0x66 xou 0x73 xor 0x74 => 0x61 PD [1] = 0x69 xou 0x65 xor 0x63 => 0x64 PD [2] = 0x72 xor 0x63 xor 0x69 => 0x78 PD [3] = 0x73 xor 0x6e xor 0x72 => 0x6f PD [4] = 0x74 xou 0x64 xor 0x64 => 0x74
Sim, é muito simples. Faça isso para discos inteiros (no nosso caso, 5 bytes) e você obterá um PD gerado corretamente:
| Disco | Dados em HEX |
|---|---|
PD
|
0x61, 0x64, 0x78, 0x6f, 0x74 |
Assim, se apenas o PD falhar, é bastante trivial restaurá-lo de D1, D2 e D3.
Situação 2. Perda de um dos armazenamentos de dados: D1, D2 ou D3
A propósito, é assim que funciona a correção de erros do RAID-5. Se apenas um disco de dados do usuário falhar, podemos usar o disco PD para recalcular os dados do usuário ausentes.
Digamos que D2 esteja perdido. D1, D3, PD e RS permaneceram em estoque. Nesse caso, nem toque em RS. Apenas as unidades D1, D3 e PD são necessárias. Para calcular os dados perdidos, você pode usar a função XOR novamente como na situação anterior.
Para recuperar os dados do usuário do deslocamento 0, xorim os bytes dos deslocamentos zero dos discos de dados do usuário que sobraram (D1 e D3) com o byte do deslocamento zero PD. Repita para o deslocamento 1 e assim por diante:
D2 [0] = D1 [0] xou D3 [0] xou PD [0] D2 [1] = D1 [1] xou D3 [1] xou PD [1] D2 [2] = D1 [2] xou D3 [2] xou PD [2] D2 [3] = D1 [3] xou D3 [3] xou PD [3] D2 [4] = D1 [4] xou D3 [4] xou PD [4]
Exemplo:
D2 [0] = 0x66 xou 0x74 xor 0x61 => 0x73 (s) D2 [1] = 0x69 xor 0x63 xor 0x64 => 0x65 (e) D2 [2] = 0x72 xou 0x69 xor 0x78 => 0x63 (c) D2 [3] = 0x73 xor 0x72 xor 0x6f => 0x6e (n) D2 [4] = 0x74 xou 0x64 xor 0x74 => 0x64 (d)
Como você pode ver, é muito fácil recuperar dados de um disco ausente. Não importa qual disco está faltando: a função XOR sempre funciona.
Situação 3. Perda de disco RS
Agora os códigos de campo de Reed-Solomon e Galois entram em ação. Mas não se preocupe, você não precisa ser um matemático para usá-los.
Quando apenas perdemos o drive RS ou criamos um novo sistema como o RAID-6, precisamos apenas regenerar os códigos. Para fazer isso, use as tabelas gflog e gfilog com conteúdo imutável, bem como dados das unidades existentes D1, D2 e D3.
A tabela gflog sempre se parece com isto:
0x00, 0x00, 0x01, 0x19, 0x02, 0x32, 0x1a, 0xc6, 0x03, 0xdf, 0x33, 0xee, 0x1b, 0x68, 0xc7, 0x4b, 0x04, 0x64, 0xe0, 0x0e, 0x34, 0x8d, 0xef, 0x81, 0x1c, 0xc1, 0x69, 0xf8, 0xc8, 0x08, 0x4c, 0x71, 0x05, 0x8a, 0x65, 0x2f, 0xe1, 0x24, 0x0f, 0x21, 0x35, 0x93, 0x8e, 0xda, 0xf0, 0x12, 0x82, 0x45, 0x1d, 0xb5, 0xc2, 0x7d, 0x6a, 0x27, 0xf9, 0xb9, 0xc9, 0x9a, 0x09, 0x78, 0x4d, 0xe4, 0x72, 0xa6, 0x06, 0xbf, 0x8b, 0x62, 0x66, 0xdd, 0x30, 0xfd, 0xe2, 0x98, 0x25, 0xb3, 0x10, 0x91, 0x22, 0x88, 0x36, 0xd0, 0x94, 0xce, 0x8f, 0x96, 0xdb, 0xbd, 0xf1, 0xd2, 0x13, 0x5c, 0x83, 0x38, 0x46, 0x40, 0x1e, 0x42, 0xb6, 0xa3, 0xc3, 0x48, 0x7e, 0x6e, 0x6b, 0x3a, 0x28, 0x54, 0xfa, 0x85, 0xba, 0x3d, 0xca, 0x5e, 0x9b, 0x9f, 0x0a, 0x15, 0x79, 0x2b, 0x4e, 0xd4, 0xe5, 0xac, 0x73, 0xf3, 0xa7, 0x57, 0x07, 0x70, 0xc0, 0xf7, 0x8c, 0x80, 0x63, 0x0d, 0x67, 0x4a, 0xde, 0xed, 0x31, 0xc5, 0xfe, 0x18, 0xe3, 0xa5, 0x99, 0x77, 0x26, 0xb8, 0xb4, 0x7c, 0x11, 0x44, 0x92, 0xd9, 0x23, 0x20, 0x89, 0x2e, 0x37, 0x3f, 0xd1, 0x5b, 0x95, 0xbc, 0xcf, 0xcd, 0x90, 0x87, 0x97, 0xb2, 0xdc, 0xfc, 0xbe, 0x61, 0xf2, 0x56, 0xd3, 0xab, 0x14, 0x2a, 0x5d, 0x9e, 0x84, 0x3c, 0x39, 0x53, 0x47, 0x6d, 0x41, 0xa2, 0x1f, 0x2d, 0x43, 0xd8, 0xb7, 0x7b, 0xa4, 0x76, 0xc4, 0x17, 0x49, 0xec, 0x7f, 0x0c, 0x6f, 0xf6, 0x6c, 0xa1, 0x3b, 0x52, 0x29, 0x9d, 0x55, 0xaa, 0xfb, 0x60, 0x86, 0xb1, 0xbb, 0xcc, 0x3e, 0x5a, 0xcb, 0x59, 0x5f, 0xb0, 0x9c, 0xa9, 0xa0, 0x51, 0x0b, 0xf5, 0x16, 0xeb, 0x7a, 0x75, 0x2c, 0xd7, 0x4f, 0xae, 0xd5, 0xe9, 0xe6, 0xe7, 0xad, 0xe8, 0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf.
A tabela gfilog também é persistente:
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26, 0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9, 0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35, 0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23, 0x46, 0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0, 0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1, 0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc, 0x65, 0xca, 0x89, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f, 0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2, 0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, 0x0d, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce, 0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93, 0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc, 0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9, 0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54, 0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa, 0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73, 0xe6, 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e, 0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff, 0xe3, 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4, 0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41, 0x82, 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6, 0x51, 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef, 0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09, 0x12, 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5, 0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16, 0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83, 0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, 0x01.
Não é necessário incluir essas tabelas no programa, você pode usar o seguinte algoritmo de geração em tempo de execução:
# gflog_tables.py
def generate_tables():
polynomial = 0x11d
s = 8
gf_elements = 1 << s
gflog = gf_elements * [0]
gfilog = gf_elements * [0]
b = 1
for i in range(0, gf_elements):
gflog[b] = i & 255
gfilog[i] = b & 255
b <<= 1
if b & gf_elements:
b ^= polynomial
gflog[1] = 0;
return (gflog, gfilog)
def dump_table(caption, tab):
item = 0
print("--- {} ---".format(caption))
for i in tab:
print("0x{:02x}, ".format(i), end="")
item += 1
if item % 16 == 0:
item = 0
print()
print("")
(gflog, gfilog) = generate_tables()
# Uncomment if you want to see the tables on the console:
#
# dump_table("gflog", gflog)
# dump_table("gfilog", gfilog)
Depois que as tabelas são declaradas, algumas operações precisam ser definidas. Agora estamos trabalhando em um campo finito ( campo de Galois), então as operações aritméticas básicas têm uma implementação diferente (embora o significado seja um tanto semelhante). Você precisa redefinir as operações básicas - adição, multiplicação e divisão:
# rs_functions.py
from gflog_tables import *
# Addition
def gf_add(*args):
result = 0
for arg in args:
result ^= arg
return result
# Indexing
# First drive is 1, second drive is 2, etc...
def gf_drive(index):
global gfilog
return gfilog[index - 1]
# Multiplication
def gf_mul(a, b):
global gflog
global gfilog
if a == 0 or b == 0:
return 0
else:
return gfilog[(gflog[a] + gflog[b]) % 255]
# Division helper
def sub_gf8(a, b):
if a > b:
return a - b
else:
return (255 - (0 - (a - b)))
# Division
def gf_div(a, b):
global gfilog
global gflog
return gfilog[sub_gf8(gflog[a], gflog[b])]
Uma vez que as funções auxiliares são declaradas, vamos tentar gerar os dados do disco RS.
# case 3 -- recover_rs.py
from rs_functions import *
# Here are our drives, together with their data.
image1 = [ ord('f'), ord('i'), ord('r'), ord('s'), ord('t') ]
image2 = [ ord('s'), ord('e'), ord('c'), ord('n'), ord('d') ]
image3 = [ ord('t'), ord('h'), ord('i'), ord('r'), ord('d') ]
# This is a placeholder for our RS drive. It will be regenerated
# in the lines below.
imageRS = [0] * 5
# And this is our loop that generates the RS data using nothing more
# than the user data drives.
for i in range(0, 5):
imageRS[i] = gf_add(gf_mul(gf_drive(1), image1[i]),
gf_mul(gf_drive(2), image2[i]),
gf_mul(gf_drive(3), image3[i]))
dump_table("imageRS", imageRS)
Depois de executar o script
recover_rs.py
, o disco RS contém os seguintes dados:
| Disco | Dados em HEX |
|---|---|
RS
|
0x4d, 0x1e, 0x0d, 0x7a, 0x31 |
No momento, as unidades D1, D2 e D3 estão protegidas pelo algoritmo de correção de erros RAID-6 completo, uma vez que geramos PD e RS corretamente.
É importante lembrar que os dados RS atuais são válidos apenas para D1, D2 e D3 nessa ordem específica . Portanto, o RS para D1, D2 e D3 será diferente de D3, D2 e D1, embora os dados reais nos discos sejam os mesmos. É importante lembrar disso porque, ao recuperar dados RAID-6, você precisa saber a sequência correta do disco dentro da matriz. Felizmente, se o array for pequeno, você pode forçar os dados RS a serem gerados para encontrar a sequência de disco correta.
Situação 4. Perda de PD e RS
Esta também é uma situação simples: primeiro executamos o cenário nº 1 e depois o nº 3.
Repito, neste caso os dados do usuário não são afetados. Podemos usá-los para criar um PD. Então, para criar RS. Ambos os casos já foram descritos nos pontos 1 e 3.
Situação 5. Perda de RS e um disco de dados
E aqui não é difícil. Perdemos um disco de dados, mas ainda temos um PD, portanto, podemos executar o cenário 2 para recuperar o disco de dados ausente. Em seguida, use todos os discos de dados para regeneração RS, como no cenário nº 3. O conjunto completo de discos agora está recuperado.
Situação 6. Perda de PD e um disco de dados
A abordagem geral é primeiro recuperar o disco de dados ausente usando outros discos em combinação com RS e, em seguida, depois que todos os discos de dados forem recuperados, continuar a regenerar o PD (cenário 2).
Nesta situação, você deve fazer alguns cálculos. Suponha que, junto com PD, perdemos o disco de dados D2. Portanto, temos D1, D3 e RS em estoque.
Graças ao disco RS, podemos restaurar D2 combinando D1, D3 e RS, assim:
# case 6 -- recover_d2_and_pd.py
from rs_functions import *
# We have these drives...
image1 = [ ord('f'), ord('i'), ord('r'), ord('s'), ord('t') ]
image3 = [ ord('t'), ord('h'), ord('i'), ord('r'), ord('d') ]
imageRS = [ 0x4d, 0x1e, 0x0d, 0x7a, 0x31 ]
# ...and these drives are dead
imagePD = [0] * 5
image2 = [0] * 5
for i in range(0, 5):
partialRS = gf_add(gf_mul(gf_drive(1), image1[i]),
imageRS[i], # Use RS drive instead of the dead drive.
gf_mul(gf_drive(3), image3[i]))
# gf_drive(2) is our dead drive.
div_result = gf_div(1, gf_drive(2))
# This will generate the data from the dead D2 drive.
image2[i] = gf_mul(div_result, partialRS)
# This will generate the data from the dead PD drive.
imagePD[i] = gf_add(image1[i], image2[i], image3[i])
dump_table("image2", image2)
dump_table("imagePD", imagePD)
Primeiro, você precisa gerar um valor
partialRS
adicionando (gf_add) os valores de retorno
gf_mul
para todos os bytes de todos os discos válidos junto com o valor RS em vez do disco de dados ausente (em nosso caso, D2).
Em seguida, usamos o valor
partialRS
para gerar novamente os dados D2, dividindo um pelo índice de disco morto (
gf_drive(2)
) e multiplicando o resultado por
partialRS
. O argumento
gf_drive(2)
indica o índice do nosso disco morto. Se D1 falhasse, usaríamos aqui
gf_drive(1)
.
Depois de regenerar D2, restaure todos os discos de dados. Neste caso, realizamos a regeneração PD como no cenário # 1: no código acima, isso é feito adicionando (gf_add) dados de todos os discos. Se você lembrar
gf_add
O campo Galois é uma operação XOR simples, portanto, em vez de xorar manualmente os bytes de todos os discos de dados, você pode usar o
gf_add
.
Situação 7. Perda de dois coletores de dados
Este é o cenário mais interessante e difícil. Suponha que os discos D2 e D3 estejam fora de serviço. Neste caso, você precisa usar de alguma forma os discos D1, PD e RS para regenerar os discos ausentes.
Esta é uma abordagem diferente dos casos anteriores. Uma abordagem geral é primeiro gerar dados para D2 e, em seguida, usar a mesma estimativa do cenário 2 para gerar dados para D3. Aqui está o código:
# case 7 -- recover_d2_and_d3.py
from rs_functions import *
# These drives are still alive.
image1 = [ ord('f'), ord('i'), ord('r'), ord('s'), ord('t') ]
imagePD = [ 0x61, 0x64, 0x78, 0x6f, 0x74 ]
imageRS = [ 0x4d, 0x1e, 0x0d, 0x7a, 0x31 ]
# These drives are dead, we can't read from them.
image2 = [0] * 5
image3 = [0] * 5
for i in range(0, 5):
partialPD = gf_add(image1[i]) # add other drives if they exist
partialRS = gf_add(gf_mul(gf_drive(1), image1[i])) # add other drives if they exist
g = gf_div(1, gf_add(gf_drive(2), gf_drive(3)))
xoredPD = gf_add(partialPD, imagePD[i])
xoredRS = gf_add(partialRS, imageRS[i])
mid = gf_add(gf_mul(gf_drive(3), xoredPD), xoredRS) # gf_drive(3) is the second drive we've lost
# Regenerate data for D2.
data = gf_mul(mid, g)
image2[i] = data
# Regenerate data for D3.
image3[i] = gf_add(image1[i], image2[i], imagePD[i])
# or:
#
# image3[i] = gf_add(data, xoredPD)
dump_table("image2", image2)
dump_table("image3", image3)
Primeiro, você precisa adicionar todos os bytes de todos os discos de dados existentes para gerar
partialPD
. Neste exemplo, temos apenas um disco de dados, então o parâmetro
partialPD
será simplesmente o conteúdo do disco D1. Mas as matrizes RAID-6 abrangem várias unidades. Portanto, se tivermos mais de um disco de dados, por exemplo, três discos de dados ativos, o cálculo parcial do PD será semelhante a este:
partialPD = gf_add(image1[i], image2[i], image3[i])
Em seguida, precisamos de um parâmetro
partialRS
. Ele pode ser calculado adicionando dados de discos existentes da seguinte maneira:
partialRS = gf_add(A, B, C, ..., Z)
where A = gf_mul(gf_drive(1), image1[i])
B = gf_mul(gf_drive(2), image2[i]) if we have drive 2
C = gf_mul(gf_drive(3), image3[i]) if we have drive 3
etc.
No nosso caso, existe apenas um drive de dados (D1), então o nosso
partialRS
é simples
gf_mul(gf_drive(1), image1[i])
.
Então você precisa gerar o parâmetro
g
dividindo um pela soma dos índices de disco morto (D2 e D3).
Em seguida, vem o parâmetro
xoredPD
; é calculado adicionando o conteúdo do PD ao parâmetro
partialPD
calculado anteriormente. O próximo parâmetro é
xoredRS
calculado de forma semelhante, adicionando-o
partialRS
ao conteúdo RS.
Agora para a parte mais complicada. Você pode calcular os dados do primeiro disco quebrado, ou seja, do disco D2. Para fazer isso, multiplique o índice do segundo disco quebrado (D3) por um parâmetro
xoredPD
e adicione um parâmetro ao resultado
xoredRS
. Então, depois de multiplicar o resultado pelo parâmetro
g
, obtemos o conteúdo do disco D2.
Como acabamos de recuperar os dados do D2, este caso não é diferente do cenário 2 - perda de um disco de dados (D3). Para criar uma unidade D3, todas as unidades de dados dinâmicas (D1 e D2) devem ser adicionadas ao PD.
Feito! Devolvemos um conjunto completo de discos.
Epílogo
Escolhi Python para demonstrar que corrigir erros com códigos Reed-Solomon não requer muita programação ou poder de processamento. Tudo é muito rápido e a implementação pode ser bastante compacta. Obviamente, uma implementação mais eficiente deve ser escrita com o paralelismo em mente. Como cada byte é calculado independentemente dos outros, a paralelização não é difícil.
Deve-se observar que o método de recuperação de dados descrito não precisa ser usado em discos físicos separados. Os "discos" podem ser considerados "buffers" no processo de transmissão de dados por um canal não confiável, e essa correção de erro permanece eficaz. Requer cálculos mais intensivos do que com os códigos de Hamming, mas dois fluxos caídos podem ser gerados. Este é um recurso de resiliência poderoso.
É claro que o RAID-6 está longe de ser uma invenção nova e os códigos Reed-Solomon são ainda mais antigos. Eles foram usados na missão Voyager 2 , o que é muito legal.
Entre as alternativas mais modernas para os códigos Reed-Solomon estão os códigos turbo - espero ter a oportunidade de aprofundá-los também.