PlanetScale 性能优化 #
本章将介绍如何优化 PlanetScale 数据库性能,包括查询优化、索引策略、监控告警等最佳实践。
性能基础 #
PlanetScale 性能特点 #
text
┌─────────────────────────────────────────────────────────────┐
│ 性能架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 优势: │
│ │
│ ✅ 自动扩展 │
│ - 读取自动负载均衡 │
│ - 无需手动扩容 │
│ │
│ ✅ 高可用 │
│ - 主从复制 │
│ - 自动故障转移 │
│ │
│ ✅ 优化网络 │
│ - 全球边缘节点 │
│ - 低延迟连接 │
│ │
│ 注意事项: │
│ ⚠️ 查询优化仍需手动处理 │
│ ⚠️ 索引设计需要规划 │
│ ⚠️ 连接池需要合理配置 │
│ │
└─────────────────────────────────────────────────────────────┘
性能指标 #
text
┌─────────────────────────────────────────────────────────────┐
│ 关键性能指标 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 查询性能: │
│ ├── 查询延迟 (Latency) │
│ ├── 查询吞吐量 (QPS) │
│ └── 慢查询比例 │
│ │
│ 连接性能: │
│ ├── 连接延迟 │
│ ├── 连接池使用率 │
│ └── 连接错误率 │
│ │
│ 资源使用: │
│ ├── 行读取量 │
│ ├── 行写入量 │
│ └── 存储使用量 │
│ │
└─────────────────────────────────────────────────────────────┘
查询优化 #
查询分析 #
sql
-- 使用 EXPLAIN 分析查询
EXPLAIN SELECT * FROM users WHERE email = 'test@example.com';
-- EXPLAIN 输出示例
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | users | NULL | ALL | NULL | NULL | NULL | NULL | 1000 | 10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
-- 使用 EXPLAIN ANALYZE(如果支持)
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'test@example.com';
查询优化技巧 #
text
┌─────────────────────────────────────────────────────────────┐
│ 查询优化建议 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 选择合适的列 │
│ ❌ SELECT * FROM users │
│ ✅ SELECT id, name, email FROM users │
│ │
│ 2. 使用索引列过滤 │
│ ❌ WHERE YEAR(created_at) = 2024 │
│ ✅ WHERE created_at >= '2024-01-01' │
│ │
│ 3. 避免 SELECT DISTINCT │
│ ❌ SELECT DISTINCT name FROM users │
│ ✅ SELECT name FROM users GROUP BY name │
│ │
│ 4. 使用 LIMIT │
│ ❌ SELECT * FROM users │
│ ✅ SELECT * FROM users LIMIT 100 │
│ │
│ 5. 避免 OR 条件 │
│ ❌ WHERE status = 'active' OR status = 'pending' │
│ ✅ WHERE status IN ('active', 'pending') │
│ │
│ 6. 使用 JOIN 代替子查询 │
│ ❌ SELECT * FROM users WHERE id IN (SELECT user_id...)│
│ ✅ SELECT users.* FROM users JOIN orders... │
│ │
└─────────────────────────────────────────────────────────────┘
分页优化 #
sql
-- 传统分页(大偏移量性能差)
SELECT * FROM users ORDER BY id LIMIT 10000, 20;
-- 游标分页(推荐)
SELECT * FROM users WHERE id > 10000 ORDER BY id LIMIT 20;
-- 使用覆盖索引
SELECT id FROM users ORDER BY created_at LIMIT 10000, 20;
SELECT * FROM users WHERE id IN (...);
批量操作优化 #
sql
-- 批量插入
INSERT INTO users (name, email) VALUES
('User 1', 'user1@example.com'),
('User 2', 'user2@example.com'),
('User 3', 'user3@example.com');
-- 批量更新
UPDATE users SET status = 'active'
WHERE id IN (1, 2, 3, 4, 5);
-- 使用 CASE 批量更新不同值
UPDATE users SET status = CASE id
WHEN 1 THEN 'active'
WHEN 2 THEN 'inactive'
WHEN 3 THEN 'pending'
END
WHERE id IN (1, 2, 3);
索引优化 #
索引类型 #
text
┌─────────────────────────────────────────────────────────────┐
│ 索引类型 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 主键索引(Primary Key) │
│ ├── 自动创建 │
│ ├── 唯一且非空 │
│ └── 聚簇索引 │
│ │
│ 唯一索引(Unique Index) │
│ ├── 确保值唯一 │
│ ├── 允许 NULL │
│ └── CREATE UNIQUE INDEX idx_email ON users(email) │
│ │
│ 普通索引(Index) │
│ ├── 加速查询 │
│ ├── 允许重复 │
│ └── CREATE INDEX idx_status ON users(status) │
│ │
│ 复合索引(Composite Index) │
│ ├── 多列组合 │
│ ├── 遵循最左前缀 │
│ └── CREATE INDEX idx_status_created ON users(status, created_at)│
│ │
│ 全文索引(Fulltext Index) │
│ ├── 文本搜索 │
│ └── CREATE FULLTEXT INDEX idx_content ON posts(content) │
│ │
└─────────────────────────────────────────────────────────────┘
索引设计原则 #
text
┌─────────────────────────────────────────────────────────────┐
│ 索引设计原则 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 选择性高的列优先 │
│ - 唯一值多的列 │
│ - 区分度高 │
│ │
│ 2. 复合索引顺序 │
│ - 高选择性列在前 │
│ - 范围查询列在后 │
│ - 遵循最左前缀原则 │
│ │
│ 3. 覆盖索引 │
│ - 索引包含查询所需列 │
│ - 避免回表 │
│ │
│ 4. 避免过度索引 │
│ - 索引占用空间 │
│ - 影响写入性能 │
│ - 维护成本 │
│ │
│ 5. 定期审查索引 │
│ - 删除未使用索引 │
│ - 优化冗余索引 │
│ │
└─────────────────────────────────────────────────────────────┘
索引示例 #
sql
-- 单列索引
CREATE INDEX idx_email ON users(email);
-- 复合索引(注意顺序)
CREATE INDEX idx_status_created ON users(status, created_at);
-- 覆盖索引
CREATE INDEX idx_covering ON users(status, created_at, name);
-- 查看索引使用情况
SHOW INDEX FROM users;
-- 删除索引
DROP INDEX idx_email ON users;
-- 分析索引使用
EXPLAIN SELECT * FROM users WHERE status = 'active' AND created_at > '2024-01-01';
索引使用检查 #
sql
-- 检查索引是否被使用
EXPLAIN SELECT * FROM users WHERE email = 'test@example.com';
-- type 列说明:
-- ALL: 全表扫描(需要优化)
-- index: 索引扫描
-- range: 范围扫描
-- ref: 索引查找
-- const: 主键/唯一索引查找(最优)
连接池优化 #
连接池配置 #
text
┌─────────────────────────────────────────────────────────────┐
│ 连接池配置建议 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Node.js (mysql2): │
│ { │
│ connectionLimit: 10, // 连接池大小 │
│ waitForConnections: true, // 等待可用连接 │
│ queueLimit: 0, // 队列无限制 │
│ connectTimeout: 10000, // 连接超时 10s │
│ acquireTimeout: 30000 // 获取连接超时 30s │
│ } │
│ │
│ Python (SQLAlchemy): │
│ { │
│ pool_size: 10, // 连接池大小 │
│ max_overflow: 5, // 溢出连接数 │
│ pool_timeout: 30, // 获取超时 │
│ pool_recycle: 3600 // 连接回收时间 │
│ } │
│ │
│ Go (sql.DB): │
│ db.SetMaxOpenConns(10) // 最大打开连接 │
│ db.SetMaxIdleConns(5) // 最大空闲连接 │
│ db.SetConnMaxLifetime(time.Hour) // 连接最大生命周期 │
│ │
└─────────────────────────────────────────────────────────────┘
连接池监控 #
javascript
import mysql from 'mysql2/promise';
const pool = mysql.createPool({
host: 'aws.connect.psdb.cloud',
user: 'abc123',
password: 'pscale_pw_xxx',
database: 'my-database',
ssl: { rejectUnauthorized: true },
connectionLimit: 10
});
setInterval(() => {
const poolInfo = pool.pool;
console.log({
total: poolInfo._allConnections.length,
free: poolInfo._freeConnections.length,
waiting: poolInfo._connectionQueue.length
});
}, 60000);
监控与告警 #
Web 控制台监控 #
text
┌─────────────────────────────────────────────────────────────┐
│ 监控面板 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Overview 页面: │
│ ├── 存储使用量 │
│ ├── 行读取统计 │
│ ├── 行写入统计 │
│ └── 连接数统计 │
│ │
│ Insights 页面: │
│ ├── 慢查询列表 │
│ ├── 查询性能分析 │
│ └── 优化建议 │
│ │
│ 告警设置: │
│ ├── 存储使用超过阈值 │
│ ├── 行读取接近限制 │
│ └── 慢查询数量增加 │
│ │
└─────────────────────────────────────────────────────────────┘
慢查询分析 #
sql
-- 查找慢查询
SELECT * FROM users WHERE name LIKE '%test%';
-- 优化方案
-- 1. 添加索引
CREATE INDEX idx_name ON users(name);
-- 2. 使用前缀匹配
SELECT * FROM users WHERE name LIKE 'test%';
-- 3. 使用全文索引
CREATE FULLTEXT INDEX idx_name_ft ON users(name);
SELECT * FROM users WHERE MATCH(name) AGAINST('test');
性能监控脚本 #
javascript
import mysql from 'mysql2/promise';
async function monitorPerformance() {
const connection = await mysql.createConnection(process.env.DATABASE_URL);
const [tables] = await connection.execute(`
SELECT
table_name,
table_rows,
ROUND(data_length / 1024 / 1024, 2) as data_mb,
ROUND(index_length / 1024 / 1024, 2) as index_mb
FROM information_schema.tables
WHERE table_schema = DATABASE()
`);
console.table(tables);
await connection.end();
}
monitorPerformance();
缓存策略 #
应用层缓存 #
text
┌─────────────────────────────────────────────────────────────┐
│ 缓存架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 请求流程: │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │
│ │ 客户端 │────>│ 缓存 │────>│ PlanetScale │ │
│ └─────────┘ └─────────┘ └─────────────┘ │
│ │ │
│ │ 命中 │
│ ▼ │
│ 直接返回 │
│ │
│ 缓存策略: │
│ ├── Redis / Memcached │
│ ├── CDN 缓存静态数据 │
│ └── 应用内存缓存 │
│ │
│ 适用场景: │
│ - 热点数据 │
│ - 读多写少 │
│ - 实时性要求不高 │
│ │
└─────────────────────────────────────────────────────────────┘
Redis 缓存示例 #
javascript
import Redis from 'ioredis';
import mysql from 'mysql2/promise';
const redis = new Redis(process.env.REDIS_URL);
const pool = mysql.createPool(process.env.DATABASE_URL);
async function getUser(id) {
const cacheKey = `user:${id}`;
const cached = await redis.get(cacheKey);
if (cached) {
return JSON.parse(cached);
}
const [rows] = await pool.execute(
'SELECT * FROM users WHERE id = ?',
[id]
);
if (rows[0]) {
await redis.setex(cacheKey, 3600, JSON.stringify(rows[0]));
}
return rows[0];
}
async function updateUser(id, data) {
await pool.execute(
'UPDATE users SET name = ? WHERE id = ?',
[data.name, id]
);
await redis.del(`user:${id}`);
}
性能优化清单 #
text
┌─────────────────────────────────────────────────────────────┐
│ 优化检查清单 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 查询优化: │
│ □ 使用 EXPLAIN 分析慢查询 │
│ □ 避免 SELECT * │
│ □ 使用 LIMIT 限制结果集 │
│ □ 优化 JOIN 操作 │
│ □ 使用游标分页 │
│ │
│ 索引优化: │
│ □ 为常用查询条件添加索引 │
│ □ 使用复合索引优化多条件查询 │
│ □ 删除未使用的索引 │
│ □ 检查索引选择性 │
│ │
│ 连接优化: │
│ □ 配置合理的连接池大小 │
│ □ 监控连接池使用率 │
│ □ 处理连接泄漏 │
│ │
│ 缓存优化: │
│ □ 为热点数据添加缓存 │
│ □ 设置合理的缓存过期时间 │
│ □ 处理缓存失效 │
│ │
└─────────────────────────────────────────────────────────────┘
下一步 #
现在你已经掌握了性能优化,接下来学习 安全配置,了解如何保护你的 PlanetScale 数据库!
最后更新:2026-03-29