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 可以用到索引
主键索引不存在模糊查询的 本身就唯一 不存在多个
如果列类型是字符串,一定要在条件中将数据使用引号,否则用不到索引
- 索引无法存储null值
- 前导模糊查询不能利用索引(like '%XX'或者like '%XX%')
- 索引失效的几种情况
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)并行同时查询,让后在交给第三方中间进行组合返回给客户端。