
こんにちは。
アプリケーションサービス本部、DevOps担当の兼安です。
去年から、AIの隆盛を受けて、大量のデータを扱うお話をいただくことが増えてきました。
これを受けて、データ投入の機会も増えてきています。
大量のデータをデータベースに投入する際、ポイントとなるのがインデックスの扱いです。
今回は、このことをテーマにお話しします。
今回の記事では、データベースの例にAmazon Aurora PostgreSQLを使用しています。
- 本記事のターゲット
- データ投入する際に気にするべきはインデックス
- インデックスの作成に伴う要求スペックと処理時間の増加
- インデックスの後付けとスペックの一時的な引き上げ
- データの再投入時のインデックスの取り扱い
本記事のターゲット
本記事は、データベースのデータ投入がテーマなので、データベースとそのパフォーマンスに関する基礎知識があることを前提としています。
データ投入する際に気にするべきはインデックス
データ投入を行う際に、最も気にするべきポイントはインデックスです。
大体のRDBMSにおいて、データ投入自体は何百万、何千万、億のレコードであっても、そんなに苦労することなく可能です。
問題は、大量のデータを投入した上でインデックスを作成し切れるか?というところにあります。
インデックスとは?
インデックスは、データベースにおける検索を高速化するための仕組みです。
本における目次や索引にあたります。
大量のデータを投入した場合、インデックスが張られていないと多くの場合は実用に耐えられるパフォーマンスが出ません。
目次や索引がない本で特定のキーワードを探すのを強いられるような状態になります。
インデックスはテーブルの特定のカラムに対して設定するもので、インデックスを作成する/張るという言い方をすることが多いです。
インデックスには種類があり、一般的に最もよく使われるのはB-treeインデックスです。
PostgreSQLの場合もデフォルトだとB-treeインデックスが使用されます。
PostgreSQL 17.6文書 - 第11章 インデックス - 11.2. インデックスの種類
デフォルトでCREATE INDEXコマンドは、B-treeインデックスを作成し、それは最も一般的な状況に適合します。
B-treeインデックスは、木構造を持ったインデックスです。
インデックス全般として、書き込み時にインデックスの更新が必要になるため、インデックスが多いと書き込みパフォーマンスが低下します。
B-treeインデックスは木構造を持つため、データが増えれば増えるほど負荷が増すのが想像できるのではと思います。
データが増えるに従い木構造が大きくなり分割・再編成が大変になることにより、インデックスの更新にかかるコストが増加するイメージです。
インデックスの作成に伴う要求スペックと処理時間の増加
一般的にデータベースのテーブルは、テーブルを作成してからインデックスを設定します。
CREATE TABLE users ( id SERIAL PRIMARY KEY, user_name VARCHAR(100), user_email VARCHAR(100) ); CREATE INDEX idx_users_email ON users(user_email);
この状態で、大量のデータを投入した場合、最初は投入ペースが速くても、データが増えるに従い、だんだんと投入ペースが落ちていくことがあります。
データの増加に伴い、インデックスの更新にかかる負荷が増加する上に、インデックスの更新が高速で繰り返されるのが原因です。
難しいのは、データ投入の前半の挙動を見てスペックを決めてしまうと、後半でスペック不足になり、処理時間が大幅に伸びてしまうことです。
最後の10〜20%で全然進まなくなるのを経験したことがあります。
こうなると、データ投入のスケジュールが大幅に狂ってしまいます。
インデックスの後付けとスペックの一時的な引き上げ
この問題を回避するための方法として、データ投入時はインデックスをつけず、データ投入完了後にインデックスを追加する方法があります。
この場合、データ投入時の負荷は低下し、負荷は最後のインデックス追加時に集中します。
データ投入が完了した後に一気にインデックスを追加するため、少なくともだんだんと処理が遅くなることは避けられるため、スケジュールが狂うリスクを減らせます。
flowchart TD
A[CREATE TABLE] --> B[大量データ投入] --> C[CREATE INDEX]
もう一つのポイントとして、一時的にスペックを引き上げることも有効です。
上記の方法と組み合わせた場合、最後のインデックス追加時に一時的にスペックを引き上げることで、インデックス追加の処理時間の短縮と安定化が期待できます。
インデックス追加に必要なスペックが読みづらい場合は、要求に応じてスペックが自動で増減するAurora Serveless v2で一時的に最大ACUを高く設定しておくのも有効です。
Aurora Serverless v2 の使用 - Amazon Aurora
もう少し踏み込むと、データ投入時・インデックス追加時・その後の運用時でスペックを変えることも考えられます。
読み取り専用のデータベースであれば、データ投入時と投入後の運用では求められるスペックの方向性が異なりますからね。
データの再投入時のインデックスの取り扱い
前項で述べた内容は、新規にデータを投入する場合ですが、既存のデータに対して再投入する場合もあります。
更新の場合は、もう少し悩ましいポイントが増えます。
初回のデータ投入で最後にインデックスを追加したとして、その後にデータを追加投入・再投入する場合は、既にインデックスが存在している状態でスタートします。
このまま続けると、またインデックスの更新負荷が増大していく現象にぶつかります。
一般的に、インデックスには更新を停止させるという仕組みはありませんので、再投入時にインデックスの更新負荷を回避するにはインデックスを一旦削除する必要があります。
flowchart TD
A[DROP INDEX] --> B[大量データ再投入] --> C[CREATE INDEX]
ただし、インデックスの削除と再作成はあまり乱発すると、データベースの深い部分に悪影響を与えて性能劣化する可能性があります。(いわゆる断片化など)
そのため、定期的にデータの再投入を行う場合は、いっそテーブルを丸ごと再作成するような運用も検討した方が良いと思います。
今回は以上です。
余談ですが、この記事のタグに「プロジェクトマネジメント」を付けているのは、データ投入作業のペースはスケジュールに関わることであり、マネジメントにも関係する話でもあるなと思ったからです。
兼安 聡(執筆記事の一覧)
アプリケーションサービス部 DS3課所属
2025 Japan AWS Top Engineers (AI/ML Data Engineer)
2025 Japan AWS All Certifications Engineers
2025 AWS Community Builders
Certified ScrumMaster
PMP
広島在住です。今日も明日も修行中です。
X(旧Twitter)