虽然InnoDB也使用B+Tree做为索引结构,但具体实现方式却与MyISAM大相径庭。由于InnoDB支持聚簇索引(主键索引),聚簇索引就是表,因此InnoDB不用像MyISAM那样须要独立的行存储。也就是说,InnoDB的数据文件自己就是索引文件。数据库
聚簇索引的每个叶子节点都包含了主键值、事务ID、用于事务和MVCC的回滚指针以及全部的剩余列。假设咱们以col1为主键,则下图是一个InnoDB表的聚簇索引(主键索引)(Primary key)示意。服务器
与MyISAM不一样的是,InnoDB的二级索引和聚簇索引很不相同。InnoDB的二级索引的叶子节点存储的不是行号(行指针),而是主键列。这种策略的缺点是二级索引须要两次索引查找,第一次在二级索引中查找主键,第二次在聚簇索引中经过主键查找须要的数据行。并发
画外音:能够经过咱们前面提到过的索引覆盖来避免回表查询,这样就只须要一次回表查询,对于InnoDB而言,就是只须要一次索引查找就能够查询到须要的数据记录,由于须要的数据记录已经被索引到二级索引中,直接就能够找到。高并发
由于InnoDB的索引的方式经过主键汇集数据,严重依赖主键。索引若是没有定义主键,那么InnoDB会选择一个惟一的非空索引代替。若是没有这样的索引,InnoDB会隐式定义一个主键来做为聚簇索引。性能
画外音:关于页,咱们在上一篇文章中也提到过。页是计算机管理存储器的逻辑块,硬件及操做系统每每将主存和磁盘存储区分割为连续的 大小相等的块,每一个存储块称为一页。存和磁盘以页为单位交换数据。数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设 为等于一个页,这样每一个节点只须要一次磁盘I/O就能够彻底载入
基于聚簇索引以上的这些特色,在InnoDB中,咱们应该尽可能使用和应用无关的主键,例如自增主键,这样能够保证数据行是按照顺序写入的。而不是使用GUID、UUID生成随机的主键。索引分裂我的理解:在 MySQL插入记录的同时会更新配置的相应索引文件,根据以上的了解,在插入索引时,可能会存在索引的页的分裂,所以会致使磁盘数据的移动。当插入的主键是随机字符串时,每次插入不会是在B+树的最后插入,每次插入位置都是随机的,每次均可能致使数据页的移动,并且字符串的存储空间占用也很大,这样重建索引不只仅效率低并且 MySQL的负载也会很高,同时还会致使大量的磁盘碎片,磁盘碎片多了也会对查询形成必定的性能开销,由于存储位置不连续致使更多的磁盘I/O,这就是为何推荐定义主键为递增整型的一个缘由优化
自增主键的弊端 对于高并发的场景,在InnoDB中按照主键的顺序插入可能会形成明显的争用,主键的上界会成为“热点”,由于全部的插入都发生在此处,索引并发的插入可能会形成间隙锁竞争,何为间隙锁竞争,下个会详细介绍;另一个缘由多是Auto_increment的锁机制,在 MySQL处理自增主键时,当innodb_autoinc_lock_mode为0或1时,在不知道插入有多少行时,好比insert t1 xx select xx from t2,对于这个statement的执行会进行锁表,只有这个statement执行完之后才会释放锁,而后别的插入才可以继续执行,可是在innodb_autoinc_lock_mode=2时,这种状况不会存在表锁,可是只能保证全部并发执行的statement插入的记录是惟一而且自增的,可是每一个statement作的多行插入之间是不链接的spa
优化器不使用索引选择全表扫描 好比一张order表中有联合索引(order_id, goods_id),在此例子上来讲明这个问题是从两个方面来讲:操作系统
select order_id from order where order_id > 1000; --若是查看其执行计划的话,发现是用use index condition,走的是索引覆盖。
select * from order where order_id > 1000;
此条语句查询的是该表全部字段,有一部分字段并未在此联合索引中,所以走联合索引查询会走两步,首先经过联合索引肯定符合条件的主键id,而后利用这些主键id再去聚簇索引中去查询,而后获得全部记录,利用主键id在聚簇索引中查询记录的过程是无序的,在磁盘上就变成了离散读取的操做,假如当读取的记录不少时(通常是整个表的20%左右),这个时候优化器会选择直接使用聚簇索引,也就是扫全表,由于顺序读取要快于离散读取,这也就是为什么通常不用区分度不大的字段单独作索引,注意是单独由于利用此字段查出来的数据会不少,有很大几率走全表扫描。设计
范围查询以后的条件不走索引 根据 MySQL的查询原理的话,当处理到where的范围查询条件后,会将查询到的行所有返回到服务器端(查询执行引擎),接下来的条件操做在服务器端进行处理,这也就是为何范围条件不走索引的缘由了,由于以后的条件过滤已经不在存储引擎完成了。可是在 MySQL 5.6之后假如了一个新的功能index condition pushdown(ICP),这个功能容许范围查询条件以后的条件继续走索引,可是须要有几个前提条件:指针
分页offset值很大性能问题
在 MySQL中,分页当offset值很大的时候,性能会很是的差,好比limit 100000, 20,须要查询100020条数据,而后取20条,抛弃前100000条,在这个过程当中产生了大量的随机I/O,这是性能不好的缘由,为了解决这个问题,切入点即是减小无用数据的查询,减小随机I/O
select * from t1 inner join (select id from t1 where xxx order by xx limit 1000000,5) as t2 using(id); --子查询先走索引覆盖查得id,而后根据获得的id直接取5条得数据。
select * from t1 where id > 1000000 order by id limit 0, 5; --即利用条件id > 1000000在扫描索引是跳过1000000条记录,而后取5条便可,这种处理方式的offset值便成为0了,但此种方式一般分页不能用,可是能够用来分批取数据。