소스 정보
- 저장소
- tools-only/X-Skills
- 최근 소스 활동
- 2026년 2월 9일 04:08
- 감지된 SKILL.md 언어
- 영어
- 스타
- 7
- 포크
- 1
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/tools-only/X-Skills --skill index-advisor명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Index of Build Systems Skills
Coordination patterns for distributed dataflow systems including barriers, epochs, and distributed snapshots
Windowing, sessionization, time-series aggregation, and late data handling for streaming systems
SOC 직업 분류 기준
SKILL.md 표시 중
| name | index-advisor |
| description | Analyze query patterns and recommend optimal database indexes |
| shortcut | inde |
Analyze query workloads, identify missing indexes, detect unused indexes, and recommend optimal indexing strategies with automated index impact analysis and maintenance scheduling for production databases.
Use /index-advisor when you need to:
DON'T use this when:
This command implements workload-based index analysis because:
Alternative considered: Static schema analysis
Alternative considered: Manual EXPLAIN analysis
Before running this command:
Capture real production queries from logs or pg_stat_statements.
Run EXPLAIN ANALYZE to identify sequential scans and suboptimal query plans.
Identify missing indexes, composite index opportunities, and covering indexes.
Estimate query performance improvements with hypothetical indexes.
Create recommended indexes and track query performance improvements.
The command generates:
analysis/missing_indexes.sql - CREATE INDEX statements for missing indexesanalysis/unused_indexes.sql - DROP INDEX statements for unused indexesreports/index_impact_report.html - Visual impact analysis with before/after metricsmonitoring/index_health.sql - Queries to monitor index bloat and usagemaintenance/reindex_schedule.sh - Automated index maintenance script-- Enable pg_stat_statements extension for query tracking
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
-- Configure extended statistics
ALTER SYSTEM SET pg_stat_statements.track = 'all';
ALTER SYSTEM SET pg_stat_statements.max = 10000;
SELECT pg_reload_conf();
-- View most expensive queries without proper indexes
CREATE OR REPLACE VIEW slow_queries_needing_indexes AS
SELECT
queryid,
LEFT(query, 100) AS query_snippet,
calls,
total_exec_time,
mean_exec_time,
max_exec_time,
stddev_exec_time,
ROUND((100.0 * total_exec_time / SUM(total_exec_time) OVER ()), 2) AS pct_total_time
FROM pg_stat_statements
WHERE query NOT LIKE '%pg_stat%'
AND mean_exec_time > 100 -- Queries averaging >100ms
ORDER BY total_exec_time DESC
LIMIT 50;
-- Identify missing indexes by analyzing sequential scans
CREATE OR REPLACE tables_needing_indexes
schemaname,
tablename,
seq_scan sequential_scans,
seq_tup_read rows_read_sequentially,
idx_scan index_scans,
idx_tup_fetch rows_fetched_via_index,
pg_size_pretty(pg_relation_size(schemanametablename)) table_size,
seq_scan ROUND( seq_scan (seq_scan (idx_scan, )), )
pct_sequential_scans
pg_stat_user_tables
seq_scan
seq_tup_read
seq_tup_read ;
REPLACE unused_indexes
schemaname,
tablename,
indexname,
idx_scan index_scans,
idx_tup_read tuples_read,
idx_tup_fetch tuples_fetched,
pg_size_pretty(pg_relation_size(indexrelid)) index_size,
pg_get_indexdef(indexrelid) index_definition
pg_stat_user_indexes
idx_scan
indexname
indexname
pg_relation_size(indexrelid) ;
REPLACE index_bloat_analysis
schemaname,
tablename,
indexname,
pg_size_pretty(pg_relation_size(indexrelid)) index_size,
pg_size_pretty(pg_relation_size(tablename::regclass)) table_size,
ROUND( pg_relation_size(indexrelid) (pg_relation_size(tablename::regclass), ), ) index_to_table_ratio,
pg_relation_size(indexrelid) pg_relation_size(tablename::regclass)
pg_relation_size(indexrelid) pg_relation_size(tablename::regclass)
bloat_status
pg_stat_user_indexes
pg_relation_size(indexrelid)
pg_relation_size(indexrelid) ;
# scripts/index_advisor.py - Comprehensive Index Analysis Tool
import psycopg2
from psycopg2.extras import DictCursor
import re
import logging
from typing import List, Dict, Tuple, Optional
from dataclasses import dataclass, asdict
from collections import defaultdict
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@dataclass
class IndexRecommendation:
"""Represents an index recommendation with impact analysis."""
table_name: str
recommended_index: str
reason: str
affected_queries: List[str]
estimated_speedup: str
storage_cost_mb: float
priority: str # 'high', 'medium', 'low'
def to_dict(self) -> dict:
return asdict(self)
class PostgreSQLIndexAdvisor:
"""Analyze queries and recommend optimal indexes."""
def __init__(self, connection_string: str):
self.conn_string = connection_string
def connect(self):
psycopg2.connect(.conn_string, cursor_factory=DictCursor)
() -> []:
conn = .connect()
:
conn.cursor() cur:
cur.execute(, (min_duration_ms,))
[(row) row cur.fetchall()]
:
conn.close()
() -> [[, ]]:
columns = []
where_pattern =
matches = re.finditer(where_pattern, query, re.IGNORECASE)
matches:
table = .group()
column = .group()
columns.append((table, column))
join_pattern =
matches = re.finditer(join_pattern, query, re.IGNORECASE)
matches:
table = .group()
column = .group()
columns.append((table, column))
columns
() -> :
conn = .connect()
:
conn.cursor() cur:
cur.execute(, (table, ))
result = cur.fetchone()
result[] result
:
conn.close()
() -> [IndexRecommendation]:
recommendations = []
slow_queries = .analyze_slow_queries()
logger.info()
column_usage = defaultdict(: {: , : [], : })
query_info slow_queries:
query = query_info[]
total_time = (query_info[])
columns = .extract_where_columns(query)
table, column columns:
.check_existing_indexes(table, column):
key =
column_usage[key][] +=
column_usage[key][].append(query[:])
column_usage[key][] += total_time
key, usage column_usage.items():
table, column = key.split()
usage[] > :
priority =
speedup =
usage[] > :
priority =
speedup =
:
priority =
speedup =
storage_cost = .estimate_index_size(table)
recommendation = IndexRecommendation(
table_name=table,
recommended_index=,
reason=,
affected_queries=usage[][:],
estimated_speedup=speedup,
storage_cost_mb=storage_cost,
priority=priority
)
recommendations.append(recommendation)
recommendations.sort(
key= r: (
{: , : , : }[r.priority],
-( _ r.affected_queries)
)
)
recommendations
() -> :
conn = .connect()
:
conn.cursor() cur:
cur.execute(, (table, table))
result = cur.fetchone()
result:
(result[] * , )
Exception e:
logger.warning()
:
conn.close()
() -> []:
conn = .connect()
:
conn.cursor() cur:
cur.execute()
[(row) row cur.fetchall()]
:
conn.close()
():
logger.info()
recommendations = .generate_recommendations()
recommendations:
logger.info()
i, rec (recommendations, ):
logger.info()
logger.info()
logger.info()
logger.info()
logger.info()
logger.info()
logger.info()
unused = .find_unused_indexes()
unused:
logger.info()
idx unused:
logger.info()
logger.info()
logger.info()
logger.info()
logger.info()
logger.info()
() -> :
size_str:
(size_str.replace(, ).replace(, )) *
size_str:
(size_str.replace(, ).replace(, ))
size_str:
(size_str.replace(, ).replace(, )) /
__name__ == :
advisor = PostgreSQLIndexAdvisor(
)
advisor.generate_report()
-- Enable performance schema for query analysis
UPDATE performance_schema.setup_instruments
SET ENABLED = 'YES', TIMED = 'YES'
WHERE NAME LIKE 'statement/%';
UPDATE performance_schema.setup_consumers
SET ENABLED = 'YES'
WHERE NAME LIKE '%statements%';
-- Identify slow queries needing indexes
CREATE OR REPLACE VIEW slow_queries_analysis AS
SELECT
DIGEST_TEXT AS query,
COUNT_STAR AS executions,
ROUND(AVG_TIMER_WAIT / 1000000000, 2) AS avg_time_ms,
ROUND(MAX_TIMER_WAIT / 1000000000, 2) AS max_time_ms,
ROUND(SUM_TIMER_WAIT / 1000000000, 2) AS total_time_ms,
SUM_ROWS_EXAMINED AS total_rows_examined,
SUM_ROWS_SENT AS total_rows_sent,
ROUND(SUM_ROWS_EXAMINED / COUNT_STAR, 0) AS avg_rows_examined
FROM performance_schema.events_statements_summary_by_digest
WHERE DIGEST_TEXT IS NOT NULL
AND SCHEMA_NAME NOT IN (, , )
AVG_TIMER_WAIT
SUM_TIMER_WAIT
LIMIT ;
object_schema database_name,
object_name table_name,
count_read select_count,
count_fetch rows_fetched,
ROUND(count_fetch (count_read, ), ) avg_rows_per_select
performance_schema.table_io_waits_summary_by_table
object_schema (, , )
count_read
count_fetch ;
t1.TABLE_SCHEMA database_name,
t1.TABLE_NAME table_name,
t1.INDEX_NAME index1,
t2.INDEX_NAME index2,
GROUP_CONCAT(t1.COLUMN_NAME t1.SEQ_IN_INDEX) columns
information_schema.STATISTICS t1
information_schema.STATISTICS t2
t1.TABLE_SCHEMA t2.TABLE_SCHEMA
t1.TABLE_NAME t2.TABLE_NAME
t1.INDEX_NAME t2.INDEX_NAME
t1.COLUMN_NAME t2.COLUMN_NAME
t1.SEQ_IN_INDEX t2.SEQ_IN_INDEX
t1.TABLE_SCHEMA (, , )
t1.TABLE_SCHEMA, t1.TABLE_NAME, t1.INDEX_NAME, t2.INDEX_NAME
() (
()
information_schema.STATISTICS
TABLE_SCHEMA t1.TABLE_SCHEMA
TABLE_NAME t1.TABLE_NAME
INDEX_NAME t1.INDEX_NAME
);
// scripts/mysql-index-advisor.js
const mysql = require('mysql2/promise');
class MySQLIndexAdvisor {
constructor(config) {
this.pool = mysql.createPool({
host: config.host,
user: config.user,
password: config.password,
database: config.database,
waitForConnections: true,
connectionLimit: 10
});
}
async analyzeTableIndexes(tableName) {
const [rows] = await this.pool.query(`
SELECT
COLUMN_NAME,
CARDINALITY,
INDEX_NAME,
SEQ_IN_INDEX,
NON_UNIQUE
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = ?
ORDER BY INDEX_NAME, SEQ_IN_INDEX
`, [tableName]);
return rows;
}
async findMissingIndexes() {
// Analyze slow queries from performance schema
const [slowQueries] = await this.pool.query(`
SELECT
DIGEST_TEXT AS query,
COUNT_STAR AS executions,
ROUND(AVG_TIMER_WAIT / 1000000000, 2) AS avg_time_ms,
SUM_ROWS_EXAMINED AS total_rows_examined
FROM performance_schema.events_statements_summary_by_digest
WHERE AVG_TIMER_WAIT > 100000000
AND SCHEMA_NAME = DATABASE()
ORDER BY AVG_TIMER_WAIT DESC
LIMIT 20
`);
recommendations = [];
( query slowQueries) {
whereMatch = query..();
(whereMatch) {
column = whereMatch[];
recommendations.({
: ,
: column,
: query..(, ),
: query.,
:
});
}
}
recommendations;
}
() {
.();
recommendations = .();
.();
recommendations.( {
.();
.();
.();
.();
});
}
}
( () => {
advisor = ({
: ,
: ,
: ,
:
});
advisor.();
})();
| Error | Cause | Solution |
|---|---|---|
| "Index too large" | Index exceeds max key length | Use partial index or hash index for long columns |
| "Duplicate key violation" | Creating unique index on non-unique data | Check for duplicates before creating unique index |
| "Out of disk space" | Index creation requires temporary storage | Free up disk space or use CONCURRENTLY option |
| "Lock timeout" | Index creation blocking queries | Use CREATE INDEX CONCURRENTLY (PostgreSQL) or ALGORITHM=INPLACE (MySQL) |
| "Statistics out of date" | Old cardinality estimates | Run ANALYZE (PostgreSQL) or ANALYZE TABLE (MySQL) |
Index Types
Index Options
CONCURRENTLY: Create without blocking writes (PostgreSQL)ALGORITHM=INPLACE: Online index creation (MySQL)INCLUDE columns: Covering index (PostgreSQL 11+)WHERE clause: Partial index for filtered queriesDO:
DON'T:
/sql-query-optimizer - Rewrite queries for better performance/database-partition-manager - Partition large tables for faster queries/database-health-monitor - Monitor index bloat and maintenance needs/database-backup-automator - Schedule REINDEX during maintenance windows