* 目次 [#be7fea81]
* 目次 [#j5b2c0ea]
#contents

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

** はじめに [#xf7a7b08]
** はじめに [#n695770a]

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

** 失敗談:Neo4jの落とし穴 [#o5ce9cc5]
** 失敗談:Neo4jの落とし穴 [#o5a149bb]

*** 何が起こったか [#k0a9d567]
*** 何が起こったか [#z0355f52]
- データサイズ: 1GB未満
- 現象: 少しでもループするクエリでシステム全体がダウン
- CPU使用率: 180%(Dockerで確認)
- 復旧: ループ解除すらできない状態

*** 根本原因 [#s19b2a32]
*** 根本原因 [#c8b760ed]
Neo4jは確かに強力ですが、''セーフではない設計''が問題でした:
- 無限ループの検知機能が不十分
- メモリ制限の設定が複雑
- クエリタイムアウトのデフォルト設定が甘い
- リソース消費量が予想以上に大きい

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

** 調査した代替案と比較 [#b31a5fb8]
** グラフクエリ言語の基礎知識 [#b966f677]

*** PostgreSQL + AGE拡張(最推奨) [#e7657eaa]
グラフデータベースを選ぶ前に、主要なクエリ言語について理解しておきましょう。~

*** Cypherとは? [#o0c5b01c]

Cypherは''Neo4j発祥のグラフクエリ言語''で、SQLに似た読みやすい構文が特徴です:~

 -- Cypher例:友達の友達を検索
 MATCH (me:Person {name: 'Alice'})-[:FRIENDS]->(friend)-[:FRIENDS]->(fof)
 WHERE fof <> me
 RETURN DISTINCT fof.name

''Cypherの特徴'':~
- パターンマッチング重視の構文~
- ASCII アート風の関係表現 `(a)-[:関係]->(b)`~
- SQLユーザーには親しみやすい~
- 直感的で読みやすい~

*** Gremlinとは? [#h05d12d3]

Gremlinは''Apache TinkerPop''のグラフトラバーサル言語で、プログラミング的なアプローチが特徴です:~

 // Gremlin例:同じ検索をトラバーサルで表現
 g.V().has('name', 'Alice')
      .out('friends')
      .out('friends')
      .where(neq('Alice'))
      .dedup()
      .values('name')

''Gremlinの特徴'':~
- ステップバイステップのトラバーサル~
- プログラマー向けの関数型アプローチ~
- 複数のデータベースで標準サポート~
- 複雑な処理に向いている~

*** CypherとGremlinの違い [#o9e0d2fa]

|項目|Cypher|Gremlin|h
|''書き方''|宣言的(SQLライク)|手続き的(プログラムライク)|
|''学習コスト''|SQLユーザーには易しい|プログラマーには自然|
|''表現力''|パターンマッチングが得意|複雑なロジックが得意|
|''対応DB''|Neo4j、PostgreSQL+AGE|多数のグラフDB|
|''可読性''|非常に高い|慣れが必要|

*** 実際の例で比較 [#ad08af45]

同じ「30歳以上のユーザーとその友達」を検索する場合:~

 -- Cypher版(直感的)
 MATCH (user:Person)-[:FRIENDS]->(friend:Person)
 WHERE user.age >= 30
 RETURN user.name, friend.name

 // Gremlin版(ステップ的)
 g.V().hasLabel('Person')
      .has('age', gte(30))
      .out('friends')
      .path()
      .by('name')

どちらも同じ結果ですが、''Cypherの方が SQL に慣れた人には読みやすく''、''Gremlinの方がプログラマーには柔軟''です。~

** 調査した代替案と比較 [#ka6f83d5]

*** PostgreSQL + AGE拡張(最推奨) [#l24f36fe]

''安全性'': ⭐⭐⭐⭐⭐~
''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);

''メリット'':
- Neo4jの1/10のメモリ使用量
- 枯れたPostgreSQLベース
- Cypherクエリがほぼそのまま使える
- 軽量Docker運用可能
- Git管理しやすい

*** NetworkX + SQLite [#c79fb28f]
*** NetworkX + SQLite [#i8859d26]

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

 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)

''メリット'':
- プロセス分離で安全
- Pythonの豊富なエコシステム
- SQLiteでGit管理可能
- 学習コストが最小

*** ArangoDB [#r5a53f01]
*** ArangoDB [#gdbc67c4]

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

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

** マイクロサーバ分散アーキテクチャ [#d4d0bdb4]
** マイクロサーバ分散アーキテクチャ [#l64bd940]

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

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

''メリット'':
- 障害の影響範囲を限定
- 各サーバの負荷制御が可能
- スケールアウト対応

** 循環グラフ安全性の比較 [#h8b90dbf]
** 循環グラフ安全性の比較 [#jfd59085]

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

** PostgreSQLのGremlin対応について [#q6642efc]
** PostgreSQLのGremlin対応について [#jd0d551e]

実は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

** 実際のクエリ比較 [#u815e2ca]
** 実際のクエリ比較 [#ua1345a2]

同じ結果を得るための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運用でのリソース比較 [#i616cb1d]
** Docker運用でのリソース比較 [#lef032ea]

実測したメモリ使用量:

 # 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管理との親和性 [#lcae0777]
** Git管理との親和性 [#yb69af91]

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

 # データベースファイルをGitで管理
 git add graph_data.db
 git commit -m "ユーザー関係グラフ更新"
 git push origin main
 
 # 問題発生時の即座復旧
 git checkout HEAD~1 graph_data.db

** 結論:安全第一の選択 [#k2a66bee]
** 結論:安全第一の選択 [#l09d610a]

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

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

特に''PostgreSQL + AGE''は:~
- リソース消費が1/10~
- Cypherクエリがそのまま使える~
- 軽量Docker運用~
- Git管理も簡単~
- Gremlin対応も可能~

** 次のステップ [#zd5b0914]
** 次のステップ [#d4c307b8]

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

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

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

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

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS