ストレージのOSインターフェース
Table of Contents
これは Databaseの実装を学ぶ の記事です
今回はどうやってストレージを扱うか、そんなお話。DBは基本的にOSの上に、ソフトウェアとして構築される。この場合、ソフトウェアとしてのDBは、直接ストレージを操作することはできなくなっている。なぜなら、IOの処理は基本的にOSが管理しているから。
ということで、DBを作成するためには、OSが提供するストレージへのアクセスインターフェースを理解する必要がある。今回はその勉強デス。
ストレージアクセスにおけるOSの役割
さて、本格的な議論の前に、OSの役割をはっきりとさせた方が、論を進めやすいと思う。ので、この場合のOSの役割を整理したい。
OS全般の大きな役割として、 「コンピュータハードウェアの抽象化」 が上げられる。一口にパソコンといっても、様々なメーカーから発売されていて、どれも違うパーツを組み合わせてハードウェアを構成している。
果てには自作パソコンなんかもあり、この場合はメモリ・ストレージ・ネットワーク関連のハードウェアは、パソコンごとに点でバラバラ。
例えば、メモリはcrucialで、ストレージはSanDiskで、NICはIntelとか。こんなことがザラにある。
ここで問題になってくるのが、各社のハードウェアごとに、使用方法や命令ルールが異なる場合があること。ハードウェア以外にも、規格ごとの差なんかもこれかな。
極端な例としては、
- メーカーAのストレージに、番号1の命令を送るとデータを保存
- メーカーBのストレージに、番号1の命令を送るとデータを削除
なんてことがあるかもしれない。
こんなようなハードウェアの差を、埋める役割をOSは担っている。OSはハードウェアを捨象して、「データを保存」とか「データを削除」といった、共通のAPIだけを提供してくれる。
なので、ストレージを利用するDBの作成にあっては、、これらのOSの役割を理解していないといけない。
小話 : ドライバの存在
ここでは、OSのハードウェアの抽象化について、もう少し深堀。気になってないひとは、読み飛ばし推奨。
さて、OSはハードウェアを捨象すると書いた。もしかしたら、気になっていた人がいるのでは。
そう。OSが勝手に「抽象化します!」と一人でいっても、実際のハードウェアは結局のところ色々なバリエーションがあるまま。だから、ハードウェア側からも歩み寄りをしてもらう形になる。
具体的には、「ドライバ」という形になる。よくプリンタにPCを接続する時に、なんか出で来るイメージ、ドライバ。「対応するプリンタドライバーがありません」的な形で。
あそこでのドライバは、この抽象化のためのソフトウェア。(ミドルウェアといったほうが正確かな)
このドライバは、各ハードウェアの会社が基本的には供給する。役割としては、前述の通りに、OSの抽象化の補完。さっきのAとBの二社のストレージを例にとる。
--- title: ストレージドライバの例 theme: base themeVariables: darkMode: true --- flowchart TB OS OS_Drive[OSに登録されたドライバ] StorageA_Driver[ストレージAのドライバ] StorageB_Driver[ストレージBのドライバ] OS -->|データの保存を指示| OS_Drive OS_Drive -..->|各ドライバに命令| StorageA_Driver & StorageB_Driver StorageA_Driver -->|命令1| StorageA StorageB_Driver -->|命令2| StorageB
上図のように、ドライバを経由することで、各ストレージの差異を吸収することができる。で、当然、内部の正確な命令とかはOSは知らないから、各社が提供するドライバをインストールする形になる。
こんな感じで、どんなハードウェアでもドライバを誰かが作ってくれれば、一定のインターフェースで利用できる仕組みが、OSには組み込まれている。
以上、OSの抽象化の内容まとめでしたー。では、改めて本題にもどっていきます。
File-level インターフェース
まずは理解しやすい File-level のインターフェースから見ていきましょう。
これは普段使っている「ファイル」のことです。私たちがよく使ってるアレですね。このFile-levelインターフェースには、いくつか特徴があります。
まず、OSはファイルを 連続したバイト列 として提供してくれます。実際のところ、本来は次に説明するブロック単位の管理がメインなんですが、扱いやすくするために単純なバイトの配列として公開されているんです。便利。
プログラムからの操作も簡単で、オフセット――ファイルの先頭からターゲットまでの距離――を指定すれば、ファイル上の任意の位置でデータの読み書きができちゃう。正確にはOSが直で触れるのは、RAM・メモリだけ。そのため
- ストレージからRAMにロード
- RAM上で編集
- RAMの内容をストレージに反映
というステップを踏む。
Block-level インターフェース
続いて、File-levelよりハードウェアに近い Block-level のインターフェースについて。
ブロックって何かというと、セクタというものを複数まとめた単位。セクタは、各ストレージハードウェアでの、読み書きの最小単位のこと。例えば4KBとか単位で、まとめてストレージは読み書きする。これを「セクタ」と呼んでいる。
で、ブロックは、OSの読み書きの最小単位になります。イメージとしては、文章を分解すると「文章 → 文 → 文節 → 単語」となるように、ブロックはファイル内の最小パーツに相当してくる。(この例だと、セクタは形態素的なイメージ)
このブロック単位での操作ができるメリットは、ハードウェアごとのセクタサイズの違いを気にせずに済むこと。例のハードウェアの抽象化ってやつですな。
データベースにおける組み合わせ
さて、データベースではFile-levelとBlock-levelを組み合わせて使うのが基本になります。どうやって組み合わせるのかというと:
- File-level でまとまったブロックをファイルとして読み込む
- Block-level でそのファイル内の個々のブロックを細かく操作する
こうすることで、ファイルという形で永続化しつつ、ブロック単位での細かなデータ管理が可能になる。これにより、極限まで密度を上げた効率的なデータ保存が可能!
DBのように大きなデータを扱うときは、いかに空間の無駄を減らすかが大事。このタネが、この二つのインターフェースなのでした。ちゃんちゃん。
と、まぁこんな感じで、DBが使うOSのストレージの機能について確認した。こっからは、だんだんとコードを書いていくフェーズになろう。
コーディングでは、コードの内容を言語化して、ブログにまとめる予定。
参考文献
書名 | Database Design and Implementation - Second Edition |
著者 | Edward Sciore |
出版社 | Springer |
リンク | https://link.springer.com/book/10.1007/978-3-030-33836-7 |