with one click
database
查询优化、索引策略和数据库性能调优,覆盖 PostgreSQL 和 MySQL
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
查询优化、索引策略和数据库性能调优,覆盖 PostgreSQL 和 MySQL
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
单文件代码审查,6个维度逐项检查。覆盖正确性、安全性、性能、可维护性、测试、无障碍
CI/CD 流水线和容器化部署,覆盖 GitHub Actions、GitLab CI、Docker、测试策略和部署自动化
微服务设计模式,涵盖服务网格、事件驱动架构、Saga 模式和 API 网关
Web 性能优化,涵盖包分析、懒加载、缓存策略和 Core Web Vitals
TypeScript 进阶模式,涵盖泛型、条件类型、映射类型、模板字面量和类型守卫
UI 原型产出技能,根据选定风格生成静态 HTML 原型,作为 Builder 的设计契约
| name | database |
| description | 查询优化、索引策略和数据库性能调优,覆盖 PostgreSQL 和 MySQL |
Always run EXPLAIN ANALYZE before optimizing. Read the output bottom-up.
-- PostgreSQL
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT) SELECT ...;
-- MySQL
EXPLAIN ANALYZE SELECT ...;
Key metrics to watch:
ANALYZECREATE INDEX idx_users_email ON users (email);
CREATE INDEX idx_orders_user_date ON orders (user_id, created_at DESC);
Use for: equality, range queries, sorting. Column order matters in composite indexes: put equality columns first, then range/sort columns.
CREATE INDEX idx_orders_pending ON orders (created_at)
WHERE status = 'pending';
Use when queries always filter on a specific condition. Dramatically smaller than full indexes.
CREATE INDEX idx_products_tags ON products USING GIN (tags);
CREATE INDEX idx_docs_search ON documents USING GIN (to_tsvector('english', content));
CREATE INDEX idx_locations_point ON locations USING GiST (coordinates);
CREATE INDEX idx_events_period ON events USING GiST (tsrange(start_at, end_at));
-- PostgreSQL
CREATE INDEX idx_users_email_name ON users (email) INCLUDE (name);
-- MySQL
CREATE INDEX idx_users_email_name ON users (email, name);
Symptom: 1 query to fetch parent + N queries for each child.
// BAD: N+1
const users = await db.select().from(users);
for (const user of users) {
const orders = await db.select().from(orders).where(eq(orders.userId, user.id));
}
// GOOD: eager load (Prisma)
const users = await prisma.user.findMany({ include: { orders: true } });
// GOOD: join query (Drizzle)
const users = await db.query.users.findMany({
with: { orders: true },
});
Detection: enable query logging, count queries per request. More than 10 queries for a single endpoint is a red flag.
Rule of thumb: pool_size = (core_count * 2) + disk_count
Typical web app: 10-20 connections per app instance
PostgreSQL:
idle_in_transaction_session_timeout = '30s'pg_stat_activityMySQL:
max_connections based on available RAM (each connection uses ~10MB)SHOW PROCESSLISTSELECT queries to replicas// Read replica routing (Kysely)
function getDatabase(write: boolean) {
return write ? primaryDb : replicaDb;
}
// Usage
const users = await getDatabase(false).selectFrom('users').selectAll().execute();
await getDatabase(true).insertInto('users').values({ name: 'Alice' }).execute();
-- PostgreSQL
CREATE TABLE events (
id bigint GENERATED ALWAYS AS IDENTITY,
created_at timestamptz NOT NULL,
data jsonb
) PARTITION BY RANGE (created_at);
CREATE TABLE events_2025_q1 PARTITION OF events
FOR VALUES FROM ('2025-01-01') TO ('2025-04-01');
CREATE TABLE events_2025_q2 PARTITION OF events
FOR VALUES FROM ('2025-04-01') TO ('2025-07-01');
CREATE TABLE sessions (
id uuid PRIMARY KEY,
user_id bigint NOT NULL
) PARTITION BY HASH (user_id);
CREATE TABLE sessions_0 PARTITION OF sessions FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE sessions_1 PARTITION OF sessions FOR VALUES WITH (MODULUS 4, REMAINDER 1);
Partition when tables exceed 50-100GB or when you need to drop old data quickly.
EXPLAIN ANALYZE and read the planidx_scan in pg_stat_user_indexes)SELECT * with specific columnsLIMIT to queries that only need a subsetEXISTS instead of COUNT(*) > 0INSERT/UPDATE operations (500-1000 rows per batch)WHERE clauseslog_min_duration_statement = 100)LIKE '%term%' on unindexed columns (use full-text search instead)ORDER BY RANDOM() (use TABLESAMPLE or application-level randomization)SELECT DISTINCT masking a join problemWHERE on UPDATE/DELETE (always verify with SELECT first)OFFSET for deep pagination (use keyset/cursor pagination instead)