标签:write cells bin lin 取值 使用 money handle auto
MySQL 客户端连接成功后,通过【 show [session|global] status 】命令可以提供服务器状态信息,也可以在操作系统上使用 mysqladmin extended-status 命令获得这些消息。show [session|global] status 可以根据需要加上参数“session”或者“global”来显示 session 级(当前连接)的统计结果和 global 级(自数据库上次启动至今)的统计结果。如果不写,默认使用参数是“session”。
Com_xxx 表示每个 xxx 语句执行的次数,我们通常比较关心的是以下几个统计参数。
上面这些参数对于所有存储引擎的表操作都会进行累计。
下面这几个参数只是针对InnoDB 存储引擎的,累加的算法也略有不同。
通过以上几个参数,可以很容易地了解当前数据库的应用是以插入更新为主还是以查询操作为主,以及各种类型的 SQL 大致的执行比例是多少。对于更新操作的计数,是对执行次数的计数,不论提交还是回滚都会进行累加。
对于事务型的应用,通过 Com_commit 和 Com_rollback 可以了解事务提交和回滚的情况,对于回滚操作非常频繁的数据库,可能意味着应用编写存在问题。
此外,以下几个参数便于用户了解数据库的基本情况。
可以通过以下两种方式定位执行效率较低的 SQL 语句。
通过以上步骤查询到效率低的 SQL 语句后,可以通过 EXPLAIN 或者 DESC 命令获取 MySQL如何执行 SELECT 语句的信息,包括在 SELECT 语句执行过程中表如何连接和连接的顺序,比如想计算 2006 年所有公司的销售额,需要关联 sales 表和 company 表,并且对 moneys 字段做求和(sum)操作,相应 SQL 的执行计划如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
mysql> explain select sum (moneys) from sales a,company b where a.company_id = b.id and a. year = 2006\G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table : a type: ALL possible_keys: NULL key : NULL key_len: NULL ref: NULL rows : 1000 Extra: Using where *************************** 2. row *************************** id: 1 select_type: SIMPLE table : b type: ref possible_keys: ind_company_id key : ind_company_id key_len: 5 ref: sakila.a.company_id rows : 1 Extra: Using where ; Using index 2 rows in set (0.00 sec) |
每个列的简单解释如下:
经过以上步骤,基本就可以确认问题出现的原因。此时用户可以根据情况采取相应的措施,进行优化提高执行的效率。在上面的例子中,已经可以确认是对 a 表的全表扫描导致效率的不理想,那么对 a 表的year 字段创建索引:
1
|
mysql> create index ind_sales2_year on sales2( year ); |
创建索引后,再看一下这条语句的执行计划:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
mysql> explain select sum (moneys) from sales2 a,company2 b where a.company_id = b.id and a. year = 2006\G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table : a type: ref possible_keys: ind_sales2_year key : ind_sales2_year key_len: 2 ref: const rows : 1 Extra: Using where *************************** 2. row *************************** id: 1 select_type: SIMPLE table : b type: ref possible_keys: ind_company2_id key : ind_company2_id key_len: 5 ref: sakila.a.company_id rows : 1 Extra: Using where ; Using index 2 rows in set (0.00 sec) |
可以发现建立索引后对 a 表需要扫描的行数明显减少(从 1000 行减少到 1 行),可见索引的使用可以大大提高数据库的访问速度,尤其在表很庞大的时候这种优势更为明显。
索引是数据库优化中最常用也是最重要的手段之一,通过索引通常可以帮助用户解决大多数的 SQL 性能问题。
MyISAM 存储引擎的表的数据和索引是自动分开存储的,各自是独立的一个文件; InnoDB存储引擎的表的数据和索引是存储在同一个表空间里面,但可以有多个文件组成。
MySQL 中索引的存储类型目前只有两种(BTREE 和 HASH),具体和表的存储引擎相关:MyISAM 和 InnoDB 存储引擎都只支持 BTREE 索引;MEMORY/HEAP 存储引擎可以支持 HASH和 BTREE 索引。
MySQL 目前不支持函数索引,但是能对列的前面某一部分进索引(前缀索引),例如 name 字段,可以只取 name 的前 4 个字符进行索引,这个特性可以大大缩小索引文件的大小,用户在设计表结构的时候也可以对文本列根据此特性进行灵活设计。下面是创建前缀索引的一个例子:
1
|
mysql> create index ind_company2_name on company2( name (4)); |
索引用于快速找出在某个列中有一特定值的行。对相关列使用索引是提高 SELECT 操作性能的最佳途径。
查询要使用索引最主要的条件是查询条件中需要使用索引关键字,如果是多列索引,那么只有查询条件使用了多列关键字最左边的前缀时,才可以使用索引,否则将不能使用索引。
1.使用索引
在 MySQL 中,下列几种情况下有可能使用到索引:
(1)对于创建的多列索引,只要查询的条件中用到了最左边的列,索引一般就会被使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
#首先按 company_id,moneys 的顺序创建一个复合索引,具体如下: mysql> create index ind_sales2_companyid_moneys on sales2(company_id,moneys); Query OK, 1000 rows affected (0.03 sec) Records: 1000 Duplicates: 0 Warnings: 0 #然后按 company_id 进行表查询,具体如下: mysql> explain select * from sales2 where company_id = 2006\G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table : sales2 type: ref possible_keys: ind_sales2_companyid_moneys 208key: ind_sales2_companyid_moneys key_len: 5 ref: const rows : 1 Extra: Using where 1 row in set (0.00 sec) #可以发现即便 where 条件中不是用的 company_id 与 moneys 的组合条件,索引仍然能用到,这就是索引的前缀特性。#但是如果只按 moneys 条件查询表,那么索引就不会被用到,具体如下: mysql> explain select * from sales2 where moneys = 1\G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table : sales2 type: ALL possible_keys: NULL key : NULL key_len: NULL ref: NULL rows : 1000 Extra: Using where 1 row in set (0.00 sec) |
(2)对于使用 like 的查询,后面如果是常量并且只有%号不在第一个字符,索引才可能会被使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
mysql> explain select * from company2 where name like ‘%3‘ \G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table : company2 type: ALL possible_keys: NULL key : NULL key_len: NULL ref: NULL rows : 1000 Extra: Using where 1 row in set (0.00 sec) mysql> explain select * from company2 where name like ‘3%‘ \G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table : company2 type: range 209possible_keys: ind_company2_name key : ind_company2_name key_len: 11 ref: NULL rows : 103 Extra: Using where 1 row in set (0.00 sec) #可以发现第一个例子没有使用索引,而第二例子就能够使用索引, #区别就在于“%”的位置不同,前者把“%”放到第一位就不能用到索引,而后者没有放到第一位就使用了索引。 #另外,如果如果 like 后面跟的是一个列的名字,那么索引也不会被使用。 |
(3)如果对大的文本进行搜索,使用全文索引而不用使用 like ‘%...%’。
(4)如果列名是索引,使用 column_name is null 将使用索引。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
mysql> explain select * from company2 where name is null \G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table : company2 type: ref possible_keys: ind_company2_name key : ind_company2_name key_len: 11 ref: const rows : 1 Extra: Using where 1 row in set (0.00 sec) |
2.存在索引但不使用索引
在下列情况下,虽然存在索引,但是 MySQL 并不会使用相应的索引。
(1)如果 MySQL 估计使用索引比全表扫描更慢,则不使用索引。例如,如果列key_part1 均匀分布在 1 和 100 之间,下列查询中使用索引就不是很好:
1
|
SELECT * FROM table_name where key_part1 > 1 and key_part1 < 90; |
(2)如果使用 MEMORY/HEAP 表并且 where 条件中不使用“=”进行索引列,那么不会用到索引。heap 表只有在“=”的条件下才会使用索引。
(3)用 or 分割开的条件,如果 or 前的条件中的列有索引,而后面的列中没有索引,那么涉及到的索引都不会被用到,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
mysql> show index from sales\G; *************************** 1. row *************************** Table : sales Non_unique: 1 Key_name: ind_sales_year Seq_in_index: 1 Column_name: year 210Collation: A Cardinality: NULL Sub_part: NULL Packed: NULL Null : Index_type: BTREE Comment: 1 row in set (0.00 sec) |
从上面可以发现只有 year 列上面有索引,来看如下的执行计划:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
mysql> explain select * from sales where year = 2001 or country = ‘China‘ \G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table : sales type: ALL possible_keys: ind_sales_year key : NULL key_len: NULL ref: NULL rows : 12 Extra: Using where 1 row in set (0.00 sec) |
可见虽然在 year 这个列上存在索引 ind_sales_year,但是这个 SQL 语句并没有用到这个索引,原因就是 or 中有一个条件中的列没有索引。
(4)如果不是索引列的第一部分,如下例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
mysql> explain select * from sales2 where moneys = 1\G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table : sales2 type: ALL possible_keys: NULL key : NULL key_len: NULL ref: NULL rows : 1000 Extra: Using where 1 row in set (0.00 sec) |
可见虽然在 money 上面建有复合索引,但是由于 money 不是索引的第一列,那么在查询中这个索引也不会被 MySQL 采用。
(5)如果 like 是以%开始,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
mysql> explain select * from company2 where name like ‘%3‘ \G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table : company2 type: ALL possible_keys: NULL key : NULL key_len: NULL ref: NULL rows : 1000 Extra: Using where 1 row in set (0.00 sec) |
可见虽然在 name 上建有索引,但是由于 where 条件中 like 的值的“%”在第一位了,那么MySQL 也不会采用这个索引。
(6) 如果列类型是字符串,那么一定记得在 where 条件中把字符常量值用引号引起来,否则的话即便这个列上有索引,MySQL 也不会用到的,因为,MySQL 默认把输入的常量值进行转换以后才进行检索。如下面的例子中 company2 表中的 name 字段是字符型的,但是 SQL 语句中的条件值是一个数值型值,因此即便在 name 上有索引, MySQL 也不能正确地用上索引,而是继续进行全表扫描。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
mysql> explain select * from company2 where name = 294\G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table : company2 type: ALL possible_keys: ind_company2_name key : NULL key_len: NULL ref: NULL rows : 1000 Extra: Using where 1 row in set (0.00 sec) mysql> explain select * from company2 where name = ‘294‘ \G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table : company2 type: ref possible_keys: ind_company2_name key : ind_company2_name key_len: 23 ref: const rows : 1 Extra: Using where 1 row in set (0.00 sec) |
从上面的例子中可以看到,第一个 SQL 语句中把一个数值型常量赋值给了一个字符型的列name,那么虽然在 name 列上有索引,但是也没有用到;而第二个 SQL 语句就可以正确使用索引。
如果索引正在工作,Handler_read_key 的值将很高,这个值代表了一个行被索引值读的次数,很低的值表明增加索引得到的性能改善不高,因为索引并不经常使用。
Handler_read_rnd_next 的值高则意味着查询运行低效,并且应该建立索引补救。这个值的含义是在数据文件中读下一行的请求数。如果正进行大量的表扫描,Handler_read_rnd_next 的值较高,则通常说明表索引不正确或写入的查询没有利用索引,具体如下。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
mysql> show status like ‘Handler_read%‘ ; + -----------------------+-------+ | Variable_name | Value | + -----------------------+-------+ | Handler_read_first | 0 | | Handler_read_key | 5 | | Handler_read_next | 0 | | Handler_read_prev | 0 | | Handler_read_rnd | 0 | | Handler_read_rnd_next | 2055 | + -----------------------+-------+ 6 rows in set (0.00 sec) |
从上面的例子中可以看出,目前使用的 MySQL 数据库的索引情况并不理想。
分析表的语法如下:
1
|
ANALYZE [ LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ... |
本语句用于分析和存储表的关键字分布,分析的结果将可以使得系统得到准确的统计信息,使得 SQL 能够生成正确的执行计划。如果用户感觉实际执行计划并不是预期的执行计划,执行一次分析表可能会解决问题。在分析期间,使用一个读取锁定对表进行锁定。这对于 MyISAM, BDB 和 InnoDB 表有作用。对于 MyISAM 表,本语句与使用 myisamchk -a 相当,下例中对表 sales 做了表分析:
1
2
3
4
5
6
7
8
9
10
11
|
mysql> analyze table sales; + --------------+---------+----------+----------+ | Table | Op | Msg_type | Msg_text | + --------------+---------+----------+----------+ | sakila.sales | analyze | status | OK | + --------------+---------+----------+----------+ 1 row in set (0.00 sec) |
检查表的语法如下:
1
|
CHECK TABLE tbl_name [, tbl_name] ... [ option ] ... option = {QUICK | FAST | MEDIUM | EXTENDED | CHANGED} |
检查表的作用是检查一个或多个表是否有错误。 CHECK TABLE 对 MyISAM 和 InnoDB 表有作用。对于 MyISAM 表,关键字统计数据被更新,例如:
1
2
3
4
5
6
7
8
9
10
11
|
mysql> check table sales; + --------------+-------+----------+----------+ | Table | Op | Msg_type | Msg_text | + --------------+-------+----------+----------+ | sakila.sales | check | status | OK | + --------------+-------+----------+----------+ 1 row in set (0.00 sec) |
CHECK TABLE 也可以检查视图是否有错误,比如在视图定义中被引用的表已不存在,举例如下。
(1)首先我们创建一个视图。
1
2
|
mysql> create view sales_view3 as select * from sales3; Query OK, 0 rows affected (0.00 sec) |
(2)然后 CHECK 一下该视图,发现没有问题。
1
2
3
4
5
6
7
8
9
10
11
|
mysql> check table sales_view3; + --------------------+-------+----------+----------+ | Table | Op | Msg_type | Msg_text | + --------------------+-------+----------+----------+ | sakila.sales_view3 | check | status | OK | + --------------------+-------+----------+----------+ 1 row in set (0.00 sec) |
(3)现在删除掉视图依赖的表。
1
2
|
mysql> drop table sales3; Query OK, 0 rows affected (0.00 sec) |
(4)再来 CHECK 一下刚才的视图,发现报错了。
1
2
3
4
5
6
7
8
|
mysql> check table sales_view3\G; *************************** 1. row *************************** Table : sakila.sales_view3 Op: check Msg_type: error Msg_text: View ‘sakila.sales_view3‘ references invalid table (s) or column (s) or function (s) or definer/invoker of view lack rights to use them 1 row in set (0.00 sec) |
优化表的语法如下:
1
|
OPTIMIZE [ LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ... |
如果已经删除了表的一大部分,或者如果已经对含有可变长度行的表(含有 VARCHAR、BLOB 或 TEXT 列的表)进行了很多更改,则应使用 OPTIMIZE TABLE 命令来进行表优化。这个命令可以将表中的空间碎片进行合并,并且可以消除由于删除或者更新造成的空间浪费,但OPTIMIZE TABLE 命令只对 MyISAM、BDB 和 InnoDB 表起作用。
1
2
3
4
5
6
7
8
9
10
11
|
mysql> optimize table sales; + --------------+----------+----------+----------+ | Table | Op | Msg_type | Msg_text | + --------------+----------+----------+----------+ | sakila.sales | optimize | status | OK | + --------------+----------+----------+----------+ 1 row in set (0.00 sec) |
注意:ANALYZE、CHECK、OPTIMIZE 执行期间将对表进行锁定,因此一定注意要在数据库不繁忙的时候执行相关的操作。
当用 load 命令导入数据的时候,适当的设置可以提高导入的速度。
对于 MyISAM 存储引擎的表,可以通过以下方式快速的导入大量的数据。
ALTER TABLE tbl_name DISABLE KEYS; loading the data ALTER TABLE tbl_name ENABLE KEYS;
DISABLE KEYS 和 ENABLE KEYS 用来打开或者关闭 MyISAM 表非唯一索引的更新。在导入大量的数据到一个非空的 MyISAM 表时,通过设置这两个命令,可以提高导入的效率。对于导入大量数据到一个空的 MyISAM 表,默认就是先导入数据然后才创建索引的,所以不用进行设置。
mysql> alter table film_test2 disable keys; Query OK, 0 rows affected (0.00 sec) mysql> load data infile ‘/home/mysql/film_test.txt‘ into table film_test2; Query OK, 529056 rows affected (6.34 sec) Records: 529056 Deleted: 0 Skipped: 0 Warnings: 0 mysql> alter table film_test2 enable keys; Query OK, 0 rows affected (12.25 sec)
上面是对MyISAM表进行数据导入时的优化措施,对于InnoDB类型的表,这种方式并不能提高导入数据的效率,可以有以下几种方式提高InnoDB表的导入效率。
(1)因为 InnoDB 类型的表是按照主键的顺序保存的,所以将导入的数据按照主键的顺序排列,可以有效地提高导入数据的效率。
(2)在导入数据前执行 SET UNIQUE_CHECKS=0,关闭唯一性校验,在导入结束后执行SET UNIQUE_CHECKS=1,恢复唯一性校验,可以提高导入的效率。
SET UNIQUE_CHECKS=0; #loading the data
load data infile ‘/home/mysql/film_test.txt‘ into table film_test2;
SET UNIQUE_CHECKS=1;
(3)如果应用使用自动提交的方式,建议在导入前执行 SET AUTOCOMMIT=0,关闭自动提交,导入结束后再执行 SET AUTOCOMMIT=1,打开自动提交,也可以提高导入的效率。
SET AUTOCOMMIT=0; #loading the data
load data infile ‘/home/mysql/film_test.txt‘ into table film_test2;
SET AUTOCOMMIT=1;
当进行数据 INSERT 的时候,可以考虑采用以下几种优化方式。
insert into test values(1,2),(1,3),(1,4)...
默认情况下,MySQL 对所有 GROUP BY col1,col2....的字段进行排序。这与在查询中指定ORDER BY col1,col2...类似。因此,如果显式包括一个包含相同的列的 ORDER BY 子句,则对 MySQL 的实际执行性能没有什么影响。
如果查询包括 GROUP BY 但用户想要避免排序结果的消耗,则可以指定 ORDER BY NULL禁止排序。
在某些情况中, MySQL 可以使用一个索引来满足 ORDER BY 子句,而不需要额外的排序。WHERE 条件和 ORDER BY 使用相同的索引,并且 ORDER BY 的顺序和索引顺序相同,并且ORDER BY 的字段都是升序或者都是降序。
例如,下列 SQL 可以使用索引。
SELECT * FROM t1 ORDER BY key_part1,key_part2,... ; SELECT * FROM t1 WHERE key_part1=1 ORDER BY key_part1 DESC, key_part2 DESC; SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 DESC;
但是在以下几种情况下则不使用索引:
SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC; --order by 的字段混合 ASC 和 DESC SELECT * FROM t1 WHERE key2=constant ORDER BY key1; --用于查询行的关键字与 ORDER BY 中所使用的不相同 SELECT * FROM t1 ORDER BY key1, key2; --对不同的关键字使用 ORDER BY:
MySQL 4.1 开始支持 SQL 的子查询。这个技术可以使用 SELECT 语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中。使用子查询可以一次性地完成很多逻辑上需要多个步骤才能完成的 SQL 操作,同时也可以避免事务或者表锁死,并且写起来也很容易。但是,有些情况下,子查询可以被更有效率的连接(JOIN)替代。
连接(JOIN)之所以更有效率一些,是因为 MySQL 不需要在内存中创建临时表来完成这个逻辑上的需要两个步骤的查询工作。
对于含有 OR 的查询子句,如果要利用索引,则 OR 之间的每个条件列都必须用到索引;如果没有索引,则应该考虑增加索引。
MySQL 在处理含有 OR字句的查询时,实际是对 OR 的各个字段分别查询后的结果进行了 UNION。
SQL 提示(SQL HINT)是优化数据库的一个重要手段,简单来说就是在 SQL 语句中加入一些人为的提示来达到优化操作的目的。
1.USE INDEX
在查询语句中表名的后面,添加 USE INDEX 来提供希望 MySQL 去参考的索引列表,就可以让 MySQL 不再考虑其他可用的索引。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
mysql> explain select * from sales2 use index (ind_sales2_id) where id = 3\G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table : sales2 type: ref possible_keys: ind_sales2_id key : ind_sales2_id key_len: 5 ref: const rows : 1 Extra: Using where 1 row in set (0.00 sec). |
2.IGNORE INDEX
如果用户只是单纯地想让 MySQL 忽略一个或者多个索引,则可以使用 IGNORE INDEX 作为 HINT。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
mysql> explain select * from sales2 ignore index (ind_sales2_id) where id = 3\G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table : sales2 type: ALL possible_keys: NULL key : NULL key_len: NULL ref: NULL rows : 1000 Extra: Using where 1 row in set (0.00 sec). |
3.FORCE INDEX
为强制 MySQL 使用一个特定的索引,可在查询中使用 FORCE INDEX 作为 HINT。
例如,
当不强制使用索引的时候,因为 id 的值都是大于 0 的,因此 MySQL 会默认进行全表扫描,而不使用索引,如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
mysql> explain select * from sales2 where id > 0 \G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table : sales2 type: ALL possible_keys: ind_sales2_id key : NULL key_len: NULL ref: NULL rows : 1000 Extra: Using where 1 row in set (0.00 sec) |
但是,当使用 FORCE INDEX 进行提示时,即便使用索引的效率不是最高,MySQL 还是选择使用了索引,这是 MySQL 留给用户的一个自行选择执行计划的权力。加入 FORCE INDEX 提示后再次执行上面的 SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
mysql> explain select * from sales2 force index (ind_sales2_id) where id > 0\G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table : sales2 type: range possible_keys: ind_sales2_id key : ind_sales2_id key_len: 5 ref: NULL rows : 1000 223Extra: Using where 1 row in set (0.00 sec). |
执行计划中使用了 FORCE INDEX 后的索引。
2.IGNORE INDEX
如果用户只是单纯地想让 MySQL 忽略一个或者多个索引,则可以使用 IGNORE INDEX 作
为 HINT。
标签:write cells bin lin 取值 使用 money handle auto
原文地址:https://www.cnblogs.com/wuzm/p/11568654.html