目次

Neo4jでシステムダウン!グラフデータベース選択の失敗談と安全な代替案

はじめに

グラフデータベースの導入を検討中に、Neo4jで手痛い失敗を経験しました。1GB未満のデータでシステムがダウンし、CPU使用率が180%に達する事態に。この記事では、その失敗談と調査した安全な代替案をシェアします。

失敗談:Neo4jの落とし穴

何が起こったか

根本原因

Neo4jは確かに強力ですが、セーフではない設計が問題でした:

# このようなクエリで簡単にシステムダウン
MATCH (a)-[*]-(b) 
RETURN a, b
# 深度制限なしの全探索で即死

調査した代替案と比較

PostgreSQL + AGE拡張(最推奨)

安全性: ⭐⭐⭐⭐⭐
Cypher互換性: ⭐⭐⭐⭐⭐
リソース消費: ⭐⭐⭐⭐⭐

-- Cypherがそのまま使える
SELECT * FROM cypher('graph', $$
    MATCH (a:Person)-[:FRIENDS]->(b:Person)
    WHERE a.age > 25
    RETURN a.name, b.name
$$) AS (person1 agtype, person2 agtype);

メリット:

NetworkX + SQLite

安全性: ⭐⭐⭐⭐⭐
学習コスト: ⭐⭐⭐⭐⭐
制御しやすさ: ⭐⭐⭐⭐⭐

import networkx as nx
import sqlite3

# 完全制御可能な安全設計
class SafeGraphProcessor:
    def __init__(self, max_memory_mb=500):
        self.max_memory = max_memory_mb
        
    def safe_shortest_path(self, G, source, target):
        if len(G.nodes()) > 100000:  # ノード数制限
            raise ValueError("グラフサイズ上限超過")
        return nx.shortest_path(G, source, target)

メリット:

ArangoDB

安全性: ⭐⭐⭐⭐
商用実績: ⭐⭐⭐⭐
マルチモデル: ⭐⭐⭐⭐⭐

-- AQL(Cypher風だがより制御しやすい)
FOR vertex, edge, path IN 1..3 OUTBOUND 'Person/alice' Friends
  FILTER vertex.age > 25
  LIMIT 1000  -- 結果数制限が簡単
  RETURN vertex.name

マイクロサーバ分散アーキテクチャ

100万ノード規模への対応として、マイクロサーバ分散を検討:

サーバA: ユーザー関係グラフ (10万ノード)
サーバB: 商品関係グラフ (30万ノード)  
サーバC: カテゴリ・タググラフ (5万ノード)

メリット:

循環グラフ安全性の比較

データベース循環グラフ耐性システム安全性メモリ使用量
Neo4j❌ 非常に危険❌ システムダウン❌ 非常に高い
PostgreSQL+AGE⭐⭐⭐⭐ 深度制限可能⭐⭐⭐⭐ 枯れた技術⭐⭐⭐⭐ 低い
NetworkX⭐⭐⭐⭐⭐ 完全制御可能⭐⭐⭐⭐⭐ プロセス分離⭐⭐⭐ 中程度
ArangoDB⭐⭐⭐⭐ 制限機能あり⭐⭐⭐⭐ タイムアウト可能⭐⭐⭐⭐ 低い

PostgreSQLのGremlin対応について

実はPostgreSQLでもGremlin言語が使用可能です:

# Python Gremlin + PostgreSQL
from gremlin_python.structure.graph import Graph
from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection

# AGEデータをGremlin風にアクセス
def postgres_to_gremlin():
    # PostgreSQLからデータ取得
    conn = psql.connect("postgresql://localhost/graph_db")
    
    # Gremlin風のトラバーサル
    g = Graph().traversal()
    result = g.V().has('name', 'alice').out('knows').toList()
    return result

実際のクエリ比較

同じ結果を得るための3つの書き方:

-- AGE Cypher風
SELECT * FROM cypher('graph', $$
    MATCH (a)-[:KNOWS]->(b) 
    RETURN a.name, b.name
$$);

-- Gremlin風(同じデータ)
g.V().outE('knows').inV().path().by('name')

-- 純粋SQL(同じ結果)
SELECT a.name, b.name 
FROM nodes a 
JOIN edges e ON a.id = e.source_id 
JOIN nodes b ON e.target_id = b.id 
WHERE e.label = 'knows';

Docker運用でのリソース比較

実測したメモリ使用量:

# Neo4j
docker stats
# CONTAINER   CPU %   MEM USAGE / LIMIT     MEM %
# neo4j      180%    2.5GiB / 4.0GiB      62.5%

# PostgreSQL + AGE
docker stats  
# CONTAINER   CPU %   MEM USAGE / LIMIT     MEM %
# postgres    15%     200MiB / 4.0GiB      5.0%

軽量Docker設定例:

# PostgreSQL + AGE の軽量運用
docker run -d \
  -p 5432:5432 \
  -e POSTGRES_PASSWORD=password \
  apache/age-postgres:latest
# メモリ使用量: ~100-200MB(neo4jの1/10以下)

Git管理との親和性

SQLiteベースならGitでバージョン管理が可能:

# データベースファイルをGitで管理
git add graph_data.db
git commit -m "ユーザー関係グラフ更新"
git push origin main

# 問題発生時の即座復旧
git checkout HEAD~1 graph_data.db

結論:安全第一の選択

Neo4jでの痛い経験から学んだ教訓:

  1. PostgreSQL + AGE: Cypher使いたいならこれ一択
  2. NetworkX + SQLite: 最も安全で制御しやすい
  3. ArangoDB: 商用実績重視なら

特にPostgreSQL + AGEは:

次のステップ

失敗を糧に、今度は安全性を最優先にした設計で進めていきます。同じような問題で悩んでいる方の参考になれば幸いです。

循環グラフでシステムダウンするリスクを避けるためには、制御可能な仕組みを選ぶことが重要だと痛感しました。

重要: グラフデータベース選択では、機能性だけでなく安全性とリソース効率を最優先に検討することをお勧めします。


この記事があなたのグラフデータベース選択の助けになりましたか?コメントやフィードバックをお待ちしています!

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS