Sub-item
type
status
date
slug
summary
tags
category
icon
password
Parent item
日期
Sep 22, 2023 08:15 AM
登录查看数据库使用数据库查看数据库的表查看表结构show status 命令 了解 SQL 执行次数Com_xxx 表示的是每个 xxx 语句执行的次数,以 Innodb_ 为开头的参数主要有show processlist命令查看当前 MySQL 正在进行的线程。EXPLAIN 命令分析 SQL 的执行计划索引-数据库优化的最常用和最重要的手段索引的分类建立索引普通索引:唯一索引主键索引组合索引全文索引索引的使用复合索引走不走like不走索引存在索引但是 MySQL 并不会使用查看索引使用情况常用的SQL优化InnoDB提高导入效率insert的优化group by 的优化order by 的优化优化嵌套查询count 的优化limit 分页的优化SQL 中 IN 包含的值不应该太多只需要一条数据的情况如果没有使用索引,就尽量减少排序尽量用 union all 来代替 unionwhere 条件优化查询时,尽量指定查询的字段名索引操作命令
登录
mysql -uroot -p
输入密码:root
查看数据库
show databases;
使用数据库
use information_schema
查看数据库的表
showtables;
查看表结构
desc table-name;
show status 命令 了解 SQL 执行次数
Com_xxx 表示的是每个 xxx 语句执行的次数,
我们通常关心的是 select 、insert 、update、delete 语句的执行次数,即
Com_select:执行 select 操作的次数,一次查询会使结果 + 1。
Com_insert:执行 INSERT 操作的次数,对于批量插入的 INSERT 操作,只累加一次。
Com_update:执行 UPDATE 操作的次数。
Com_delete:执行 DELETE 操作的次数。
以 Innodb_ 为开头的参数主要有
Innodb_rows_read:执行 select 查询返回的行数。
Innodb_rows_inserted:执行 INSERT 操作插入的行数。
Innodb_rows_updated:执行 UPDATE 操作更新的行数。
Innodb_rows_deleted:执行 DELETE 操作删除的行数
- Connections:查询 MySQL 数据库的连接次数,这个次数是不管连接是否成功都算上。
- Uptime:服务器的工作时间。
- Slow_queries:满查询次数。
- Threads_connected:查看当前打开的连接的数量。
show processlist命令查看当前 MySQL 正在进行的线程。
- Id :Id 就是一个标示,在我们使用 kill 命令杀死进程的时候很有用,比如 kill 进程号。
- User:显示当前的用户,如果不是 root,这个命令就只显示你权限范围内的 SQL 语句。
- Host:显示 IP ,用于追踪问题
- Db:显示这个进程目前连接的是哪个数据库,为 null 是还没有 select 数据库。
- Command:显示当前连接锁执行的命令,一般有三种:查询 query,休眠 sleep,连接 connect。
- Time:这个状态持续的时间,单位是秒
- State:显示当前 SQL 语句的状态,非常重要,下面会具体解释。
- Info:显示这个 SQL 语句。
这个命令中最关键的就是state列,mysql列出的状态主要有以下几种:
Checking table
正在检查数据表(这是自动的)。
Closing tables
正在将表中修改的数据刷新到磁盘中,同时正在关闭已经用完的表。这是一个很快的操作,如果不是这样的话,就应该确认磁盘空间是否已经满了或者磁盘是否正处于重负中。
Connect Out
复制从服务器正在连接主服务器。
Copying to tmp table on disk
由于临时结果集大于tmp_table_size,正在将临时表从内存存储转为磁盘存储以此节省内存。
Creating tmp table
正在创建临时表以存放部分查询结果。
deleting from main table
服务器正在执行多表删除中的第一部分,刚删除第一个表。
deleting from reference tables
服务器正在执行多表删除中的第二部分,正在删除其他表的记录。
Flushing tables
正在执行FLUSH TABLES,等待其他线程关闭数据表。
Killed
发送了一个kill请求给某线程,那么这个线程将会检查kill标志位,同时会放弃下一个kill请求。MySQL会在每次的主循环中检查kill标志位,不过有些情况下该线程可能会过一小段才能死掉。如果该线程程被其他线程锁住了,那么kill请求会在锁释放时马上生效。
Locked
被其他查询锁住了。
Sending data
正在处理SELECT查询的记录,同时正在把结果发送给客户端。
Sorting for group
正在为GROUP BY做排序。
Sorting for order
正在为ORDER BY做排序。
Opening tables
这个过程应该会很快,除非受到其他因素的干扰。例如,在执ALTER TABLE或LOCK TABLE语句行完以前,数据表无法被其他线程打开。正尝试打开一个表。
Removing duplicates
正在执行一个SELECT DISTINCT方式的查询,但是MySQL无法在前一个阶段优化掉那些重复的记录。因此,MySQL需要再次去掉重复的记录,然后再把结果发送给客户端。
Reopen table
获得了对一个表的锁,但是必须在表结构修改之后才能获得这个锁。已经释放锁,关闭数据表,正尝试重新打开数据表。
Repair by sorting
修复指令正在排序以创建索引。
Repair with keycache
修复指令正在利用索引缓存一个一个地创建新索引。它会比Repair by sorting慢些。
Searching rows for update
正在讲符合条件的记录找出来以备更新。它必须在UPDATE要修改相关的记录之前就完成了。
Sleeping
正在等待客户端发送新请求.
System lock
正在等待取得一个外部的系统锁。如果当前没有运行多个mysqld服务器同时请求同一个表,那么可以通过增加--skip-external-locking参数来禁止外部系统锁。
Upgrading lock
INSERT DELAYED正在尝试取得一个锁表以插入新记录。
Updating
正在搜索匹配的记录,并且修改它们。
User Lock
正在等待GET_LOCK()。
Waiting for tables
该线程得到通知,数据表结构已经被修改了,需要重新打开数据表以取得新的结构。然后,为了能的重新打开数据表,必须等到所有其他线程关闭这个表。以下几种情况下会产生这个通知:FLUSH TABLES tbl_name, ALTER TABLE, RENAME TABLE, REPAIR TABLE, ANALYZE TABLE,或OPTIMIZE TABLE。
waiting for handler insert
INSERT DELAYED已经处理完了所有待处理的插入操作,正在等待新的请求。
大部分状态对应很快的操作,只要有一个线程保持同一个状态好几秒钟,那么可能是有问题发生了,需要检查一下。
还有其他的状态没在上面中列出来,不过它们大部分只是在查看服务器是否有存在错误是才用得着。
EXPLAIN 命令分析 SQL 的执行计划
通过以上步骤查询到效率低的 SQL 语句后,可以通过 EXPLAIN 或者 DESC 命令获取 MySQL 如何执行 SELECT 语句的信息,包括在 SELECT 语句执行过程中表如何连接和连接的顺序。
- select_type:表示常见的 SELECT 类型,常见的有
SIMPLE 表示的是简单的 SQL 语句,不包括 UNION 或者子查询操作
PRIMARY ,查询中最外层的 SELECT(如两表做 UNION 或者存在子查询的外层的表操作为 PRIMARY,内层的操作为 UNION)
UNION,在 UNION 操作中,查询中处于内层的 SELECT(内层的 SELECT 语句与外层的 SELECT 语句没有依赖关系时)。
SUBQUERY:子查询中首个SELECT(如果有多个子查询存在),如我们上面的查询语句,子查询第一个是 sr(sys_role)表,所以它的 select_type 是 SUBQUERY。
- table ,这个选项表示输出结果集的表
- possible_keys :表示查询时,可能使用的索引。
- key :表示实际使用的索引。
- key_len :索引字段的长度。
- rows :扫描行的数量。
- filtered :通过查询条件查询出来的 SQL 数量占用总行数的比例。
- extra :执行情况的描述。
- type,这个选项表示表的连接类型,这个选项很有深入研究的价值,因为很多 SQL 的调优都是围绕 type 来讲的,但是这篇文章我们主要围绕优化方式来展开的,
type 这个字段会牵扯到连接的性能,它的不同类型的性能由好到差分别是
system :表中仅有一条数据时,该表的查询就像查询常量表一样。
const :当表中只有一条记录匹配时,比如使用了表主键(primary key)或者表唯一索引(unique index)进行查询。
eq-ref :表示多表连接时使用表主键或者表唯一索引,比如
select A.text, B.text where A.ID = B.ID
这个查询语句,对于 A 表中的每一个 ID 行,B 表中都只能有唯一的 B.Id 来进行匹配时。
ref :这个类型不如上面的 eq-ref 快,因为它表示的是因为对于表 A 中扫描的每一行,表 C 中有几个可能的行,C.ID 不是唯一的。
ref_or_null :与 ref 类似,只不过这个选项包含对 NULL 的查询。
index_merge :查询语句使用了两个以上的索引,比如经常在有 and 和 or 关键字出现的场景,但是在由于读取索引过多导致其性能有可能还不如 range(后面说)。
unique_subquery :这个选项经常用在 in 关键字后面,子查询带有 where 关键字的子查询中,用 sql 来表示就是这样
value IN (SELECT primary_key FROM single_table WHERE some_expr)
range :索引范围查询,常见于使用 =,<>,>,>=,<,<=,IS NULL,<=>,BETWEEN,IN() 或者 like 等运算符的查询中。
index :索引全表扫描,把索引从头到尾扫一遍。
all :这个我们接触的最多了,就是全表查询,select * from xxx ,性能最差。
上面就是 type 内容的大致解释,关于 type 我们经常会在 SQL 调优的环节使用 explain 分析其类型,然后改进查询方式,越靠近 system 其查询效率越高,越靠近 all 其查询效率越低。
System-->const-->eq_ref-->ref--> fulltext-->ref_or_null-->index_merge-->unique_subquery-->index_subquery-->range-->index-->all
索引-数据库优化的最常用和最重要的手段
索引的目的就是用于快速查找某一列的数据,对相关数据列使用索引能够大大提高查询操作的性能。
索引的分类
全局索引(FULLTEXT)
:全局索引,目前只有 MyISAM 引擎支持全局索引,它的出现是为了解决针对文本的模糊查询效率较低的问题,并且只限于 CHAR、VARCHAR 和 TEXT 列。
哈希索引(HASH)
:哈希索引是 MySQL 中用到的唯一 key-value 键值对的数据结构,很适合作为索引。HASH 索引具有一次定位的好处,不需要像树那样逐个节点查找,但是这种查找适合应用于查找单个键的情况,对于范围查找,HASH 索引的性能就会很低。默认情况下,MEMORY 存储引擎使用 HASH 索引,但也支持 BTREE 索引。
B-Tree 索引
:B 就是 Balance 的意思,BTree 是一种平衡树,它有很多变种,最常见的就是 B+ Tree,它被 MySQL 广泛使用。
R-Tree 索引
:R-Tree 在 MySQL 很少使用,仅支持 geometry 数据类型,支持该类型的存储引擎只有MyISAM、BDb、InnoDb、NDb、Archive几种,相对于 B-Tree 来说,R-Tree 的优势在于范围查找。
建立索引
普通索引:
普通索引是最基础的索引类型,它没有任何限制 。
CERATEINDEXindex_name ON table_name(column_name);
删除方式
dropindexindex_nameontable_name;
唯一索引
唯一索引:唯一索引列的值必须唯一,允许有空值,如果是组合索引,则列值的组合必须唯一.
创建
createunique indexindex_name ontable_name(column_name);
主键索引
是一种特殊的索引,一个表只能有一个主键,不允许有空值。一般是在建表的时候同时创建主键索引。
CREATETABLE 'TABLE_NAME'(
'id' int(11) NOTNULLAUTO_INCREMENT,
'title'char(255) NOTNULL,
PRIMARY KEY('id')
);
组合索引
指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引时遵循最左前缀原则,下面我们就会创建组合索引。
全文索引
主要用来查找文本中的关键字,而不是直接与索引中的值相比较,目前只有 char、varchar,text 列上可以创建全文索引,创建表的适合添加全文索引
CREATE TABLE
table
(id
int(11) NOT NULL AUTO_INCREMENT ,title
char(255) CHARACTER NOT NULL ,content
text CHARACTER NULL ,time
int(10) NULL DEFAULT NULL ,PRIMARY KEY (
id
),FULLTEXT (content)
);
当然也可以直接创建全局索引
索引的使用
复合索引走不走
创建一个复合索引
create index id_hash_index ontable_name(id,hash);
然后根据 id 进行执行计划的分析
可以发现,即使 where 条件中使用的不是复合索引(Id 、hash),索引仍然能够使用,这就是索引的前缀特性。但是如果只按照 hash 进行查询的话,索引就不会用到
like不走索引
如果 where 条件使用了 like 查询,并且
%
不在第一个字符,索引才可能被使用。如果列名是索引的话,那么对列名进行 NULL 查询,将会触发索引。
存在索引但是 MySQL 并不会使用
- 如果使用索引后比不使用索引的效率还差,那么 MySQL 就不会使用索引。
- 如果 SQL 中使用了 OR 条件,OR 前的条件列有索引,而后面的列没有索引的话,那么涉及到的索引都不会使用
- 在带有复合索引的列上查询不是第一列的数据,也不会使用索引。
- 如果 where 条件的列参与了计算,那么也不会使用索引
- 索引列使用函数,一样也不会使用索引
- 索引列使用了 like ,并且
%
位于第一个字符,则不会使用索引。
- 在 order by 操作中,排序的列同时也在 where 语句中,将不会使用索引。
- 当数据类型出现隐式转换时,比如 varchar 不加单引号可能转换为 int 类型时,会使索引无效,触发全表扫描。
- 在索引列上使用 IS NOT NULL 操作
- 在索引字段上使用 <>,!=。不等于操作符是永远不会用到索引的,因此对它的处理只会产生全表扫描。
查看索引使用情况
show statuslike'Handler_read%';
常用的SQL优化
InnoDB提高导入效率
- 因为 InnoDB 类型的表是按照主键的顺序保存的,所以将导入的数据按照主键的顺序排列,可以有效的提高导入数据的效率。如果 InnoDB 表没有主键,那么系统会默认创建一个内部列作为主键,所以如果可以给表创建一个主键,将可以利用这个优势提高导入数据的效率。
- 在导入数据前执行 SET UNIQUE_CHECKS = 0,关闭唯一性校验,在导入结束后执行SETUNIQUE_CHECKS = 1,恢复唯一性校验,可以提高导入的效率。
- 如果应用使用自动提交的方式,建议在导入前执行 SET AUTOCOMMIT = 0,关闭自动提交,导入结束后再执行 SET AUTOCOMMIT = 1,打开自动提交,也可以提高导入的效率。
insert的优化
当进行插入语句的时候,可以考虑采用下面这几种方式进行优化
如果向同一张表插入多条数据的话,最好一次性插入,这样可以减少数据库建立连接 -> 断开连接的时间,如下所示
- 如果向不同的表插入多条数据,可以使用insert delayed语句提高执行效率。delayed 的含义是让 insert 语句马上执行,要么数据都会放在内存的队列中,并没有真正写入磁盘。
- 对于 MyISAM 表来说,可以增加 bulk_insert_buffer_size 的值提高插入效率。
- 最好将索引和数据文件在不同的磁盘上存放。
group by 的优化
在使用分组和排序的场景下,如果先进行 Group By 再进行 Order By 的话,可以指定o**rder by null**禁止排序,因为 order bynull可以避免filesort,filesort 往往很耗费时间。如下所示
order by 的优化
在执行计划中,经常可以看到
Extra
列出现了 filesort,filesort 是一种文件排序,这种排序方式比较慢,我们认为是不好的排序,需要进行优化。如果创建索引和 order by 的顺序不一致,将会使用 Using filesort。
MySQL 支持两种方式的排序,filesort 和 index,Using index 是指 MySQL 扫描索引本身完成排序。index 效率高,filesort 效率低。
order by 在满足下面这些情况下才会使用 index
- order by 语句使用索引最左前列。
- 使用 where 子句与 order by 子句条件列组合满足索引最左前列。
优化嵌套查询
嵌套查询是我们经常使用的一种查询方式,这种查询方式可以使用SELECT语句来创建一个单独的查询结果,然后把这个结果当作嵌套语句的查询范围用在另一个查询语句中。使用时子查询可以将一个复杂的查询拆分成一个个独立的部分,逻辑上更易于理解以及代码的维护和重复使用。
但是某些情况下,子查询的效率不高,一般使用join来替代子查询。
从 explain 分析结果可以看到,主表查询和子查询分别是 index 和 ref,而 ref 的执行效率相对较高,一般 type 的效率由高到低是 System-->const-->eq_ref-->ref--> fulltext-->ref_or_null-->index_merge-->unique_subquery-->index_subquery-->range-->index-->all 。
count 的优化
count 我们大家用的太多了,一般都用来统计某一列结果集的行数,当 MySQL 确认括号内的表达式不可能为空时,实际上就是在统计行数。
其实 count 还有另一层统计方式:统计某个列值的数量,在统计列值数量的时候,它默认不会统计
NULL
值。我们经常犯的一个错误就是,在括号内指定一个列但是却希望统计结果集的行数。如果想要知道结果集行数的话,最好使用 count(*)。
limit 分页的优化
通常我们的系统会进行分页,一般情况下我们会使用 limit 加上偏移量来实现。同时还会加上 order by 语句进行排序。如果使用索引的情况下,效率一般不会有什么问题,如果没有使用索引的话,MySQL 就可能会做大量的文件排序操作。
通常我们可能会遇到比如 limit 1000 , 50 这种情况,抛弃 1000 条,只取 50 条,这样的代价非常高,如果所有页面被访问的频率相同,那么这样的查询平均需要访问半个表的数据。
要优化这种查询,要么限制分页的数量,要么优化大偏移量的性能。
SQL 中 IN 包含的值不应该太多
MySQL 中对 IN 做了相应的优化,MySQL 会将全部的常量存储在一个数组里面,如果数值较多,产生的消耗也会变大,比如
像这种 SQL 语句的话,能用 between 使用就不要再使用 in 了。
只需要一条数据的情况
如果只需要一条数据的情况下,推荐使用
limit 1
,这样会使执行计划中的 type 变为const
。如果没有使用索引,就尽量减少排序
尽量用 union all 来代替 union
union 和 union all 的差异主要是前者需要将结果集合并后再进行唯一性过滤操作,这就会涉及到排序,增加大量的 CPU 运算,加大资源消耗及延迟。当然,union all 的前提条件是两个结果集没有重复数据。
where 条件优化
- 避免在 WHERE 字句中对字段进行 NULL 判断
- 避免在 WHERE 中使用 != 或 <> 操作符
- 不建议使用 % 前缀模糊查询,例如 LIKE “%name”或者LIKE “%name%”,这种查询会导致索引失效而进行全表扫描。但是可以使用LIKE “name%”。
- 避免在 where 中对字段进行表达式操作,比如select user_id,user_project from table_name where age*2=36就是一种表达式操作,建议改为select user_id,user_project from table_name where age=36/2
- 建议在 where 子句中确定 column 的类型,避免 column 字段的类型和传入的参数类型不一致的时候发生的类型转换。
查询时,尽量指定查询的字段名
我们在日常使用 select 查询时,尽量使用 select 字段名 这种方式,避免直接select*,这样增加很多不必要的消耗(cpu、io、内存、网络带宽);而且查询效率比较低。
索引操作命令
ignore index
:忽略某个索引,让MySQL在查询时不使用指定的索引,这在某些情况下可以提高查询性能。例如,当某个索引存在缺陷时,可以使用ignore index
来暂时忽略它,以免影响查询效率。
force index
:强制使用某个索引,即使MySQL优化器认为其他索引更适合当前查询。这种操作常常用于对已知效率高的索引进行强制使用,或者是对性能要求极高的关键查询进行优化。
use index
:建议使用某个索引,让MySQL在查询时优先使用指定的索引。这种操作通常用于查询优化,通过指定索引来加速查询。如果MySQL优化器没有使用指定的索引,它仍然会尝试使用其他索引进行优化。
create index
:创建一个新的索引。可以在单个或多个列上创建索引,以加速查询和排序操作。
drop index
:删除一个现有的索引。可以通过索引名或者列名来指定要删除的索引。
alter table
:用于更改表的结构,包括添加、删除、修改索引等操作。可以使用add index
和drop index
子命令来添加或删除索引。
show index
:显示表的索引信息,包括索引名、列名、索引类型等。可以使用from
子命令指定要显示索引信息的表名。
analyse table
:用于对表进行分析,生成统计信息,以便优化查询和索引。可以使用index
子命令指定要分析的索引,或者使用all
子命令分析表中所有索引。
repair table
:用于修复或优化表,包括重建索引、检查表结构、删除空间等操作。可以使用quick
子命令快速修复表,或者使用extended
子命令进行更彻底的修复。
flush table
:用于刷新表的缓存,将表从内存中写入磁盘。可以使用with read lock
子命令锁定表,以便在备份或数据恢复时使用。
- 作者:fighting-bug
- 链接:https://www.fighting-bug.top//post/MysqlOptimize
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。