【ITニュース解説】How I Built a MongoDB Archiving System for Crawled Data
2025年10月03日に「Dev.to」が公開したITニュース「How I Built a MongoDB Archiving System for Crawled Data」について初心者にもわかりやすく解説しています。
ITニュース概要
MongoDBでスクレイピングデータを効率的に管理するシステムを紹介。アクティブとアーカイブにデータを分割し、取得日時と更新日時で鮮度を管理する。重複排除と自動アーカイブでデータを整理し、インデックスで検索を高速化。大規模データ運用とパフォーマンスの両立を実現する。
ITニュース解説
システムエンジニアを目指す初心者が、Webから集めた大量のデータを効率的に管理するためのシステムについて解説する。この記事では、Webスクレイピングによって日々収集される情報の課題を解決し、データの鮮度と履歴の両方を保つMongoDBアーカイブシステムの構築方法が紹介されている。
まず、Webスクレイピングとは、インターネット上のWebサイトからプログラムを使って自動的に情報を集める技術のことである。たとえば、大学のウェブサイトから毎日新しいお知らせやイベント情報を集めると想像してみてほしい。最初は順調にデータが集まるが、時間が経つにつれて、同じお知らせが何度も保存されたり、すでに終了したイベントの情報が残り続けたりして、データベースの中が古い情報や重複データでいっぱいになる。これは「データカオス」と呼ばれ、データが多ければ多いほど状況は悪化する。記事の筆者は、大学のニュースを集約する「CollegeBuzz」というシステムを開発する中で、まさにこの問題に直面したという。
この問題に対処するためには、単にデータを集めるだけでなく、データのライフサイクル、つまりデータが生まれてから使われなくなり、最終的に保管されるまでの全過程を適切に管理する必要がある。既存のデータを上書きしてしまうと過去の情報を失い、無闇に新しいデータを追加すれば重複だらけになる。手作業での整理は、日々大量に増え続けるデータの前では現実的ではない。そこで、データの鮮度を保ちつつ、履歴も失わずに、かつシステム全体の性能を維持できる自動化されたアーカイブシステムが必要となったのである。
このシステムの中核となるのは、「アクティブ+アーカイブパターン」という設計思想だ。これは、全てのデータを一つの巨大な場所にまとめるのではなく、「アクティブコレクション」と「アーカイブコレクション」という二つの目的に特化したデータベース領域にデータを分割するという考え方である。アクティブコレクションには、現在使われている最新のデータだけが保存される。例えば、まだ開催されていないイベントの情報や、現在掲載されているお知らせなどだ。一方、アーカイブコレクションには、古くなった情報や、過去の履歴として残しておきたいデータが保存される。この分割によって、ユーザーが最新の情報を検索する際はアクティブコレクションだけを参照するため、非常に高速な検索が可能となる。また、アーカイブコレクションはいくらデータが増えても、アクティブコレクションの性能には影響を与えないため、システムの全体的なパフォーマンスが維持される。さらに、何か問題があった際には過去のデータを参照したり、長期的な傾向を分析したりすることも可能になる。
次に、このシステムで最も重要な課題の一つである「重複の検出」について見てみよう。Webから取得したデータは、同じ内容であっても細かい部分が異なったり、別のページで再掲載されたりすることがよくある。「2025年度入学案内」というお知らせが複数のページに掲載されたり、毎日再クロールされたりする状況を想像すると分かりやすいだろう。そこで、記事ではMongoDBが自動で割り当てるIDではなく、データの内容に基づいた「複合ユニークキー」という仕組みを採用している。具体的には、お知らせの「タイトル」と「URL」の組み合わせをユニークな識別子として使う。もしタイトルやURLが取得できない場合は、タイムスタンプなどの変動しやすいフィールドを除いた全てのフィールドを比較することで、データの同一性を判断する。コンテンツ全体をハッシュ値に変換する方法もあるが、複合キーは、どの情報が重複の原因となっているかを目で見て確認できるため、デバッグがしやすいという利点がある。
データの鮮度と履歴を管理するために、「タイムスタンプ」も重要な役割を果たす。システムでは、全てのデータに対して二種類のタイムスタンプを持たせる。一つはcrawled_atで、これはシステムがそのデータを初めて取得した日時を示す。この値は一度設定されたら決して変更されない。もう一つはlast_updated_atで、これはシステムがそのデータを最後に更新した日時を示す。この値はデータが再クロールされるたびに常に最新の日時に更新される。この二つのタイムスタンプを組み合わせることで、「crawled_atが60日前だが、last_updated_atが昨日である」データは、コンテンツ自体は安定しているものの、まだ有効な情報だと判断できる。逆に、両方のタイムスタンプが60日前の場合は、おそらくすでに更新されなくなり、古い情報だと推測できる。これにより、データの「鮮度」と「寿命」を正確に追跡できるのだ。
アーカイブのルールは二つの条件に基づいて適用される。一つは「期間ベースのアーカイブ」だ。これは、データが初めて取得された日時、つまりcrawled_atが指定された期間(例として30日)より古くなった場合にアーカイブするというものだ。もう一つは「イベントベースのアーカイブ」で、これはデータ自体が持っている日付情報(例えばイベントの開催日や申し込み締切日など)が過去になった場合にアーカイブするというルールである。例えば、「入学締切日:2025年9月15日」というデータがあれば、9月16日になったら自動的にアーカイブされる仕組みだ。この二つの条件を組み合わせることで、本当にアクティブコレクションに残しておくべきデータだけが厳選される。アーカイブ処理では、対象となるデータはアクティブコレクションからアーカイブコレクションにコピーされ、その後アクティブコレクションからは削除される。この際、crawled_atやlast_updated_atといった重要なタイムスタンプはそのまま保持され、後の分析に活用される。
大量のデータを扱う上で、システム性能を保つためには「インデックス」が不可欠である。インデックスは、データベースから特定のデータを素早く見つけ出すための目次のようなものだ。記事の筆者は、crawled_at(アーカイブ対象の検索用)、last_updated_at(最新情報でソートするための検索用)、そして複合ユニークキーである(title, url)(重複検出用)の三つのインデックスをアクティブコレクションとアーカイブコレクションの両方に設定している。インデックスがない状態では5万件のデータからアーカイブ対象を探すのに4秒以上かかっていた処理が、インデックスを導入した後はわずか120ミリ秒に短縮されたという具体的な例が示されている。これは、インデックスがいかに重要であるかを物語っている。
また、現実世界のデータは常にきれいな形では提供されないため、「エッジケース」への対応も重要である。例えば、Webサイトから取得される日付文字列は「2024年5月15日」や「May 15, 2024」など多種多様な形式があるため、どんな形式でも正しく解釈できるような堅牢な日付解析関数が必要になる。さらに、過去に取得されたデータの中にはcrawled_atのような重要なタイムスタンプが欠落している場合もある。このような場合でもシステムが停止しないように、タイムスタンプがなければ現在時刻をデフォルトとして設定するなど、防御的なコーディングが求められる。
開発したシステムが意図通りに機能するかを確認するために、「テスト」も非常に重要だ。記事では、crawled_atが31日前のレコードを意図的に作成し、それをアクティブコレクションに挿入するテスト関数を紹介している。その後、アーカイブ処理を実行し、そのレコードがアクティブコレクションから削除され、アーカイブコレクションに無事に移動したことを確認する。このようなテストを行うことで、アーカイブロジックが正しく動作することを検証できる。
このシステムを構築する過程で、いくつかの貴重な教訓が得られたと筆者は述べている。まず、Webスクレイピングでは、Webサイトの構造が予期せず変更されることが頻繁にあるため、取得したデータは常に矛盾する可能性があることを念頭に置く必要がある。日付のパース処理など、エラーが起こりやすい箇所には必ずエラーハンドリングを導入し、失敗した場合はログに残して後で確認できるようにしておくべきである。二つ目に、インデックスは後回しにせず、最初の段階で必ず導入すべきだという。インデックスなしでは、大量データの処理に途方もない時間がかかってしまう。三つ目に、アクティブなデータとアーカイブされたデータを一つのコレクションにis_archivedのようなフラグで管理するのではなく、最初から完全に別のコレクションに分けることが、クエリの複雑性を劇的に減らす上で効果的である。最後に、手動でアーカイブを強制実行できる機能も必要だという。これは、例えば日付解析のバグ修正後など、特定の条件下で過去のデータを一括して整理したい場合に役立つ。
このアーカイブシステムを導入した結果、具体的な改善効果が見られた。アクティブコレクションのレコード数が45万件から1万2千件へと大幅に減少し、ユーザー検索の平均クエリ時間は3.2秒から180ミリ秒へと劇的に高速化された。重複するお知らせは15%から0%になり、以前は失われていた過去のデータが全て監査可能な形で残るようになった。ディスク使用量も大幅に削減されたという。このシステムのおかげで、「入学案内がどれくらいの期間掲載されているか」や「どの大学が最も頻繁にニュースを更新しているか」といった、以前は不可能だった分析が可能になり、過去のイベント情報の復元もできるようになった。
今後、筆者はMongoDBのTTLインデックス(一定期間が過ぎたら自動削除・アーカイブする機能)、バッチ処理による段階的なアーカイブ、データ変更履歴の検出、アーカイブデータの圧縮など、さらなる機能強化を検討している。
このシステムの構築を通じて得られた最大の教訓は、データを集めること自体よりも、集めたデータをいかに効率的かつ適切に管理するかが、大規模なシステム開発では非常に重要であるということだ。Webスクレイピング、ニュース集約、イベントトラッキング、時系列データなどを扱うシステムを開発する際は、システムの開発初期段階からデータのアーカイブについて真剣に検討することが、将来の自分とデータベースを助けることになるだろう。