MySQL中如何查看“慢查询”,如何分析执行SQL的效率

MySQL中如何查看“慢查询”,如何分析执行SQL的效率,第1张

一、MySQL数据库有几个配置选项可以帮助我们及时捕获低效SQL语句\x0d\\x0d\1,slow_query_log\x0d\这个参数设置为ON,可以捕获执行时间超过一定数值的SQL语句。\x0d\\x0d\2,long_query_time\x0d\当SQL语句执行时间超过此数值时,就会被记录到日志中,建议设置为1或者更短。\x0d\\x0d\3,slow_query_log_file\x0d\记录日志的文件名。\x0d\\x0d\4,log_queries_not_using_indexes\x0d\这个参数设置为ON,可以捕获到所有未使用索引的SQL语句,尽管这个SQL语句有可能执行得挺快。\x0d\\x0d\二、检测mysql中sql语句的效率的方法\x0d\\x0d\1、通过查询日志\x0d\(1)、Windows下开启MySQL慢查询\x0d\MySQL在Windows系统中的配置文件一般是是myini找到[mysqld]下面加上\x0d\代码如下\x0d\log-slow-queries = F:/MySQL/log/mysqlslowquery。log\x0d\long_query_time = 2\x0d\\x0d\(2)、Linux下启用MySQL慢查询\x0d\MySQL在Windows系统中的配置文件一般是是mycnf找到[mysqld]下面加上\x0d\代码如下\x0d\log-slow-queries=/data/mysqldata/slowquery。log\x0d\long_query_time=2\x0d\说明\x0d\log-slow-queries = F:/MySQL/log/mysqlslowquery。\x0d\为慢查询日志存放的位置,一般这个目录要有MySQL的运行帐号的可写权限,一般都将这个目录设置为MySQL的数据存放目录;\x0d\long_query_time=2中的2表示查询超过两秒才记录;\x0d\\x0d\2show processlist 命令\x0d\\x0d\SHOW PROCESSLIST显示哪些线程正在运行。您也可以使用mysqladmin processlist语句得到此信息。\x0d\各列的含义和用途:\x0d\ID列\x0d\一个标识,你要kill一个语句的时候很有用,用命令杀掉此查询 //mysqladmin kill 进程号。\x0d\user列\x0d\显示单前用户,如果不是root,这个命令就只显示你权限范围内的sql语句。\x0d\host列\x0d\显示这个语句是从哪个ip的哪个端口上发出的。用于追踪出问题语句的用户。\x0d\db列\x0d\显示这个进程目前连接的是哪个数据库。\x0d\command列\x0d\显示当前连接的执行的命令,一般就是休眠(sleep),查询(query),连接(connect)。\x0d\time列\x0d\此这个状态持续的时间,单位是秒。\x0d\state列\x0d\显示使用当前连接的sql语句的状态,很重要的列,后续会有所有的状态的描述,请注意,state只是语句执行中的某一个状态,一个 sql语句,以查询为例,可能需要经过copying to tmp table,Sorting result,Sending data等状态才可以完成\x0d\info列\x0d\显示这个sql语句,因为长度有限,所以长的sql语句就显示不全,但是一个判断问题语句的重要依据。\x0d\\x0d\这个命令中最关键的就是state列,mysql列出的状态主要有以下几种:\x0d\Checking table\x0d\ 正在检查数据表(这是自动的)。\x0d\Closing tables\x0d\ 正在将表中修改的数据刷新到磁盘中,同时正在关闭已经用完的表。这是一个很快的 *** 作,如果不是这样的话,就应该确认磁盘空间是否已经满了或者磁盘是否正处于重负中。\x0d\Connect Out\x0d\ 复制从服务器正在连接主服务器。\x0d\\x0d\Copying to tmp table on disk\x0d\ 由于临时结果集大于tmp_table_size,正在将临时表从内存存储转为磁盘存储以此节省内存。\x0d\Creating tmp table\x0d\ 正在创建临时表以存放部分查询结果。\x0d\deleting from main table\x0d\ 服务器正在执行多表删除中的第一部分,刚删除第一个表。\x0d\deleting from reference tables\x0d\ 服务器正在执行多表删除中的第二部分,正在删除其他表的记录。\x0d\\x0d\Flushing tables\x0d\ 正在执行FLUSH TABLES,等待其他线程关闭数据表。\x0d\Killed\x0d\ 发送了一个kill请求给某线程,那么这个线程将会检查kill标志位,同时会放弃下一个kill请求。MySQL会在每次的主循环中检查kill标志位,不过有些情况下该线程可能会过一小段才能死掉。如果该线程程被其他线程锁住了,那么kill请求会在锁释放时马上生效。\x0d\Locked\x0d\ 被其他查询锁住了。\x0d\Sending data\x0d\ 正在处理SELECT查询的记录,同时正在把结果发送给客户端。\x0d\\x0d\Sorting for group\x0d\ 正在为GROUP BY做排序。\x0d\ Sorting for order\x0d\ 正在为ORDER BY做排序。\x0d\Opening tables\x0d\ 这个过程应该会很快,除非受到其他因素的干扰。例如,在执ALTER TABLE或LOCK TABLE语句行完以前,数据表无法被其他线程打开。正尝试打开一个表。\x0d\Removing duplicates\x0d\ 正在执行一个SELECT DISTINCT方式的查询,但是MySQL无法在前一个阶段优化掉那些重复的记录。因此,MySQL需要再次去掉重复的记录,然后再把结果发送给客户端。\x0d\\x0d\Reopen table\x0d\ 获得了对一个表的锁,但是必须在表结构修改之后才能获得这个锁。已经释放锁,关闭数据表,正尝试重新打开数据表。\x0d\Repair by sorting\x0d\ 修复指令正在排序以创建索引。\x0d\Repair with keycache\x0d\ 修复指令正在利用索引缓存一个一个地创建新索引。它会比Repair by sorting慢些。\x0d\Searching rows for update\x0d\ 正在讲符合条件的记录找出来以备更新。它必须在UPDATE要修改相关的记录之前就完成了。\x0d\Sleeping\x0d\ 正在等待客户端发送新请求\x0d\\x0d\System lock\x0d\ 正在等待取得一个外部的系统锁。如果当前没有运行多个mysqld服务器同时请求同一个表,那么可以通过增加--skip-external-locking参数来禁止外部系统锁。\x0d\Upgrading lock\x0d\ INSERT DELAYED正在尝试取得一个锁表以插入新记录。\x0d\Updating\x0d\ 正在搜索匹配的记录,并且修改它们。\x0d\\x0d\User Lock\x0d\ 正在等待GET_LOCK()。\x0d\Waiting for tables\x0d\ 该线程得到通知,数据表结构已经被修改了,需要重新打开数据表以取得新的结构。然后,为了能的重新打开数据表,必须等到所有其他线程关闭这个表。以下几种情况下会产生这个通知:FLUSH TABLES tbl_name, ALTER TABLE, RENAME TABLE, REPAIR TABLE, ANALYZE TABLE,或OPTIMIZE TABLE。\x0d\waiting for handler insert\x0d\ INSERT DELAYED已经处理完了所有待处理的插入 *** 作,正在等待新的请求。\x0d\ 大部分状态对应很快的 *** 作,只要有一个线程保持同一个状态好几秒钟,那么可能是有问题发生了,需要检查一下。\x0d\ 还有其他的状态没在上面中列出来,不过它们大部分只是在查看服务器是否有存在错误是才用得着。\x0d\\x0d\例如如图:\x0d\\x0d\3、explain来了解SQL执行的状态\x0d\explain显示了mysql如何使用索引来处理select语句以及连接表。可以帮助选择更好的索引和写出更优化的查询语句。\x0d\使用方法,在select语句前加上explain就可以了:\x0d\例如:\x0d\explain select surname,first_name form a,b where aid=bid\x0d\结果如图\x0d\\x0d\EXPLAIN列的解释\x0d\table\x0d\显示这一行的数据是关于哪张表的\x0d\type\x0d\这是重要的列,显示连接使用了何种类型。从最好到最差的连接类型为const、eq_reg、ref、range、indexhe和ALL\x0d\possible_keys\x0d\显示可能应用在这张表中的索引。如果为空,没有可能的索引。可以为相关的域从WHERE语句中选择一个合适的语句\x0d\key\x0d\实际使用的索引。如果为NULL,则没有使用索引。很少的情况下,MYSQL会选择优化不足的索引。这种情况下,可以在SELECT语句 中使用USE INDEX(indexname)来强制使用一个索引或者用IGNORE INDEX(indexname)来强制MYSQL忽略索引\x0d\key_len\x0d\使用的索引的长度。在不损失精确性的情况下,长度越短越好\x0d\ref\x0d\显示索引的哪一列被使用了,如果可能的话,是一个常数\x0d\rows\x0d\MYSQL认为必须检查的用来返回请求数据的行数\x0d\Extra\x0d\关于MYSQL如何解析查询的额外信息。将在表43中讨论,但这里可以看到的坏的例子是Using temporary和Using filesort,意思MYSQL根本不能使用索引,结果是检索会很慢\x0d\\x0d\extra列返回的描述的意义\x0d\Distinct\x0d\一旦MYSQL找到了与行相联合匹配的行,就不再搜索了\x0d\Not exists\x0d\MYSQL优化了LEFT JOIN,一旦它找到了匹配LEFT JOIN标准的行,就不再搜索了\x0d\Range checked for each Record(index map:#)\x0d\没有找到理想的索引,因此对于从前面表中来的每一个行组合,MYSQL检查使用哪个索引,并用它来从表中返回行。这是使用索引的最慢的连接之一\x0d\Using filesort\x0d\看到这个的时候,查询就需要优化了。MYSQL需要进行额外的步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键值和匹配条件的全部行的行指针来排序全部行\x0d\Using index\x0d\列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候\x0d\Using temporary\x0d\看到这个的时候,查询需要优化了。这里,MYSQL需要创建一个临时表来存储结果,这通常发生在对不同的列集进行ORDER BY上,而不是GROUP BY上\x0d\Where used\x0d\使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。如果不想返回表中的全部行,并且连接类型ALL或index,这就会发生,或者是查询有问题不同连接类型的解释(按照效率高低的顺序排序)\x0d\const\x0d\表中的一个记录的最大值能够匹配这个查询(索引可以是主键或惟一索引)。因为只有一行,这个值实际就是常数,因为MYSQL先读这个值然后把它当做常数来对待\x0d\eq_ref\x0d\在连接中,MYSQL在查询时,从前面的表中,对每一个记录的联合都从表中读取一个记录,它在查询使用了索引为主键或惟一键的全部时使用\x0d\ref\x0d\这个连接类型只有在查询使用了不是惟一或主键的键或者是这些类型的部分(比如,利用最左边前缀)时发生。对于之前的表的每一个行联合,全部记录都将从表中读出。这个类型严重依赖于根据索引匹配的记录多少—越少越好\x0d\range\x0d\这个连接类型使用索引返回一个范围中的行,比如使用>或回答于 2022-11-16

TABLE 语句

具体语法:TABLE table_name [ORDER BY column_name] [LIMIT number [OFFSET number]]

其实从语法上看,可以排序,也可以过滤记录集,不过比较简单,没有 SELECT 那么强大。

示例 1

简单的建一张很小的表 y1,记录数为 10 条。表 t1,插入 10 条记录

mysql-(ytt/3305)->create table t1 (r1 int,r2 int);

Query OK, 0 rows affected (002 sec)

mysql-(ytt/3305)->insert into t1

with recursive aa(a,b) as (

select 1,1

union all

select a+1,ceil(rand()20) from aa where a < 10

) select from aa;

Query OK, 10 rows affected (000 sec)

Records: 10  Duplicates: 0  Warnings: 0

简单全表扫描mysql-(ytt/3305)->select from t1;+------+------+| r1   | r2   |+------+------+|    1 |    1 ||    2 |    9 ||    3 |    9 ||    4 |   17 ||    5 |   17 ||    6 |   16 ||    7 |    6 ||    8 |    1 ||    9 |   10 ||   10 |    3 |+------+------+10 rows in set (000 sec)

TABLE 结果mysql-(ytt/3305)->table t1;+------+------+| r1   | r2   |+------+------+|    1 |    1 ||    2 |    9 ||    3 |    9 ||    4 |   17 ||    5 |   17 ||    6 |   16 ||    7 |    6 ||    8 |    1 ||    9 |   10 ||   10 |    3 |+------+------+10 rows in set (000 sec)

看下 table 的执行计划mysql-(ytt/3305)->explain table t1 order by r1 limit 2\G 1 row           id: 1  select_type: SIMPLE        table: t1   partitions: NULL         type: ALLpossible_keys: NULL          key: NULL      key_len: NULL          ref: NULL         rows: 10     filtered: 10000        Extra: Using filesort1 row in set, 1 warning (000 sec)

其实可以看到 TABLE 内部被 MySQL 转换为 SELECT 了。mysql-(ytt/3305)->show warnings\G 1 row  Level: Note   Code: 1003Message: / select#1 / select `ytt``t1``r1` AS `r1`,`ytt``t1``r2` AS `r2` from `ytt``t1` order by `ytt``t1``r1` limit 21 row in set (000 sec)

那其实从上面简单的例子可以看到 TABLE 在内部被转成了普通的 SELECT 来处理。示例 2应用于子查询里的子表。这里要注意,内表的字段数量必须和外表过滤的字段数量一致。克隆表 t1 结构mysql-(ytt/3305)->create table t2 like t1;Query OK, 0 rows affected (002 sec)

克隆表 t1 数据mysql-(ytt/3305)->insert into t2 table t1;Query OK, 10 rows affected (000 sec)Records: 10  Duplicates: 0  Warnings: 0

table t1 被当做内表,表 t1 有两个字段,必须同时满足 t2 检索时过滤的字段也是两个。mysql-(ytt/3305)->select from t2 where (r1,r2) in (table t1);+------+------+| r1   | r2   |+------+------+|    1 |    1 ||    2 |    9 ||    3 |    9 ||    4 |   17 ||    5 |   17 ||    6 |   16 ||    7 |    6 ||    8 |    1 ||    9 |   10 ||   10 |    3 |+------+------+10 rows in set (000 sec)

注意:这里如果过滤的字段数量和子表数量不一致,则会报错。

环境准备

安装 Visual Studio 2008 或者 Visual Studio 2010

在本机上安装 MySQL 数据库,也可使用其他机器上已经装好的 MySQL 数据库

MySQL 数据库管理工具可以创建数据库和运行 SQL 语句,这里使用 phpMyAdmin

下载并安装 MySQL Connector

开始

运行 XAMPP 程序将自动为你安装 Apache 、MySQL 和 FileZilla。安装完毕后检查服务器是否运行,下图所示的 XAMPP 控制面板显示了当前正在运行的服务。

下面的步骤展示如何通过 C# 连接到 MySQL。

Step 1

在 phpMyAdmin 打开 MySQL 管理页然后创建新数据库

Step 2

创建完数据库后紧接着创建一个名为 PhoneBook 的表:

Step 3

接下来我们打开 Visual Studio 然后创建新项目,点击 Solution Explorer (F4), 在 “Reference” 节点上右键加入新的引用,这里我们需要引用两个 dll 文件到项目中,分别是:(MySqldll (Win apps), MySqlwebdll(Web apps))

Step 4

添加命名空间到项目中

Step 5

创建一个 MySQL 连接字符串

Step 6

下面代码将插入数据到表中:

Step 7

下面函数从表中加载数据并显示在 GridView 中:

Step 8

最终的 Windows Form 结果:

TABLE 语句

具体语法:TABLE table_name [ORDER BY column_name] [LIMIT number [OFFSET number]]

其实从语法上看,可以排序,也可以过滤记录集,不过比较简单,没有 SELECT 那么强大。

示例 1

简单的建一张很小的表 y1,记录数为 10 条。表 t1,插入 10 条记录

mysql-(ytt/3305)->create table t1 (r1 int,r2 int);

Query OK, 0 rows affected (002 sec)

mysql-(ytt/3305)->insert into t1

with recursive aa(a,b) as (

select 1,1

union all

select a+1,ceil(rand()20) from aa where a < 10

) select from aa;

Query OK, 10 rows affected (000 sec)

Records: 10  Duplicates: 0  Warnings: 0

简单全表扫描mysql-(ytt/3305)->select from t1;+------+------+| r1   | r2   |+------+------+|    1 |    1 ||    2 |    9 ||    3 |    9 ||    4 |   17 ||    5 |   17 ||    6 |   16 ||    7 |    6 ||    8 |    1 ||    9 |   10 ||   10 |    3 |+------+------+10 rows in set (000 sec)

TABLE 结果mysql-(ytt/3305)->table t1;+------+------+| r1   | r2   |+------+------+|    1 |    1 ||    2 |    9 ||    3 |    9 ||    4 |   17 ||    5 |   17 ||    6 |   16 ||    7 |    6 ||    8 |    1 ||    9 |   10 ||   10 |    3 |+------+------+10 rows in set (000 sec)

看下 table 的执行计划mysql-(ytt/3305)->explain table t1 order by r1 limit 2\G 1 row           id: 1  select_type: SIMPLE        table: t1   partitions: NULL         type: ALLpossible_keys: NULL          key: NULL      key_len: NULL          ref: NULL         rows: 10     filtered: 10000        Extra: Using filesort1 row in set, 1 warning (000 sec)

其实可以看到 TABLE 内部被 MySQL 转换为 SELECT 了。mysql-(ytt/3305)->show warnings\G 1 row  Level: Note   Code: 1003Message: / select#1 / select `ytt``t1``r1` AS `r1`,`ytt``t1``r2` AS `r2` from `ytt``t1` order by `ytt``t1``r1` limit 21 row in set (000 sec)

那其实从上面简单的例子可以看到 TABLE 在内部被转成了普通的 SELECT 来处理。示例 2应用于子查询里的子表。这里要注意,内表的字段数量必须和外表过滤的字段数量一致。克隆表 t1 结构mysql-(ytt/3305)->create table t2 like t1;Query OK, 0 rows affected (002 sec)

克隆表 t1 数据mysql-(ytt/3305)->insert into t2 table t1;Query OK, 10 rows affected (000 sec)Records: 10  Duplicates: 0  Warnings: 0

table t1 被当做内表,表 t1 有两个字段,必须同时满足 t2 检索时过滤的字段也是两个。mysql-(ytt/3305)->select from t2 where (r1,r2) in (table t1);+------+------+| r1   | r2   |+------+------+|    1 |    1 ||    2 |    9 ||    3 |    9 ||    4 |   17 ||    5 |   17 ||    6 |   16 ||    7 |    6 ||    8 |    1 ||    9 |   10 ||   10 |    3 |+------+------+10 rows in set (000 sec)

注意:这里如果过滤的字段数量和子表数量不一致,则会报错。

下列哪个子句可以用于SQL中的排序: ( ) 、order by

2 在MySQL中,通常使用________语句来指定一个已有数据库作为当前工作数据库 D、USE

3Statement中的executeUpdate方法的返回值是:----- ()单选 A、int

4下列表示删除表user的语句正确的是:( ) ----- 单选 C、delete from user;

5SQL语言又称________。( ) ----- 单选 C、结构化查询语言

6下列哪些是JDBC用到的接口和类: ( ) ----- 多选 abc

7下列为MYSQL数据库的单表约束为:( ) ----- 多选 a b d

8以下关于使用JDBC连接到本地的MYSQL数据库test的时候正确的URL的写法有哪些: ----- ()多选 abc

9使用DBUtils工具完成查询 *** 作:select count() from 表;采用哪个ResultSetHandler来封装数据:----- ()单选 D、MapHandler

10

在JDBC编程中执行完下列SQL语句SELECT name, rank, serialNo

FROM employee,能得到rs的第一列数据的代码是哪两个?----- ()多选

A、sgetString(0);

B、rsgetString("name");

using System;

using SystemDataSqlClient;

using SystemData;

public class DB

{

static string Sql="server=;uid=名字;pwd=密码;database=你要连的数据库";

private SqlConnection objSqlConnection = new SqlConnection(Sql);

//以上是加载驱动你要连到的数据库

public int Update(string sql)

{

objSqlConnectionOpen();

SqlCommand objSqlCommand = new SqlCommand(sql,objSqlConnection);

int r=objSqlCommandExecuteNonQuery();

objSqlConnectionClose();

return r; //r如果是=1就是增删改成功!

}

//上面的方法是增删改

public DataSet Select(string sql)

{

DataSet objDataSet = new DataSet();

SqlDataAdapter objSqlDataAdapter = new SqlDataAdapter(sql,objSqlConnection);

objSqlDataAdapterFill(objDataSet);

return objDataSet;

}

//这个方法是查;

}

这是一个连接SQL的类;

你建这样的一个类,在哪个地方要就实例后调他的方法带入一个参数;

参数是SQL增删改查的语句;

如果要在你做的那个程序实现增删改的话就

DB db = new DB();

int i = DBUpdate(增的SQL语句);

// 返回的i是1就是成功了!

===========================

DB db = new DB();

int i = DBUpdate(删的SQL语句);

// 返回的i是1就是成功了!

==========================

DB db = new DB();

int i = DBUpdate(改的SQL语句);

// 返回的i是1就是成功了!

========================如果要查询

DB db = new DB();

DataSet ds = new DataSet();

ds = dbSelect(查的SQL语句);

//查询出的结果就放在ds里面;

通常对用户上传的需要保存到数据库中。解决方法一般有两种:一种是将保存的路径存储到数据库;另一种是将以二进制数据流的形式直接写入数据库字段中。以下为具体方法:

一、保存的上传路径到数据库:

string uppath="";//用于保存上传路径

//获取上传的文件名

string fileFullname = thisFileUpload1FileName;

//获取上传的时间,以时间作为的名字可以防止重名

string dataName = DateTimeNowToString("yyyyMMddhhmmss");

//获取的文件名(不含扩展名)

string fileName = fileFullnameSubstring(fileFullnameLastIndexOf("\\") + 1);

//获取扩展名

string type = fileFullnameSubstring(fileFullnameLastIndexOf("") + 1);

//判断是否为要求的格式

if (type == "bmp" || type == "jpg" || type == "jpeg" || type == "gif" || type == "JPG" || type == "JPEG" || type == "BMP" || type == "GIF")

{

//将上传到指定路径的文件夹

thisFileUpload1SaveAs(ServerMapPath("~/upload") + "\\" + dataName + "" + type);

//将路径保存到变量,将该变量的值保存到数据库相应字段即可

uppath = "~/upload/" + dataName + "" + type;

}

二、将以二进制数据流直接保存到数据库:

引用如下命名空间:

using SystemDrawing;

using SystemIO;

using SystemDataSqlClient;

设计数据库时,表中相应的字段类型为iamge

保存:

//路径

string strPath = thisFileUpload1PostedFileFileNameToString ();

//读取

FileStream fs = new SystemIOFileStream(strPath, FileModeOpen, FileAccessRead);

BinaryReader br = new BinaryReader(fs);

byte[] photo = brReadBytes((int)fsLength);

brClose();

fsClose();

//存入

SqlConnection myConn = new SqlConnection("Data Source=;Initial Catalog=stumanage;User ID=sa;Password=123");

string strComm = " INSERT INTO stuInfo(stuid,stuimage) VALUES(107,@photoBinary )";// *** 作数据库语句根据需要修改

SqlCommand myComm = new SqlCommand(strComm, myConn);

myCommParametersAdd("@photoBinary", SqlDbTypeBinary, photoLength);

myCommParameters["@photoBinary"]Value = photo;

myConnOpen();

if (myCommExecuteNonQuery() > 0)

{

thisLabel1Text = "ok";

}

myConnClose();

读取:

连接数据库字符串省略

myconOpen();

SqlCommand command = new

SqlCommand("select stuimage from stuInfo where stuid=107", mycon);//查询语句根据需要修改

byte[] image = (byte[])commandExecuteScalar ();

//指定从数据库读取出来的的保存路径及名字

string strPath = "~/Upload/zhangsanJPG";

string strPhotoPath = ServerMapPath(strPath);

//按上面的路径与名字保存文件

BinaryWriter bw = new BinaryWriter(FileOpen(strPhotoPath,FileModeOpenOrCreate));

bwWrite(image);

bwClose();

//显示

thisImage1ImageUrl = strPath;

采用俩种方式可以根据实际需求灵活选择。

问题

我们有一个 SQL,用于找到没有主键 / 唯一键的表,但是在 MySQL 57 上运行特别慢,怎么办

实验

我们搭建一个 MySQL 57 的环境,此处省略搭建步骤。

写个简单的脚本,制造一批带主键和不带主键的表:

执行一下脚本:

现在执行以下 SQL 看看效果:

执行了 1680s,感觉是非常慢了。

现在用一下 DBA 三板斧,看看执行计划:

感觉有点惨,由于 information_schemacolumns 是元数据表,没有必要的统计信息。

那我们来 show warnings 看看 MySQL 改写后的 SQL:

我们格式化一下 SQL:

可以看到 MySQL 将

select from A where Ax not in (select x from B) //非关联子查询

转换成了

select from A where not exists (select 1 from B where Bx = ax) //关联子查询

如果我们自己是 MySQL,在执行非关联子查询时,可以使用很简单的策略:

select from A where Ax not in (select x from B where ) //非关联子查询:1 扫描 B 表中的所有记录,找到满足条件的记录,存放在临时表 C 中,建好索引2 扫描 A 表中的记录,与临时表 C 中的记录进行比对,直接在索引里比对,

而关联子查询就需要循环迭代:

select from A where not exists (select 1 from B where Bx = ax and ) //关联子查询扫描 A 表的每一条记录 rA:     扫描 B 表,找到其中的第一条满足 rA 条件的记录。

显然,关联子查询的扫描成本会高于非关联子查询。

我们希望 MySQL 能先"缓存"子查询的结果(缓存这一步叫物化,MATERIALIZATION),但MySQL 认为不缓存更快,我们就需要给予 MySQL 一定指导。

可以看到执行时间变成了 067s。

整理

我们诊断的关键点如下:

\1 对于 information_schema 中的元数据表,执行计划不能提供有效信息。

\2 通过查看 MySQL 改写后的 SQL,我们猜测了优化器发生了误判。

\3 我们增加了 hint,指导 MySQL 正确进行优化判断。

但目前我们的实验仅限于猜测,猜中了万事大吉,猜不中就无法做出好的诊断。

以上就是关于MySQL中如何查看“慢查询”,如何分析执行SQL的效率全部的内容,包括:MySQL中如何查看“慢查询”,如何分析执行SQL的效率、mysql里面查某个数据库的所有表名,语句该怎么写不要什么show tables,因为那样会把视图也查出来!、用C#代码 远程连接到网络上的 mysql数据库等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/sjk/10187337.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-06
下一篇 2023-05-06

发表评论

登录后才能评论

评论列表(0条)

保存