FastAPI + SQLAlchemy性能改善プレイブック: 遅いAPIを計測ベースで高速化する

FastAPI + SQLAlchemy性能改善プレイブック: 遅いAPIを計測ベースで高速化する FastAPIの初期実装は非常に快適です。しかし運用フェーズに入ると、次のような症状が出てきます。 一覧APIのレスポンスが急に遅くなる 同時接続が増えるとp95が跳ねる CPUは余っているのにタイムアウトが増える DB接続数が上限に張り付く こうした問題の多くは「Pythonが遅い」のではなく、SQLAlchemyの使い方とDBアクセス設計 に起因します。 本記事では、FastAPI + SQLAlchemy + PostgreSQL構成を前提に、実際の改善手順を計測ベースで整理します。 1. 最初に測るべき指標 最適化は、体感ではなく数値で進めます。最低限、以下を可視化します。 APIのp50/p95/p99レイテンシ エンドポイント別SQL発行回数 1リクエストあたりのDB滞在時間 connection pool待ち時間 slow query件数(200ms以上など) OpenTelemetryやNew Relicを使っているなら、アプリspanとDB spanを必ず紐付けてください。これだけでボトルネック特定速度が上がります。 2. N+1問題を最優先で潰す 最も頻出するのがN+1です。例えばユーザー一覧でプロフィールを参照すると、ユーザー数分の追加クエリが発行されます。 2.1 悪い例 1 2 3 4 5 6 7 8 users = session.query(User).limit(100).all() result = [] for u in users: result.append({ "id": u.id, "name": u.name, "profile": u.profile.bio, }) 2.2 改善例(joinedload/selectinload) 1 2 3 4 5 6 7 8 from sqlalchemy.orm import selectinload users = ( session.query(User) .options(selectinload(User.profile)) .limit(100) .all() ) joinedload と selectinload はデータ量で使い分けます。 ...

March 7, 2026 · 2 min · AI2CORE 編集部

PostgreSQL接続プール枯渇の実戦対処:再発防止までつなげる調査・改善プレイブック

PostgreSQL接続プール枯渇の実戦対処:再発防止までつなげる調査・改善プレイブック 本番障害でよくあるのが、too many clients already や remaining connection slots are reserved です。アプリ側から見ると「急にDBに繋がらない」、ユーザー側から見ると「全機能が遅い・失敗する」という最悪の体験になります。 厄介なのは、接続枯渇が「DBサーバー性能不足」だけで起こるわけではない点です。リーク、タイムアウト設定、長時間トランザクション、プールサイズ不整合など、複数要因が重なって起きます。 この記事では、接続枯渇に対して 発生時の初動 → 根本原因の特定 → 恒久対策 の順で、手順を実務レベルでまとめます。 1. まず初動:サービス継続を優先する 障害対応では、完璧な原因究明より「止血」が先です。以下を順番に実施します。 直近リリース有無を確認(機能フラグ含む) アプリの接続数・待機数・エラー率を確認 DB側で pg_stat_activity を取得 長時間実行クエリを必要に応じて停止 一時的にアプリ Pod 数を制限して雪だるま増幅を止める pg_stat_activity の基本クエリ: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 SELECT pid, usename, application_name, client_addr, state, wait_event_type, wait_event, now() - query_start AS query_duration, now() - xact_start AS xact_duration, left(query, 120) AS query_head FROM pg_stat_activity WHERE datname = current_database() ORDER BY xact_start NULLS LAST, query_start NULLS LAST; ここで見るべきは、state='idle in transaction' と異常に長い xact_duration です。これがあるとコネクションを握ったまま解放されず、枯渇の引き金になります。 ...

March 5, 2026 · 2 min · AI2CORE 編集部

PostgreSQL肥大化対策の実務:VACUUM/Autovacuum/Index再編成を止めずに回す運用プレイブック

PostgreSQL肥大化対策の実務:VACUUM/Autovacuum/Index再編成を止めずに回す運用プレイブック PostgreSQL を長期運用すると、遅かれ早かれぶつかるのが bloat(テーブル/インデックス肥大化)です。CPU やメモリを増やしても、実体は不要領域の蓄積なので、根本原因を処理しない限り性能は戻りません。 本記事では、サービス停止なしで bloat を抑える運用を目標に、Autovacuum 設計、監視、メンテ手順を実践ベースで解説します。 1. なぜ肥大化が起きるのか PostgreSQL は MVCC を採用しているため、UPDATE/DELETE で古い行バージョンが即時削除されません。不要バージョンは VACUUM で回収されますが、追いつかないと肥大化します。 肥大化が進むと以下が起こります。 同じデータ量でも I/O が増える インデックス探索が遅くなる キャッシュ効率が落ち、p95 レイテンシが悪化 自動メンテの時間がさらに伸びる(悪循環) 重要なのは、「遅くなってから対処」だと回復コストが高いという点です。 2. 最初に見るべき指標 運用でまず可視化するのは次の4つです。 n_dead_tup(死んだタプル数) last_autovacuum(最後に vacuum が走った時刻) テーブルサイズ・インデックスサイズ推移 age(relfrozenxid)(XID 消費進行) 確認クエリ例: 1 2 3 4 5 6 7 8 9 10 SELECT schemaname, relname, n_live_tup, n_dead_tup, last_autovacuum, last_vacuum FROM pg_stat_user_tables ORDER BY n_dead_tup DESC LIMIT 20; XID の健全性チェック: ...

March 4, 2026 · 2 min · AI2CORE 編集部

Redisキャッシュスタンピード対策ガイド:高負荷時にDBを守る設計と実装

Redisキャッシュスタンピード対策ガイド:高負荷時にDBを守る設計と実装 Redis を使っていても、ピークトラフィック時に DB が突然落ちることがあります。原因の多くはキャッシュスタンピードです。人気キーの TTL が同時に切れると、大量リクエストが一斉に DB へ流れ、接続プールが飽和します。 「Redis を入れたのに遅い」「ピーク時だけ 500 が増える」という現象は、このパターンで説明できることが非常に多いです。 本記事では、キャッシュスタンピードを実運用で防ぐために、設計原則・実装パターン・監視方法を順に解説します。 1. キャッシュスタンピードとは何か 典型シナリオ: 商品ランキング API が ranking:daily を Redis に 300 秒で保存 300 秒後、人気時間帯にキー期限切れ 同時に 1000 リクエストが miss 1000 回 DB 集計が走ってレイテンシ急増 このとき Redis 自体は正常でも、背後の DB が壊れます。つまり、問題はキャッシュ障害ではなく「再生成の同時実行制御」です。 2. 防御の基本は三層構え スタンピード対策は単一施策では不十分です。次の三層を組み合わせると安定します。 同時再生成の抑制(singleflight / 分散ロック) 期限切れの分散(TTL ジッター) 期限切れ後の挙動制御(stale-while-revalidate) 3. パターン1: singleflight で同時再生成を止める 同一キーの miss が同時発生しても、1 リクエストだけ再生成し、他は待つ設計です。 TypeScript 例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const inflight = new Map<string, Promise<string>>(); async function getOrCompute(key: string, ttlSec: number, compute: () => Promise<string>) { const cached = await redis.get(key); if (cached) return cached; if (!inflight.has(key)) { const p = (async () => { try { const value = await compute(); await redis.set(key, value, { EX: ttlSec }); return value; } finally { inflight.delete(key); } })(); inflight.set(key, p); } return await inflight.get(key)!; } 単一プロセスではこれで十分ですが、複数インスタンス構成では分散ロックも必要です。 ...

March 2, 2026 · 2 min · AI2CORE 編集部

Python asyncioバックプレッシャー設計:落ちない非同期バッチを作る実装パターン

Python asyncioバックプレッシャー設計:落ちない非同期バッチを作る実装パターン asyncio は速く作れる一方で、負荷が上がった瞬間に崩壊する設計を作りやすいという側面があります。特に「処理待ちが無限に積み上がる」「外部API遅延で全体が詰まる」「リトライ嵐でさらに遅くなる」は典型的です。 本記事では、非同期ワーカーを本番運用する前提で、バックプレッシャーを実装に落とす方法を解説します。単なる概念ではなく、すぐ使えるコード断片を中心に進めます。 1. なぜバックプレッシャーが必要か バックプレッシャーは「これ以上は受けない」仕組みです。これがない設計は、ピーク時に次の順で壊れます。 入力が処理速度を超える キューが無限増加してメモリ圧迫 GC増加でスループット低下 タイムアウト増加→リトライ増加 システム全体が雪崩れる つまり、受けすぎないことは性能ではなく可用性の話です。 2. 基本設計:3つの制限を必ず入れる 2-1. キュー上限(bounded queue) 1 2 3 4 import asyncio QUEUE_MAX = 1000 queue: asyncio.Queue[dict] = asyncio.Queue(maxsize=QUEUE_MAX) maxsize なしは原則禁止です。業務要件で「捨てられない」場合でも、無限キューより「受け付け停止 + 明示エラー」のほうが復旧可能です。 2-2. 同時実行数上限(semaphore) 1 2 3 4 5 6 CONCURRENCY = 20 semaphore = asyncio.Semaphore(CONCURRENCY) async def guarded_call(fn, *args, **kwargs): async with semaphore: return await fn(*args, **kwargs) CPU でも I/O でも、同時実行数に上限を持たせると遅延の尾が短くなります。 ...

March 1, 2026 · 2 min · AI2CORE 編集部

PostgreSQLインデックス最適化の現場手順:遅いクエリを再現・診断・改善する実践プレイブック

PostgreSQLインデックス最適化の現場手順:遅いクエリを再現・診断・改善する実践プレイブック 「CPUは余っているのに画面が遅い」「特定時間帯だけ API が詰まる」。この手の問題の多くは、アプリではなく SQL の実行計画に原因があります。特に PostgreSQL では、インデックス設計と統計情報の状態が性能をほぼ決めます。 本記事では、実務で使う手順に沿って、遅延クエリの改善を再現可能な形で解説します。単なる理論紹介ではなく、調査順序、判断基準、リリース時の注意点まで含めてまとめます。 まず守るべき3原則 推測でインデックスを作らない 体感で追加すると write 性能とストレージが悪化します。必ず実行計画を見てから判断します。 改善前後を数値で比較する P95、rows、shared read blocks を記録し、効果を証明します。 本番反映は CONCURRENTLY を基本にする テーブルロックで事故らないため、CREATE INDEX CONCURRENTLY を優先します。 ケース設定:注文一覧APIが遅い 次のクエリが遅いとします。 1 2 3 4 5 6 7 SELECT id, user_id, status, total_amount, created_at FROM orders WHERE tenant_id = $1 AND status IN ('paid', 'shipped') AND created_at >= NOW() - INTERVAL '30 days' ORDER BY created_at DESC LIMIT 50; データ量は orders 1.2億件、1テナントあたり数百万件。現象は「特定テナントだけ 3〜6 秒」です。 ...

February 27, 2026 · 2 min · AI2CORE 編集部

Python 3.15の新機能:JITコンパイラ標準搭載へ

Python 3.15の新機能:JITコンパイラ標準搭載へ - 待ち望んだパフォーマンス革命がついに始まる はじめに 「Pythonは書きやすいけど、遅い」。これは、多くのエンジニアが一度は耳にしたことがある、あるいは実感したことがある言葉ではないでしょうか。Webアプリケーション開発からデータサイエンス、機械学習まで、Pythonはその圧倒的な生産性と豊富なエコシステムで世界中の開発者を魅了してきました。しかし、その一方で、パフォーマンスが要求される場面では、C/C++による拡張モジュールの作成や、Cython/Numbaといった特殊なツールの導入、あるいはGoやRustといった他の言語の採用を検討せざるを得ない状況がしばしばありました。 もし、あなたがこれまでに、 計算量の多い処理がボトルネックとなり、ユーザー体験を損なっている パフォーマンス向上のためにPython以外の言語知識を要求され、開発の複雑性が増している 高速化ライブラリを導入したものの、環境構築や互換性の問題に悩まされている といった課題に直面したことがあるなら、この記事はまさにあなたのためにあります。 長年の課題であったパフォーマンス問題に終止符を打つべく、Python開発チームは「Faster CPython」プロジェクトを推進してきました。そして、その集大成とも言える機能が、ついに Python 3.15 に標準搭載される見込みです。それが、JIT (Just-In-Time) コンパイラです。 この記事では、Python 3.15で導入されるJITコンパイラが、なぜPythonの歴史における「革命」とまで言えるのか、その仕組みから具体的な効果、そして我々開発者が享受できるメリットと注意点まで、詳細に解説していきます。Pythonの未来を大きく変えるこの新機能の全貌を、一緒に見ていきましょう。 なぜJITコンパイラが今、重要なのか? - Python高速化の歩み CPython(標準のPython実装)にJITコンパイラが搭載されることの重要性を理解するためには、まずPythonがどのようにコードを実行しているのか、そしてこれまでどのような高速化の試みが行われてきたのかを知る必要があります。 CPythonの実行モデル:インタプリタの長所と短所 私たちが普段書いているPythonコード (.pyファイル) は、そのままではコンピュータが理解できません。CPythonは、以下のステップでコードを実行します。 コンパイル: Pythonのソースコードを、プラットフォームに依存しない中間表現である「バイトコード」に変換します。この結果は .pyc ファイルとしてキャッシュされることがあります。 実行: Python仮想マシン (PVM) と呼ばれるプログラムが、このバイトコードを一行ずつ解釈し、対応するC言語の関数を実行していきます。 +------------------+ (1) コンパイル +-----------------+ (2) 実行 +----------------+ | ソースコード | -------------------> | バイトコード | -------------> | Python仮想マシン | | (hello.py) | | (hello.pyc) | | (PVM) | +------------------+ +-----------------+ +----------------+ | | 実行 V [ 結果 ] この「インタプリタ方式」は、動的型付け(変数の型を実行時に決定する)といったPythonの柔軟性を支える重要な仕組みです。しかし、これがパフォーマンスのボトルネックにもなっています。PVMはバイトコードを実行するたびに、変数の型をチェックし、どの処理を呼び出すかを判断する必要があります。この間接的な処理が、C++やRustのような事前に全てのコードを機械語にコンパイル(AOT: Ahead-Of-Timeコンパイル)する言語に比べて、大きなオーバーヘッドとなるのです。 ...

February 19, 2026 · 3 min · AI2CORE 編集部