数据库优化 - MYSQL优化

判断问题SQL
判断SQL是否有问题时可以通过两个表象进行判断:

系统级别表象
CPU消耗严重
IO等待严重
页面响应时间过长
应用的日志出现超时等错误
可以使用sar命令,top命令查看当前系统状态。

也可以通过Prometheus、Grafana等监控工具观察系统状态。(感兴趣的可以翻看我之前的文章)

SQL语句表象
冗长
执行时间过长
从全表扫描获取数据
执行计划中的rows、cost很大
冗长的SQL都好理解,一段SQL太长阅读性肯定会差,而且出现问题的频率肯定会更高。更进一步判断SQL问题就得从执行计划入手,如下所示:

640?wx_fmt=png

执行计划告诉我们本次查询走了全表扫描Type=ALL,rows很大(9950400)基本可以判断这是一段"有味道"的SQL。

获取问题SQL
不同数据库有不同的获取方法,以下为目前主流数据库的慢查询SQL获取工具

MySQL

慢查询日志
测试工具loadrunner
Percona公司的ptquery等工具


Oracle

AWR报告
测试工具loadrunner等
相关内部视图如v$、$session_wait等
GRID CONTROL监控工具
达梦数据库AWR报告
测试工具loadrunner等
达梦性能监控工具(dem)
相关内部视图如v$、$session_wait等
SQL编写技巧
SQL编写有以下几个通用的技巧:

• 合理使用索引

索引少了查询慢;索引多了占用空间大,执行增删改语句的时候需要动态维护索引,影响性能 选择率高(重复值少)且被where频繁引用需要建立B树索引;一般join列需要建立索引;复杂文档类型查询采用全文索引效率更好;索引的建立要在查询和DML性能之间取得平衡;复合索引创建时要注意基于非前导列查询的情况

 

使用UNION ALL替代UNION

UNION ALL的执行效率比UNION高,UNION执行时需要排重;UNION需要对数据进行排序

• 避免select * 写法

执行SQL时优化器需要将 * 转成具体的列;每次查询都要回表,不能走覆盖索引。

• JOIN字段建议建立索引

一般JOIN字段都提前加上索引

• 避免复杂SQL语句

提升可阅读性;避免慢查询的概率;可以转换成多个短查询,用业务端处理

• 避免where 1=1写法

• 避免order by rand()类似写法

RAND()导致数据列被多次扫描
 

SQL优化

执行计划

完成SQL优化一定要先读执行计划,执行计划会告诉你哪些地方效率低,哪里可以需要优化。我们以MYSQL为例,看看执行计划是什么。(每个数据库的执行计划都不一样,需要自行了解)explain sql

字段	解释
id	每个被独立执行的操作标识,标识对象被操作的顺序,id值越大,先被执行,如果相同,执行顺序从上到下
select_type	查询中每个select 字句的类型
table	被操作的对象名称,通常是表名,但有其他格式
partitions	匹配的分区信息(对于非分区表值为NULL)
type	连接操作的类型
possible_keys	可能用到的索引
key	优化器实际使用的索引(最重要的列) 从最好到最差的连接类型为const、eq_reg、ref、range、index和ALL。当出现ALL时表示当前SQL出现了“坏味道”
key_len	被优化器选定的索引键长度,单位是字节
ref	表示本行被操作对象的参照对象,无参照对象为NULL
rows	查询执行所扫描的元组个数(对于innodb,此值为估计值)
filtered	条件表上数据被过滤的元组个数百分比
extra	执行计划的重要补充信息,当此列出现Using filesort , Using temporary 字样时就要小心了,很可能SQL语句需要优化

接下来我们用一段实际优化案例来说明SQL优化的过程及优化技巧。

优化案例

  • 表结构

CREATE TABLE `a`(`id`          int(11) NOT NULL AUTO_INCREMENT,`seller_id`   bigint(20)                                       DEFAULT NULL,`seller_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,`gmt_create`  varchar(30)                                      DEFAULT NULL,PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE `b`(`id`          int(11) NOT NULL AUTO_INCREMENT,`seller_name` varchar(100) DEFAULT NULL,`user_id`     varchar(50)  DEFAULT NULL,`user_name`   varchar(100) DEFAULT NULL,`sales`       bigint(20)   DEFAULT NULL,`gmt_create`  varchar(30)  DEFAULT NULL,PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE `c`(`id`         int(11) NOT NULL AUTO_INCREMENT,`user_id`    varchar(50)  DEFAULT NULL,`order_id`   varchar(100) DEFAULT NULL,`state`      bigint(20)   DEFAULT NULL,`gmt_create` varchar(30)  DEFAULT NULL,PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

三张表关联,查询当前用户在当前时间前后10个小时的订单情况,并根据订单创建时间升序排列,具体SQL如下

select a.seller_id,a.seller_name,b.user_name,c.state
from a,b,c
where a.seller_name = b.seller_nameand b.user_id = c.user_idand c.user_id = 17and a.gmt_createBETWEEN DATE_ADD(NOW(), INTERVAL – 600 MINUTE)AND DATE_ADD(NOW(), INTERVAL 600 MINUTE)
order by a.gmt_create;

查看数据量  

原执行时间

原执行计划

初步优化思路1,SQL中 where条件字段类型要跟表结构一致,表中user_id 为varchar(50)类型,实际SQL用的int类型,存在隐式转换,也未添加索引。将b和c表user_id 字段改成int类型。
2,因存在b表和c表关联,将b和c表user_id创建索引
3,因存在a表和b表关联,将a和b表seller_name字段创建索引
4,利用复合索引消除临时表和排序

 

初步优化SQLalter table b modify `user_id` int(10) DEFAULT NULL;
alter table c modify `user_id` int(10) DEFAULT NULL;
alter table c add index `idx_user_id`(`user_id`);
alter table b add index `idx_user_id_sell_name`(`user_id`,`seller_name`);
alter table a add index `idx_sellname_gmt_sellid`(`gmt_create`,`seller_name`,`seller_id`);

查看优化后执行时间

查看优化后执行计划

查看warnings信息

继续优化   alter table a modify "gmt_create" datetime DEFAULT NULL;

查看执行时间

查看执行计划

总结1,查看执行计划 explain
2,如果有告警信息,查看告警信息 show warnings;
3,查看SQL涉及的表结构和索引信息
4,根据执行计划,思考可能的优化点
5,按照可能的优化点执行表结构变更、增加索引、SQL改写等操作
6,查看优化后的执行时间和执行计划
7,如果优化效果不明显,重复第四步操作

 

原文链接:https://blog.csdn.net/jianzhang11/article/details/102867120

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

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

相关文章

队列入队和出队程序演示

#include <stdio.h> #include <string.h> #include <malloc.h>typedef struct Queue {int * pBase;int front;int rear;} QUEUE;void init(QUEUE *); bool en_queue(QUEUE *pQ, int val); //入队 void traverse_queue(QUEUE * pQ); bool full_queue(QUEUE…

图的遍历DFS与BFS(邻接表)

#include "stdafx.h" #include <iostream> #include <fstream> #include <queue> #include <Windows.h>using namespace std;#define INFINITY INT_MAX #define MAX_VERTEX_NUM 20 //顶点最多个数 #define LENGTH 5 //顶点字符长…

递归求n的阶层算法实现

专题:递归 定义:一个函数自己直接或间接调用自己 递归满足三个条件 1、递归必须得有-一个明确的中止条件 2、该函数所处理的数据规模必须在递减 3、这个转化必须是可解的 #include <stdio.h> #include <string.h> #include <malloc.h> #include <stdlib…

递归求1到100的和算法演示

循环和递归 递归:易于理解、速度慢、存储空间大 循环&#xff1a;不易理解、速度快、存储空间小 #include <stdio.h> #include <string.h> #include <malloc.h> #include <stdlib.h>long sum_sum(int n) {if (1 n) {return 1;}else {int sum n sum…

递归实现汉诺塔程序

[将汉诺塔]这不是线性递归&#xff0c;这是个非线性递归! n1 1 n2 3 n3 7 ................. n64 2的64次方减1 [这是个天文数字&#xff0c;就算世界上最快的计算机也解决不了 汉诺塔的复杂度是2的n次方减一 问题很复杂&#xff0c;恒真正解决问题的编码就…

解决scanf_s函数报错:没有为格式字符串传递足够的参数

报错&#xff1a;scanf_s函数报错:没有为格式字符串传递足够的参数 int main() {char s1[20] "i m name";char *p1, *p2;char s3[10] {0};p1 s1;p2 s3;printf("请输入你的名字: \n");scanf_s("%s",&s3);printf("%s\n", s3);f…

已知先序和中序求后序

先序: ABCDEFGH 中序: BDCEAFHG 求后序:DECBHGFA 具体看B站郝斌视频&#xff1a;https://www.bilibili.com/video/BV11s41167h6?p71

微软的Surface平板电脑RT版,RT是什么缩写?|转|

http://www.guokr.com/question/240887/ 1# 其实rt是win8家族的新成员&#xff0c;专为arm系列处理器设计的windows 8版本&#xff0c;不兼容win32程序 2# zzllrr 2012-06-21 09:28:19 找到权威解答了&#xff1a;“RT”代表“Runtime”的意思。既Windows Runtime Library&…

字符串移位包含

题目&#xff1a;给定两个字符串s1和s2&#xff0c;要求判定s2是否能够被s1做循环移位得到的字符串包含。例如&#xff0c;给定s1AABCD和s2CDAA,返回true;给定s1ABCD和s2ACBD,返回false。 答&#xff1a; #include "stdafx.h" #include <iostream>using namesp…

C++分文件的编写函数

不分文件编写函数&#xff1a; #include<iostream>using namespace std;//函数的声明 void swap(int a, int b); //函数的定义void swap(int a, int b) {int temp a;a b;b temp;cout << "a的值" << a << endl;cout << "b的值…

C++ 空指针和野指针

//1&#xff0c;指针变量指向内存中编号为0的空间为空指针 //2&#xff0c;空指针指向的内存空间是不可以访问的 #include<iostream> using namespace std; int main() {int a 10;int * p &a;cout << p << endl;cout << &a << e…

GL 与 CV 管线 (pipeline) 比较与相互转换

这里把二者作一个比较&#xff0c;并对相关的矩阵做一个联系以使能够相互转换。 1. CV中图像形成过程 (transformation stages) 这一过程可概括为四个坐标系的转换&#xff0c;分别为&#xff1a;世界坐标系、相机坐标系、图像坐标系、像素坐标系&#xff0c;如下图&#xff1a…

C++结构体作为函数参数传参

#include<iostream> using namespace std;#include<string>//结构体 struct Student {string name;int age;int score;}st3;/**结构体作为函数参数传参*/ //值传递 void printStufdent1(struct Student st3) {cout << "子函数" << endl;st3.…

C++结构体中const使用场景

看如下代码&#xff1a; #include<iostream> using namespace std;#include<string>//结构体 struct Student {string name;int age;int score;}st3;//值传递 void printStufdents(struct Student st2) {cout << "子函数" << endl;st2.age…

C++结构体案例

#include<iostream> using namespace std;#include<string>//学生结构体 struct Student {string name;int age;int score; };struct Teacher {string tName;struct Student sArray[5];};//定义一个给老师学生赋值的函数 void allocateSpace(struct Teacher tArray…

C++ rand()函数和srand()函数

随机数和伪随机的区别 &#xff1a; #include<iostream> using namespace std; #include<string>#include<string> #include <stdlib.h> #include <time.h> #define MIN 0 //随机数产生的范围 #define MAX 99 //函数声明…

301重定向IIS设置,及伪静态后 301重定向出错案例分享

301重定向&#xff0c;网络上的知识已经很多了&#xff0c;我在这第一次实践过程中&#xff0c;出现了一个算是低级的错误&#xff0c;但也比较典型&#xff0c;也许其它新手也会碰到&#xff0c;分享一下&#xff1a; 一、301重定向实施&#xff1a; 我们哪里体检网&#xff0…

C++ 通讯录设计(一)

#include<iostream> #include<string> using namespace std;//- 封装函数显示该界面如void showMenu() // - 在main函数中调用封装好的函数//菜单界面函数功能 void showMenu() {cout << "***********************" << endl;cout << &…

C++ 通讯录设计(二)

功能描述: 实现添加联系人功能&#xff0c;联系人上限为1000人&#xff0c;联系人信息包括(姓名、性别、年龄、联系电话、家庭住址) 添加联系人实现步骤: ●设计联系人结构体 ●设计通讯录结构体 ●main函数中创建通讯录 ●封装添加联系人函数 ●测试添加联系人功能 1.1&#…

C++ 通讯录设计(三)

显示联系人 功能描述:显示通讯录中已有的联系人信息 显示联系人实现步骤: . ●封装显示联系人函数 ●测试显示联系人功能 1.1封装显示联系人函数 思路:判断如果当前通讯录中没有人员,就提示记录为空&#xff0c;人数大于0,显示通讯录中信息 //显示联系人 void showPerson(Addr…