はじめに
今回作成するストレージは個人用のものであり、これを利用したことに起こった損害について一切の責任を負いませんのであしからず。
なぜ作るのか
自分のデータを預けるのに他人の作ったソフトウェアだと不都合がありそうだから。あと自分にあったストレージソフトウェアが見つからなかったため
背景
大容量ストレージにおける状況
大容量ストレージを扱う技術はすでにあります。企業などでは昔から大容量ストレージを扱っていたため当然でしょう。しかし個人で使うにあたってそのソリューションを導入するのは高価で現実的ではありません。例としてSANストレージを束ねるスイッチの価格はまともな速度なもので10万円を超える。(Lenovo ThinkSystem DB610S、8 ポート )
私の求めるストレージ要件
ここは人によって異なるでしょうが、私の用途はデータ置き場に近いものなので以下のような要件になりました
- ストレージ効率が良い
値段に直結するので当たり前です。HDDはもちろん容量単価の一番安い16TBのものを使用します - 待ち時間無しで拡張可能
毎日保存処理が走るのでRAIDの再構築のように数十時間かかるようではいけません。1年に1回ならともかく、月に1回増設予定なので許容できません。 - 十分な堅牢性
家にある最も大きいストレージなのでこれを更にバックアップすることはできません。可能な限り堅牢にしたい
いらない機能
- 権限管理
私しか使わないため - かっこいいダッシュボード
私しか使わないため。別にあってもいい - 速度
せいぜい我が家のNICの限界である10Gbpsあればよい - ARMアーキテクチャ対応
MACなどどうでもいい。Intelの拡張命令を使います
MinIOでよくね?...と思ったあなた!MinIOは商用利用で月額課金が必要です!
採用技術
- イレイジャーコーディング
ストレージ効率が高く破壊耐性もある - HighWayHash
ビット反転したデータを検出できる
実装編
とりあえずファイルを分割して結合したいと思います。言語はGoを使います。早いからです。
package main
import (
"fmt"
"io"
"os"
)
func main() {
fmt.Println("Hello World!")
// binary/inputをバイナリファイルとして読み込み、4つに分割してbinary/output_%dに書き込むコード
inputfile := "binary/input.mp4"
outputdir := "binary/"
// inputfileを読み込む
input, err := os.Open(inputfile)
if err != nil {
fmt.Println(err)
return
}
// 関数が終了する際にinputを閉じる
defer input.Close()
// inputfileのサイズを取得
fileInfo, err := input.Stat()
if err != nil {
fmt.Println(err)
return
}
fileSize := fileInfo.Size()
// ファイルサイズを4で割り、4つのファイルに分割する. 4で割り切れなかった余りは最後のファイルに含める
partSize := fileSize / 4
remainder := fileSize % 4
for i := 0; i < 4; i++ {
// output_%dを作成
outputFile, err := os.Create(fmt.Sprintf("%soutput_%d", outputdir, i))
if err != nil {
fmt.Println(err)
return
}
// 関数が終了する際にoutput_%dを閉じる
defer outputFile.Close()
// inputfileからoutput_%dにコピー
_, err = input.Seek(int64(i)*partSize, 0)
if err != nil {
fmt.Println(err)
return
}
// 4つ目のファイルは余りを含む。実際は4や3のような具体的な数字(マジックナンバー)ではなく、変数を使って管理する
sizeToCopy := partSize
if i == 3 {
sizeToCopy += remainder
}
// inputからoutput_%dに分割して書き込み
_, err = io.CopyN(outputFile, input, sizeToCopy)
if err != nil && err != io.EOF {
fmt.Println(err)
return
}
}
// output_0-4を読み込み、それらを結合してmergedを作成するコード
mergedFile, err := os.Create(outputdir + "merged.mp4")
if err != nil {
fmt.Println(err)
return
}
defer mergedFile.Close()
for i := 0; i < 4; i++ {
partFile, err := os.Open(outputdir + "output_" + fmt.Sprintf("%d", i))
if err != nil {
fmt.Println(err)
return
}
defer partFile.Close()
_, err = io.Copy(mergedFile, partFile)
if err != nil {
fmt.Println(err)
return
}
}
}
#実行結果
part1$ time ./part1
Hello World!
real 3m0.855s
user 0m0.001s
sys 0m26.098s
さすがにi/oがほとんどなのでreal timeが多いですね。
merged.mp4ファイルは無事再生できました。
タスクマネージャーの雰囲気的に,SSDがボトルネックになっています。今後様々な処理を加えたらどうなるのでしょうか?
次やること
データを1MBのブロックに分ける。
part2につづく