質問集
データベースについて、いくつかの質問をあげて、しっかり自分で言語化できるかどうかを確認しました。
リレーショナルデータベースって?
テーブルを使ってデータを管理するデータベースのこと。各テーブルは、行と列で構成され、主キーや外部キーを使って関連付けを行う。これにより、データの整合性が保ちやすくなり、SQLを使って効率的にデータを操作することができる。
なぜデータの整合性が保ちやすくなる?
制約を使って、ルール通りのデータしか登録できないようにできるため。データが壊れた状態で入るのを防ぐ機能がそもそも備わっているため整合性を保ちやすくなる。
リレーショナルデータベースのデメリットは?
- スケーラブルな処理が難しい、スケーラビリティが弱い:データ量やアクセス数が増えたときに、水平スケーリング(複数サーバーでの分散処理)が難しいため、大規模サービスには向いていない。
- JSONや画像・動画などの複雑なデータ構造には弱くなる:RDBでは、事前に各テーブルの定義を行う必要があるが、JSONなどでは、「項目が動的に変わる」データに柔軟に対応が行える。画像や動画はサイズが大きいため、パフォーマンス低下しやすいなどの問題あり。
整合性が求められるのってどういう時?
整合性が求められるのは、正確なデータが必要な場合、間違ったデータが致命的な問題を産む場合である。
- 銀行の送金処理:AからBへ1万円送る時、Aの口座から引かれたのにBの残高が増えなかったらやばい。
- 在庫管理:ECサイトで在庫1個の商品を購入した時、在庫が減らなかったら、在庫がないのに買えてしまうという問題が起きる。
SQLとNoSQLの違いは?
- SQL:構造化されたデータ、強い整合性が必要なケースに強い
- NoSQL:柔軟なデータ構造、大量データやスピードが重視される場面に強い
つまり、SQLは「行と列で構成されるテーブル形式の管理を行い」で、整合性を重視するシステムに向いており、NoSQLは「柔軟でスケーラブルなデータ形式」で、大量データやスピードを求める開発に適している。
スケーラブルって?
システムの負荷が増えても、それに応じて性能を拡張できる性質のこと。
- 垂直スケーリング:サーバーの性能(CPU,メモリ)を上げる
- 水平スケーリング:サーバーの台数を増やして処理を分散する
データベースの正規化って何?なぜ必要?
正規化って?
データの重複を減らし、整合性を保ちやすくするために、データベースの構造を整理する手法のこと。
正規化で具体的にどのように保守性が上がるの?
正規化について、第一正規化、第二正規化、第三正規化ってそれぞれ何?
- 第一正規形:繰り返しのあるデータを排除して、全ての値を原子値に分解すること。
- 第二正規形:第一正規形を満たした上で、「主キーの一部にしか関係ない列を分離する」、「部分関数従属」を排除する。つまり、主キーの一部にしか依存しない列を別テーブルに分けること。
- 第三正規形:「推移的関数従属」を排除する。つまり、主キー以外の列が、他の列に依存している時、それを別テーブルにすることによりデータの独立性と整合性を保つようにする。
部分関数従属とは?
複合主キー(A,B)のうち、一部にしか依存していない列がある状態のこと。
例:1つのテーブルのカラムで、学生ID、科目ID、学生名、科目名があった時
- 学生名が学生IDにしか関係ない(科目IDは関係ない)
- 科目名は科目IDにしか関係しないと言う状態で、これが部分関数従属の状態である。
これを(学生ID(主キー)、学生名)。(科目ID(主キー)、科目ID)にそれぞれ分けることによって第二正規系になる。
推移的関数従属とは?
主キー以外の列に間接的に依存している状態のことを言う。
例:社員ID(主キー)、部署ID、部署名
これは、社員ID→部署ID→部署名で、部署名は、社員IDではなく、間接的に部署IDに依存している状態で、これを分けることによって、推移的関数従属を排除をする。これで第三正規系になる。
分け方:テーブルA(社員ID、部署ID) テーブルB(部署ID、部署名)
正規化と逆正規化の違いは?
- 正規化:重複を排除して、データの整合性を保つ
- 逆正規化:正規化によって、分割されたテーブルをあえてくっつけること
逆正規化ってどういう場面で使う?
例えば、Webアプリで、画面を表示するのに3つ、4つもテーブルJOINしていたら遅くなってしまう。そこで、よく使う情報を冗長になるが、あらかじめ「逆正規化」で、JOINで結合して1つのテーブルにして、サッと取り出せるようにするために使われる。こういう処理が逆正規化。
シャーディングとレプリケーションの違いは?
-
レプリケーション(複製):同じデータを複数のサーバーに持たせる、すなわちコピーすること。サーバーが落ちた時でも、他のサーバーが同じデータを持っているので、復旧が容易になる。
-
シャーディング(分割):大量のデータをいくつかに分割して、複数のサーバーに分けて保存すること。これによって、DBへの負荷が分散されて、高速化、スケーラビリティが向上する。例えば、ユーザーIDが1-10000のデータをサーバーAに、ユーザーIDが10001-20000のデータをサーバーBに保存するという感じで保存する。サーバーは全体のデータの一部しか持っていないが、その分高速に処理を行うことができる。データ量が非常に多い時、負荷分散を最適化したいときに最適である。
- 水平分割(よく使われる):行単位(縦)でデータを分ける方法:ユーザーデータが100件ある場合、ユーザーを1-50,51-100に分ける。データ量をバランス良く分ける。ユーザー単位の処理に強くなる。
- 垂直分割:列単位(横)でデータを分ける方法
インデックスって何?どのように機能する?
-
インデックスとは?:データベース内の特定のカラムに対して作成され、データを効率よく検索するための「索引」のようなもの。インデックスを使うと、データベースは全てのデータを順番に検索するのではなく、インデックスに基づいて特定のデータを素早く見つけることができる。例えば、社員IDで検索を頻繁に行う場合、社員IDのインデックスを作成することで、全ての社員を1つ1つ調べるのではなく、インデックスを使って該当する社員を素早く見つけることができるため、検索の効率を図ることができる。つまり、本の索引が「〇〇について調べたい時、どのページを見ればいいかを教えてくれる」のと同じようにインデックスは「〇〇を検索したい時、どこを見ればいいかを教えてくれる」役割を果たす。
-
インデックスのメリットは?:インデックスは、データベースの検索効率を大幅に向上させるために使用される。例えばテーブルに大量のデータがある場合、インデックスがないと、検索時に全てのデータを1つ1つ調べる必要があり、この時検索が非常に遅くなる。インデックスを作成することで、検索対象のデータはここにありますよと言う情報を示してくれるので、検索のパフォーマンスが大幅に向上する。
インデックスを使う際のデメリットや注意点は?
- 書き込み(INSERT/UPDATE/DELETE)が遅くなる
- インデックスは検索を高速化する代わりに、データの追加、更新、削除のたびにインデックスの情報も更新する必要があるため、書き込み性能が低下する。
- 容量を多く使う、インデックスを使いすぎると逆効果に:インデックス自体もデータベース内に格納するため、インデックスを多く作成すると容量の圧迫につながってしまう。必要以上に多くのインデックスを貼ると、最適なインデックスを選ぶ処理が遅くなる。SQL実行時に「どのインデックスを使うか?」を最適化するため、インデックスが多いと判断に時間がかかってしまう。なので、インデックスを使いすぎず、適切なインデックスを用いて、設計を行うのが大事。
N+1問題はどういう問題?
データベースやプログラムでデータを取得する際、非効率な方法でクエリが発行されてしまう問題のこと。この問題がシステムのパフォーマンスが大きく低下し、データベースへの負荷が増加する。
- N+1問題の本質:クエリの発行回数が一回の基本的なクエリに加えて、追加でN回のクエリが発行されることから来ている。つまり、必要以上に多くのクエリが発行されてしまっているのが、「N+1問題の本質」である。
- 例:親子関係のデータを取得するケースの場合
ユーザーテーブル:id(主キー) name
投稿テーブル:id、user_id(userテーブルの外部キー)、content(投稿内容)で
N人のユーザーがいて、SELECT * FROM users;とやった場合、各ユーザーN人の個別の投稿を取得するために、N回アクセスするといった形をとる。クエリの回数が増えると、データベースの負荷が高まり、システム全体のパフォーマンスが低下する。これがN+1問題の本質である。
N+1問題の解決策は?(データベースのパフォーマンスを向上させる方法は?)
- JOINを活用して一括で取得する:内部結合(INNER JOIN)や左外部結合(LEFT OUTER JOIN)を使って1回のクエリで親子データをまとめて取得できるようにする。
例:userテーブルとpostsテーブルがある場合
SELECT user.id,user.name,post.id,post.title
FROM user LEFT JOIN post ON user.id = post.user_id;
とやることで、1回のクエリで親と子の情報を全て取得することを行える。
JOINの違い(内部結合と外部結合との違い)
- INNER JOIN:両方のテーブルでON以下の結合条件に一致するデータのみを取得する
- LEFT JOIN:左側のテーブルのデータは必ず取得し、右側はON以下の結合条件に一致する場合であれば取得する、一致しない場合でもnullと記述し取得を行う。
クエリの最適化とは?
クエリの最適化は、SQLクエリができるだけ効率的に実行されるように工夫すること。
具体的な工夫
- インデックスを活用する:クエリの検索条件にインデックスを使用することで、検索速度を劇的に向上させることができる。特に、検索の条件やORDER BY句に使用されるカラムにインデックスをつけるとより効果的である。
- クエリを簡素化する:SELECT *を避けて、必要なカラムだけを指定する。or複雑なJOINを避けるなど。
複雑なJOINって具体的には?
- JOINを多様しすぎる:正しい設計がされていて、インデックスが適切なら、3つ以上JOINしても問題ないが、JOINを多様しすぎてしまうと、
- 可読性が落ちる
- 結合条件を間違えてしまうと、バグや意図しないデータ重複のリスクが上がる
- 不要なLEFT JOINやOUTER JOINをしない、無駄なnullデータを引っ張ってきてしまうと、パフォーマンスが低下する。nullを持ってきたくない場合は、INNER JOINでOK。
JOINとサブクエリの違いは?どちらが効率的か?
- JOINとは?:JOINは、複数のテーブルを行ごとに結合し、関連するデータをまとめて取得するSQLの機能のこと。
- サブクエリとは?:あるクエリの中に埋め込まれた別のクエリのこと。「SELECT文」「WHERE句」「FROM句」の中に別のSQL(SELECT)を埋め込むということ
どちらが効率的か?
- パフォーマンス重視の場合:一般的に、JOINが効率的である。JOINは、SQLエンジンによって最適化されるため、複雑なクエリでも高速に実行される。特に、大量のデータを扱う場合、JOINの方が高速である
- 可読性や特定の条件の確認が重要な場合:サブクエリは、特定の条件を明確に分けて記述できるため、可読性が高くなりやすい。明確な条件を掲示して、分かりやすくしたい場合では、サブクエリが適切である
トランザクションって何?どのように管理する?
トランザクションは、データベースにおける一連の操作(クエリ)のことを指す。これらの操作は、すべて成功するか、すべて失敗するかのどちらを保証すること。トランザクションは、データベースの一貫性を保つために重要である。
トランザクションは、ACID特性を守りながら処理される:
- Atomicity(原子性):すべての操作が成功するか、失敗するか。失敗した場合は、ロールバックされる。(最初に戻る)
- Consistency(一貫性):一貫性のある状態だけが保存される。
- Isolation(独立性):同時実行されているトランザクションがお互いに影響しない。
- Durability(耐久性):成功したトランザクションは永続的に保存され、システム障害後もデータが失われない
トランザクションの管理
データベース管理システム(DBMS)によって管理される。
一般的な管理方法:
- 開始:明示的に開始する(BEGIN TRANSACTION)か暗黙的に開始される場合がある。トランザクションの開始後、複数の操作が行われる
- コミット:トランザクションがすべて正常に終了した場合、その変更をデータベースに永続的に反映させるためにCOMMITを実行する。これにより、トランザクション内で行った後の操作がデータベースに保存される
- ロールバック:トランザクション中にエラーが発生した場合や、途中で変更を取り消す必要がある場合、ROLLBACKを使用する。リセットを行い、最初の状態にでき、データの整合性を保つことができる。
ACID特性とは何?
データベースにおけるトランザクションが信頼性を持って実行されることを保証する4つの特性のこと。
# デットロックってなに?なぜ起こる?
- デットロックとは?:複数のトランザクションが、互いのロック解除を待ち続けてしまう状態のこと
- なぜ起こるのか?
例:- トランザクションAが「商品テーブルの行X」にロックをかける
- トランザクションBが「在庫テーブルの行Y」にロックをかける
その後、
A → Yをロックしようとする(しかしBが既にロックしている)
B → Xをロックしようとする(しかしAが既にロックしている)
お互いがお互いのロック解除を待つ状態に入り先に処理が進まず、詰んでしまう状態のこと
もっと具体的に
- トランザクションAの流れ
商品テーブルの行Xをロックした後、在庫テーブルの行Yをロックしようとする。しかし、YはBが既にロックをしているので、Aはロック待ち状態に入り、処理が永久に進まなくなる。 - トランザクションBの流れ
在庫テーブルの行Yをロックした後、商品テーブルの行Xをロックしようとする。しかし、XはAがロックしているので、Bはロック待ち状態に入り、処理が永久に進まなくなる。
どちらもcommitやrollbackできない。AもBも処理が進まず、ロック解除を待つためその場にとどまり続ける。 - デットロックを防ぐ方法
ロックの順番を統一する:全てのトランザクションに対し、ロックをする際のルールを設ける。「必ずXからYの順番にロックを行うと言うルールを作る。それに準拠する形にすることで、ロックする順番が相互になって、ロックを待ち続けるといった状態を防ぐことができる。」
楽観ロックと悲観ロックの違いってなに?
ロックとは?
データベースで複数のトランザクションが同じデータにアクセスする際の競合を防ぐための手段のこと。ロックの種類には、楽観ロックと悲観ロックがあり、それぞれ異なるアプローチでデータの整合性を確保する。
- 楽観ロック:「大丈夫!多分他の人と競合しないでしょ。競合してエラーになっても最初からデータ取得して編集すればいいと言う楽観的な考え」データの取得時点ではロックしない方法。例えば、AさんとBさんが同じデータを取得する場合、Aさんは編集して、保存する。また、Bさんも編集して、保存を行う。しかし、Bさんは、Aさんの編集後ではなく、最初のデータを取得するので、競合が発生してエラーが起きる。そこで、Bさんはもう一回データを取得し直して再編集をする。これが楽観ロックである
- 悲観ロック:「誰かが先に触ると予想して、先にロックしておく」データ取得時にロックをかける(他の人がデータ取得できなくなる。)他の人がそのデータを使いたくても、ロックが解除されるまで待たなければならなくなる
ER図って何?
データベースの構造を視覚的に表現した図のこと。データそれぞれがどのような関係にあるかを整理するために使う。
ER図の内容
- エンティティ:データの主役となるもの(ユーザー、商品、注文など)
- 属性:エンティティが持つデータ(ユーザー名、メールアドレス)
- リレーションシップ:エンティティ同士のつながり
主キーと外部キーの違いは何?
- 主キー:テーブル内の各レコード(行)を一意に識別するためのキーのこと
- 単一or複数:主キーは単一の列で構成されることもあるし、複数の列を組み合わせた複合主キーとして定義されることもある。
- 外部キー:テーブル間の関連付けを行うための橋渡しを行う。あるテーブルの中で、この値は別テーブルのこの行を指していると言うことを表す。
- 外部キーの目的
- 存在しないデータを参照しないようにする
- データの整合性(矛盾がないこと)を保証する
- テーブル同士の関係性を明確にする
- 外部キーの目的
NoSQLデータベースの代表的な種類(キー・バリュー型、カラム型、ドキュメント型、グラフ型)について
RDB(リレーショナルデータベース)と異なり、柔軟なデータモデルを持ち、スケーラビリティやパフォーマンスに優れるデータベースのこと
NoSQLの種類
- キー・バリュー型:キーとそれに対応するバリュー(値)のペアでデータ管理を行う。
- 例:キャッシュ(Redis)、DynamoDB
- カラム指向型:行と列でデータを保存するが、RDBとは異なり、行ごとに異なる列を持つことが可能。列単位でデータを保存するため、集計処理(売上合計など)に非常に強くなる。
- なぜカラム指向型は、集計処理に強いの?:通常のRDBは、SELECT SUM(price)のようなクエリを行った時、全行のすべての列を読み込んでから、priceに関する情報を取り出すと言うことを行う。カラム指向型は、priceのみ集計を行うので、RDBと比べて処理が早くなるから
- ドキュメント指向型:データをJSONや配列などの複雑なデータの保存を行う。Webアプリのデータ構造と相性がよく、APIとの連携がしやすい。
- グラフ型:ノード(点)とエッジ(線)でデータを表現する。ノード=エンティティ(人・商品など)、エッジが関係性(購入した)
- メリット:複雑な関係性の表現や探索が得意。JOIN不要で即時に関連データにアクセスすることが可能
CAP定理ってなに?
主に分散データベースに向けた理論で、分散データベースが同時に全ての要素(Consistency、Availaility、Partition Tolerance)を満たすことができず、多くとも2つまでしか満たすことができないという理論。
- Consistency(一貫性):トランザクションの前後でデータの整合性をとれることを保証すること。銀行口座の取引では、一貫性が重要である。
- Availability(可用性):常にリクエストに応答できることを保証する。どのノードにアクセスしても何かしらのレスポンスを返す。SNSやECサイトでは、Availabilityが重要。
- Partition Tolerance(分断耐性):ネットワーク障害が発生しても、システムが動作し続けることを保証する。ノード間の通信が切れても、サービスを提供し続ける
分散データベースと通常データベースの違いは?
- 通常のデータベース(MySQL、PostgreSQL):1台のサーバー上にデータが保存される。全ての処理(読み書き、トランザクション管理)は1つのサーバー上で行われる
- メリット:データの整合性を保ちやすい
- デメリット:サーバーが1台なので、障害時にシステム全体がダウンする。この対処法が、サーバーの性能を上げるしかないので、拡張性がない
- 分散データベース:複数のサーバーにデータを分散して保存・管理を行う、ネットワークを通じてデータをやり取りし、1つのデータベースのように動作する。
- メリット:スケーラビリティなデータベース(状況に応じて、性能を拡張させることができる。)、高可用性:1つのサーバーが故障しても、他のサーバーがデータ処理に回ってくれるので、システム全体が止まりにくい。
- デメリット:サーバーが個々で別れているので、一貫性を確保しにくい。複雑な設計になりやすい。
複雑な設計ってどんな設計?
以下の3つを考慮して設計を行わなければならない点。
- 一貫性の確保:同じデータが複数のサーバーにある場合、どのサーバーを基準にするかを設計で決める必要がある。
- シャーディング(データの分割):データをどのように分けて、どのサーバーに保存するかの設計が必要。
- レプリケーションと障害対策(データの複製):高可用性を保つために、データを複製して、複数のサーバーに持たせる。障害が起きた時、どのサーバーが代わりになるかなどの設計が必要。