40.javaEE-mysql数据库(三)-引擎/索引/sql优化

mysql数据库引擎

什么是存储引擎?
数据库存储引擎是数据库底层软件组件,数据库管理系统使用数据引擎进行创建、查询、更新和删除数据操作。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能,使用不同的存储引擎还可以获得特定的功能。

查看数据库引擎的语句

SHOW ENGINES

40.javaEE-mysql数据库(三)-引擎/索引/sql优化_第1张图片
InnoDB
1.事务型数据库的首选引擎,InnoDB 作为默认存储引擎。
2.支持事务安全表(ACID).
3.支持行锁定和外键.

MyISAM
是基于 ISAM 的存储引擎,并对其进行扩展,是在 Web、数据仓储和其他应用环境下最常使用的存储引擎之一。MyISAM 拥有较高的插入、查询速度,但不支持事务。

MEMORY
存储引擎将表中的数据存储到内存中,为查询和引用其他数据提供快速访问,如果数据库重启或崩溃,数据丢失,因此它非常适合存储临时数据。

各数据库引擎之间的对比:
40.javaEE-mysql数据库(三)-引擎/索引/sql优化_第2张图片

索引

一.索引概述

索引是数据库性能调优技术的基础,常用于实现数据的快速检索。索引对于数据库就好比书的目录对于书一样的重要.

在 MySQL 中,通常有以下两种方式访问数据库表的行数据:
1) 顺序访问
顺序访问是在表中实行全表扫描,从头到尾逐行遍历,直到找到符合的数据.
特点:
1.实现简单.
2.但是当表中有大量数据的时候,效率非常低下
2) 索引访问
索引访问是通过遍历索引来直接访问表中记录行的方式。查找目录找到对应的数据存储位置,获取数据.
特点:
1.需要先创建索引.
2.索引运用得到,会大大提高sql执行效率.
3.索引创建不当会拖慢数据库插入/修改的效率

二.索引的语法

1.创建索引
1).使用ALTER TABLE语句创建索引。

alter table table_name add index index_name (column_list) ;
alter table table_name add unique (column_list) ;
alter table table_name add primary key (column_list) ;

例如:

ALTER TABLE emp ADD INDEX sal_index (sal);

ALTER TABLE emp ADD UNIQUE (id);

ALTER TABLE emp add PRIMARY KEY(id);

2).使用CREATE INDEX语句对表增加索引。

create index index_name on table_name (column_list) ;
create unique index index_name on table_name (column_list) ;
例如:

CREATE INDEX bonus_index ON emp (bonus);

CREATE UNIQUE INDEX id_index on emp (id);

2.删除索引
drop index index_name on table_name ;
alter table table_name drop index index_name ;
alter table table_name drop primary key ;

DROP INDEX bonus_index on emp;

ALTER TABLE emp DROP INDEX id_index;

三.索引的分类

1.按照索引存储的数据结构分类

1.1 hash索引

原理:

哈希索引也称为散列索引或 HASH索引。也就是将需要索引的列进行hash运算,将所有的值放入一张hash表中.查找数据时,直接将值hash,然后取到内存地址.

支持hash索引的引擎:

MySQL 目前仅有 MEMORY 存储引擎和 HEAP存储引擎支持这类索引。innodb是不支持hash索引的.

优势劣势:

1.查找数据非常快,因为是运用的hash表,直接取到目标数据的存储位置.
2.如果发生hash碰撞次数过大,会影响索引的效率.
3.不支持范围查询,因为经过hash运算之后的值,排列顺序不同,没有办法运用hash索引.
4.HASH 索引不支持键的部分匹配,因为在计算 HASH 值的时候是通过整个索引值来计算的。

1.2 B+ 树索引
1.2.1 二叉搜索树
二叉树,每个结点只存储一个关键字,等于则命中,小于走左结点,大于走右结点;

1.2.2 B-树:多路搜索树,每个结点存储M/2到M个关键字,非叶子结点存储指向关键字范围的子结点;所有关键字在整颗树中出现,且只出现一次,非叶子结点可以命中;

1.2.3 B+树:在B-树基础上,为叶子结点增加链表指针,所有关键字都在叶子结点中出现,非叶子结点作为叶子结点的索引;B+树总是到叶子结点才命中;并且所有的叶子节点按顺序组合成一个双向链表结构.
40.javaEE-mysql数据库(三)-引擎/索引/sql优化_第3张图片

支持B+索引的引擎:
innodb默认只有B+索引,mylSAM和MEMORY也支持B+树索引.

面试问题:为什么索引使用B+树而不是B树做索引呢?
1.B+树的根节点不存放数据,而B树的根节点存放数据,因为计算机IO操作非常耗时,如果在根节点存放数据,则会导致树增高,并且IO次数过多.举个简单的例子,你见过那本书的目录是写了书的内容的.
2.B+树的所有叶子节点构成一个双向链表,在进行范围操作的时候可以直接从链表上找数据,非常的快.
聚集索引和非聚集索引的区别是什么?
聚集索引就是索引的顺序和实际存储的顺序是一致的,而非聚集索引则是随机的.举个例子,字典里有两种目录结构,一种是拼音目录,一种是笔画目录,但是字典的内容是按照拼音先后顺序编写的,而不是按照笔画编写的,所以innodb引擎支持的表,主键是唯一索引,并且一张表只有一个聚集索引,其他的都是非聚集索引.

2.根据索引的具体用途分类

1) 普通索引
普通索引是最基本的索引类型,唯一任务是加快对数据的访问速度,没有任何限制。创建普通索引时,通常使用的关键字是 INDEX 或 KEY。
2) 唯一性索引
唯一性索引是不允许索引列具有相同索引值的索引。如果能确定某个数据列只包含彼此各不相同的值,在为这个数据列创建索引的时候就应该用关键字 UNIQUE 把它定义为一个唯一性索引。创建唯一性索引的目的往往不是为了提高访问速度,而是为了避免数据出现重复。
3) 主键索引
主键索引是一种唯一性索引,即不允许值重复或者值为空,并且每个表只能有一个主键。主键可以在创建表的时候指定,也可以通过修改表的方式添加,必须指定关键字 PRIMARY KEY。
注意:主键是数据库考察的重点。注意每个表只能有一个主键。
4) 空间索引
空间索引主要用于地理空间数据类型 GEOMETRY。
5) 全文索引
全文索引只能在 VARCHAR 或 TEXT 类型的列上创建,并且只能在 MyISAM 表中创建。

我们还可以分为:
单列索引就是索引只包含原表的一个列。组合索引也称为复合索引或多列索引,相对于单列索引来说,组合索引是将原表的多个列共同组成一个索引。

比如,在一个表中创建了一个组合索引(c1,c2,c3),在实际查询中,系统用来实际加速的索引有三个:单个索引(c1)、双列索引(c1,c2)和多列索引(c1,c2,c3)。

为了提高索引的应用性能,MySQL中的索引可以根据具体应用采用不同的索引策略。这些索引策略所对应的索引类型有聚集索引、次要索引、覆盖索引、复合索引、前缀索引、唯一索引等。

3.索引使用注意事项.

使用索引时需要注意的地方
1.首先要满足最左前缀原则 ,如果不是按照索引的最左列开始查找,则无法使用索引。
2.不能跳过索引中的列。例如:where A = ‘1’ and C = ‘3’;在这当中,只有 A 能使用索引,C 不能使用索引。

3.MySQL 能在索引中做最左前缀匹配的 like 比较,但是如果是通配符开头的 like 比较,就不能使用索引。例如:like’name%’ 可以使用索引,like ‘%name’ 则不能使用索引。

4.如果查询中有某个列的范围查询,则其右边所有列都无法使用索引优化查找。例如4.:where A = ‘name’ and B like ‘name%’ and C = ‘name’; 这里只有 A,B 能使用索引,C不能使用索引。

5.where 条件中包含表达式或函数的参数不能使用索引。例如 where A + 1 = 2; 使用了表达式,不能使用索引。

6.将选择性最高的列放在索引最前列。

7.order by 子句和查找型查询的限制是一样的,需要满足索引的最左前缀的要求。

使用explain执行sql计划来验证是否使用索引.
40.javaEE-mysql数据库(三)-引擎/索引/sql优化_第4张图片
EXPLAIN 语句输出结果的各个行的解释如下:

id:select 识别符,这是 select 查询序列号。

select_type: 表示查询中 select 类型,简单或复杂。

table:显示这一行的数据是关于哪张表的。

type:连接类型,结果值从好到坏依次是:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL。一般来说,得保证查询至少达到 range 级别,最好能达到 ref。如果值为 index,则说明 MySQL 使用了索引扫描来做排序。

possible_keys :指出 MySQL 能使用哪个索引在表中找到行,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用。

key: 显示 MySQL 在查询中实际使用的索引,若没有使用索引,显示为 NULL。

key_len :表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度,也就是是否完全充分的使用了索引.

ref :表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值。

rows :表示 MySQL 根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数。

Extra :包含不适合在其他列中显示但十分重要的额外信息。如

using where(这表示 MySQL 服务器将存储引擎返回行以后再应用 where 过滤条件);
using index(使用了覆盖索引)。
using filesort(MySQL需要进行额外的步骤来发现如何对返回的行排序,它根据连接类型以及存储排序键值和匹配条件的全部行的行指针来排序全部行,一般看到这个,查询就需要优化了);
using temporary(这表示MySQL需要创建一个临时表来存储结果,这通常发生在对不同的列集进行order by上,而不是group by上,一般看到这个,查询就需要优化了)。

例子:

#我建立了一个联合索引(name,sal,bonus),则在系统中会有三个索引(name),(name,sal),(name,sal,bonus)
#索引使用的列越多,检索效果就越好.
#1.使用了索引(name,sal,bonus)
EXPLAIN SELECT * FROM emp WHERE name='齐雷' and sal =2500 and bonus = 600;
#2.使用了索引(name,sal,bonus),这证明了和条件的位置无关,引擎会自动给我们优化条件位置
EXPLAIN SELECT * FROM emp WHERE  sal =2500 and bonus = 600 and name='齐雷';
#3.使用了索引(name,sal)
EXPLAIN SELECT * FROM emp WHERE name='齐雷' and sal =2500;
#4.使用了索引(name)
EXPLAIN SELECT * FROM emp WHERE name='齐雷'  and bonus = 600;
#5.没使用上索引
EXPLAIN SELECT * FROM emp WHERE sal =2500 and bonus = 600;
#6.使用了索引(name)
EXPLAIN SELECT * FROM emp WHERE name='齐雷';
#7.没使用索引
EXPLAIN SELECT * FROM emp WHERE sal = 2500;
#8.没使用索引
EXPLAIN SELECT * FROM emp WHERE bonus =600;

sql优化

步骤:
1.首先找出具体那些sql语句执行时间长,效率慢.

查看是否开启: show variables like ‘%slow_query_log%’;
开启慢查询日志:set global slow_query_log=1; (重启会失效)
设置阀值命令: set global long_query_time=3 (修改为阀值到3秒钟的就是慢sql)

慢查询-查询较慢的语句输出在日志文档中.
(1) 找出mysql的配置初始化文件.
C:\ProgramData\MySQL\MySQL Server 5.7/my.ini

#日志输出存放在文件中
log-output=FILE  
general-log=0
general_log_file="LAPTOP-KV01ES52.log"
slow-query-log=1 
#慢查询日志输出文件位置
slow_query_log_file="LAPTOP-KV01ES52-slow.log"
#慢查询时间阈值,也就是超过10秒的sql语句都会输出到此文件中.
long_query_time=10

2.慢查询日志文件:
C:\ProgramData\MySQL\MySQL Server 5.7\Data\LAPTOP-KV01ES52-slow.log
运用工具找出筛选出你想要优化的sql语句.

3.执行sql语句优化(使用explain优化sql语句)
1、对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。

2、应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。

3、应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select id from t where num=0

4、应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or num=20
可以这样查询:
select id from t where num=10 union all select id from t where num=20

5、下面的查询也将导致全表扫描:
select id from t where name like ‘%abc%’
若要提高效率,可以考虑全文检索。

6、in 和 not in 也要慎用,否则会导致全表扫描,如:
select id from t where num in(1,2,3)
对于连续的数值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3

7、如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:
select id from t where num=@num
可以改为强制查询使用索引:
select id from t with(index(索引名)) where num=@num

8、应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2=100
应改为:
select id from t where num=100*2

9、应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3)=‘abc’–name以abc开头的id
select id from t where datediff(day,createdate,‘2005-11-30’)=0–'2005-11-30’生成的id
应改为:
select id from t where name like ‘abc%’
select id from t where createdate>=‘2005-11-30’ and createdate<‘2005-12-1’

10、不要在where子句中的=左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。

11、在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。

12、不要写一些没有意义的查询,如需要生成一个空表结构:
select col1,col2 into #t from t where 1=0
这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:
create table #t(…)

13、很多时候用 exists 代替 in 是一个好的选择:
select num from a where num in(select num from b)
用下面的语句替换:
select num from a where exists(select 1 from b where num=a.num)

14、并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。

15、索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。

16、应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。

17、尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

18、尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。

19、任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。

20、尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。

21、避免频繁创建和删除临时表,以减少系统表资源的消耗。

22、临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。

23、在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。

24、如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。

25、尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。

26、使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。

27、与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。

28、在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。

29、尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。

30、尽量避免大事务操作,提高系统并发能力。
4.索引优化(用explain检测索引是否用好)
(1) 是否添加索引.
(2) 是否使用到索引.
(3)是否充分使用索引.

你可能感兴趣的:(javaEE,数据库)