資料備份 reed-solomn 庫 的使用
資料備份 reed-solomn 庫 的使用
1. reed-solomn 是什麼?
reed-solomn 假如磁碟損壞了一部分,或者光碟一部分出現了汙漬,那是不是我們的資訊就丟了呢?
當然不是,有一種糾錯演算法,可以在資料明顯缺失的前提下,依然可以恢復資料,它就是 Reed–Solomon 演算法。當然,它事先需要冗餘備份,當丟失資料在一定範圍內,就可以恢復原始檔。
2. Reed Solomon 編碼原理
把輸入資料視為向量 D=(D1,D2,..., Dn), 編碼後資料視為向量(D1, D2,..., Dn, C1, C2,.., Cm),RS 編碼可視為如下圖所示矩陣運算。
需要注意:編碼矩陣 B 必須具有任意子矩陣可逆的特性。
3. Reed Solomon 解碼原理
RS 最多能容忍 m 個資料塊被刪除,m 包括實際資料和冗餘資料。 資料恢復的過程如下:
(1)假設 D1、D4、C2 丟失,從編碼矩陣中刪掉丟失的資料塊/編碼塊對應的行。
(2)由於 B' 是可逆的,記 B'的逆矩陣為 (B'^-1),則 B' * (B'^-1) = I 單位矩陣。兩邊左乘 B' 逆矩陣。
(3)得到如下原始資料 D 的計算公式
4. 程式碼實現及測試
github 地址: "github.com/klauspost/reedsolomon"
下載即可使用。
1.reed-solomon 編碼測試 此時,輸入 in.txt,go run main.go
輸出 out 目錄下,30 個 shards 檔案。
package main
import (
"fmt"
"io"
"os"
"path/filepath"
"github.com/klauspost/reedsolomon"
)
func main() {
var DataShards = 10
var ParShards = 20
var OutDir = "./out"
var dataShards = &DataShards
var parShards = &ParShards
var outDir = &OutDir
fname := "in.txt"
// 1.Create encoding matrix.
enc, err := reedsolomon.NewStream(*dataShards, *parShards)
checkErr2(err)
fmt.Println("Opening", fname)
f, err := os.Open(fname)
checkErr2(err)
instat, err := f.Stat()
checkErr2(err)
shards := *dataShards + *parShards
out := make([]*os.File, shards)
// 2.建立輸入檔案 30個shards
dir, file := filepath.Split(fname)
if *outDir != "" {
dir = *outDir
}
for i := range out {
outfn := fmt.Sprintf("%s.%d", file, i)
fmt.Println("Creating", outfn)
out[i], err = os.Create(filepath.Join(dir, outfn))
checkErr2(err)
}
// Split into files.
data := make([]io.Writer, *dataShards)
for i := range data {
data[i] = out[i]
}
// 3.原始檔案拆分
err = enc.Split(f, data, instat.Size())
checkErr2(err)
// Close and re-open the files.
input := make([]io.Reader, *dataShards)
for i := range data {
out[i].Close()
f, err := os.Open(out[i].Name())
fmt.Println("Error ", err)
input[i] = f
defer f.Close()
}
// 4.封裝 parity
parity := make([]io.Writer, *parShards)
for i := range parity {
parity[i] = out[*dataShards+i]
defer out[*dataShards+i].Close()
}
// 5.Encode 編碼rs格式
err = enc.Encode(input, parity)
checkErr2(err)
fmt.Printf("File split into %d data + %d parity shards.\n", *dataShards, *parShards)
}
func checkErr2(err error) {
if err != nil {
os.Exit(1)
}
}
直接執行,最後輸出如下:
$ go run main.go
Opening in.txt
Creating in.txt.0
Creating in.txt.1
Creating in.txt.2
Creating in.txt.3
Creating in.txt.4
Creating in.txt.5
Creating in.txt.6
Creating in.txt.7
Creating in.txt.8
Creating in.txt.9
Creating in.txt.10
Creating in.txt.11
Creating in.txt.12
Creating in.txt.13
Creating in.txt.14
Creating in.txt.15
Creating in.txt.16
Creating in.txt.17
Creating in.txt.18
Creating in.txt.19
Creating in.txt.20
Creating in.txt.21
Creating in.txt.22
Creating in.txt.23
Creating in.txt.24
Creating in.txt.25
Creating in.txt.26
Creating in.txt.27
Creating in.txt.28
Creating in.txt.29
File split into 10 data + 20 parity shards.
經檢視,30個檔案已經生成,其中前10個拼接就是原始檔案。
2.reed-solomon 恢復測試
在上面基礎上,刪掉out目錄下面20個檔案(編號6-24),剩下10個,執行 go run recover_main.go
開始恢復原始檔,並恢復刪掉的20個檔案。
package main
import (
"fmt"
"io"
"os"
"github.com/klauspost/reedsolomon"
)
var OutFile = "out2.txt"
var outFile = &OutFile
var DataShards = 10
var ParShards = 20
var OutDir = "./out"
var dataShards = &DataShards
var parShards = &ParShards
var outDir = &OutDir
func main() {
fname := "out/in.txt"
// 1.Create matrix
enc, err := reedsolomon.NewStream(*dataShards, *parShards)
checkErr(err)
// 2.Open the inputs
shards, size, err := openInput(*dataShards, *parShards, fname)
checkErr(err)
// 3.Verify the shards
ok, err := enc.Verify(shards)
if ok {
fmt.Println("No reconstruction needed")
} else {
fmt.Println("Verification failed. Reconstructing data")
shards, size, err = openInput(*dataShards, *parShards, fname)
checkErr(err)
// 3.1 重新建立刪除的檔案
out := make([]io.Writer, len(shards))
for i := range out {
if shards[i] == nil {
//dir, _ := filepath.Split(fname)
outfn := fmt.Sprintf("%s.%d", fname, i)
fmt.Println("Creating", outfn)
out[i], err = os.Create(outfn)
checkErr(err)
}
}
fmt.Println("reconstruct")
// 3.2 重建30個shards
err = enc.Reconstruct(shards, out)
if err != nil {
fmt.Println("Reconstruct failed -", err)
os.Exit(1)
}
// Close output.
for i := range out {
if out[i] != nil {
err := out[i].(*os.File).Close()
checkErr(err)
}
}
shards, size, err = openInput(*dataShards, *parShards, fname)
ok, err = enc.Verify(shards)
if !ok {
fmt.Println("Verification failed after reconstruction, data likely corrupted:", err)
os.Exit(1)
}
checkErr(err)
}
// 4.Join the shards and write them
outfn := *outFile
if outfn == "" {
outfn = fname
}
fmt.Println("Writing data to", outfn)
f, err := os.Create(outfn)
checkErr(err)
shards, size, err = openInput(*dataShards, *parShards, fname)
checkErr(err)
// join恢復原檔案 but We don't know the exact filesize.
err = enc.Join(f, shards, int64(*dataShards)*size)
checkErr(err)
}
func openInput(dataShards, parShards int, fname string) (r []io.Reader, size int64, err error) {
// Create shards and load the data.
shards := make([]io.Reader, dataShards+parShards)
for i := range shards {
infn := fmt.Sprintf("%s.%d", fname, i)
fmt.Println("Opening", infn)
f, err := os.Open(infn)
if err != nil {
fmt.Println("Error reading file", err)
shards[i] = nil
continue
} else {
shards[i] = f
}
stat, err := f.Stat()
checkErr(err)
if stat.Size() > 0 {
size = stat.Size()
} else {
shards[i] = nil
}
}
return shards, size, nil
}
func checkErr(err error) {
if err != nil {
os.Exit(1)
}
}
先刪除0-5,25-29 這些檔案,
然後執行recover邏輯,
$ go run recover_main.go
Verification failed. Reconstructing data
Opening out/in.txt.0
Opening out/in.txt.1
Opening out/in.txt.2
Opening out/in.txt.3
Opening out/in.txt.4
Opening out/in.txt.5
Opening out/in.txt.25
Opening out/in.txt.26
Opening out/in.txt.27
Opening out/in.txt.28
Opening out/in.txt.29
Creating out/in.txt.5
Creating out/in.txt.6
Creating out/in.txt.7
Creating out/in.txt.8
Creating out/in.txt.9
Creating out/in.txt.10
Creating out/in.txt.11
Creating out/in.txt.12
Creating out/in.txt.13
Creating out/in.txt.14
Creating out/in.txt.15
Creating out/in.txt.16
Creating out/in.txt.17
Creating out/in.txt.18
Creating out/in.txt.19
Creating out/in.txt.20
Creating out/in.txt.21
Creating out/in.txt.22
Creating out/in.txt.23
Creating out/in.txt.24
reconstruct ...
Writing data to out2.txt
最後可以看到0-5,25-29 這些檔案已經恢復出來,並且原始檔也恢復出來了 out2.txt.
總結
reed solomon 糾錯碼是一種特殊型別的糾錯碼。在事先冗餘備份下,當丟失資料在一定範圍內,呼叫恢復過程,就可以恢復原始檔。 一些大型的分散式檔案儲存,都用它來保證檔案的高可用性。當然,使用起來非常方便,大家可以多動手試試,希望你能喜歡哦!
以上所有內容均採用最新官方案例做示例
參考資料
更多原創文章乾貨分享,請關注公眾號
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- 使用RMAN備份資料庫資料庫
- 【RMAN】使用增量備份更新資料庫備份映象資料庫
- 使用innobackupex備份mysql資料庫MySql資料庫
- 使用Xtrabackup備份mysql資料庫MySql資料庫
- 使用begin backup備份資料庫資料庫
- 資料庫備份資料庫
- 使用JOB定時備份資料庫資料庫
- (轉)使用Xtrabackup備份MySQL資料庫MySql資料庫
- 使用RMAN建立資料庫備份庫(筆記)資料庫筆記
- Backup And Recovery User's Guide-備份資料庫-使用RMAN備份資料庫檔案GUIIDE資料庫
- Oracle資料庫的備份方法-冷備份(轉)Oracle資料庫
- Dedecms備份的資料檔案位置及備份資料庫的方法資料庫
- mysql 資料庫 備份MySql資料庫
- 資料庫備份策略資料庫
- MongoDB資料庫備份MongoDB資料庫
- mysql 資料庫備份MySql資料庫
- 資料庫備份方案資料庫
- oracle資料庫使用rman備份指令碼Oracle資料庫指令碼
- (轉)使用Xtrabackup備份MySQL資料庫(續)MySql資料庫
- 使用MySQL Workbench進行資料庫備份MySql資料庫
- MySQL資料庫的基本備份MySql資料庫
- 資料庫備份的種類資料庫
- 使用備份的控制檔案恢復資料庫資料庫
- 使用OSB進行ORACLE rac資料庫的備份Oracle資料庫
- oracle資料庫備份之exp增量備份Oracle資料庫
- 【備份恢復】noarchive模式下使用增量備份恢復資料庫Hive模式資料庫
- 簡單的使用rman備份oracle資料庫的做法Oracle資料庫
- BMMySQL定時備份資料庫(全庫備份)的實現meuMySql資料庫
- 資料庫資料的恢復和備份資料庫
- oracle 備份資料庫,匯出資料庫Oracle資料庫
- 使用Mysqldump備份和恢復MySQL資料庫MySql資料庫
- SQL SERVER備份資料庫檔案(使用SSMS)SQLServer資料庫SSM
- MySQL資料庫備份工具Mydumper使用介紹MySql資料庫
- 使用RMAN增量備份前滾STANDBY資料庫資料庫
- 使用 xtrabackup 進行MySQL資料庫物理備份MySql資料庫
- mysqlpump 資料庫備份程式MySql資料庫
- mysqldump 資料庫備份程式MySql資料庫
- 資料庫備份指令碼資料庫指令碼