Spanner性能优化 #

一、性能优化概述 #

1.1 优化方向 #

text
性能优化方向
├── Schema设计
│   ├── 主键设计
│   ├── 交错表设计
│   └── 索引设计
│
├── 查询优化
│   ├── 使用索引
│   ├── 避免全表扫描
│   └── 优化JOIN
│
├── 事务优化
│   ├── 减少事务大小
│   ├── 使用只读事务
│   └── 批量操作
│
└── 配置优化
    ├── 节点数量
    ├── 连接池配置
    └── 区域选择

1.2 性能指标 #

text
关键性能指标:
├── 延迟(Latency)
│   ├── P50延迟
│   ├── P99延迟
│   └── 读写延迟
│
├── 吞吐量(Throughput)
│   ├── QPS
│   ├── 写入TPS
│   └── 并发连接数
│
├── 资源使用
│   ├── CPU使用率
│   ├── 存储使用量
│   └── 会话使用率
│
└── 可用性
    ├── 错误率
    ├── 重试次数
    └── 超时次数

二、Schema设计优化 #

2.1 主键设计 #

sql
-- 不推荐: 单调递增主键产生热点
CREATE TABLE bad_users (
    id INT64 NOT NULL,  -- 单调递增
    name STRING(100)
) PRIMARY KEY (id);

-- 推荐: 使用位反转或分散主键
CREATE TABLE good_users (
    id INT64 NOT NULL DEFAULT (GET_NEXT_SEQUENCE_VALUE(SEQUENCE user_seq)),
    name STRING(100)
) PRIMARY KEY (id);

-- 推荐: 复合主键分散写入
CREATE TABLE orders (
    shard_id INT64 NOT NULL,
    order_id INT64 NOT NULL,
    user_id INT64
) PRIMARY KEY (shard_id, order_id);

2.2 交错表设计 #

sql
-- 使用交错表优化父子表查询
CREATE TABLE users (
    user_id INT64 NOT NULL,
    name STRING(100)
) PRIMARY KEY (user_id);

CREATE TABLE orders (
    user_id INT64 NOT NULL,
    order_id INT64 NOT NULL,
    amount FLOAT64
) PRIMARY KEY (user_id, order_id)
INTERLEAVE IN PARENT users ON DELETE CASCADE;

-- 查询时利用交错优势
SELECT u.name, o.order_id, o.amount
FROM users u
INNER JOIN orders o ON u.user_id = o.user_id
WHERE u.user_id = 1;

2.3 索引设计 #

sql
-- 创建合适的索引
CREATE INDEX idx_users_email ON users(email);

-- 复合索引注意顺序
CREATE INDEX idx_orders_user_date ON orders(user_id, order_date);

-- 使用存储索引避免回表
CREATE INDEX idx_users_status_storing ON users(status)
STORING (name, email);

三、查询优化 #

3.1 使用EXPLAIN分析 #

sql
-- 查看执行计划
EXPLAIN SELECT * FROM users WHERE email = 'john@example.com';

-- 查看实际执行统计
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'john@example.com';

3.2 使用索引 #

sql
-- 创建索引
CREATE INDEX idx_users_status ON users(status);

-- 使用索引查询
SELECT * FROM users WHERE status = 'active';

-- 强制使用索引
SELECT * FROM users@{FORCE_INDEX=idx_users_status}
WHERE status = 'active';

3.3 避免全表扫描 #

sql
-- 不推荐: 全表扫描
SELECT * FROM users;

-- 推荐: 使用主键或索引
SELECT * FROM users WHERE user_id = 1;

-- 不推荐: 函数导致索引失效
SELECT * FROM users WHERE UPPER(name) = 'JOHN';

-- 推荐: 避免在条件列上使用函数
SELECT * FROM users WHERE name = 'John';

3.4 优化JOIN #

sql
-- 使用交错表优化JOIN
SELECT u.name, o.order_id
FROM users u
INNER JOIN orders o ON u.user_id = o.user_id
WHERE u.user_id = 1;

-- 使用索引优化JOIN条件
CREATE INDEX idx_orders_user ON orders(user_id);

3.5 分页优化 #

sql
-- 不推荐: OFFSET分页(大数据量性能差)
SELECT * FROM users ORDER BY user_id LIMIT 10 OFFSET 10000;

-- 推荐: 游标分页
SELECT * FROM users
WHERE user_id > 10000  -- 上一页最后的user_id
ORDER BY user_id
LIMIT 10;

四、事务优化 #

4.1 减少事务大小 #

java
// 不推荐: 大事务
client.readWriteTransaction().run(transaction -> {
    // 处理大量数据
    for (int i = 0; i < 100000; i++) {
        transaction.buffer(Mutation.newInsertBuilder("logs")
            .set("log_id").to((long) i)
            .build());
    }
    return null;
});

// 推荐: 批量写入
List<Mutation> mutations = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
    mutations.add(Mutation.newInsertBuilder("logs")
        .set("log_id").to((long) i)
        .build());
}
client.write(mutations);

4.2 使用只读事务 #

java
// 推荐: 只读事务性能更好
try (ReadOnlyTransaction transaction = client.readOnlyTransaction()) {
    ResultSet rs = transaction.executeQuery(
        Statement.of("SELECT * FROM users"));
    // 处理结果
}

4.3 避免热点 #

java
// 不推荐: 热点主键
for (int i = 0; i < 1000; i++) {
    mutations.add(Mutation.newInsertBuilder("users")
        .set("user_id").to((long) i)  // 单调递增
        .build());
}

// 推荐: 分散主键
for (int i = 0; i < 1000; i++) {
    long id = getNextBitReversedId();  // 位反转
    mutations.add(Mutation.newInsertBuilder("users")
        .set("user_id").to(id)
        .build());
}

五、配置优化 #

5.1 节点数量规划 #

text
节点数量计算:
├── 存储需求: 每1TB数据约需1个节点
├── CPU需求: 每1000 QPS约需1个节点
├── 最终节点数: max(存储需求, CPU需求)
└── 预留20%余量

5.2 连接池配置 #

java
// Java连接池配置
import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.SessionPoolOptions;

SessionPoolOptions poolOptions = SessionPoolOptions.newBuilder()
    .setMinSessions(10)
    .setMaxSessions(100)
    .setIncStep(5)
    .build();

SpannerOptions options = SpannerOptions.newBuilder()
    .setSessionPoolOption(poolOptions)
    .build();

5.3 区域选择 #

text
区域选择建议:
├── 单区域用户: 选择最近区域
├── 多区域用户: 选择多区域配置
├── 全球用户: 选择nam-eur-asia1
└── 考虑延迟和成本平衡

六、性能监控 #

6.1 关键指标 #

sql
-- 查看查询统计
SELECT 
    text,
    execution_count,
    avg_latency_seconds,
    avg_rows
FROM INFORMATION_SCHEMA.QUERY_STATS_TOP_HOUR
ORDER BY avg_latency_seconds DESC
LIMIT 10;

-- 查看表统计
SELECT * FROM INFORMATION_SCHEMA.TABLE_STATS;

-- 查看索引统计
SELECT * FROM INFORMATION_SCHEMA.INDEX_STATS;

6.2 Cloud Monitoring #

bash
# 创建性能告警
gcloud alpha monitoring policies create \
    --display-name="Spanner High Latency" \
    --condition-display-name="P99 Latency > 100ms" \
    --condition-filter='resource.type="spanner_instance" AND metric.type="spanner.googleapis.com/instance/latency"' \
    --condition-threshold-value=0.1

6.3 性能分析 #

text
性能分析步骤:
1. 识别慢查询
2. 使用EXPLAIN分析
3. 检查索引使用
4. 优化查询语句
5. 验证优化效果

七、性能最佳实践 #

7.1 Schema设计 #

text
Schema设计建议:
├── 使用位反转主键
├── 使用交错表优化关联
├── 创建合适的索引
├── 使用存储索引
└── 合理设置分区

7.2 查询设计 #

text
查询设计建议:
├── 使用索引查询
├── 避免全表扫描
├── 使用游标分页
├── 使用EXPLAIN分析
└── 优化JOIN操作

7.3 事务设计 #

text
事务设计建议:
├── 减少事务大小
├── 使用只读事务
├── 批量操作
├── 避免热点
└── 处理冲突重试

八、总结 #

性能优化要点:

方向 关键点
Schema 主键设计、交错表、索引
查询 使用索引、避免全扫描
事务 减少大小、只读事务
配置 节点数量、连接池

最佳实践:

text
1. 优化Schema设计
   └── 主键、交错表、索引

2. 优化查询语句
   └── 使用索引、避免全扫描

3. 优化事务处理
   └── 减少大小、批量操作

4. 监控性能指标
   └── 及时发现问题

5. 持续优化改进
   └── 定期评估和优化

下一步,让我们学习监控告警!

最后更新:2026-03-27