电商系统架构设计系列(十):怎么能避免写出慢SQL?

news2024/12/24 10:25:52

上篇文章中,我给你留了一个思考题:怎么能避免写出慢SQL?

我们知道,一个慢 SQL 就可以直接让 MySQL 瘫痪。以我个人经验总结来看,一般情况下系统出问题,大多数都是因为SQL语句的问题。掌握和用好了SQL,可以避免至少80%以上的系统问题。

所以,这篇文章我们一起看一下,怎么才能避免写出危害数据库的慢 SQL。

引言

所谓慢 SQL,就是执行特别慢的 SQL 语句。

什么样的 SQL 语句是慢 SQL?多慢才算是慢 SQL?并没有一个非常明确的标准或者说是界限。

但并不是说,我们就很难区分正常的 SQL 和慢 SQL,在大多数实际的系统中,慢 SQL 消耗掉的数据库资源,往往是正常 SQL 的几倍、几十倍甚至几百倍,所以还是非常容易区分的。

但问题是,我们不能等着系统上线,慢 SQL 吃光数据库资源之后,再找出慢 SQL 来改进,那样就晚了。

那么,怎样才能在开发阶段尽量避免写出慢 SQL 呢?

定量认识 MySQL

慢 SQL 对数据库的影响,是一个量变到质变的过程,对“量”的把握,就很重要。作为一个合格的程序员,你需要对数据库的能力,有一个定量的认识。

影响 MySQL 处理能力的因素很多,比如:服务器的配置、数据库中的数据量大小、MySQL 的一些参数配置、数据库的繁忙程度等等。但是,通常情况下,这些因素对于 MySQL 性能和处理能力影响范围,大概在几倍的性能差距。

所以,我们不需要精确的性能数据,只要掌握一个大致的量级,就足够指导我们的开发工作了。

一台 MySQL 数据库,大致处理能力的极限是,每秒一万条左右的简单 SQL,这里的“简单 SQL”,指的是类似于主键查询这种不需要遍历很多条记录的 SQL。根据服务器的配置高低,可能低端的服务器只能达到每秒几千条,高端的服务器可以达到每秒钟几万条,所以这里给出的一万 TPS 是中位数的经验值。考虑到正常的系统不可能只有简单 SQL,所以实际的 TPS 还要打很多折扣。

我的经验数据,一般一台 MySQL 服务器,平均每秒钟执行的 SQL 数量在几百左右,就已经是非常繁忙了,即使看起来 CPU 利用率和磁盘繁忙程度没那么高,你也需要考虑给数据库“减负”了。

另外一个重要的定量指标是,到底多慢的 SQL 才算慢 SQL。这里面这个“慢”,衡量的单位本来是执行时长,但是时长这个东西,我们在编写 SQL 的时候并不好去衡量。那我们可以用执行 SQL 查询时,需要遍历的数据行数替代时间作为衡量标准,因为查询的执行时长基本上是和遍历的数据行数正相关的。

你在编写一条查询语句的时候,可以依据你要查询数据表的数据总量,估算一下这条查询大致需要遍历多少行数据。如果遍历行数在百万以内的,只要不是每秒钟都要执行几十上百次的频繁查询,可以认为是安全的。遍历数据行数在几百万的,查询时间最少也要几秒钟,你就要仔细考虑有没有优化的办法。遍历行数达到千万量级和以上的,我只能告诉你,这种查询就不应该出现在你的系统中。当然,我们这里说的都是在线交易系统,离线分析类系统另说。

遍历行数在千万左右,是 MySQL 查询的一个坎儿。MySQL 中单个表数据量,也要尽量控制在一千万条以下,最多不要超过二三千万这个量级。原因也很好理解,对一个千万级别的表执行查询,加上几个 WHERE 条件过滤一下,符合条件的数据最多可能在几十万或者百万量级,这还可以接受。但如果再和其他的表做一个联合查询,遍历的数据量很可能就超过千万级别了。所以,每个表的数据量最好小于千万级别。

如果数据库中的数据量就是很多,而且查询业务逻辑就需要遍历大量数据怎么办?

使用索引避免全表扫描

使用索引可以有效地减少执行查询时遍历数据的行数,提高查询性能。

数据库索引的原理也很简单,举个例子你就明白了。比如说,有一个无序的数组,数组的每个元素都是一个用户对象。如果说我们要把所有姓李的用户找出来。比较笨的办法是,用一个循环把数组遍历一遍。

有没有更好的办法?很多办法是吧?比如说,我们用一个 Map(在有些编程语言中是 Dictionary)来给数组做一个索引,Key 保存姓氏,值是所有这个姓氏的用户对象在数组中序号的集合。这样再查找的时候,就不用去遍历数组,先在 Map 中查找,然后再直接用序号去数组中拿用户数据,这样查找速度就快多了。

这个例子对应到数据库中,存放用户数据的数组就是表,我们构建的 Map 就是索引。实际上数据库的索引,和编程语言中的 Map 或者 Dictionary,它们的数据结构都是差不多的,基本上就是各种 B 树和 HASH 表。

绝大多数情况下,我们编写的查询语句,都应该使用索引,避免去遍历整张表,也就是通常说的,避免全表扫描。你在每次开发新功能,需要给数据库增加一个新的查询时,都要评估一下,是不是有索引可以支撑新的查询语句,如果有必要的话,需要新建索引来支持新增的查询。

但是,增加索引付出的代价是,会降低数据插入、删除和更新的性能。这个也很好理解,增加了索引,在数据变化的时候,不仅要变更数据表里的数据,还要去变更每个索引。所以,对于更新频繁并且对更新性能要求较高的表,可以尽量少建索引。而对于查询较多更新较少的表,可以根据查询的业务逻辑,适当多建一些索引

怎么写 SQL 能更好地使用索引,查询效率更高,这是一门手艺,需要丰富的经验,不是通过一下的学习能练成的。但是,我们是有方法,可以评估写出来的 SQL 的查询性能怎么样,是不是一个潜在的“慢 SQL”。

逻辑不是很复杂的单表查询,我们可能还可以分析出来,查询会使用哪个索引。但如果是比较复杂的多表联合查询,我们单看 SQL 语句本身,就很难分析出查询到底会命中哪些索引,会遍历多少行数据。MySQL 和大部分数据库,都提供一个帮助我们分析查询功能:执行计划。

分析 SQL 执行计划

在 MySQL 中使用执行计划也非常简单,只要在你的 SQL 语句前面加上 EXPLAIN 关键字,然后执行这个查询语句就可以了。

举个例子说明,比如有一个用户表,包含用户 ID、姓名、部门编号和状态这几个字段:

 我们希望查询某个二级部门下的所有人,查询条件就是,部门代号以 00028 开头的所有人。下面这两个 SQL,他们的查询结果是一样的,都满足要求,但是,哪个查询性能更好呢?

SELECT * FROM user WHERE left(department_code, 5) = '00028';
SELECT * FROM user WHERE department_code LIKE '00028%';

我们分别查看一下这两个 SQL 的执行计划:

我们一起来分析一下这两个 SQL 的执行计划:

首先来看 rows 这一列,rows 的含义就是,MySQL 预估执行这个 SQL 可能会遍历的数据行数。第一个 SQL 遍历了四千多行,这就是整个 User 表的数据条数;第二个 SQL 只有 8 行,这 8 行其实就是符合条件的 8 条记录。显然第二个 SQL 查询性能要远远好于第一个 SQL。

为什么第一个 SQL 需要全表扫描,第二个 SQL 只遍历了很少的行数呢?注意看 type 这一列,这一列表示这个查询的访问类型。ALL 代表全表扫描,这是最差的情况。range 代表使用了索引,在索引中进行范围查找,因为第二个 SQL 语句的 WHERE 中有一个 LIKE 的查询条件。如果直接命中索引,type 这一列显示的是 index。如果使用了索引,可以在 key 这一列中看到,实际上使用了哪个索引。

通过对比这两个 SQL 的执行计划,就可以看出来,第二个 SQL 虽然使用了普遍认为低效的 LIKE 查询条件,但是仍然可以用到索引的范围查找,遍历数据的行数远远少于第一个 SQL,查询性能更好。

总结

在开发阶段,衡量一个 SQL 查询语句查询性能的手段是,估计执行 SQL 时需要遍历的数据行数。遍历行数在百万以内,可以认为是安全的 SQL,百万到千万这个量级则需要仔细评估和优化,千万级别以上则是非常危险的。为了减少慢 SQL 的可能性,每个数据表的行数最好控制在千万以内。

索引可以显著减少查询遍历数据的数量,所以提升 SQL 查询性能最有效的方式就是,让查询尽可能多的命中索引,但索引也是一把双刃剑,它在提升查询性能的同时,也会降低数据更新的性能。

对于复杂的查询,最好使用 SQL 执行计划,事先对查询做一个分析。在 SQL 执行计划的结果中,可以看到查询预估的遍历行数,命中了哪些索引。执行计划也可以很好地帮助你优化你的查询语句。

感谢阅读,如果你觉得这篇文章对你有一些启发,也欢迎把它分享给你的朋友。

思考题

在电商的交易类系统中,如何正确地使用 Redis 这样的缓存系统呢?需要考虑哪些问题?

期待、欢迎你留言或在线联系,与我一起讨论交流,“一起学习,一起成长”。

上一篇文章

电商系统架构设计系列(九):如何规划和设计分库分表?


推荐阅读

  • 【总结】SQL性能优化技巧整理汇总
  • 采坑案例:分析执行计划
  • 【架构】高可用高并发系统设计原则
  • 【总结】互联网技术架构中常用的分库分表方案汇总
  • 技术破局,业绩狂飙十倍:亿级电商平台重构大揭秘
  • 当我们聊高并发时,到底是在聊什么?如何真正地掌握高并发设计能力?
  • 微服务架构实战 - 我的经验分享总结2019(系统架构师)架构演进过程-从信息流架构到电商中台架构​​​​​​

系列分享

  • Elasticsearch教程
  • 微服务架构实战
  • 架构思维成长系列
  • 电商系统架构设计系列

------------------------------------------------------

------------------------------------------------------

我的CSDN主页

关于我(个人域名,更多我的信息)

我的开源项目集Github

期望和大家 一起学习,一起成长,共勉,O(∩_∩)O谢谢

如果你有任何建议,或想学习的知识,可与我一起讨论交流

欢迎交流问题,可加个人QQ 469580884,

或者,加我的群号 751925591,一起探讨交流问题

不讲虚的,只做实干家

Talk is cheap,show me the code

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件面试题:文件上传下载测试点

目前关于云文档的业务还是挺多的,相信出去面试的同学,大多会遇到这道高频软件测试面试题:文件上传下载测试点。今天向大家分享下,希望对大家有所启发。 一、文件上传测试点 1、文件大小 一般情况下,系统会设定上传文…

基于OV2640/ OV5640 的图像采集显示系统

基于OV2640/ OV5640 的图像采集显示系统系列文章目录: (1)基于 OV5640 摄像头理论知识讲解-成像和采样原理 (2)基于 OV5640 摄像头理论知识讲解-数字接口和控制接口 (3)基于 OV5640 摄像头理论知…

Mac软件删除方法?如何删除不会有残留

Mac电脑如果有太多无用的应用程序,很有可能会拖垮Mac系统的运行速度。因此,卸载电脑中无用的软件是优化Mac系统运行速度的最佳方式之一。Mac卸载应用程序的方式是和Windows有很大的区别,特别对于Mac新用户来说,如何无残留的卸载删…

Python Qt(七)Listview

源代码: # -*- coding: utf-8 -*-# Form implementation generated from reading ui file qt_listview.ui # # Created by: PyQt5 UI code generator 5.15.9 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not…

2024王道408数据结构P144 T18

2024王道408数据结构P144 T18 思考过程 首先还是先看题目的意思,让我们在中序线索二叉树里查找指定结点在后序的前驱结点,这题有一点难至少对我来说…我讲的不清楚理解一下我做的也有点糊涂。在创建结构体时多两个变量ltag和rtag,当ltag0时…

基于Axios完成前后端分离项目数据交互

一、安装Axios npm i axios -S 封装一个请求工具:request.js import axios from axios// 创建可一个新的axios对象 const request axios.create({baseURL: http://localhost:9090, // 后端的接口地址 ip:porttimeout: 30000 })// request 拦截器 // 可以自请求…

全网首发,人体姿态估计算法在OK3588上部署应用(十三)

一、主机模型转换 采用FastDeploy来部署应用深度学习模型到OK3588板卡上 进入主机Ubuntu的虚拟环境 conda activate ok3588 主机环境搭建可以参考上一篇 《OK3588板卡实现人像抠图(十二)》 生成onnx文件 cd FastDeploy # 下载Paddle静态图模型并解压…

【AGC】集成APMS SDK后台无数据问题

【问题描述】 开发者按照文档集成了APMS SDK,但是在AGC后台没有数据,需要帮忙定位。 【问题分析】 后台没有性能数据的原因有很多,要从端侧和与云侧进行定位分析。 1. 首先需要查看端侧的调试日志,调试日志可以直观的看到性…

【Linux】系统启动过程

草稿 文章目录 〇、概述一、固件加载:BIOS / UEFI1.1 BIOS 和 UEFI1.2 UEFI 启动过程1.21 1.21.3 二、启动管理器阶段三、内核阶段四、init阶段五、登录阶段 〇、概述 Linux系统的启动过程是一个复杂而有趣的话题,它涉及到计算机的硬件、软件、操作系统…

【链表OJ】相交链表 环形链表1

前言: 💥🎈个人主页:​​​​​​Dream_Chaser~ 🎈💥 ✨✨刷题专栏:http://t.csdn.cn/UlvTc ⛳⛳本篇内容:力扣上链表OJ题目 目录 一.leetcode 160. 相交链表 1.问题描述: 2.解题思路: 二.leetcode 141.环形链表 …

2024毕业设计选题指南【附选题大全】

title: 毕业设计选题指南 - 如何选择合适的毕业设计题目 date: 2023-08-29 categories: 毕业设计 tags: 选题指南, 毕业设计, 毕业论文, 毕业项目 - 如何选择合适的毕业设计题目 当我们站在大学生活的十字路口,毕业设计便成了我们面临的一项重要使命。这不仅是对我们…

centos安装Nginx配置Nginx

1. 查看操作系统有没有安装Nginx which nginx 2. 使用epel的方式进行安装(方法二) 先安装epel sudo yum install yum-utils 安装完成后,查看安装的epel包即可 sudo yum install epel 3 开始安装nginx 上面的两个方法不管选择哪个&…

数学建模——校园供水系统智能管理

import pandas as pd data1pd.read_excel("C://Users//JJH//Desktop//E//附件_一季度.xlsx") data2pd.read_excel("C://Users//JJH//Desktop//E//附件_二季度.xlsx") data3pd.read_excel("C://Users//JJH//Desktop//E//附件_三季度.xlsx") data4…

MySQL官网下载安装包

MySQL官网: MySQL MySQL 8.0官网下载地址: MySQL :: Download MySQL Community Server 2023-07-18 MySQL 8.1.0 发布,这是 MySQL 变更发版模型后的第一个创新版本 (Innovation Release) 。 如果在官网中找不到下载位置,点击第二个…

【【萌新的STM32-22中断概念的简单补充】】

萌新的STM32学习22-中断概念的简单补充 我们需要注意的是这句话 从上面可以看出,STM32F1 供给 IO 口使用的中断线只有 16 个,但是 STM32F1 的 IO 口却远远不止 16 个,所以 STM32 把 GPIO 管脚 GPIOx.0~GPIOx.15(xA,B,C,D,E,F,G)分别对应中断…

RV64和ARM64栈结构差异

RV64和ARM64栈结构差异 1 RV64和ARM64栈结构差异示意图1.1 RV64和ARM64寄存器介绍1.1.1 RV64寄存器1.1.2 ARM64寄存器 1.2 RV64和ARM64栈结构差异示意图 2 RV64和ARM64栈使用示例2.1 测试的程序2.2 RV64反汇编的汇编程序2.3 ARM64反汇编的汇编程序2.4 RV64和ARM64测试程序的栈结…

Future

Future Future接口由FutureTask 实现类定义了操作异步任务执行的一些方法,比如异步任务的执行结果、取消任务的执行、判断任务是否被取消、判断任务执行是否完毕等。Future 接口可以为主线程开一个分支任务,专门为主线程处理耗时和费力的业务。 Future…

制造业物联网革命:智慧工厂数据采集与远程监控管理

智慧工厂是指运用现代信息技术和物联网技术,实现制造业生产过程的智能数字化。智慧工厂的工业设备不仅能够自动化运行,还可以通过网络技术帮助企业实现数据采集、远程监控与管理。4G工业网关便成为了智慧工厂通讯的重要组成部分,起到了连接工…

uni-app开发小程序中遇到的map地图的点聚合以及polygon划分区域问题

写一篇文章来记录以下我在开发小程序地图过程中遇到的两个小坑吧,一个是点聚合,用的是joinCluster这个指令,另一个是polygon在地图上划分多边形的问题: 1.首先说一下点聚合问题,由于之前没有做过小程序地图问题&#…

LNMP架构之搭建Discuz论坛

LNMP 一、编译安装Nginx1)前置准备2)开始编译安装3)添加到系统服务(systemd启动) 二、编译安装MySQL服务1)前置准备2)编译安装3)编辑配置文件4)更改mysql安装目录和配置文…