jpskill.com
🛠️ 開発・MCP コミュニティ

api-performance-api-performance

データベースの検索効率を上げ、データを一時的に保存して高速化、必要なデータだけを抽出、接続を効率的に管理、処理を並行して行うことで、システム全体の性能を向上させるSkill。

📜 元の英語説明(参考)

Query optimization, caching, indexing, connection pooling, async patterns

🇯🇵 日本人クリエイター向け解説

一言でいうと

データベースの検索効率を上げ、データを一時的に保存して高速化、必要なデータだけを抽出、接続を効率的に管理、処理を並行して行うことで、システム全体の性能を向上させるSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o api-performance-api-performance.zip https://jpskill.com/download/10246.zip && unzip -o api-performance-api-performance.zip && rm api-performance-api-performance.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/10246.zip -OutFile "$d\api-performance-api-performance.zip"; Expand-Archive "$d\api-performance-api-performance.zip" -DestinationPath $d -Force; ri "$d\api-performance-api-performance.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して api-performance-api-performance.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → api-performance-api-performance フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

バックエンドのパフォーマンス最適化

クイックガイド: データベースクエリの最適化(インデックス、プリペアドステートメント、N+1の回避)、キャッシング戦略(cache-aside、write-through)、コネクションプーリング、およびノンブロッキングな非同期パターンを通じて、バックエンドのパフォーマンスを最適化します。最適化を行う前に必ず測定してください。複雑さを増す前に、EXPLAIN ANALYZEを実行し、イベントループの遅延を確認し、キャッシュヒット率を追跡してください。


<critical_requirements>

重要: このSkillを使用する前に

すべてのコードは、CLAUDE.mdのプロジェクト規約に従う必要があります (kebab-case、名前付きエクスポート、インポート順序、import type、名前付き定数)

(データベース接続は、finallyブロックを使用して必ずプールに戻す必要があります)

(N+1クエリを防ぐために、必ず事前ロードまたはバッチ処理(DataLoader)を使用する必要があります。ループ内で遅延ロードしないでください)

(古いデータやメモリ枯渇を防ぐために、キャッシュされたすべてのデータにTTLを設定する必要があります)

(CPU負荷の高い作業はWorker Threadsにオフロードする必要があります。イベントループをブロックすると、すべてのリクエストのパフォーマンスが低下します)

</critical_requirements>


詳細なリソース:

  • examples/core.md - データベースパターン: コネクションプーリング、N+1防止、インデックス作成、プリペアドステートメント、ページネーション
  • examples/caching.md - Cache-aside、write-through、無効化、キーストラテジー、TTLガイダンス
  • examples/async.md - イベントループの最適化、worker threads、チャンク処理、並行性制御
  • reference.md - 意思決定フレームワーク、パフォーマンスモニタリング

自動検出: コネクションプール、クエリ最適化、データベースインデックス、N+1、キャッシング、キャッシュ無効化、プリペアドステートメント、worker threads、イベントループ、CPU-bound、レイテンシ、スループット、パフォーマンスチューニング、EXPLAIN ANALYZE、keyset pagination、cache-aside、write-through

使用するタイミング:

  • データベースクエリの実行時間が100msを超える場合
  • 繰り返しデータフェッチが発生する高トラフィックのエンドポイント
  • 複数の関連エンティティを持つAPIレスポンス(N+1のリスク)
  • リクエスト処理をブロックするCPU負荷の高い操作
  • キャッシングによるデータベース負荷の軽減が必要な場合

使用しないタイミング:

  • 最初に測定せずに早まって最適化する場合
  • トラフィックの少ない単純なCRUD(メリットなしに複雑さが増す)
  • 頻繁に変更され、常に最新である必要のあるデータ(キャッシングによりデータが古くなる)
  • 開発/デバッグ(キャッシングにより問題が隠蔽される)

カバーされる主要なパターン:

  • データベースインデックス戦略(複合、部分、カバリング)
  • 保証された解放によるコネクションプーリング
  • N+1クエリの防止(事前ロード、DataLoader)
  • キャッシング戦略(cache-aside、write-through、無効化)
  • イベントループの最適化(非同期I/O、setImmediateチャンク処理)
  • CPU負荷の高い操作のためのworker threads
  • 大規模データセットのためのkeyset pagination

<philosophy>

Philosophy

バックエンドのパフォーマンス最適化は、1つのコア原則に従います。最初に測定し、次に最適化する。早まった最適化は開発時間を浪費し、メリットの証拠なしに複雑さを増します。

バックエンドパフォーマンスの3つの柱:

  1. データベースの最適化 - インデックス、クエリプランニング、N+1の防止、ページネーション
  2. キャッシング - TTLで制限されたキャッシュを使用して、繰り返されるコストのかかる操作を削減します
  3. 非同期効率 - イベントループを絶対にブロックしないでください

最適化するタイミング:

  • レスポンス時間がSLAの閾値を超える場合
  • データベースのCPU/メモリが制限に近づいている場合
  • メトリクスが特定のボトルネックを示している場合(EXPLAIN ANALYZE、イベントループの遅延)
  • ロードテストでスケーリングの問題が明らかになった場合

最適化しないタイミング:

  • 「いつか遅くなるかもしれない」(時期尚早)
  • コールドパス(めったに実行されないコード)の最適化
  • プロファイリングで実際のボトルネックが特定される前

</philosophy>


<patterns>

コアパターン

パターン1: 保証された解放によるコネクションプーリング

コネクションプーリングは、リクエストごとに新しい接続を作成する代わりに、データベース接続を再利用します。PostgreSQLのハンドシェイクには20〜30msかかります。プーリングはこのオーバーヘッドを排除します。

重要なルール:

  • 単純なクエリにはpool.query()を使用します(接続ライフサイクルを自動的に管理します)
  • トランザクションの場合は、pool.connect()で手動でチェックアウトし、常にfinallyで解放します
  • プールエラーをリッスンします(アイドル状態のクライアントでもエラーが発生する可能性があります)
// 保証された接続解放によるトランザクション
async function createUserWithProfile(
  userData: UserData,
  profileData: ProfileData,
) {
  const client = await pool.connect();
  try {
    await client.query("BEGIN");
    const userResult = await client.query(
      "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id",
      [userData.name, userData.email],
    );
    await client.query("INSERT INTO profiles (user_id, bio) VALUES ($1, $2)", [
      userResult.rows[0].id,
      profileData.bio,
    ]);
    await client.query("COMMIT");
    return userResult.rows[0];
  } catch (error) {
    await client.query("ROLLBACK");
    throw error;
  } finally {
    client.release(); // 重要: 必ずプールに戻してください
  }
}

良い理由: finallyは、エラーが発生した場合でも接続の解放を保証し、プールの枯渇を防ぎます

完全なプール構成、サイジング式、および外部プーラーのガイダンスについては、examples/core.mdを参照してください。


パターン2: N+1クエリの防止

N+1問題は、N個のレコードをフェッチすると、関連データに対してN個の追加クエリがトリガーされる場合に発生します。100個のレコードの場合、これは101回のデータベースラウンドトリップになります。

2つの解決策:

  1. 事前ロード (ORM .with()) -- 既知の関係に対するJOINを使用した単一のクエリ
  2. DataLoader -- .load()呼び出しをティックごとに単一のクエリにバッチ処理します。GraphQLに最適です
// 事前ロード: 単一のクエリでjobs + companies + skillsをフェッチします
const jobs = await db.query.jobs.findMany({
  where: and(eq(jobs.isActive, true), isNull(jobs.deletedAt)),
  with: {
    company: { with: { locations: true } },
    jobSkills: { with: { skill: true } },
  },
});
// 悪い例: N+1アンチパターン -- ジョブごとに1つのクエリ
for (const job of jobs) {
  job.company = await db.query.companies.findFirst({
    where: eq(companies.id, job.companyId),
  });
}

悪い理由: jobsに対して1つのクエリ + N個のクエリ

(原文がここで切り詰められています)

📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

Backend Performance Optimization

Quick Guide: Optimize backend performance through database query optimization (indexes, prepared statements, avoiding N+1), caching strategies (cache-aside, write-through), connection pooling, and non-blocking async patterns. Always measure before optimizing -- run EXPLAIN ANALYZE, check event loop lag, and track cache hit rates before adding complexity.


<critical_requirements>

CRITICAL: Before Using This Skill

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)

(You MUST always release database connections back to the pool using finally blocks)

(You MUST use eager loading or batching (DataLoader) to prevent N+1 queries -- never lazy load in loops)

(You MUST set TTL on all cached data to prevent stale data and memory exhaustion)

(You MUST offload CPU-intensive work to Worker Threads -- blocking the event loop degrades all requests)

</critical_requirements>


Detailed Resources:

  • examples/core.md - Database patterns: connection pooling, N+1 prevention, indexing, prepared statements, pagination
  • examples/caching.md - Cache-aside, write-through, invalidation, key strategies, TTL guidance
  • examples/async.md - Event loop optimization, worker threads, chunked processing, concurrency control
  • reference.md - Decision frameworks, performance monitoring

Auto-detection: connection pool, query optimization, database index, N+1, caching, cache invalidation, prepared statement, worker threads, event loop, CPU-bound, latency, throughput, performance tuning, EXPLAIN ANALYZE, keyset pagination, cache-aside, write-through

When to use:

  • Database queries taking > 100ms
  • High-traffic endpoints with repeated data fetches
  • API responses with multiple related entities (N+1 risk)
  • CPU-intensive operations blocking request handling
  • Need to reduce database load via caching

When NOT to use:

  • Premature optimization without measuring first
  • Simple CRUD with low traffic (adds complexity without benefit)
  • Data that changes frequently and must always be fresh (caching adds staleness)
  • Development/debugging (caching obscures issues)

Key patterns covered:

  • Database indexing strategies (composite, partial, covering)
  • Connection pooling with guaranteed release
  • N+1 query prevention (eager loading, DataLoader)
  • Caching strategies (cache-aside, write-through, invalidation)
  • Event loop optimization (async I/O, setImmediate chunking)
  • Worker threads for CPU-bound operations
  • Keyset pagination for large datasets

<philosophy>

Philosophy

Backend performance optimization follows one core principle: measure first, optimize second. Premature optimization wastes development time and adds complexity without evidence of benefit.

The Three Pillars of Backend Performance:

  1. Database Optimization - Indexes, query planning, N+1 prevention, pagination
  2. Caching - Reduce repeated expensive operations with TTL-bounded cache
  3. Async Efficiency - Never block the event loop

When to optimize:

  • Response times exceed SLA thresholds
  • Database CPU/memory approaching limits
  • Metrics show specific bottlenecks (EXPLAIN ANALYZE, event loop lag)
  • Load testing reveals scaling issues

When NOT to optimize:

  • "It might be slow someday" (premature)
  • Optimizing cold paths (rarely executed code)
  • Before profiling identifies the actual bottleneck

</philosophy>


<patterns>

Core Patterns

Pattern 1: Connection Pooling with Guaranteed Release

Connection pooling reuses database connections instead of creating new ones per request. A PostgreSQL handshake takes 20-30ms -- pooling eliminates this overhead.

Key rules:

  • Use pool.query() for simple queries (auto-manages connection lifecycle)
  • For transactions, manually checkout with pool.connect() and always release in finally
  • Listen for pool errors (idle clients can still emit errors)
// Transaction with guaranteed connection release
async function createUserWithProfile(
  userData: UserData,
  profileData: ProfileData,
) {
  const client = await pool.connect();
  try {
    await client.query("BEGIN");
    const userResult = await client.query(
      "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id",
      [userData.name, userData.email],
    );
    await client.query("INSERT INTO profiles (user_id, bio) VALUES ($1, $2)", [
      userResult.rows[0].id,
      profileData.bio,
    ]);
    await client.query("COMMIT");
    return userResult.rows[0];
  } catch (error) {
    await client.query("ROLLBACK");
    throw error;
  } finally {
    client.release(); // CRITICAL: Always release back to pool
  }
}

Why good: finally guarantees connection release even on error, preventing pool exhaustion

See examples/core.md for full pool configuration, sizing formula, and external pooler guidance.


Pattern 2: N+1 Query Prevention

The N+1 problem occurs when fetching N records triggers N additional queries for related data. With 100 records, that's 101 database round-trips.

Two solutions:

  1. Eager loading (ORM .with()) -- single query with JOINs for known relationships
  2. DataLoader -- batches .load() calls into single query per tick, ideal for GraphQL
// Eager loading: single query fetches jobs + companies + skills
const jobs = await db.query.jobs.findMany({
  where: and(eq(jobs.isActive, true), isNull(jobs.deletedAt)),
  with: {
    company: { with: { locations: true } },
    jobSkills: { with: { skill: true } },
  },
});
// BAD: N+1 anti-pattern -- one query per job
for (const job of jobs) {
  job.company = await db.query.companies.findFirst({
    where: eq(companies.id, job.companyId),
  });
}

Why bad: 1 query for jobs + N queries for companies, latency grows linearly with data size

See examples/core.md for DataLoader batching pattern.


Pattern 3: Database Indexing

Indexes speed up queries by avoiding full table scans. Index columns used in WHERE, JOIN, and ORDER BY clauses.

// Strategic indexes on a table definition
export const jobs = pgTable(
  "jobs",
  {
    id: uuid("id").primaryKey().defaultRandom(),
    companyId: uuid("company_id").notNull(),
    country: varchar("country", { length: 100 }),
    employmentType: varchar("employment_type", { length: 50 }),
    isActive: boolean("is_active").default(true),
    createdAt: timestamp("created_at").defaultNow(),
    deletedAt: timestamp("deleted_at"),
  },
  (table) => [
    // Composite index for common filter combination
    index("jobs_country_employment_idx").on(
      table.country,
      table.employmentType,
    ),
    // Partial index -- only indexes active non-deleted jobs
    index("jobs_active_idx")
      .on(table.isActive, table.createdAt)
      .where(sql`${table.deletedAt} IS NULL`),
    // Foreign key index for JOIN performance
    index("jobs_company_id_idx").on(table.companyId),
  ],
);

Index Decision Framework:

Column Usage Index Type When to Use
WHERE equality B-tree (default) High-selectivity columns
WHERE range (>, <, BETWEEN) B-tree Date ranges, numeric ranges
WHERE multiple columns Composite Queries always filter by same columns together
WHERE on subset Partial Most queries filter on active/non-deleted
Full-text search GIN/GiST Text search with LIKE, tsvector
JSON field access GIN JSONB column queries

Composite index column order: Equality conditions first, range conditions last, high selectivity first.

See examples/core.md for EXPLAIN ANALYZE examples, index monitoring queries, and unused index detection.


Pattern 4: Cache-Aside with TTL

The most common caching pattern. Check cache first, fetch from database on miss, store with TTL.

const CACHE_TTL_SECONDS = 300;
const CACHE_PREFIX = "app:user";

async function getUserById(userId: string): Promise<User | null> {
  const cacheKey = `${CACHE_PREFIX}:${userId}`;
  const cached = await cacheClient.get(cacheKey);
  if (cached) return JSON.parse(cached) as User;

  const user = await db.query.users.findFirst({ where: eq(users.id, userId) });
  if (!user) return null;

  await cacheClient.set(cacheKey, JSON.stringify(user), {
    EX: CACHE_TTL_SECONDS,
  });
  return user;
}

Why good: TTL prevents stale data accumulation, namespaced keys prevent collisions, early return on cache hit

See examples/caching.md for write-through, tag-based invalidation, key strategies, and TTL guidance.


Pattern 5: Worker Threads for CPU-Bound Operations

Node.js uses a single thread for JavaScript. CPU-intensive work blocks ALL concurrent requests.

Rule of thumb:

CPU Duration Solution Rationale
< 50ms Keep on main thread Worker overhead not worth it
50-500ms setImmediate chunking Yields to event loop between chunks
> 500ms Worker Threads Offload completely to separate thread
// Chunked processing with setImmediate -- yields to event loop between batches
const CHUNK_SIZE = 100;

async function processLargeArray(items: Item[]): Promise<ProcessedItem[]> {
  const results: ProcessedItem[] = [];
  for (let i = 0; i < items.length; i += CHUNK_SIZE) {
    const chunk = items.slice(i, i + CHUNK_SIZE);
    for (const item of chunk) {
      results.push(expensiveTransform(item));
    }
    if (i + CHUNK_SIZE < items.length) {
      await new Promise((resolve) => setImmediate(resolve));
    }
  }
  return results;
}

See examples/async.md for worker pool implementation, concurrency control with p-limit, and event loop lag monitoring.


Pattern 6: Keyset Pagination for Large Datasets

OFFSET pagination scans all previous rows -- at OFFSET 100,000 the database reads and discards 100,000 rows. Keyset pagination uses a cursor for constant-time performance.

-- BAD: OFFSET scans all previous rows
SELECT * FROM products ORDER BY id LIMIT 20 OFFSET 100000;

-- GOOD: Keyset pagination -- constant time regardless of position
SELECT * FROM products
WHERE id > :last_seen_id
ORDER BY id LIMIT 20;

When to use offset: Small datasets (< 100k rows), need total count, random page access required.

When to use keyset: Large datasets, infinite scroll, real-time data where inserts shouldn't cause duplicates.

See examples/core.md for full TypeScript implementations of both patterns.

</patterns>


<red_flags>

RED FLAGS

High Priority Issues:

  • Missing connection release -- connections never returned to pool cause pool exhaustion and application hangs
  • N+1 queries in loops -- fetching related data one-by-one instead of eager loading destroys performance
  • Blocking event loop -- synchronous I/O or CPU-intensive work blocks all concurrent requests
  • Cache without TTL -- unbounded cache grows until memory exhaustion or serves infinitely stale data
  • Full table scans on large tables -- missing indexes on WHERE/JOIN columns

Medium Priority Issues:

  • No index on foreign keys -- JOINs and ON DELETE CASCADE become slow
  • Over-indexing -- every index slows writes; remove unused indexes with pg_stat_user_indexes
  • Cache key collisions -- generic keys cause wrong data returned to wrong users
  • Offset pagination on large tables -- OFFSET scans all previous rows; use keyset pagination
  • Unbounded parallelism -- Promise.all on 10,000 items overwhelms downstream services

Gotchas & Edge Cases:

  • EXPLAIN shows estimates; EXPLAIN ANALYZE shows actual -- always use ANALYZE for real performance data
  • Composite index (a, b) does NOT help queries filtering only on b -- column order matters
  • Applying functions to indexed columns (e.g., YEAR(created_at)) prevents index use -- rewrite as range conditions
  • DataLoader caches within request -- don't reuse across requests or you get stale data
  • Worker threads have ~30ms startup overhead -- don't use for fast operations
  • Connection pool idleTimeoutMillis can cause "connection terminated unexpectedly" if set too short
  • SELECT * fetches unnecessary data and prevents covering index optimization -- select specific columns
  • Cache SET with EX option replaces existing TTL -- calling SET again resets expiration timer

</red_flags>


<critical_reminders>

CRITICAL REMINDERS

All code must follow project conventions in CLAUDE.md

(You MUST always release database connections back to the pool using finally blocks)

(You MUST use eager loading or batching (DataLoader) to prevent N+1 queries -- never lazy load in loops)

(You MUST set TTL on all cached data to prevent stale data and memory exhaustion)

(You MUST offload CPU-intensive work to Worker Threads -- blocking the event loop degrades all requests)

Failure to follow these rules will cause connection pool exhaustion, N+1 performance degradation, memory leaks from unbounded caches, and blocked event loops affecting all concurrent requests.

</critical_reminders>