IT の基礎

データベースの基礎

データベースには、表と SQL で扱うリレーショナルと、形を固定せず大量のデータを速く扱う NoSQL があります。止まってもデータを失わないために複製を持ち、同期の複製は失わない代わりに書き込みが遅く、非同期は速い代わりに直近の書き込みを失うことがあります。

データベースは、データを保存し、探す、書き換えるといった操作を、同時に大勢が使っても矛盾なく行うソフトウェアです。大きく分けて、表の形でデータを持ち SQL で操作するリレーショナルデータベースと、表に縛られずに大量のデータを速く読み書きする NoSQL の 2 種類があります。どちらでも、1 台が壊れてもデータを失わないように別の場所へ複製 (レプリケーション) を持ちます。複製を同期で行えば失うデータはなくなる代わりに書き込みが遅くなり、非同期で行えばその逆になります。

リレーショナルは表に分けて持ち、SQL で操作する

リレーショナルデータベースは、データを行と列からなる表 (テーブル) に入れます。顧客の表と注文の表のように、種類ごとに表を分け、注文の行には「どの顧客の注文か」を顧客の id で書いておきます。読むときに id で表どうしを結びつけるので、顧客の名前が変わっても直すのは顧客の表の 1 行だけです。

表への命令には SQL という言語を使います。次の文は「顧客 42 の注文の番号と金額を取り出す」という意味です。

SELECT id, total FROM orders WHERE customer_id = 42;

NoSQL は、リレーショナル以外のデータベースをまとめた呼び名です。代表的なドキュメント型は、顧客 1 人分の情報と注文の一覧を 1 つの文書 (JSON に似た形) にまとめて持ちます。表を結びつける手間がないので読み出しが速く、データの形を後から変えやすい一方、複数の文書にまたがる集計や結合は苦手です。

リレーショナルNoSQL (ドキュメント型)顧客の表id ・ 名前注文の表id ・ 顧客 id ・ 金額顧客 id で結ぶ顧客 42 の文書名前: 佐藤注文3 件をここに並べる
リレーショナルは種類ごとに表を分けて id で結び、ドキュメント型は 1 人分をまるごと 1 つの文書に入れる。

トランザクションは「全部やるか、何もしないか」

トランザクションは、複数の書き込みを 1 つのまとまりとして扱う仕組みです。A さんの口座から 1 万円を引き、B さんの口座に 1 万円を足す振り込みで、引いた直後にサーバーが止まったとします。トランザクションの中で行っていれば、途中までの書き込みは取り消され、どちらの口座も振り込み前に戻ります。お金が消える状態は残りません。リレーショナルデータベースは、このトランザクションを得意とします。

インデックスは、探す場所を先に絞る索引

100 万行の表から顧客 42 の注文を探すとき、何も準備がなければ 100 万行を先頭から全部読みます。インデックスは、本の巻末の索引のように「顧客 id がこの値の行はここ」という対応を別に持っておく仕組みです。よく使われる木の形のインデックスなら、100 万行でも 20 回前後の比較で目的の行にたどり着きます。代わりに、行を書くたびにインデックスも更新するので書き込みは少し遅くなり、保存の容量も増えます。そのため、インデックスは検索の条件によく使う列にだけ付けるのが基本です。

レプリケーションは、主系の写しを別の場所に持つ

レプリケーションでは、書き込みを受け付けるデータベース (主系、プライマリ) の内容を、別のサーバーにある副系 (レプリカ) へ写し続けます。主系が壊れたら、副系を新しい主系に切り替えて (フェイルオーバー) サービスを続けます。

写すタイミングには 2 通りあります。同期では、副系に届いたことを確かめてからアプリに「書けた」と返します。非同期では、主系に書けた時点で返し、副系へは後から送ります。

同期アプリ主系副系書く複製副系に届いてから完了を返す非同期アプリ主系副系書く・完了後で複製副系を待たずに完了を返す
同期は副系を待つぶん書き込みが遅く、非同期は待たないぶん、届く前に主系が落ちると直近の書き込みを失う。

障害でどこまで前のデータを失ってよいかを時間で表したものを RPO (目標復旧時点) と呼びます。同期の RPO は 0 で、非同期の RPO は副系への複製の遅れの分だけになります。下のシミュレーターで、方式を切り替えて書き込み、主系を落としてください。数値は目安です。

試すレプリケーションの同期と非同期

複製の方式 (切り替えると最初からやり直します)
200 ms

書き込むを 1 回押すと、100 ms ごとに来る書き込みが 1 件進みます。

主系

    副系

      書き込みの待ち時間
      5 ms / 件
      RPO (失いうる時間幅)
      200 ms
      失われた書き込み
      主系は稼働中

      非同期では、主系に書けた時点で完了を返すので、待ち時間は 5 ms で済みます。そのかわり、直近 200 ms の書き込みは主系にしかありません。

      Google Cloud のデータベースとの対応

      Cloud SQL は、MySQL、PostgreSQL、SQL Server を動かすマネージドなリレーショナルデータベースです。高可用性構成にすると、主系への書き込みは 2 つのゾーンのディスクに同期で複製されます。読み取りを分散するためのリードレプリカには、非同期で複製されます。Spanner は、同期の複製を使う、リージョンをまたいで広げられるリレーショナルデータベースです。

      製品種類向く用途
      Cloud SQLリレーショナル1 つのリージョンで動く業務アプリ
      Spannerリレーショナル世界規模で強い整合性が要る処理
      FirestoreNoSQL (ドキュメント型)モバイルや Web アプリのデータ
      BigtableNoSQL (ワイドカラム型)時系列や IoT の大量の書き込み

      製品の選び方はストレージとデータベースの選び方で、RPO を満たす構成は可用性と災害復旧で扱います。

      理解度チェック

      答えを開く前に、自分で考えてください。結果はこのブラウザにだけ残り、間違えた問題は復習のページでまとめて解き直せます。

      思い出す

      トランザクションの中で「A さんの口座から 1 万円を引く」「B さんの口座に 1 万円を足す」を行い、引いた直後にサーバーが止まりました。2 人の口座はどうなりますか。理由も答えてください。

      状況で選ぶ

      注文を記録する Cloud SQL のデータベースがあります。要件は、主系のゾーンが障害で止まっても書けたはずの注文を 1 件も失わないこと (RPO 0) です。書き込みが少し遅くなるのはかまいません。どう構成しますか。

      説明する

      主系から副系への複製で、同期と非同期のどちらを選ぶかを、それぞれを選ぶ条件とあわせて説明してください。