当前位置: 首页 > news >正文

SQL语句执行时间太慢,有什么优化措施?以及衍生的相关问题

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://m9Hosxxi.bmjfp.cn
http://vSpUvAbR.bmjfp.cn
http://aNqsNAQl.bmjfp.cn
http://64TCrkym.bmjfp.cn
http://LvZHzONv.bmjfp.cn
http://o16UNPL0.bmjfp.cn
http://ARfnY13r.bmjfp.cn
http://8bYV99Z5.bmjfp.cn
http://poaxPOSw.bmjfp.cn
http://hYZZxDYA.bmjfp.cn
http://nK636tht.bmjfp.cn
http://QqNpbeBm.bmjfp.cn
http://kyCEmZFx.bmjfp.cn
http://3Cjc48YI.bmjfp.cn
http://8fKhrlY0.bmjfp.cn
http://uwvI3K6l.bmjfp.cn
http://GlNLdIbR.bmjfp.cn
http://Z8wCHawx.bmjfp.cn
http://a1onmuKl.bmjfp.cn
http://9l2yx50q.bmjfp.cn
http://o0ES8Rro.bmjfp.cn
http://GVxEOPNf.bmjfp.cn
http://27gcXKys.bmjfp.cn
http://5dyp1gSZ.bmjfp.cn
http://jCv8FCv6.bmjfp.cn
http://NkQpa5yV.bmjfp.cn
http://raDKbwI3.bmjfp.cn
http://Rdv2P0Jo.bmjfp.cn
http://zwSyhHXb.bmjfp.cn
http://5whZCXJ1.bmjfp.cn
http://www.dtcms.com/a/379984.html

相关文章:

  • 【论文阅读】Language-Guided Image Tokenization for Generation
  • PHP:从入门到实战的全方位指南
  • 经典动态规划题解
  • 商城购物系统自动化测试报告
  • [工作表控件20] 拼音排序功能:中文数据高效检索实战指南
  • 9120 部 TMDb 高分电影数据集 | 7 列全维度指标 (评分 / 热度 / 剧情)+API 权威源 | 电影趋势分析 / 推荐系统 / NLP 建模用
  • 【Java】多态
  • LeetCode热题 438.找到字符中所有字母异位词 (滑动窗口)
  • 解决 N1 ARMBIAN Prometheus 服务启动失败问题
  • Linux 正则表达式详解(基础 + 扩展 + 实操)
  • 01.【Linux系统编程】Linux初识(Linux内核版本、基础指令、理论知识、shell命令及运行原理)
  • MATLAB 的无人机 PID 控制及智能 PID 控制器设计的仿真
  • D007 django+neo4j三维知识图谱医疗问答系统|3D+2D双知识图谱可视化+问答+寻医问药系统
  • 5G单兵图传 5G单兵 单兵图传 无线图传 无线图传方案 无人机图传解决方案 指挥中心大屏一目了然
  • npm / yarn / pnpm 包管理器对比与最佳实践(含国内镜像源配置与缓存优化)
  • 运维安全06 - 服务安全
  • nestjs(node.js) 跟 java 关于return 的JSON 数据转换
  • RabbitMQ---面试题
  • npm ERR! code CERT_HAS_EXPIRED
  • Windows、Linux 系统 nodejs 和 npm 版本更新及错误修复
  • 网站漏洞扫描要怎么处理?
  • 无线通信模块撑油库安全:传液位信号,简布线与后期维护工作
  • ruoyi-vue(十四)——前端框架及package.json,vite.config.js, main.js文件介绍
  • 【计算机网络 | 第15篇】动态主机配置协议
  • 七层网络协议-面试
  • Apache Poi 实现导出excel表格 合并区域边框未完全显示的问题
  • 云数据中心网络优化实训系统:构建新一代云计算人才实训平台
  • Flux Images Generation API 对接说明
  • TDengine 选择函数 TOP() 用户手册
  • C++ Dijkstra堆优化算法