python入门教程13-08 (python语法入门之python索引原理与慢查询优化)

每个语言在开发软件的过程中就提前为相应的字段加上索引,学好python这门语言更需要对索引有很高的认知,下面我们一起来看看吧!

1、索引介绍、方法、类型

①介绍

可以帮助用户快速的找到需要的内容;在MySQL中也叫做“键”,是存储引擎用于快速找到记录的一种数据结构。能够大大提高查询效率。特别是当数据量非常大,查询涉及多个表时,使用索引往往能使查询速度加快成千上万倍。

总结:

索引的目的在于提高查询效率,与我们查阅图书所用的目录是一个道理:先定位到章,然后定位到该章下的一个小节,然后找到页数。相似的例子还有:查字典,查地图等等

本质:

通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据。

②索引方法- MySQL 的 B+TREE 改进索引

就是一种将索引值按一定的算法,存入一个树形的数据结构中

概念铺垫——B-Tree数据结构

扫描二维码关注公众号,回复: 12734495 查看本文章

结构 B-TREE 每个节点都是一个二元数组: [key, data],所有节点都可以存储数据。key为索引key,data为除key之外的数据。 结构图

检索原理

首先从根节点进行二分查找,如果找到则返回对应节点的data,否则对相应区间的指针指向的节点递归进行查找,直到找到节点或未找到节点返回null指针。 缺点

插入删除新的数据记录会破坏B-Tree的性质,因此在插入删除时,需要对树进行一个分裂、合并、转移等操作以保持B-Tree性质。造成IO操作频繁。

区间查找可能需要返回上层节点重复遍历,IO操作繁琐。

概念铺垫——B+Tree(B-Tree的变种)

结构 与B-Tree相比,B+Tree有以下不同点:非叶子节点不存储data,只存储索引key;只有叶子节点才存储data。 结构图

这里写图片描述

Mysql中B+Tree介绍

结构 在经典B+Tree的基础上进行了优化,增加了顺序访问指针。在B+Tree的每个叶子节点增加一个指向相邻叶子节点的指针,就形成了带有顺序访问指针的B+Tree。这样就提高了区间访问性能:如果要查询key为从18到49的所有数据记录,当找到18后,只需顺着节点和指针顺序遍历就可以一次性访问到所有数据节点,极大提到了区间查询效率(无需返回上层父节点重复遍历查找减少IO操作)。 结构图

这里写图片描述 Mysql选择B+TREE索引的原因

索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗,相对于内存存取,I/O存取的消耗要高几个数量级,所以索引的结构组织要尽量减少查找过程中磁盘I/O的存取次数,提升索引效率。 MyISAM & InnoDB 都使用B+Tree索引结构。但是底层索引存储不同,MyISAM 采用非聚集索引,而InnoDB采用聚集索引。

这里写图片描述

这里写图片描述

更多关于MySQL 索引资料链接

https://www..com/gym333/p/6877023.html

https://www.jianshu.com/p/486a514b0ded

https://www.jianshu.com/p/0371c9569736

③索引方法-HASH索引

hash就是一种(key=>value)形式的键值对,允许多个key对应相同的value,但不允许一个key对应多个value,为某一列或几列建立hash索引,就会利用这一列或几列的值通过一定的算法计算出一个hash值,对应一行或几行数据。hash索引可以一次定位,不需要像树形索引那样逐层查找,因此具有极高的效率。

④HASH 与 B+TREE 比较

hash类型的索引:查询单条快,范围查询慢

btree类型的索引:b+树,层数越多,数据量越大,范围查询和随机查询快(innodb默认索引类型)

不同的存储引擎支持的索引类型也不一样

InnoDB支持事务,支持行级别锁定,支持 Btree、Hash 等索引,不支持Full-text 索引;

MyISAM不支持事务,支持表级别锁定,支持 Btree、Full-text 等索引,不支持 Hash 索引;

Memory不支持事务,支持表级别锁定,支持 Btree、Hash 等索引,不支持 Full-text 索引;

NDB支持事务,支持行级别锁定,支持 Hash 索引,不支持 Btree、Full-text 等索引;

Archive不支持事务,支持表级别锁定,不支持 Btree、Hash、Full-text 等索引;

⑤索引类型

普通索引:加速查询

创建表+索引

创建表同时添加name字段为普通索引

create table tb1(

id int not null auto_increment primary key,

name varchar(100) not null,

index idx_name(name)

);

创建索引

单独为表指定普通索引

create index idx_name on tb1(name);

删除索引

drop index idx_name on tb1;

查看索引

show index from tb1;

查看索引、列介绍

·Table 表的名称。

·Non_unique 如果索引为唯一索引,则为0,如果可以则为1。

·Key_name 索引的名称

·Seq_in_index 索引中的列序列号,从1开始。

·Column_name 列名称。

·Collation 列以什么方式存储在索引中。在MySQL中,有值‘A’(升序)或NULL(无分类)。

·Cardinality 索引中唯一值的数目的估计值。

·Sub_part 如果列只是被部分地编入索引,则为被编入索引的字符的数目。如果整列被编入索引,则为NULL。

·Packed 指示关键字如何被压缩。如果没有被压缩,则为NULL。

·Null 如果列含有NULL,则含有YES。如果没有,则该列含有NO。

·Index_type 用过的索引方法(BTREE, FULLTEXT, HASH, RTREE)。

·Comment 多种评注

唯一索引:加速查询 和 唯一约束(可含一个null值)

创建表+唯一(unique)索引

            create table tb2(

              id int not null auto_increment primary key,

              name varchar(50) not null,

              age int not null,

              unique index idx_age (age) 

            );

创建unique索引

create unique index idx_age on tb2(age);

主键索引:加速查询 和 唯一约束(不可含null)、一个表中最多只能有一个主键索引

创建表 + 主键

方式一:

create table tb3(

id int not null auto_increment primary key,

name varchar(50) not null,

age int default 0

);

方式二:

create table tb3(

id int not null auto_increment,

name varchar(50) not null,

age int default 0 ,

primary key(id)

);

创建主键

alter table tb3 add primary key(id);

删除主键

方式一

alter table tb3 drop primary key;

方式二:

如果当前主键为自增主键,则不能直接删除.需要先修改自增属性,再删除

alter table tb3 modify id int ,drop primary key;

组合索引:组合索引是将n个列组合成一个索引

创建表+组合索引

create table tb4(

id int not null ,

name varchar(50) not null,

age int not null,

index idx_name_age (name,age)

);

创建组合索引

create index idx_name_age on tb4(name,age);

索引应用场景

比如你在为某商场做一个会员卡的系统。这个系统有一个会员表包含下列字段:

会员编号 INT、会员姓名 VARCHAR(10)、会员身份证号码 VARCHAR(18)、会员电话 VARCHAR(10)

会员住址 VARCHAR(50)、会员备注信息 TEXT

那么这个会员编号,作为主键,使用 PRIMARY

会员姓名如果要建索引的话,那么就是普通的 INDEX

会员身份证号码如果要建索引的话,那么可以选择 UNIQUE(唯一的,不允许重复)

2、聚合索引、辅助索引

①聚集索引:InnoDB表索引组织表,即表中数据按主键B+树存放,叶子节点直接存放整条数据,每张表只能有一个聚集索引。

当你定义一个主键时,InnnodDB存储引擎则把它当做聚集索引 如果你没有定义一个主键,则InnoDB定位到第一个唯一索引,且该索引的所有列值均飞空的,则将其当做聚集索引 如果表没有主键或合适的唯一索引INNODB会产生一个隐藏的行ID值6字节的行ID聚集索引 补充:由于实际的数据页只能按照一颗B+树进行排序,因此每张表只能有一个聚集索引,聚集索引对于主键的排序和范围查找非常有利。 例子: 比如图书馆新进了一批书。那么这些书需要放到图书馆内。书如何放一般都有一个规则,杂志类的放到101货架,文学类的放到102货架,理工类的放到103货架等等。这些存储的规则决定了每本书应该放到哪里,找到对应的货架就相当于找到了所有的书。而这个例子中聚集索引为书的类别。

②辅助索引:(也称非聚集索引)是指叶节点不包含行的全部数据,叶节点除了包含键值之外,还包含一个书签连接,通过该书签再去找相应的行数据。

对于这种方式来说,你需要两个步骤:

查询该记录所在的位置。 通过该位置去取要找的记录。

③二者区别以及适用场景

二者区别:

相同的是:不管是聚集索引还是辅助索引,其内部都是B+树的形式,即高度是平衡的,叶子结点存放着所有的数据。

不同的是:聚集索引叶子结点存放的是一整行的信息,而辅助索引叶子结点存放的是单个索引列信息。

何时使用聚集索引或非聚集索引

动作描述 使用聚集索引 使用非聚集索引 列经常被分组排序 应 应 返回某范围内的数据 应 不应 一个或极少不同值 不应 不应 频繁更新的列 不应 应 外键列 应 应 主键列 应 应 频繁修改索引列 不应 应

3、测试索引

①创建数据

-- 1.创建表

CREATE TABLE userInfo(

id int NOT NULL,

name VARCHAR(16) DEFAULT NULL,

age int,

sex char(1) not null,

email varchar(64) default null

)ENGINE=MYISAM DEFAULT CHARSET=utf8;

注意:MYISAM存储引擎不产生引擎事务,数据插入速度极快,为方便快速插入测试数据,等我们插完数据,再把存储类型修改为InnoDB

②创建存储过程,插入数据

-- 2.创建存储过程

delimiter$$

CREATE PROCEDURE insert_user_info(IN num INT)

BEGIN

DECLARE val INT DEFAULT 0;

DECLARE n INT DEFAULT 1;

-- 循环进行数据插入

WHILE n <= num DO

    set val = rand()*50;

    INSERT INTO userInfo(id,name,age,sex,email)values(n,concat('alex',val),rand()*50,if(val%2=0,'女','男'),concat('alex',n,'@qq.com'));

    set n=n+1;

end while;

END $$

delimiter;

③调用存储过程,插入500万条数据

call insert_user_info(5000000);

④此步骤可以忽略。修改引擎为INNODB

ALTER TABLE userinfo ENGINE=INNODB;

⑤测试索引

·在没有索引的前提下测试查询速度

SELECT * FROM userinfo WHERE id = 4567890;

注意:无索引情况,mysql根本就不知道id等于4567890的记录在哪里,只能把数据表从头到尾扫描一遍,此时有多少

个磁盘块就需要进行多少IO操作,所以查询速度很慢。

·在表中已经存在大量数据的前提下,为某个字段段建立索引,建立速度会很慢

CREATE INDEX idx_id on userinfo(id);

·在索引建立完毕后,以该字段为查询条件时,查询速度提升明显

select * from userinfo where id  = 4567890;

⑥注意

mysql先去索引表里根据b+树的搜索原理很快搜索到id为4567890的数据,IO大大降低,因而速度明显提升 我们可以去mysql的data目录下找到该表,可以看到添加索引后该表占用的硬盘空间多了  如果使用没有添加索引的字段进行条件查询,速度依旧会很慢

4、正确使用索引

①数据库表中添加索引后确实会让查询速度起飞,但前提必须是正确的使用索引来查询,如果以错误的方式使用,则即使建立索引也会不奏效。即使建立索引,索引也不会生效

·1. 范围查询(>、>=、<、<=、!= 、between...and)

#1. = 等号

select count(*) from userinfo where id = 1000 -- 执行索引,索引效率高

#2. > >= < <= between...and 区间查询

select count(*) from userinfo where id <100; -- 执行索引,区间范围越小,索引效率越高

select count(*) from userinfo where id >100; -- 执行索引,区间范围越大,索引效率越低

select count(*) from userinfo where id between 10 and 500000; -- 执行索引,区间范围越大,索引效率越低

#3. != 不等于

select count(*) from userinfo where id != 1000; -- 索引范围大,索引效率低

·2.like '%xx%'

#为 name 字段添加索引

create index idx_name on userinfo(name);

select count(*) from userinfo where name like '%xxxx%'; -- 全模糊查询,索引效率低

select count(*) from userinfo where name like '%xxxx';  -- 以什么结尾模糊查询,索引效率低

#例外: 当like使用以什么开头会索引使用率高

select * from userinfo where name like 'xxxx%';

·3. or

select count(*) from userinfo where id = 12334 or email ='xxxx'; -- email不是索引字段,索引此查询全表扫描

#例外:当or条件中有未建立索引的列才失效,以下会走索引

select count(*) from userinfo where id = 12334 or name = 'alex3'; -- id 和 name 都为索引字段时, or条件也会执行索引

·4.使用函数

select count(*) from userinfo where reverse(name) = '5xela'; -- name索引字段,使用函数时,索引失效

#例外:索引字段对应的值可以使用函数,我们可以改为一下形式

select count(*) from userinfo where name = reverse('5xela');

·5.类型不一致

#如果列是字符串类型,传入条件是必须用引号引起来,不然...

select count(*) from userinfo where name = 454;

#类型一致

select count(*) from userinfo where name = '454';

·6.order by

#排序条件为索引,则select字段必须也是索引字段,否则无法命中 

select email from userinfo ORDER BY name DESC; -- 无法命中索引

select name from userinfo ORDER BY name DESC;  -- 命中索引

#特别的:如果对主键排序,则还是速度很快:

select id from userinfo order by id desc;

5、组合索引

①组合索引: 是指对表上的多个列组合起来做一个索引

②组合索引好处:简单的说有两个主要原因:

“一个顶三个”。建了一个(a,b,c)的组合索引,那么实际等于建了(a),(a,b),(a,b,c)三个索引,因为每多一个索引,都会增加写操作的开销和磁盘空间的开销。对于大量数据的表,这可是不小的开销! 索引列越多,通过索引筛选出的数据越少。有1000W条数据的表,有如下sql:select from table where a = 1 and b =2 and c = 3,假设假设每个条件可以筛选出10%的数据,如果只有单值索引,那么通过该索引能筛选出1000W10%=100w 条数据,然后再回表从100w条数据中找到符合b=2 and c= 3的数据,然后再排序,再分页;如果是组合索引,通过索引筛选出1000w 10% 10% *10%=1w,然后再排序、分页,哪个更高效,一眼便知 最左匹配原则: 从左往右依次使用生效,如果中间某个索引没有使用,那么断点前面的索引部分起作用,断点后面的索引没有起作用;

select * from mytable where a=3 and b=5 and c=4;

  #abc三个索引都在where条件里面用到了,而且都发挥了作用

select * from mytable where c=4 and b=6 and a=3;

  #这条语句列出来只想说明 mysql没有那么笨,where里面的条件顺序在查询之前会被mysql自动优化,效果跟上一句一样

select * from mytable where a=3 and c=7;

  #a用到索引,b没有用,所以c是没有用到索引效果的

select * from mytable where a=3 and b>7 and c=3;

  #a用到了,b也用到了,c没有用到,这个地方b是范围值,也算断点,只不过自身用到了索引

select * from mytable where b=3 and c=4;

  #因为a索引没有使用,所以这里 bc都没有用上索引效果

select * from mytable where a>4 and b=7 and c=9;

  #a用到了 b没有使用,c没有使用

select * from mytable where a=3 order by b;

  #a用到了索引,b在结果排序中也用到了索引的效果

select * from mytable where a=3 order by c;

  #a用到了索引,但是这个地方c没有发挥排序效果,因为中间断点了

select * from mytable where b=3 order by a;

  #b没有用到索引,排序中a也没有发挥索引效果

6、注意事项

·避免使用select *

·其他数据库中使用count(1)或count(列) 代替 count(),而mysql数据库中count()经过优化后,效率与前两种基本一样.

·创建表时尽量时 char 代替 varchar

·表的字段顺序固定长度的字段优先

·组合索引代替多个单列索引(经常使用多个条件查询时)

·使用连接(JOIN)来代替子查询(Sub-Queries)

·不要有超过4个以上的表连接(JOIN)

·优先执行那些能够大量减少结果的连接。

·连表时注意条件类型需一致

·索引散列值不适合建索引,例:性别不适合

7、查询计划

①语法格式

explain + 查询SQL - 用于显示SQL执行信息参数,根据参考信息可以进行SQL优化

②执行计划:让mysql预估执行操作(一般正确)

type : 查询计划的连接类型, 有多个参数,先从最佳类型到最差类型介绍

性能: null > system/const > eq_ref > ref > ref_or_null > index_merge > range > index > all

慢:

explain select * from userinfo where email='alex';

type: ALL(全表扫描)

特别的: select * from userinfo limit 1;

快:

explain select * from userinfo where name='alex';

type: ref(走索引)

③EXPLAIN 参数详解:http://www..com/wangfengming/articles/8275448.html

8、慢查询日志

①概念

将mysql服务器中影响数据库性能的相关SQL语句记录到日志文件,通过对这些特殊的SQL语句分析,改进以达到提高数据库性能的目的。

②慢查询日志参数:

long_query_time : 设定慢查询的阀值,超出设定值的SQL即被记录到慢查询日志,缺省值为10s

slow_query_log : 指定是否开启慢查询日志

log_slow_queries : 指定是否开启慢查询日志(该参数已经被slow_query_log取代,做兼容性保留)

slow_query_log_file : 指定慢日志文件存放位置,可以为空,系统会给一个缺省的文件host_name-slow.log

log_queries_not_using_indexes: 如果值设置为ON,则会记录所有没有利用索引的查询.

③查看 MySQL慢日志信息

#.查询慢日志配置信息 :

show variables like '%query%';

#.修改配置信息

set global slow_query_log = on;

④查看不使用索引参数状态:

显示参数

show variables like '%log_queries_not_using_indexes';

开启状态

set global log_queries_not_using_indexes = on;

⑤查看慢日志显示的方式

#查看慢日志记录的方式

show variables like '%log_output%';

#设置慢日志在文件和表中同时记录

set global log_output='FILE,TABLE';

⑥测试慢查询日志

#查询时间超过10秒就会记录到慢查询日志中

select sleep(3) FROM user ;

#查看表中的日志

select * from mysql.slow_log;

9、大数据量分页优化

①优化方案一

简单粗暴,就是不允许查看这么靠后的数据

②优化方案二

在查询下一页时把上一页的行id作为参数传递给客户端程序,即select * from tb1 where id>3000000 limit 10;

还有一种方式,比如100页的10条数据 select from tb1 where id>10010 limit 10;

③优化方案三:延迟关联

我们在来分析一下这条语句为什么慢,机就处在这个 * 里面,这个表除了id主键肯定还有其他字段,

因为select * 所以mysql在沿着id主键走的时候要回行拿数据,走一下拿一下数据,如果把语句改成

select id from tb1 limit 3000000,10;你会发现时间缩短了一半;然后我们在拿id分别去取10条数据就行了;

语句就改成这样了:

select table.* from tb1 inner join ( select id from tb1 limit 3000000,10 ) as tmp on tmp.id=table.id;

④这三种方法最先考虑第一种,其次第二种,第三种是别无选择

猜你喜欢

转载自blog.51cto.com/15064427/2653939