SuperAPI AI 工具集

database-optimization

数据与分析

查看项目

更新于 10-15 09:22

技能介绍

用途

该技能面向 PostgreSQL 与 MySQL 的数据库性能优化,提供查询优化、索引策略与性能调优的实践指导。

主要能力

EXPLAIN 分析

优化前始终运行 EXPLAIN ANALYZE,并从下往上阅读输出。

PostgreSQL:

EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT) SELECT ...;

MySQL:

EXPLAIN ANALYZE SELECT ...;

需关注的关键指标:

  • 大表上的 Seq Scan 表示缺少索引
  • 高行数的 Nested Loop 可考虑 hash/merge join
  • 无索引的 Sort 应在排序列上添加索引
  • 预估行数与实际行数偏差表示统计信息过期,运行 ANALYZE

索引策略

B-tree(默认,适用于多数场景):

CREATE INDEX idx_users_email ON users (email);
CREATE INDEX idx_orders_user_date ON orders (user_id, created_at DESC);

适用于等值、范围查询与排序。复合索引中列顺序很重要:先放等值列,再放范围/排序列。

部分索引(PostgreSQL):

CREATE INDEX idx_orders_pending ON orders (created_at)
  WHERE status = 'pending';

当查询总是过滤特定条件时使用,比完整索引小得多。

GIN(PostgreSQL,数组、JSONB、全文检索):

CREATE INDEX idx_products_tags ON products USING GIN (tags);
CREATE INDEX idx_docs_search ON documents USING GIN (to_tsvector('english', content));

GiST(PostgreSQL,空间、范围类型):

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);

N+1 查询检测

症状:1 次查询获取父记录 + 每个子记录 N 次查询。

# BAD: N+1
users = db.query(User).all()
for user in users:
    print(user.orders)  # triggers query per user

# GOOD: eager load
users = db.query(User).options(joinedload(User.orders)).all()
// BAD: N+1
const users = await User.findAll();
for (const user of users) {
  const orders = await Order.findAll({ where: { userId: user.id } });
}

// GOOD: batch load
const users = await User.findAll({ include: [Order] });

检测方法:启用查询日志,统计每个请求的查询数。单个端点超过 10 次查询即为警示信号。

连接池

经验法则:pool_size = (core_count * 2) + disk_count

典型 Web 应用:每个应用实例 10-20 个连接。

PostgreSQL:

  • 在无服务器/高连接场景使用 PgBouncer 的 transaction 模式
  • 设置 idle_in_transaction_session_timeout = '30s'
  • 使用 pg_stat_activity 监控

MySQL:

  • 根据可用内存设置 max_connections(每个连接约使用 10MB)
  • 使用 ProxySQL 进行连接多路复用
  • 使用 SHOW PROCESSLIST 监控

只读副本

  • 将所有 SELECT 查询路由到副本
  • 将所有写入路由到主库
  • 考虑复制延迟(通常 10-100ms)
  • 不要在副本上做写后读;一致性关键的读取使用主库
  • 使用连接级路由,而非查询级
# SQLAlchemy read replica routing
class RoutingSession(Session):
    def get_bind(self, mapper=None, clause=None):
        if self._flushing or self.is_modified():
            return engines["primary"]
        return engines["replica"]

分区策略

范围分区(时间序列数据):

-- 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);

当表超过 50-100GB 或需要快速删除旧数据时进行分区。

查询优化检查清单

  1. 运行 EXPLAIN ANALYZE 并阅读执行计划
  2. 检查超过 1 万行表上的顺序扫描
  3. 验证索引使用情况(检查 pg_stat_user_indexes 中的 idx_scan)
  4. 查找阻止索引使用的隐式类型转换
  5. 用具体列替换 SELECT *
  6. 为只需子集的查询添加 LIMIT
  7. 使用 EXISTS 而非 COUNT(*) > 0
  8. 批量 INSERT/UPDATE 操作(每批 500-1000 行)
  9. 避免在 WHERE 子句中对索引列使用函数
  10. 监控慢查询日志(pg: log_min_duration_statement = 100)

危险模式

  • 在无索引列上使用 LIKE '%term%'(改用全文检索)
  • ORDER BY RANDOM()(改用 TABLESAMPLE 或应用层随机化)
  • SELECT DISTINCT 掩盖连接问题
  • UPDATE/DELETE 缺少 WHERE(先用 SELECT 验证)
  • 长时间运行的事务持有锁
  • 使用 OFFSET 进行深度分页(改用 keyset/cursor 分页)

使用方式

该技能以文档形式提供数据库优化的操作指南与代码示例,供开发者在优化 PostgreSQL 或 MySQL 时参考。具体安装或调用方式暂未提供。

适用场景

适用于需要优化 PostgreSQL 或 MySQL 查询性能、设计索引、排查 N+1 查询、配置连接池、实施读写分离或分区策略的开发与运维场景。

限制

该技能仅覆盖 PostgreSQL 与 MySQL,不涉及其他数据库。具体工具集成方式、版本兼容性与自动化能力暂未提供。