I/Oとディスクの動作をイメージできれば、Oracleで発生する現象を理解しやすくなる|翔泳社の本

I/Oとディスクの動作をイメージできれば、Oracleで発生する現象を理解しやすくなる

2019/03/19 07:00

 翔泳社から発売した『絵で見てわかるOracleの仕組み 新装版』より、Oracleを理解するための第一歩となるI/Oとディスクの関係について紹介。ディスクが回転しているイメージや、データの入出力に時間がかかる理由、該当データにすぐにアクセスする仕組みを解説します。

本記事は『絵で見てわかるOracleの仕組み 新装版』の「第1章 I/Oとディスクの関係」から抜粋したものです。掲載に当たり、一部を編集しています。

 ここでは、Oracleの基本機能を「Oracleを理解するための必須キーワード」として取り上げ、これを前提にI/O(Input/Output:入出力)とディスクの関係を詳しく見ていきます。普段は意識することがないかもしれませんが、I/Oとディスクの動作をイメージできれば、Oracleで発生するさまざまな現象を理解しやすくなります。応用力のあるOracleエンジニアになるための第一歩を踏み出しましょう!

Oracleを理解するための必須キーワード

 まず、筆者が考えるOracleを理解するための3つのキーワードを紹介します。これは本書で出てくるキーワードです。

  1. 並列処理を可能にし、高スループットも実現
  2. レスポンスタイムを重視
  3. COMMITされたデータは守る

 Oracleに限らず、DBMS(DataBase Management System:データベース管理システム)の内部構造は複雑です。その理由は主にこれら3点の特性から来ています。しかもこれらは相性が悪いのです。たとえば「COMMITされたデータは守る」ためにCOMMITのタイミングでデータをディスクに書きたいところですが、そうしてしまうとレスポンスが悪くなってしまいます。

「並列処理を可能にし、高スループットも実現」も簡単ではありません。並列処理する場合、矛盾する処理が行なわれないようロック[※1]が必要になりますが、そのロックのために逆に性能が出ないこともあります。

 なお、本書では章ごとにトピックを決め、OracleのDBMSとしての基本機能のみを説明します。最近のOracleは機能豊富ですが、これらの基本機能がもとになっています。しっかりと身につけてください。また、本書に出てくる数値は読者の理解を助けるための参考数値であり、あくまで目安と考えてください。

※1 データの書き込み処理を行なう際、データの整合性を保つためデータの読み込み/書き込みを一時的に制限すること。

Oracleとディスク(ハードディスク)

 Oracleはデータベース管理システム(DBMS)です。そしてOracleにおけるデータベースとは、Oracleの管理下でディスク(ハードディスク)に入っているデータのことを指します。Oracleはディスクからデータを読み込み、処理をして、ディスクにデータを書き戻します。つまり、Oracleが取り扱うデータはディスクから取り出され、ディスクに戻るのです。そのため、Oracleとディスクは切っても切れない関係にあると言えます。

 ということで、今回はディスクの説明をします。もし手元に壊してもかまわないディスクがあったらぜひ、ドライバーで開けて中身を見ながら読み進めてください。ただし一度開けてしまうと二度とそのディスクは使えませんので、ご注意を。

 ここでさっそく最初の動作イメージを持っていただきたいと思います。まずは、ディスクを音楽のレコードやCDのようなものだとイメージしてください(図1)。

図1 ディスクとレコードのイメージ
図1 ディスクとレコードのイメージ

 ディスクがほぼ常に回転していて、その上をヘッドが動いて音楽ならぬデータを読み書きします。レコードとの違いは円盤が何枚か重なっていること、ディスクを反転させなくてもアクチュエータが両面を使うことですが、ここでは大事ではありません。もう1つの違いと言えば速度でしょうか? 音楽にたとえると1秒あたり100回の頭出しが可能なくらいヘッドがすばやく動きます。また回転速度もすさまじく、1分間に1万回くらい回ります(図2)。

図2 より現実に近いディスクのイメージ
図2 より現実に近いディスクのイメージ

ディスクの動作

 おそろしく高速なレコードのイメージができたところで、次はI/O処理に必要なディスクの動作を見ていきましょう。データを読む(もしくは書く)ためには、いわゆる「頭出し」をしなければなりません。この目的の位置を探す作業を、ディスクの用語では「シーク」と呼びます。

 その後、さらに欲しい情報が回転してくるまで待ちます。この待ち時間を「回転待ち時間」と呼びます。そしてようやくデータを読み書きします(図3)。

図3 I/O処理に必要な動作
図3 I/O処理に必要な動作

 メモリに対するアクセスはnsec(ナノ秒)の単位で行なえるのに対して、ディスクに対するアクセスはmsec(ミリ秒)の単位で時間がかかります。人間の時間感覚からすれば素早く動作するように感じられるディスクへのアクセスですが、コンピュータの速度からするととても遅いのです。

 理由を簡単に言ってしまえば、電気信号で処理されるメモリに対して、ディスクは機械動作が入ってしまうためです。ディスクへのI/OはDBMSにとって必要なものですが、処理時間短縮の観点では減らすべきものでもあるのです。

 ここからは、シークから回転待ち時間までにかかる時間の合計を10msec、ディスクの転送速度(ヘッドを動かさずにずっとデータを読み書きする場合の可能な処理量)を20MB/秒として解説します。

どうやってI/O待ちの時間を減らすのか?

 それでは、実際にどのようにしてI/O待ちの時間を減らせば良いのでしょうか? そのためにまず、シーケンシャルアクセスについて説明します。シーケンシャルとは「逐次(順を追って)」のことで、先頭から間を抜かさずにアクセス(読み/書き)することです。フルスキャン(表の全データを読み込むこと)の際に、メモリにデータがないとシーケンシャルアクセスが発生します(図4)。シーケンシャルアクセスとフルスキャン――この2つはデータベースを理解するために大事な用語なので、覚えておいてください。

図4 シーケンシャルアクセス
図4 シーケンシャルアクセス

 さて、表サイズを1GBとすると、シーケンシャルアクセスで全部読み込むだけで50秒(サイズ/転送速度=1000MB/20MB/秒)かかります。表サイズが100MBだとしても5秒かかります。こんなに時間がかかってしまうのでは、たいていの場合、SQLの性能要件を満たすことができません。

 そこでインデックス(索引)の発想が出てきます。本で何かを調べたい場合にみなさんならどうしますか? 先頭から全ページを読んで探す人は少なく、たいていの場合はインデックスを使うでしょう。ご存じのようにインデックスにはキーワードが順番に並べられていて、ページ番号も載っています。そのページ番号を見てすばやく必要なページにたどり着き、必要な情報を得るわけです。

 データベースのインデックスも同様です。データベースのインデックスには検索の際に使用するキー値(SQL文のWHERE句に書く条件の値のこと)と、そのキーが存在するアドレスが書かれています(図5)。

図5 インデックスのイメージ
図5 インデックスのイメージ

インデックスの使用例

 たとえばラリーさんのことを本で調べたい場合、インデックスで240ページに書かれていることを調べてから該当ページを開き、ラリーさんの情報を得ます。SQL文も同じで、WHERE句に“ラリー”さんのことを知りたい旨の記述をし、SELECT句に知りたい項目(次の例では“所属会社”)を書きます。

 SELECT "所属会社" FROM "個人データ" WHERE "個人名" = "ラリー";

 このSQL文をインデックスを使って処理する場合は、まず“個人名”の載っているインデックスを調べます。その結果、“ラリー”さんのアドレス(ROWID)を得られるので、そのアドレスをもとにデータを読み込みます。読み込まれたデータの中にはラリーさんのデータがそろっているため、その中から“所属会社”のデータをユーザーに返します(図6)。

図6 インデックスを使ったSQL処理の流れ
図6 インデックスを使ったSQL処理の流れ

 このように大変便利なインデックスですが、インデックス自体のサイズが大きくなってくるとやはり処理に時間がかかるのでしょうか? そんなことはありません。内部的には「インデックスのインデックス」のように、自動的に多段の構成になっていくためです(図7)。ここが本のインデックスと異なる点でしょう。

 ちなみに、このような多段の構造は「ツリー構造」と呼ばれます(木を逆さまにしたように見えますよね)。メリットは、インデックスの関係のない部分は読まなくて済むことです。

図7 多段のインデックスのイメージ
図7 多段のインデックスのイメージ

ランダムアクセス

 話をI/Oに戻しましょう。インデックスを使うと、必要な箇所だけ読めば十分です。しかし、必要な箇所がディスク上で連続していることはほとんどありません。そのためヘッドを動かしながら、とびとびにアクセスすることになります。このようなアクセスを「ランダムアクセス」と呼び、シーケンシャルアクセスの反対の意味を持ちます。

 このランダムアクセスは、ディスクから見るとある意味非効率です。とびとびにアクセスするたびにシークと回転待ちが発生し、時間を使ってしまうためです。たとえば、Oracleのブロックサイズを8KB、1秒間にシークできる回数を100回とした場合、1秒間に約800KBしか読み出せません。これは、ディスクの転送速度(ヘッドを動かさずにずっとデータを読み書きする場合の可能な処理量)を20MB/秒とした場合に、シークしなければ1秒間に20MB読み出せるのに比べると、25分の1です。

 音楽を聴くことにたとえるならば、頭出しを頻繁に繰り返して1時間のうち2分半しか音楽を聴いていないことに相当します。残りの57分半は頭出しの時間です。

 実際、データ転送の効率から見るとDBMSのI/Oはそんなものです。このようなシークを繰り返すという事情があるため、DBMS(特にOLTP[※2]のシステム)のためのディスクの指標はIOPS(I/O Per Sec:1秒あたりのI/O可能回数)が重要と言われます。そしてたいていのディスクのIOPSは100回や200回程度であるため、1つや2つのディスクでデータベースを作ってしまうと、負荷が集中したときにシークが追いつかず、ディスクがボトルネックになってしまうのです(図8)。

図8 ディスクの数が少ないとディスク自体がボトルネックに
図8 ディスクの数が少ないとディスク自体がボトルネックに
※2 OnLine Transaction Processing。イメージとしては、鉄道の座席予約システムや銀行の入出金処理です。多くの端末からオンラインで(ネットワーク越しに)大きくないデータを読み書きし、すぐに結果を求めるようなシステム形態のことです。大きくないデータということと、「すぐに」ということからわかるように、インデックスを使うべきシステム形態と言えます。

上級者向けTips 「インデックスアクセスが有利なのはデータの15%未満」なのはなぜ?

 その理由はシーケンシャルアクセスとランダムアクセスの特性にあります。表のデータが大量でその中から1行を探し出すのであれば、当然インデックスアクセスのほうが速く探し出せます。それに対してすべてのデータを見る場合、インデックスを引きながらでは逆に遅くなってしまいます(本の中身を全部読みたいのに、インデックスを引きながら読む人はいませんよね)。

 ではデータが50%ではどうでしょうか? データが25%ではどうでしょうか? ここで「ディスクに対するランダムアクセスは、データの読み出し効率から見るとシーケンシャルアクセスに劣る」という特性が重要になってきます。たとえば、2万行のデータを持つ表から半分の1万行を取り出す場合を考えてみましょう。ここで1行は8KBとします。今まで使ってきたディスクの性能値を使った計算だと、ランダムアクセスでは約100秒かかります(インデックスは頻繁に使われるので、キャッシュに載っていると想定します)。それに対して2万行全部を読み込むシーケンシャルアクセスでは、すべての行(2万行)をディスクから読み込んでも約8秒で終了します。つまりディスクの特性から、全件でなくてもある程度までであればシーケンシャルアクセスで表をフルスキャンしたほうが速いとわかります。

 ただし、実際にはキャッシュにデータが載っていることもありますし、1ブロックに複数行のデータが格納されていて1回のI/Oで多くの行を読み取れることもあります。さらにはインデックスのデータをディスクから読み出すこともあるため、一概に「15%がしきい値」とは言えません。あくまで目安としてください。

データを保証するためのディスク

 Oracleのプロセスが異常終了したとしても、データは大丈夫です。ここが、DBMSとほかのプログラムで異なる点の1つです。たとえばExcelでは保存(セーブ)した時点以降のデータは失われます。ご存じのようにプログラムが異常終了するとデータは失われ、電源ボタンを押して急に電源を切ってもデータは失われます。

 それに対して、DBMSはどんな障害にも耐えられなければなりません。CPUが壊れても、メモリが壊れても、停電してもデータを失うわけにはいかないのです。コンピュータの主記憶の中の情報は電気ですから、停電すればメモリから消えてしまいます。そのような厳しい条件があるにもかかわらず、キーワードで紹介した「COMMITされたデータは守る」という特性はどのようにして実現しているのでしょうか?

 答えは簡単で、データを変更した後にCOMMITと入力すると、Oracleはディスクにデータを書き出しているのです(図9)。「これでは遅いじゃないか」と思われるでしょうが、そこに関しても高速化の仕組みがあります。一方で、高速化の仕組みがあるためにOracleをはじめとするDBMSの構造は複雑なのですが、これについては本書内で説明します。

図9 COMMITを受信するとデータをディスクに書き出す
図9 COMMITを受信するとデータをディスクに書き出す

COLUMN 「シーケンシャル」とはどんな意味?

 Oracleは各種情報を大量に持っています。その中にどれだけI/Oを行なったのかも記録されています。しかし、その情報の中に混乱しやすいポイントがあります。

 たとえば、シーケンシャルアクセスをすると「db file scattered read」と表示され、ランダムアクセスをすると「db file sequential read」と表示されます。“scattered”は「分散した」という意味で、“sequential”は「逐次的な、連続的な」という意味です。アクセスの仕方と意味を考えると逆のように思えますが、これは表示が間違っているわけではなく、次のような意味があります。

 Oracleはブロック単位でデータを読み込み、メモリ上に配置します。シーケンシャルアクセスは「逐次(順を追って)読み込む」の意味の通り、複数のブロックを、間を抜かさずに読み込みます。このとき、読み込まれた複数のブロックはメモリ上の連続しない(分散した)箇所に置かれます。そのため“scattered”と表示されます。

 それに対してランダムアクセスは読み込むデータブロックは1つで、必然的にメモリ上の連続した領域に置かれます。そのため“sequential”と表示されます。

 一度の読み込みブロックがメモリ上でどのように配置されるかという観点で、呼び名を変えているんですね。

 筆者はシーケンシャルという名前が、「シーケンシャルI/O」を指すように変えてほしいと思うのですが、いったん決まってしまった以上、今後も変わることはないでしょう。間違えやすいのでみなさんは覚えておいてください。

参考:マニュアル『Oracle Databaseパフォーマンス・チューニング・ガイド 18c』10.3.3 db file scattered readおよび10.3.4 db file順次読取り

まとめ

 押さえていただきたいのは次の3つです。みなさんには、これらのイメージが頭の中に浮かぶようになっていただきたいと思います。

  • ディスクが回転しているイメージ
  • ヘッドが動き、さらに回転待ちをするため、I/Oに時間がかかるイメージ
  • インデックスにより、該当データにすぐアクセスできる(先頭から読まなくて済む)イメージ

 ディスクは実は遅いこと、インデックスによって効率良くデータにアクセスできることがわかりました。これらが今後どのように展開していくのかは、本書で解説しますのでお楽しみに。1つだけ紹介しておくと、ディスクの遅さがSQL文の性能にできる限り影響しないようにするためにバッファキャッシュが存在します。

 最後に、もっと深くディスクを学びたい方におすすめの記事を紹介します。

参考:ゼロからはじめるストレージ入門

現場のIT用語 玉、24365、オンプレ、オンプレミス

 現場では、テキストに出てこない用語が頻繁に出てきます。そのような用語の意味はなかなか人に聞けないものです。ここでは、そんな現場の用語をいくつか紹介します。

●玉(たま)

 ハードディスクのことです。円盤1枚(図1.1)のことではなく、全体のこと(図1.2)を指します。「100G玉」と言えば、100GBの容量を持つハードディスクのことを指し、2玉と言えば、2つのハードディスクという意味になります。この「玉」という言い方は頻繁に使われます。

●24365(にーよんさんろくご)

 24時間365日稼働のシステムのことです。高可用性とセキュアな状態を保つための対策を必要とします。

●オンプレ、オンプレミス

 コンピュータやシステムを自前で所持して運用することです。近年、インターネット上に仮想のサーバーを置く「クラウド」が広まってきたため、区別するための対義語として使われます。

絵で見てわかるOracleの仕組み 新装版

Amazon SEshop その他


絵で見てわかるOracleの仕組み 新装版

著者:杉田敦史、辻井由佳、寺村涼、山本裕美子、小田圭二
発売日:2019年3月11日(月)
価格:2,484円(税込)

本書について

2006年発売のロングセラーの新装版。内容のモダン化を図っているほか、コラムや付録(基本的なコマンド)を新設。日常のOracle運用/管理業務に役立つだけでなく、応用力のあるエンジニアになるための知識が身につく1冊です。