SQL语句执行时间太慢,有什么优化措施?

可以从四个方面进行:

第一个是查询是否添加了索引

如果没有的话,为查询字段添加索引,

还有是否存在让索引失效的场景,像是没有遵循最左前缀,进行了一些类型转化

第二点是SQL语句本身的优化,

1、如避免使用SELECT *,只查询需要的字段

2、优化JOIN操作,避免笛卡尔积

如 SELECT * FROM a JOIN b(缺少 a.id = b.a_id),会产生 “笛卡尔积”(数据量 = 表 a 行数 × 表 b 行数),瞬间耗尽数据库资源。 优化:JOIN 必须加关联条件,且关联字段需建索引(如 a.id 和 b.a_id)。

3、大表与大表直接 JOIN 若两张表均有百万级数据,直接 JOIN 会产生大量中间结果,耗时极长。

第三点是表结构设计优化

像是采用分库分表的方式,解决数据量过大问题

  • 水平分表(按行拆分):将一张表按规则拆分为多张表,每张表结构相同,数据不同。常见规则:
    • 时间范围:orders_2023orders_2024(按年份拆分);
    • 哈希:user_0~user_31(按 user_id % 32 拆分)。工具:Sharding-JDBC、MyCat。
  • 垂直分库(按业务拆分):将一个数据库按业务模块拆分为多个数据库,如电商系统拆分为 user_db(用户)、order_db(订单)、product_db(商品),避免单库压力过大。

第四点是架构优化

1、像是使用redis提前存储数据,减轻数据库的请求压力,避免每次查询都访问数据库。

2、采用读写分离的方式,将 “读操作”(如查询)路由到从库,“写操作”(如插入、更新)路由到主库,避免主库读压力过大。

衍生出的问题:

1、为什么添加索引后,SQL的执行时间就变快了呐?

 首先我们要了解索引这个概念,如果将数据库比作一本,那么索引就相当于是这本书的目录,而如果没有目录的话,当查找某个内容的话,你只能一页一页查找,,数据量越大,翻页时间越长;

当添加了目录后,你就可以精准的定位到某个对应,解决无效的翻页时间。

索引的核心原理就是将“全表扫描”转化为“精准定位”

数据库表的原始数据(行数据)存储在磁盘上,默认是 “无序” 的(除非按主键排序)。当没有索引时,查询数据(如 WHERE user_id = 123)需要做以下操作:

  1. 从磁盘读取表的第一行数据,检查 user_id 是否等于 123;
  2. 不等于则继续读第二行、第三行…… 直到遍历完所有行(全表扫描);
  3. 若表有 100 万行数据,最坏情况需要读取 100 万次磁盘 —— 而磁盘 IO 是数据库性能的 “最大瓶颈”(磁盘读写速度比内存慢 1000 倍以上)。

添加索引后,情况完全不同:索引会单独创建一个 “有序的索引结构”,把 “查询条件字段(如 user_id)” 和 “行数据的磁盘地址” 关联起来,并且按 user_id 排序。此时查询 user_id = 123 的流程变成:

  1. 去索引结构中查找 user_id = 123—— 由于索引是有序的,可通过 “二分查找”(类似查字典)快速定位,只需 3~4 次磁盘 IO(100 万数据的二分查找次数仅约 20 次,远少于全表扫描的 100 万次);
  2. 从索引中获取对应行数据的磁盘地址;
  3. 直接根据地址读取目标行数据,无需遍历其他行。

底层逻辑

索引的数据结构是B + 树索引

B+树作为索引的存储结构。选择B+树的原因包括:

  • 节点可以有更多子节点,路径更短;
  • 磁盘读写代价更低,非叶子节点只存储键值和指针,叶子节点存储数据;
  • B+树适合范围查询和扫描,因为叶子节点形成了一个双向链表。

2、如何分析这条执行很慢的SQL语句?

采用explain命令,分析这条SQL的执行情况。通过keykey_len可以检查是否命中了索引,如果已经添加了索引,也可以判断索引是否有效。通过type字段可以查看SQL是否有优化空间,比如是否存在全索引扫描或全表扫描。通过extra建议可以判断是否出现回表情况,如果出现,可以尝试添加索引或修改返回字段来优化。

3、索引失效的场景

  • 没有遵循最左前缀原则。
  • 使用了模糊查询且%号在前面。
  • 在索引字段上进行了运算或类型转换。
  • 使用了复合索引但在中间使用了范围查询,导致右边的条件索引失效。

**扩展:**最左前缀原则

索引失效的最左前缀原则是针对联合索引(多字段索引)的一条核心规则, 简单来说:在联合索引中,查询条件必须从索引的第一个字段开始匹配,且中间不能跳过任何字段,否则跳过的字段及之后的字段无法使用索引,导致索引失效或部分失效。 底层原理:

联合索引在底层(如 B + 树)的存储是 “先按第一个字段排序,第一个字段相同的再按第二个字段排序,以此类推”

如对对(a, b, c) 建立联合索引

  1. 先按 a 升序排列;
  2. 当 a 相等时,按 b 升序排列;
  3. 当 a 和 b 都相等时,按 c 升序排列。

4、读写分离模式下如何保证主从数据一致性

原因:由于主库数据同步到从库存在延迟(如网络传输、SQL 执行耗时),可能导致 “主库写入数据后,从库读取不到最新数据” 的问题。

解决方式:

  • 配置合适刷盘策越
  • 减少binlog的日志量,避免大事务,拆分为事务。
  • 写读后延迟等待,比如写操作后,线程休眠一段时间,再读从库
  • 增加重试机制,:读从库时若获取到旧数据(可通过版本号或时间戳判断),重试几次(如 3 次,每次间隔 50ms),直到获取最新数据或超时后读主库。
  • 对于强一致要求的数据,像是金融-支付,可以读主库,弱一致性的数据,像是电商商品展示,日志查询,允许一定的延迟,可以读从库。

5、如何保证缓存和数据库的数据一致性,(如,一次大量的请求到来,如何添加缓存?)

核心原则:先操作数据库,然后再是缓存

方案一:

最常用的方案,适合大多数业务场景(最终一致性),流程如下:

1. 读操作

  • 先查缓存:命中则直接返回;
  • 缓存未命中:查数据库,将结果写入缓存,再返回。

2. 写操作

  • 先更新数据库;
  • 再删除缓存(而非更新缓存)。

为什么删除缓存,而不是更新? 主要是避免 “缓存更新逻辑与数据库更新逻辑不一致” 导致的错误(如数据库有触发器 / 事务,缓存更新可能漏处理);

方案二:

相对于方案一做出一点改变: 更新数据库后主动更新缓存

需要注意的点是 必须在数据库事务内更新缓存,确保数据库与缓存操作 “同成功同失败”。

方案三:

延迟双删 在高并发场景下,可能出现 “数据库已更新,但缓存删除请求因网络延迟未执行” 的情况,导致旧数据残留。

操作原理

  • 第一次删除:尽可能在数据库更新前清除旧缓存;
  • 第二次删除:针对 “数据库更新后,缓存删除请求失败” 或 “有其他线程在数据库更新期间写入了旧数据到缓存” 的场景,再次清理。

方案四:

基于 binlog 的异步更新缓存(高可用场景)

通过监听数据库 binlog(如 MySQL 的 binlog),异步更新缓存,适合读写分离、高并发场景:

  1. 流程
    • 数据库更新后,binlog 记录数据变更;
    • 监听组件解析 binlog,获取变更数据;
    • 缓存更新服务根据变更数据,异步更新或删除缓存。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/96360.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/96360.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/96360.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QtConcurrent应用解析

目录 对比传统线程 1. QtConcurrent::run() —— 异步运行函数 2.QtConcurrent::mapped() —— 并行转换 3. QtConcurrent::filter() —— 并行过滤 4. QtConcurrent::run() QFutureWatcher —— UI 异步更新 5.线程池配置 QtConcurrent 是 Qt 框架提供的一个 高级并发编…

大疆图传十公里原理:无人机图传技术解析

大疆图传系统的核心在于把发射端的能量、机载接收的灵敏度、以及环境中的衰减因素,进行科学的预算与动态的修正。简单的说,就是通过精准的链路预算来确保在最坏环境下仍有可用的信号空间。发射功率、天线增益、空中与地面的路径损耗、接收端的噪声底线等…

jmeter 带函数压测脚本

包含时间戳获取、md5值计算、随机字符串获取<?xml version"1.0" encoding"UTF-8"?> <jmeterTestPlan version"1.2" properties"5.0" jmeter"5.6.3"><hashTree><TestPlan guiclass"TestPlanGui&…

鸿蒙app日志存储

app的pid获取 import process from @ohos.process;@Entry @Component struct MainAbility {aboutToAppear(): void {console.log(this.TAG,"pid: "+process.pid)}} 获取本应用日志 在Android中可以使用logcat --pid xxxx 获取特定进程xxxx的打印日志 在鸿蒙中也有…

02.【Linux系统编程】Linux权限(root超级用户和普通用户、创建普通用户、sudo短暂提权、权限概念、权限修改、粘滞位)

目录 1. root超级用户和普通用户 2. 创建普通用户、密码设置、切换用户 3. sudo短暂提权&#xff08;给普通用户添加sudo权限&#xff09; 4. 权限 4.1 是什么 4.2 为什么有权限&#xff1f;&#xff08;权限 角色 目标属性&#xff09; 4.2.1 角色 4.2.2 目标属性 …

阿里云可观测 2025 年 8 月产品动态

本月可观测热文回顾 文章一览&#xff1a; 零代码改造&#xff01;LoongSuite AI 采集套件观测实战 性能瓶颈定位更快更准&#xff1a;ARMS 持续剖析能力升级解析 不只是告警&#xff1a;用阿里云可观测 MCP 实现 AK 高效安全审计 金蝶云•星辰基于 SLS 构建稳定高效可观测…

绿虫零碳助手:通过电费推算用电量,确认光伏装机规模

在光伏项目开发前期&#xff0c;精准掌握用电需求与合理确定装机规模是关键环节。前者决定光伏系统需满足的用电基数&#xff0c;后者影响项目投资成本与发电收益匹配度。通过电费数据推算实际用电量&#xff0c;再结合专业工具计算光伏装机参数&#xff0c;可有效降低项目规划…

融智学:构建AI时代学术的新范式

融智学&#xff1a;构建AI时代学术新范式摘要&#xff1a;邹晓辉提出的融智学为现代学术体系困境提供系统性解决方案&#xff0c;通过"问题与价值驱动"的新范式取代传统"发表驱动"模式。该体系包含三大核心&#xff1a;哲学基础&#xff08;唯文主义、信息…

【JavaEE初阶】-- JVM

文章目录1. JVM运行流程2. Java运行时数据区2.1 方法区&#xff08;内存共享&#xff09;2.2 堆&#xff08;内存共享&#xff09;2.3 Java虚拟机栈&#xff08;线程私有&#xff09;2.4 本地方法栈&#xff08;线程私有&#xff09;2.5 程序计数器&#xff08;线程私有&#x…

第十四届蓝桥杯青少组C++选拔赛[2023.1.15]第二部分编程题(4 、移动石子)

参考程序1&#xff1a;#include <bits/stdc.h> using namespace std; int main() {int N;cin >> N;vector<int> stones(N);int sum 0;for (int i 0; i < N; i) {cin >> stones[i];sum stones[i];}int target sum / N; // 每个篮子的平均值int a…

Spring Boot 的注解是如何生效的

在 Spring 中&#xff0c;Configuration、ComponentScan、Bean、Import 等注解的扫描、解析和 BeanDefinition 注册是一个分层处理的过程。下面我们以 Configuration 类为例&#xff0c;结合代码流程详细说明其从扫描到注册的完整逻辑。 1. 整体流程概览 以下是核心步骤的流程图…

Django REST Framework响应类Response详解

概述 Response 类是一个智能的 HTTP 响应类&#xff0c;能够根据客户端请求的内容类型&#xff08;Content-Type&#xff09;自动将数据渲染成合适的格式&#xff08;JSON、XML、HTML等&#xff09;。 基本用法 from rest_framework.response import Response# 最简单的用法 de…

# 小程序 Web 登录流程完整解析

登录流程完整小白解析&#xff08;小程序 & Web&#xff09; 在开发中&#xff0c;登录是每个系统最基础的功能。为了让小白也能理解&#xff0c;我们用通俗类比和流程讲解 小程序登录、Web 登录、Token 刷新、安全存储等整个过程。1️⃣ 小程序登录流程&#xff08;小白理…

安装vcenter6.7 第二阶段安装很慢 或卡在50%

DNS、FQDN配置的问题采用VCSA安装vCenter时&#xff0c;第一步安装还算顺利&#xff0c;第二步就会安装失败&#xff0c;而且还特别慢&#xff0c;这是因为部署时需要DNS服务器&#xff0c;下面就是不采用DNS服务器的部署方案。第一步&#xff1a;正常安装&#xff0c;DNS就写本…

第十六届蓝桥杯软件赛 C 组省赛 C++ 题解

大家好&#xff0c;今天是 2025 年 9 月 11 日&#xff0c;我来给大家写一篇关于第十六届蓝桥杯软件赛 C 组省赛的C 题解&#xff0c;希望对大家有所帮助&#xff01;&#xff01;&#xff01; 创作不易&#xff0c;别忘了一键三连 题目一&#xff1a;数位倍数 题目链接&…

项目帮助文档的实现

项目帮助文档的实现 代码如下&#xff1a; #ifndef __M_HELPER_H__ #define __M_HELPER_H__ #include <iostream> #include <fstream> #include <string> #include <vector> #include <sqlite3.h> #include <random> #include <sstream…

python逆向-逆向pyinstaller打包的exe程序反编译获取源代码

python逆向-逆向pyinstaller打包的exe程序反编译获取源代码 Pyinstaller pyinstaller 是一个用于将 Python 程序打包成独立可执行文件的工具&#xff0c;能够在没有 Python 解释器的情况下运行。 Python 脚本转换为 Windows、macOS 和 Linux 操作系统上的可执行文件。 把Python…

【SQL】-- sql having 和 where 的 区别

HAVING 和 WHERE 都是用来筛选数据的&#xff0c;但它们的应用场景有所不同。WHERE&#xff1a;用于筛选行数据&#xff0c;通常在 FROM 子句之后执行。它在分组操作 (GROUP BY) 之前应用&#xff0c;用来筛选出符合条件的记录。示例&#xff1a;SELECT name, age FROM employe…

MySQL,SQL Server,PostgreSQL三种数据库各自的优缺点,分别适用哪些场景

MySQL的优缺点及适用场景优点开源免费&#xff0c;社区版可商用&#xff0c;成本低。轻量级&#xff0c;安装配置简单&#xff0c;适合中小型项目。读写性能优异&#xff0c;尤其在OLTP&#xff08;在线事务处理&#xff09;场景下表现突出。支持主从复制、分片等扩展方案&…

Java 类加载机制双亲委派与自定义类加载器

我们来深入解析 Java 类加载机制。这是理解 Java 应用如何运行、如何实现插件化、以及解决一些依赖冲突问题的关键。一、核心概念&#xff1a;类加载过程一个类型&#xff08;包括类和接口&#xff09;从被加载到虚拟机内存开始&#xff0c;到卸载出内存为止&#xff0c;它的整…