进击のpython
数据库——索引管理
这是数据库的最后一节,也是数据库最难的一节
但是!也是进阶最重要的一节!!
一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题
在生产环境中,我们遇到最多的,也是最容易出问题的,还是一些复杂的查询操作
因此对查询语句的优化显然是重中之重。说起加速查询,就不得不提到索引了
什么是索引
索引在MySQL中也叫做“键”,是存储引擎用于快速找到记录的一种数据结构
索引对于良好的性能非常关键,尤其是当表中的数据量越来越大时,
索引对于性能的影响愈发重要
索引优化应该是对查询性能优化最有效的手段了
索引能够轻易将查询性能提高好几个数量级
索引相当于字典的音序表,如果要查某个字,如果不使用音序表,则需要从几百页中逐页去查
索引是应用程序设计和开发的一个重要方面!!!!!
若索引太多,应用程序的性能可能会受到影响
而索引太少,对查询性能又会产生影响,要找到一个平衡点,这对应用程序的性能至关重要
索引原理
索引的目的在于提高查询效率,与我们查阅图书所用的目录是一个道理:
先定位到章,然后定位到该章下的一个小节,然后找到页数
相似的例子还有:查字典,查火车车次,飞机航班等
本质都是:通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据
数据库的查询机制也是这样的,但是,要比这个复杂得多!
每次访问数据的时候都会产生一次I/O阻塞,而这个阻塞,越少!速度越快!
索引的数据结构
基于前面的我们可以总结一下,我们需要一个数据结构:
每次查找数据时把磁盘IO次数控制在一个很小的数量级,最好是常数数量级
那么我们就想到如果一个高度可控的多路搜索树是否能满足需求呢?
就这样,b+树应运而生(B+树是通过二叉查找树,再由平衡二叉树,B树演化而来)
看不懂很正常,你看的懂,我跟你说什么啊~
浅蓝色的块我们称之为一个磁盘块
可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示)
如磁盘块1包含数据项17和35,包含指针P1、P2、P3
P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块
真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99
非叶子节点不存储真实的数据,只存储指引搜索方向的数据项,如17并不真实存在于数据表中
当我们想查29的时候,首先进行一次I/O阻塞,打开磁盘块1
利用二分法知道29是在17和35之间的,所以走P2指针
将磁盘块3加载进内存,此时发生第二次I/O阻塞,通过二分法确定指针为P2
将磁盘块8加载进内存,此事发生第三次I/O阻塞,查找到了29
也就是说,只需要三次I/O就可以将数据查找到,如果不用b+树需要七次I/O
提升的效率可不是一点点!!树的高度决定I/O次数!
问题就变成了,如何创建才能够使树更低呢???
1.索引字段要尽量的小:假设当前数据表的数据为N,每个磁盘块的数据项的数量是m,则有h=㏒(m+1)N
当数据量N一定的情况下,m越大,h越小;而m = 磁盘块的大小 / 数据项的大小
磁盘块的大小也就是一个数据页的大小,是固定的,如果数据项占的空间越小,数据项的数量越多,树的高度越低
这就是为什么每个数据项,即索引字段要尽量的小,比如int占4字节,要比bigint8字节少一半
这也是为什么b+树要求把真实的数据放到叶子节点而不是内层节点
一旦放到内层节点,磁盘块的数据项会大幅度下降,导致树增高
当数据项等于1时将会退化成线性表
2.索引的最左匹配特性:当b+树的数据项是复合的数据结构,比如(name,age,sex)的时候
b+数是按照从左到右的顺序来建立搜索树的,比如当(张三,20,F)这样的数据来检索的时候
b+树会优先比较name来确定下一步的所搜方向,如果name相同再依次比较age和sex最后得到检索的数据;
但当(20,F)这样的没有name的数据来的时候,b+树就不知道下一步该查哪个节点
因为建立搜索树的时候name就是第一个比较因子,必须要先根据name来搜索才能知道下一步去哪里查询
比如当(张三,F)这样的数据来检索时,b+树可以用name来指定搜索方向,但下一个字段age的缺失
所以只能把名字等于张三的数据都找到,然后再匹配性别是F的数据了, 这个是非常重要的性质
但是,索引不是越多越好!
拿写书举例子,你写完书了,目录也写完了,然后有一天你想改变书的内容
是不是你改一次,就要重新写一次目录?
那么数据库每次的修改,都会使得索引重新设置,就大大的降低效率,查询变快,修改变慢
所以,不要盲目的加索引!
索引分类
前面提过,通过innodb引擎创建表的时候必须有一个主键,而且通常是id字段是主键,为什么呢?
因为innodb是索引组织数据,就是这个表刚建好,索引就建好了,那按照什么建索引呢?估计你也猜到了,主键!
也就是为什么要设置主键!前面讲过,没有主键就会设置一个隐藏的主键!而隐藏的主键,用不到啊!
聚集索引
聚集索引的好处之一:它对主键的排序查找和范围查找速度非常快,叶子节点的数据就是用户所要查询的数据,如用户需要查找一张表,查询最后的10位用户信息,由于B+树索引是双向链表,所以用户可以快速找到最后一个数据页,并取出10条记录
聚集索引的好处之二:范围查询(range query),即如果要查找主键某一范围内的数据,通过叶子节点的上层中间节点就可以得到页的范围,之后直接读取数据页即可
辅助索引
表中除了聚集索引外其他索引都是辅助索引(Secondary Index,也称为非聚集索引)
与聚集索引的区别是:辅助索引的叶子节点不包含行记录的全部数据。
叶子节点除了包含键值以外,每个叶子节点中的索引行中还包含一个书签(bookmark)
该书签用来告诉InnoDB存储引擎去哪里可以找到与索引相对应的行数据
辅助索引的存在并不影响数据在聚集索引中的组织,因此每张表上可以有多个辅助索引,但只能有一个聚集索引
当通过辅助索引来寻找数据时,InnoDB存储引擎会遍历辅助索引并通过叶子级别的指针获得只想主键索引的主键
然后再通过主键索引来找到一个完整的行记录
但是有索引的前提是你用where来进行筛选,直接用*查询全表,有没有索引就无所谓了
而且再一个,如果是范围查询,那索引的作用其实也是微乎其微的
索引测试
create table s1(
id int,
name varchar(20),
gender char(6),
email varchar(50)
);
delimiter //
create procedure auto_insert1()
BEGIN
declare i int default 1;
while(i<3000000)do
insert into s1 values(i,'egon','male',concat('egon',i,'@oldboy'));
set i=i+1;
end while;
END//
delimiter ;
call auto_insert1();
创建完之后,先进行正常的查询:
mysql> select * from s1 where id=33333333;
Empty set (2.33 sec)
那我们为了优化效率,我们需要给id加索引!怎么加呢?
mysql> create index a on s1(id);
Query OK, 0 rows affected <5.30 sec>
很明显,创建索引确实是很慢的
在索引建立完毕后,以该字段为查询条件时,查询速度提升明显
mysql> select * from s1 where id = 33333333;
Empty set (0.00 sec)
但是你去看你的文件,你会发现他变得非常大了