1. 首页 > 国产数据库教程 > TiDB教程 > 正文

tidb教程FG137-TiDB SQL优化最佳实践

fgedu.net.cn

目录

一、基础概念

1.1 SQL优化定义

SQL优化是指通过调整SQL语句、索引设计、表结构等方式,提高SQL查询的执行效率,减少资源消耗,提升系统性能。

1.2 TiDB执行计划

执行计划是TiDB执行SQL语句的具体步骤,包括表扫描方式、连接方式、排序方式等。通过分析执行计划,可以识别SQL语句的性能瓶颈。

1.3 索引类型

  • 主键索引:唯一标识表中的记录
  • 唯一索引:确保列值唯一
  • 普通索引:加速查询
  • 组合索引:多个列的索引
  • 覆盖索引:包含查询所需的所有列

1.4 执行计划操作类型

  • TableScan:全表扫描
  • IndexScan:索引扫描
  • IndexLookUp:索引查找
  • HashJoin:哈希连接
  • IndexJoin:索引连接
  • Sort:排序
  • Aggregate:聚合

二、规划建议

2.1 表结构设计

  • 合理设计表结构:根据业务需求设计表结构
  • 选择合适的数据类型:使用合适的数据类型,减少存储空间
  • 避免使用大字段:大字段会影响查询性能
  • 合理分表:对于大表,考虑分表处理

2.2 索引设计

  • 创建合适的索引:根据查询需求创建索引
  • 避免过多索引:过多索引会影响写入性能
  • 使用组合索引:合理使用组合索引
  • 考虑索引覆盖:使用覆盖索引减少回表操作

2.3 SQL语句设计

  • 避免复杂查询:复杂查询会增加执行时间
  • 使用参数化查询:减少SQL注入风险,提高性能
  • 避免使用SELECT *:只查询需要的列
  • 合理使用分页:避免一次性查询大量数据

三、实施方案

3.1 分析执行计划

查看执行计划

-- 查看执行计划
EXPLAIN SELECT * FROM fgedu_users WHERE age > 25;

-- 查看详细执行计划
EXPLAIN ANALYZE SELECT * FROM fgedu_users WHERE age > 25;
+-------------------------+----------+-----------+---------------+--------------------------------+| id                      | estRows  | task      | access object | operator info                  |+-------------------------+----------+-----------+---------------+--------------------------------+| TableReader_7           | 9900.00  | root      |               | data:Selection_6               | | └─Selection_6           | 9900.00  | cop[tikv] |               | gt(fgedu_users.age, 25)        |   └─TableFullScan_5      | 10000.00 | cop[tikv] | table:fgedu_users | keep order:false, stats:pseudo |+-------------------------+----------+-----------+---------------+--------------------------------+

分析执行计划

-- 分析执行计划中的关键信息
-- 1. 访问方式:TableFullScan表示全表扫描
-- 2. 估计行数:estRows表示估计的结果行数
-- 3. 执行任务:root表示在TiDB层执行,cop[tikv]表示在TiKV层执行
-- 4. 操作符信息:显示具体的操作信息

-- 创建索引后查看执行计划
CREATE INDEX idx_age ON fgedu_users(age);
EXPLAIN SELECT * FROM fgedu_users WHERE age > 25;
+-------------------------------+----------+-----------+---------------+--------------------------------+| id                            | estRows  | task      | access object | operator info                  |+-------------------------------+----------+-----------+---------------+--------------------------------+| IndexReader_6                 | 9900.00  | root      |               | index:IndexRangeScan_5         | | └─IndexRangeScan_5            | 9900.00  | cop[tikv] | table:fgedu_users, index:idx_age | range:(25,+inf], keep order:false |+-------------------------------+----------+-----------+---------------+--------------------------------+

风哥提示:

3.2 索引优化

创建合适的索引

-- 创建普通索引
CREATE INDEX idx_username ON fgedu_users(username);

-- 创建组合索引
CREATE INDEX idx_username_email ON fgedu_users(username, email);

-- 创建唯一索引
CREATE UNIQUE INDEX idx_email ON fgedu_users(email);

-- 查看索引
SHOW INDEX FROM fgedu_users;
+------------+------------+------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+| Table      | Non_unique | Key_name         | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible |+------------+------------+------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+| fgedu_users |          0 | PRIMARY          |            1 | id          | A         |       10000 |     NULL |   NULL |      | BTREE      |         |               | YES     | | fgedu_users |          1 | idx_age          |            1 | age         | A         |          20 |     NULL |   NULL |      | BTREE      |         |               | YES     | | fgedu_users |          1 | idx_username     |            1 | username    | A         |       10000 |     NULL |   NULL |      | BTREE      |         |               | YES     | | fgedu_users |          1 | idx_username_email |            1 | username    | A         |       10000 |     NULL |   NULL |      | BTREE      |         |               | YES     | | fgedu_users |          1 | idx_username_email |            2 | email       | A         |       10000 |     NULL |   NULL |      | BTREE      |         |               | YES     | | fgedu_users |          0 | idx_email        |            1 | email       | A         |       10000 |     NULL |   NULL |      | BTREE      |         |               | YES     |+------------+------------+------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+

删除不需要的索引

-- 删除索引
DROP INDEX idx_age ON fgedu_users;

-- 查看索引
SHOW INDEX FROM fgedu_users;
+------------+------------+------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+| Table      | Non_unique | Key_name         | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible |+------------+------------+------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+| fgedu_users |          0 | PRIMARY          |            1 | id          | A         |       10000 |     NULL |   NULL |      | BTREE      |         |               | YES     | | fgedu_users |          1 | idx_username     |            1 | username    | A         |       10000 |     NULL |   NULL |      | BTREE      |         |               | YES     | | fgedu_users |          1 | idx_username_email |            1 | username    | A         |       10000 |     NULL |   NULL |      | BTREE      |         |               | YES     | | fgedu_users |          1 | idx_username_email |            2 | email       | A         |       10000 |     NULL |   NULL |      | BTREE      |         |               | YES     | | fgedu_users |          0 | idx_email        |            1 | email       | A         |       10000 |     NULL |   NULL |      | BTREE      |         |               | YES     |+------------+------------+------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+

3.3 SQL语句优化

避免全表扫描

-- 不好的查询:全表扫描
SELECT * FROM fgedu_users WHERE age > 25;

-- 好的查询:使用索引
CREATE INDEX idx_age ON fgedu_users(age);
SELECT * FROM fgedu_users WHERE age > 25;

避免使用SELECT *

-- 不好的查询:使用SELECT *
SELECT * FROM fgedu_users WHERE username = 'admin';

-- 好的查询:只查询需要的列
SELECT id, username, email FROM fgedu_users WHERE username = 'admin';

避免在WHERE子句中使用函数

-- 不好的查询:在WHERE子句中使用函数
SELECT * FROM fgedu_users WHERE YEAR(created_at) = 2024;

-- 好的查询:避免使用函数
SELECT * FROM fgedu_users WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';

合理使用分页

-- 不好的查询:一次性查询大量数据
SELECT * FROM fgedu_users;

-- 好的查询:使用分页
SELECT * FROM fgedu_users LIMIT 10 OFFSET 0;
SELECT * FROM fgedu_users LIMIT 10 OFFSET 10;

使用参数化查询

-- 不好的查询:直接拼接SQL
SELECT * FROM fgedu_users WHERE username = 'admin';

-- 好的查询:使用参数化查询
PREPARE stmt FROM 'SELECT * FROM fgedu_users WHERE username = ?';
SET @username = 'admin';
EXECUTE stmt USING @username;
DEALLOCATE PREPARE stmt;

3.4 表结构优化

选择合适的数据类型

-- 不好的设计:使用过大的数据类型
CREATE TABLE bad_design (
    id INT,
    name VARCHAR(255),
    age INT,
    created_at DATETIME
);

-- 好的设计:使用合适的数据类型
CREATE TABLE good_design (
    id INT UNSIGNED,
    name VARCHAR(50),
    age TINYINT UNSIGNED,
    created_at TIMESTAMP
);

避免使用大字段

学习交流加群风哥QQ113257174

-- 不好的设计:使用大字段
CREATE TABLE bad_design (
    id INT,
    content TEXT,
    image LONGBLOB
);

-- 好的设计:分离大字段
CREATE TABLE good_design (
    id INT,
    content_summary VARCHAR(255),
    content_id INT
);

CREATE TABLE content_details (
    id INT,
    content TEXT,
    image LONGBLOB
);

3.5 监控和诊断

使用TiDB Dashboard

# 访问TiDB Dashboard
# 地址:http://tidb-server:10080/dashboard

# 查看慢查询
# 在TiDB Dashboard > 慢查询

# 查看执行计划
# 在TiDB Dashboard > SQL语句分析

使用SQL诊断

-- 启用SQL诊断
SET GLOBAL tidb_enable_stmt_summary = 1;

-- 查看SQL语句统计
SELECT * FROM information_schema.statements_summary WHERE query_sample_text LIKE '%fgedu_users%' ORDER BY exec_count DESC LIMIT 10;

-- 查看慢查询日志
SHOW VARIABLES LIKE 'slow_query_log';
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 0.1;

-- 查看慢查询日志内容
SHOW GLOBAL VARIABLES LIKE 'slow_query_log_file';
-- 查看对应文件内容

四、实战案例

4.1 慢查询优化

场景:业务系统中存在慢查询,影响系统性能。

步骤1:识别慢查询

-- 查看慢查询
SHOW GLOBAL VARIABLES LIKE 'slow_query_log';
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 0.1;

-- 查看慢查询日志文件
SHOW GLOBAL VARIABLES LIKE 'slow_query_log_file';

-- 分析慢查询日志
-- 例如:/tidb-data/tidb/slow-query.log

步骤2:分析执行计划

-- 示例慢查询
SELECT * FROM fgedu_users WHERE age > 25 AND created_at > '2024-01-01';

-- 查看执行计划
EXPLAIN SELECT * FROM fgedu_users WHERE age > 25 AND created_at > '2024-01-01';
+-------------------------+----------+-----------+---------------+--------------------------------+| id                      | estRows  | task      | access object | operator info                  |+-------------------------+----------+-----------+---------------+--------------------------------+| TableReader_7           | 4950.00  | root      |               | data:Selection_6               | | └─Selection_6           | 4950.00  | cop[tikv] |               | gt(fgedu_users.age, 25), gt(fgedu_users.created_at, 2024-01-01 00:00:00) |   └─TableFullScan_5      | 10000.00 | cop[tikv] | table:fgedu_users | keep order:false, stats:pseudo |+-------------------------+----------+-----------+---------------+--------------------------------+

步骤3:优化查询

-- 创建组合索引
CREATE INDEX idx_age_created_at ON fgedu_users(age, created_at);

-- 查看优化后的执行计划
EXPLAIN SELECT * FROM fgedu_users WHERE age > 25 AND created_at > '2024-01-01';
+----------------------------------+----------+-----------+---------------+--------------------------------+| id                               | estRows  | task      | access object | operator info                  |+----------------------------------+----------+-----------+---------------+--------------------------------+| IndexReader_6                    | 4950.00  | root      |               | index:IndexRangeScan_5         | | └─IndexRangeScan_5               | 4950.00  | cop[tikv] | table:fgedu_users, index:idx_age_created_at | range:(25,+inf], keep order:false, stats:pseudo |+----------------------------------+----------+-----------+---------------+--------------------------------+

步骤4:验证优化效果

-- 执行优化后的查询
SELECT * FROM fgedu_users WHERE age > 25 AND created_at > '2024-01-01';

-- 查看查询执行时间
EXPLAIN ANALYZE SELECT * FROM fgedu_users WHERE age > 25 AND created_at > '2024-01-01';

4.2 索引优化

场景:表中有大量数据,查询性能下降。

步骤1:分析表结构和索引

-- 查看表结构
DESC fgedu_users;

-- 查看索引
SHOW INDEX FROM fgedu_users;

-- 查看表统计信息
ANALYZE TABLE fgedu_users;

步骤2:识别索引使用情况

-- 查看索引使用情况
SELECT * FROM information_schema.statements_summary WHERE query_sample_text LIKE '%fgedu_users%' ORDER BY exec_count DESC LIMIT 10;

-- 查看未使用的索引
SELECT * FROM information_schema.statistics WHERE table_schema = 'fgedudb' AND table_name = 'fgedu_users' AND index_name NOT IN (SELECT DISTINCT index_name FROM performance_schema.statements_with_temp_tables WHERE table_schema = 'fgedudb' AND table_name = 'fgedu_users');

步骤3:优化索引

-- 删除未使用的索引
DROP INDEX idx_username ON fgedu_users;

-- 创建必要的索引
CREATE INDEX idx_email ON fgedu_users(email);
CREATE INDEX idx_created_at ON fgedu_users(created_at);

-- 查看优化后的索引
SHOW INDEX FROM fgedu_users;

五、经验总结

5.1 SQL优化最佳实践

  • 分析执行计划:通过执行计划识别性能瓶颈
  • 创建合适的索引:根据查询需求创建索引
  • 优化SQL语句:避免全表扫描、使用SELECT *等
  • 合理设计表结构:选择合适的数据类型,避免大字段
  • 定期维护:定期分析表统计信息,重建索引
  • 监控慢查询:及时发现和处理慢查询
  • 使用参数化查询:减少SQL注入风险,提高性能
  • 合理使用分页:避免一次性查询大量数据

5.2 索引设计最佳实践

  • 优先考虑WHERE子句中的列:为WHERE子句中的列创建索引
  • 考虑排序和分组列:为ORDER BY和GROUP BY中的列创建索引
  • 使用组合索引:合理使用组合索引,遵循最左前缀原则
  • 避免过多索引:过多索引会影响写入性能
  • 考虑索引覆盖:使用覆盖索引减少回表操作
  • 定期维护索引:定期重建索引,保持索引效率

5.3 常见问题与解决方案

问题 原因 解决方案
全表扫描 没有合适的索引 创建合适的索引
索引未使用 索引设计不合理、SQL语句不当 优化索引设计,调整SQL语句
慢查询 SQL语句复杂、索引不合理 优化SQL语句,创建合适的索引
性能下降 数据量增长、索引失效 定期维护索引,分析表统计信息
写入性能差 过多索引 删除不必要的索引

5.4 SQL优化检查清单

检查项 配置要求 状态
执行计划分析 定期分析执行计划 □
索引设计 创建合适的索引 □
SQL语句优化 优化SQL语句 □
表结构设计 合理设计表结构 □
慢查询监控 启用慢查询日志 □
定期维护 定期分析表统计信息 □
参数化查询 使用参数化查询 □
分页查询 合理使用分页 □

更多视频教程www.fgedu.net.cn

© 2024 TiDB数据库培训文档

本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html

联系我们

在线咨询:点击这里给我发消息

微信号:itpux-com

工作日:9:30-18:30,节假日休息