SQL调优是我们后端开发人员面试中的高频考点,也是实际工作中提升数据库性能的关键技能。面对“你是如何进行SQL调优的?”这个问题,你是否能条理清晰地分析问题并提供解决方案?

1. 索引失效?

问题分析: 索引是提高查询速度的利器,但使用不当也会适得其反。常见的索引失效场景包括:

  • 对索引列进行运算或函数操作: 例如 WHERE YEAR(create_time) = 2023,即使 create_time 有索引,也无法使用。
  • 使用 NOT!=<>** 等否定操作符:** 例如 WHERE status != 1,索引无法有效过滤数据。
  • 使用 OR 连接多个条件: 例如 WHERE name = '张三' OR age = 18,如果 nameage 都有索引,数据库可能只会使用其中一个索引。
  • 数据类型不匹配: 例如 WHERE id = '123'id 是整数类型,而 '123' 是字符串类型,索引无法使用。

解决方案:

  • 避免对索引列进行运算或函数操作,可以将运算结果存储在另一列并建立索引。
  • 尽量避免使用否定操作符,可以尝试改写查询条件。
  • 对于 OR 连接的条件,可以考虑使用 UNIONUNION ALL 替代。
  • 确保查询条件中的数据类型与索引列的数据类型一致。

案例:

-- 索引失效
SELECT * FROM users WHERE YEAR(create_time) = 2023; -- 优化后
ALTER TABLE users ADD COLUMN create_year INT;
UPDATE users SET create_year = YEAR(create_time);
CREATE INDEX idx_create_year ON users(create_year);
SELECT * FROM users WHERE create_year = 2023;

2. 多表join?

问题分析: 多表join是SQL查询中常见的操作,但也容易成为性能瓶颈。影响join性能的因素包括:

  • join的表数量: join的表越多,查询复杂度越高,性能越差。
  • join的类型: inner join 性能通常优于 outer join。
  • join条件: join条件中的列是否有索引,以及索引的选择性如何。
  • 数据量: 参与join的表的数据量越大,查询性能越差。

解决方案:

  • 尽量减少join的表数量,可以通过冗余字段或子查询等方式减少join次数。
  • 优先使用inner join,避免使用outer join,除非必须查询出所有数据。
  • 确保join条件中的列有索引,并且索引的选择性较高。
  • 对于数据量大的表,可以考虑使用分库分表、分区表等技术。

案例:

-- 性能较差的join
SELECT * FROM orders o
LEFT JOIN users u ON o.user_id = u.id
LEFT JOIN products p ON o.product_id = p.id
WHERE u.name = '张三'; -- 优化后
WITH user_cte AS (
SELECT id FROM users WHERE name = '张三'
)
SELECT * FROM orders o
JOIN user_cte u ON o.user_id = u.id
JOIN products p ON o.product_id = p.id;

3. 查询字段太多?

问题分析: “SELECT * ” 虽然方便,但会查询出所有字段,包括不需要的字段,造成资源浪费,影响查询效率。

解决方案:

  • 只查询需要的字段,避免使用 SELECT *
  • 使用覆盖索引,避免回表查询。覆盖索引是指索引包含了查询所需的所有字段,数据库可以直接从索引中获取数据,而不需要回表查询。

案例:

-- 查询所有字段
SELECT * FROM users WHERE name = '张三'; -- 优化后,只查询需要的字段
SELECT id, name, age FROM users WHERE name = '张三'; -- 使用覆盖索引
CREATE INDEX idx_name_age ON users(name, age);
SELECT name, age FROM users WHERE name = '张三';

4. 表中数据量太大?

问题分析: 当单表数据量过大时,即使有索引,查询速度也会变慢。影响查询性能的因素包括:

  • 数据量: 数据量越大,查询需要扫描的数据页越多,性能越差。
  • 索引大小: 数据量越大,索引也会越大,占用更多的内存和磁盘空间。
  • 更新操作: 数据量越大,更新操作(如插入、更新、删除)需要维护的索引也越多,性能越差。

解决方案:

  • 对表进行分区: 将数据分散到不同的物理文件中,可以减少查询需要扫描的数据量。
  • 使用分库分表: 将数据分散到不同的数据库或表中,可以进一步提高查询性能。
  • 定期归档历史数据: 将不常用的历史数据迁移到其他存储介质,减少单表数据量。

案例:

-- 对表进行分区
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(255),
age INT,
create_time DATETIME
) PARTITION BY RANGE (YEAR(create_time)) (
PARTITION p0 VALUES LESS THAN (2020),
PARTITION p1 VALUES LESS THAN (2021),
PARTITION p2 VALUES LESS THAN (2022),
PARTITION p3 VALUES LESS THAN (2023)
); -- 查询2023年的数据
SELECT * FROM users PARTITION (p3) WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31';

5. 索引区分度不高?

问题分析: 索引区分度是指索引列中不同值的数量占总行数的比例。区分度越高,索引效果越好。如果索引区分度不高,数据库可能不会使用该索引,或者使用索引的效果不明显。

解决方案:

  • 选择区分度更高的列作为索引: 例如,对于性别列,区分度只有2(男、女),不适合建立索引;而对于用户ID列,区分度很高,适合建立索引。
  • 使用复合索引: 将多个列组合起来建立索引,可以提高索引的选择性。
  • 避免对区分度低的列建立索引: 例如,对于状态列,如果只有几个状态值,区分度很低,建立索引的效果不明显。

案例:

-- 区分度低的索引
CREATE INDEX idx_status ON users(status); -- 优化后,使用复合索引
CREATE INDEX idx_status_name ON users(status, name);

6. 数据库连接数不够?

问题分析: 数据库连接数不足会导致应用无法连接数据库,影响业务正常运行。

解决方案:

  • 增加数据库最大连接数: 修改数据库配置文件,增加 max_connections 参数的值。
  • 使用连接池技术: 使用连接池可以复用数据库连接,减少连接创建和销毁的开销。
  • 优化应用代码: 减少数据库连接占用时间,例如使用批量操作、异步操作等。

案例:

-- 修改MySQL最大连接数
SET GLOBAL max_connections = 1000;

7. 数据库的表结构不合理?

问题分析: 不合理的表结构会导致数据冗余、更新异常等问题,影响数据库性能。

解决方案:

  • 遵循数据库设计范式: 例如,第一范式要求每个字段都是原子性的,第二范式要求每个非主键字段都完全依赖于主键,第三范式要求每个非主键字段都不传递依赖于主键。
  • 选择合适的数据类型: 例如,对于存储年龄的字段,可以使用 TINYINT 类型,而不是 INT 类型。
  • 建立合理的索引: 根据查询需求建立索引,避免过度索引。

案例:

-- 不合理的表结构
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(255),
age INT,
address VARCHAR(255),
city VARCHAR(255),
province VARCHAR(255)
); -- 优化后,遵循第三范式
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(255),
age INT,
address_id INT,
FOREIGN KEY (address_id) REFERENCES addresses(id)
); CREATE TABLE addresses (
id INT PRIMARY KEY,
city VARCHAR(255),
province VARCHAR(255)
);

8. 数据库IO或者CPU比较高?

问题分析: 数据库IO或CPU过高会导致数据库响应变慢,影响应用性能。

解决方案:

  • 使用数据库监控工具: 例如,MySQL 可以使用 SHOW PROCESSLIST 命令查看当前正在执行的SQL语句,使用 SHOW STATUS 命令查看数据库状态信息。
  • 分析慢查询日志: 慢查询日志记录了执行时间超过指定阈值的SQL语句,可以帮助我们找出执行效率低的SQL语句。
  • 优化数据库配置参数: 例如,调整内存、连接数等参数,可以提高数据库性能。

案例:

-- 查看MySQL当前正在执行的SQL语句
SHOW PROCESSLIST; -- 查看MySQL状态信息
SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Innodb_buffer_pool_reads';

9. 数据库参数不合理?

问题分析: 数据库参数的设置对数据库性能有很大影响。

解决方案:

  • 根据数据库类型和硬件配置,调整内存、连接数等参数: 例如,MySQL 的 innodb_buffer_pool_size 参数用于设置 InnoDB 存储引擎的缓冲池大小,可以根据服务器的内存大小进行调整。
  • 参考官方文档和最佳实践,设置合理的参数值: 例如,MySQL 官方文档提供了不同场景下的参数配置建议。
  • 使用数据库性能测试工具,验证参数调整效果: 例如,可以使用 sysbench 工具对数据库进行压力测试,评估参数调整后的性能提升。

案例:

-- 修改MySQL InnoDB缓冲池大小
SET GLOBAL innodb_buffer_pool_size = 1G;

10. 事务比较长?

问题分析: 长事务会占用数据库资源,影响其他事务的执行。

解决方案:

  • 尽量缩短事务执行时间: 例如,将耗时的操作移到事务外执行。
  • 将大事务拆分为多个小事务: 例如,将批量插入操作拆分为多个小批量插入操作。
  • 避免在事务中进行耗时操作: 例如,避免在事务中进行网络请求、文件操作等。

案例:

-- 长事务
START TRANSACTION;
-- 执行耗时操作
UPDATE users SET balance = balance - 100 WHERE id = 1;
-- 执行耗时操作
UPDATE orders SET status = 'paid' WHERE user_id = 1;
COMMIT; -- 优化后,将事务拆分为两个小事务
START TRANSACTION;
UPDATE users SET balance = balance - 100 WHERE id = 1;
COMMIT; START TRANSACTION;
UPDATE orders SET status = 'paid' WHERE user_id = 1;
COMMIT;

11. 锁竞争导致的等待?

问题分析: 锁竞争会导致事务等待,影响数据库并发性能。

解决方案:

  • 使用乐观锁机制: 乐观锁假设并发冲突的概率较低,在提交事务时才会检查数据是否被修改,可以减少锁冲突。
  • 合理设置事务隔离级别: 例如,将事务隔离级别设置为 READ COMMITTED,可以避免脏读,同时提高并发性能。
  • 优化SQL语句: 例如,避免使用 SELECT ... FOR UPDATE 语句,可以减少锁的持有时间。

案例:

-- 使用乐观锁
UPDATE products SET stock = stock - 1, version = version + 1 WHERE id = 1 AND version = 1; -- 如果更新失败,说明数据已被修改,需要重新读取数据并重试

总结:

SQL调优是一个复杂的过程,需要根据具体情况进行具体分析。本文介绍的11个问题只是SQL调优中的冰山一角,实际工作中还会遇到各种各样的问题。我们需要不断学习和积累经验,才能成为一名优秀的SQL调优专家。

面试官:你是如何进行SQL调优的?的更多相关文章

  1. 初次使用SQL调优建议工具--SQL Tuning Advisor

    在10g中,Oracle推出了自己的SQL优化辅助工具: SQL优化器(SQL Tuning Advisor :STA),它是新的DBMS_SQLTUNE包. 使用STA一定要保证优化器是CBO模式下 ...

  2. Oracle SQL调优之分区表

    目录 一.分区表简介 二.分区表优势 三.分区表分类 3.1 范围分区 3.2 列表分区 3.3 散列分区 3.4 组合分区 四.分区相关操作 五.分区相关查询 附录:分区表索引失效的操作 一.分区表 ...

  3. 你们一般都是怎么进行SQL调优的?MySQL在执行时是如何选择索引的?

    前言 过年回来的第二周了,终于有时间继续总结知识了.这次来看一下SQL调优的知识,这类问题基本上面试的时候都会被问到,无论你的岗位是后端,运维,测试等等. 像本文标题中的两个问题,就是我在实际面试过程 ...

  4. SQL调优常用方法

    在使用DBMS时经常对系统的性能有非常高的要求:不能占用过多的系统内存和 CPU资源.要尽可能快的完成的数据库操作.要有尽可能高的系统吞吐量.如果系统开发出来不能满足要求的所有性能指标,则必须对系统进 ...

  5. SQL调优

    # 问题的提出 在应用系统开发初期,由于开发数据库数据比较少,对于查询SQL语句,复杂视图的的编写等体会不出SQL语句各种写法的性能优劣,但是如果将应用 系统提交实际应用后,随着数据库中数据的增加,系 ...

  6. 读《程序员的SQL金典》[4]--SQL调优

    一.SQL注入 如果程序中采用sql拼接的方式书写代码,那么很可能存在SQL注入漏洞.避免的方式有两种: 1. 对于用户输入过滤敏感字母: 2. 参数化SQL(推荐). 二.索引 ①索引分类 聚簇索引 ...

  7. [SQL SERVER系列]读书笔记之SQL注入漏洞和SQL调优

    最近读了程序员的SQL金典这本书,觉得里面的SQL注入漏洞和SQL调优总结得不错,下面简单讨论下SQL注入漏洞和SQL调优. 1. SQL注入漏洞 由于“'1'='1'”这个表达式永远返回 true, ...

  8. SQL调优日志--内存问题

    SQL调优日志--内存问题排查入门篇   概述 很多系统的性能问题,是由内存导致的.内存不够会导致页面频繁换入换出,IO队列高,进而影响数据库整体性能. 排查 内存对数据库性能非常重要.那么我当出现问 ...

  9. 读书笔记之SQL注入漏洞和SQL调优

    原文:读书笔记之SQL注入漏洞和SQL调优 最近读了程序员的SQL金典这本书,觉得里面的SQL注入漏洞和SQL调优总结得不错,下面简单讨论下SQL注入漏洞和SQL调优. 1. SQL注入漏洞 由于“' ...

  10. Oracle SQL 调优健康检查脚本

    Oracle SQL 调优健康检查脚本 我们关注数据库系统的性能,进行数据库调优的主要工作就是进行SQL的优化.良好的数据架构设计.配合应用系统中间件和写一手漂亮的SQL,是未来系统上线后不出现致命性 ...

随机推荐

  1. 《JavaScript 模式》读书笔记(6)— 代码复用模式1

    我们有开始进入新篇章了.这篇内容主要讲代码复用模式,实际上代码复用,就是继承啊,原型啊,构造函数啊等等这一类的内容.对于前端进阶来说,是很重要的基础知识.这一篇内容会对原型. 继承有很深入的讲解.我也 ...

  2. 好消息,在 Visual Studio 中可以免费使用 GitHub Copilot 了!

    前言 今天大姚给大家分享一个好消息,GitHub Copilot 可以免费使用了!在此之前若开发者要使用 GitHub Copilot 需要付费订阅,每月订阅费用起步价为 10 美元,而经过验证的学生 ...

  3. 【人工智能】【深度学习】CUDA与CUDNN安装

    cuDA安装 基础 CUDA工具包 NVIDIA CUDA 工具包提供了开发环境,可供创建经 GPU 加速的高性能应用.借助 CUDA 工具包,您可以在经 GPU 加速的嵌入式系统.台式工作站.企业数 ...

  4. Vulnhub经典靶机:from_sqli_to_shell_i386入门靶机

    靶机官网: https://www.vulnhub.com/entry/pentester-lab-from-sql-injection-to-shell,80/ 靶机镜像ISO下载地址:(转链) f ...

  5. localhost 宿主机访问

    ssh -N -f -L localhost8888:localhost:8888 hostname

  6. Spring boot 2.0 之优雅停机

    spring boot 框架在生产环境使用的有一段时间了,它"约定大于配置"的特性,体现了优雅流畅的开发过程,它的部署启动方式(java -jar xxx.jar)也很优雅.但是我 ...

  7. Qt音视频开发43-人脸识别服务端

    一.前言 上一篇文章写道人脸识别客户端程序,当然要对应一个服务端程序,客户端才能正常运行,毕竟客户端程序需要与服务端程序进行交互他才能正常工作.通常人脸识别服务端程序需要和人脸识别的相关处理库在一起, ...

  8. 解密prompt系列46. LLM结构化输出代码示例和原理分析

    最近闭源大模型们都陆续支持结构化输出,这一章我们先结合demo看下开源和闭源对结构化输出的支持,随后会介绍Constrained Decoding和Format Restricting Instruc ...

  9. 如何快速在本地运行你vue打包的的dist文件

    要在本机启动运行前端提供的dist包,需要先安装一个HTTP服务器,例如Apache,Nginx,phpstudy.这里以使用Node.js的http-server为例进行说明 首先,确保已经安装了N ...

  10. SpringCloud(四) - 微信获取用户信息

    1.项目介绍 2.微信公众平台 和 微信开放文档 2.1 微信公众平台 2.1.1 网址链接 https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?ac ...