MySQL的索引实现原理

MySQL数据库索引原理课程
1、为什么需要使用索引?
2、数据结构Hash、平衡二叉树、B树、B+树区别
3、机械硬盘、固态硬盘区别
5、Myisam与Innodb B+树的区别
6、MySQL中的索引什么数据结构
7、B+树中的节点到底存放多少

为什么需要使用索引?
MySQL官方对索引的定义为:索引(Index)是帮助 MySQL 高效获取数据的数据结构。
白话文:索引就像书的目录一样可以非常快速的定位到书的页码。
如果向mysql发出一条sql语句请求,查询的字段没有创建索引的话,可能会导致全表扫描,这样的话查询效率非常低。

全表扫描: 会将整张表数据全部扫面一遍,这样的话效率很低 

MySQL索引采用的数据结构有哪些?

   Hash  平衡二叉树  B树  B+树区别

Hash算法: 

哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。

优点:查找可以直接根据key访问
缺点: 不能进行范围查找
index=Hash(key)

比如 通过name 去做hash值

3 = Hash("jack")

4= Hash("Mack")

存放的是存放到数组中

然后 查询时候

select * from table where name = 'jack';  先计算出hash值 然后根据下表的位置就可以找到了

效率高,但是不能做范围查询  ( hash是个散列的  不能有范围! 计算出hash值去比较的  如果不是name 是数字的话 hash值散列 没法获取正确的范围)

   

平衡二叉树:

平衡二叉查找树,又称 AVL树。 它除了具备二叉查找树的基本特征之外,还具有一个非常重要的特点:它 的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值(平衡因子 ) 不超过1。 也就是说AVL树每个节点的平衡因子只可能是-1、0和1(左子树高度减去右子树高度)。

优点:平衡二叉树算法基本与二叉树查询相同,效率比较高
缺点:插入操作需要旋转,支持范围查询

 通过平衡二叉树建立id的索引,

 平衡二叉树,会取一个中间值,中间值左边成为左子树,中间值右边称为右子树

 左子树值<中间值<右子树值

 如果利用平衡二叉树建立索引的话会生成一个文件

 如果有一百万条数据 ,则会有一百万个节点。

 查询时候,索引文件加载到内存,然后进行比较。这样容易内存溢出

 一般在做的时候:

平衡二叉树查询原理:

假设查询10 (需要经历4次IO操作)

1次 从硬盘中读取4 (内存),判断下10>4,取右指针

2次 从硬盘中读取8 (内存),判断下10>8,取右指针

3次 从硬盘中读取9 (内存),判断下10>,取右指针

4次 从硬盘中读取10 (内存),判断下10=10,定位到数据

查询四次!

平衡二叉树 查询效率还可以,缺点:虽然支持范围查询,但是回旋查询效率低。

规律:如果树的高度越高,那么查询IO次数会越多。

数据结构B树 
维基百科对B树的定义为“在计算机科学中,B树(B-tree)是一种树状数据结构,它能够存储数据、对其进行排序并允许以O(log n)的时间复杂度运行进行查找、顺序读取、插入和删除的数据结构。B树,概括来说是一个节点可以拥有多于2个子节点的二叉查找树。与自平衡二叉查找树不同,B-树为系统最优化大块数据的读和写操作。B-tree算法减少定位记录时所经历的中间过程,从而加快存取速度。普遍运用在数据库和文件系统。”
因为B树节点元素比平衡二叉树要多,所以B树数据结构相比平衡二叉树数据结构实现减少磁盘IO的操作。

B树对于平衡二叉树做改进

平衡二叉树高度越高 查询IO次数越多

规律: 如果树的高度越高,查询次数越多。如何减少IO次数?  B树在平衡二叉树中,减少树的高度。

明显高度低了

假设查询10 (需要经历3次IO操作)

1次 从硬盘中读取4 (内存),判断下10>4,取右指针

2次 从硬盘中读取6,8 (内存),判断下10>8,取右指针

3次 从硬盘中读取9,10 (内存),判断下10=10 定位到数据

B树比平衡二叉树减少了一次IO操作

结论: B树查询效率比平衡二叉树高,因为B树节点中可以有多个元素,从而减少树的高度,减少IO操作,从而提高查询效率。但是范围查询效率依然比较低

数据结构B+树

B+树相比B树,继承了B数的特征。新增叶子节点与非叶子节点关系,叶子节点中包含了key和value,非叶子节点中只是包含了key,不包含value。
所有相邻的叶子节点包含非叶子节点,使用链表进行结合,有一定顺序排序,从而范围查询效率非常高

通过非叶子节点查询叶子节点获取对应的value。范围查询的效率提高!

缺点: 因为有冗余的节点数据,会比较占硬盘。

结合索引:

key就是建立索引的值

value是地址 

B+树 解决范围查询问题、减少IO查询的操

 结合数据库,B+树是怎么玩起来的?

    MyISAM和InnoDB对B-Tree索引不同的实现方式 主键索引: MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址。

下图是MyISAM主键索引的

这里设表一共有三列,假设我们以Col1为主键,图myisam1是一个MyISAM表的主索引(Primary key)示意。可以看出

    

  上图: 以主键建立的索引 

             叶子节点的key就是主键id    value定位到数据库中哪一行的数据的地址

 MyIsam和InnoDB虽然都是B+树但是实现的方式是有区别的。

 InnoDB直接存放的数据不是地址是数据,它的value直接就是那一行的数据(但是有大小限制的不可能存放无限制的)!

 MyISam是地址,再通过地址查询行数。

MyISAM和InnoDB对B-Tree索引不同的实现方式 然InnoDB也使用B+Tree作为索引结构,但具体实现方式却与MyISAM截然不同.
MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。

 

索引文件最终是存放在硬盘上的,不是内存哈。有IO操作的

 B+树相比B树,新增叶子节点与非叶子节点关系,叶子节点中包含了key和value,非叶子节点中只是包含了key,不包含value。

所有相邻的叶子节点包含非叶子节点,使用链表进行结合,有一定顺序排序,从而范围查询效率非常高
注意:MyISAM和InnoDB对B-Tree索引不同的实现方式
MyISAM底层使用B+树 叶子节点的value对应存放行数的地址,在通过行数定位到数据。
InnoDB底层使用B+树,叶子节点的value对应存放是行的data数据,相比MyISAM效率要高一些,但是比较占硬盘内存大小。

最终形成了一个索引文件,是存放在硬盘的。每次查询时候都是在读硬盘。

选择B+树的原因:

B+树索引具有范围查找和前缀查找的能力,相当于二分查找。

Hash索引只能支持等于查询,无法支持范围查询

索引文件如何查看

默认数据与索引文件位置: /var/lib/mysql

MyISAM引擎的文件:
.myd 即 my data,表数据文件
.myi 即my index,索引文件
.log 日志文件。

InnoDB引擎的文件:
采用表空间(tablespace)来管理数据,存储表数据和索引,
InnoDB数据库文件(即InnoDB文件集,ib-file set):
ibdata1、ibdata2等:系统表空间文件,存储InnoDB系统信息和用户数据库表数据和索引,所有表共用。
.ibd文件:单表表空间文件,每个表使用一个表空间文件(file per table),存放用户数据库表数据和索引。

结合图形化界面:

有好多引擎

 进入/var/lib/mysql/test   (tes为数据库名称)

  

    

对于SQL查询优化 

开启慢查询日志,可以让MySQL记录下查询超过指定时间的语句,通过定位分析性能的瓶颈,才能更好的优化数据库系统的性能。
先捕获低效SQL→慢查询优化方案→慢查询优化原则 

MySQL数据库配置慢查询
参数说明:
slow_query_log 慢查询开启状态
slow_query_log_file 慢查询日志存放的位置(这个目录需要MySQL的运行帐号的可写权限,一般设置为MySQL的数据存放目录)
long_query_time 查询超过多少秒才记录
1.查询慢查询配置
show variables like 'slow_query%';
2.查询慢查询限制时间
show variables like 'long_query_time';
3.将 slow_query_log 全局变量设置为“ON”状态
set global slow_query_log='ON';
4.查询超过1秒就记录
set global long_query_time=1;
5.查询cat /var/lib/mysql/localhost-slow.log
service mysqld restart

1、

2、

默认10s没有响应的话 就会记录到下面的文件中去:

 正确步骤是

 第一步中开启慢查询 ON, 将 slow_query_log 全局变量设置为“ON”状态。 

  set global slow_query_log='ON';

 然后再去修改时间秒数,查询超过1秒就记录 set global long_query_time=1;

 关闭当前连接,然后重新连接

查看此文件:

 

 如果满足条件 查询时间很慢 会记录在这里

关于索引失效问题

     

索引为什么会失效?注意那些事项?

1.索引无法存储null值
2.如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因)
要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引
3.对于多列索引,不是使用的第一部分,则不会使用索引
4.like查询以%开头
5.如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引
6.如果mysql估计使用全表扫描要比使用索引快,则不使用索引

   创建表:

   

CREATE TABLE `user_details` (
  `id` int(11) ,
  `user_name` varchar(50) DEFAULT NULL,
  `user_phone` varchar(11) DEFAULT NULL,
  PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

  主键索引: id   

  创建普通索引:

  ALTER TABLE `user_details` ADD INDEX user_name_index ( `user_name` )

 查看表结构:    

 DESC user_details;      看到有两个索引id   和 user_name

 

 插入数据:

insert into user_details values(1,'a1','12312312');

insert into user_details values(2,'a2','12312123');

insert into user_details values(3,'a3','15924134');

insert into user_details values(4,'a4','23423423');

insert into user_details values(5,'a5','12352343');

insert into user_details values(6,'a6','35634523');

 全表扫描:

优化查询时候 尽量使用索引字段进行查询   

 

两个索引都没有用上

 

  like 不能用到索引哦

  

 普通索引 百分号放后面时候 like 可以用到索引

 

 主键索引不存在模糊查询的  本身就唯一 不存在多个

  

如果列类型是字符串,一定要在条件中将数据使用引号,否则用不到索引

  

  1. 索引无法存储null值
  2. 前导模糊查询不能利用索引(like '%XX'或者like '%XX%')
  3. 索引失效的几种情况

         1.如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因)要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引

         2.对于多列索引,不是使用的第一部分,则不会使用索引

         3.like查询以%开头

         4.如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引

         5.如果mysql估计使用全表扫描要比使用索引快,则不使用索引

联合索引为什么需要遵循左前缀原则?

  创建表:

CREATE TABLE `user_details` (
  `id` int(11) ,
  `user_name` varchar(50) ,
  `user_phone` varchar(11) DEFAULT NULL,
  PRIMARY KEY (id,user_name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 主键是id   user_name   

  

insert into user_details values(1,'a1','12312312');

insert into user_details values(1,'a2','12312123');

可以插入成功! 联合主键索引  id+user_name保证唯一就OK了!  多个

插入如下数据:

  

insert into user_details values(1,'a1','12312123');

insert into user_details values(1,'a2','12312123');


insert into user_details values(2,'a1','12312123');

insert into user_details values(2,'a2','12312123');


insert into user_details values(3,'a1','12312123');

insert into user_details values(3,'a2','12312123');

如果只有一个条件 user_name

 

 联合主键索引中: 左前缀指的就是第一个索引 id  

 联合主键索引中,必须要加上左前缀,才会生效?

  

 叶子节点分析 链表, 

 [1+a1] ---> [1+a2] ---->  [2+a1]---> [2+a2]--->[3+a1]--->[3+a2]

  

 select * from user_deatils where id = 1 and user_name = 'a1'

 通过前缀就可以查找到叶子,知道有个范围了  “范围”是个亮点!!!

 如果不带第一个索引 查询时候 会因为不知道第一个索引是什么,只有第二个,而进行全表查询

 因为索引底层采用B+树叶子节点顺序排列,必须通过左前缀索引才能定位到具体的节点范围。

  

 分表分库能够提高数据查询效率?

  

分表分库为什么提高查询的效率?因为会将一张表的数据拆分成多个n张表进行存放,让后在使用第三方中间件(MyCat或者Sharding-JDBC)并行同时查询,让后在交给第三方中间进行组合返回给客户端。

 

同时操作,就跟多线程一样。 

分表分库为什么提高查询的效率?因为会将一张表的数据拆分成多个n张表进行存放,让后在使用第三方中间件(MyCat或者Sharding-JDBC)并行同时查询,让后在交给第三方中间进行组合返回给客户端。

猜你喜欢

转载自www.cnblogs.com/toov5/p/10340349.html