为什么用索引可以使查询变快?
如果不使用索引,数据会零散的保存在磁盘块中,查询数据需要挨个遍历每一个磁盘块,直到找到数据为止,使用索引后会将磁盘块以树桩结构保存,查询数据时会大大降低磁盘块的访问数量。想象一下,每条数据的查询都是一个io扫描的话,假设一张表有一百万条数据,你要找的数据刚好就是最后一条,那么就要进行一百万次io,这样就使数据库的性能大幅度降低了。索引就是解决这样的问题的。
那么问题来了,为什么索引能解决这样的问题呢?
首先,我们要了解索引使用的数据结构,MySQL 中存储索引用的一般都是 B + 树。它的数据都存放在叶子节点中,同时叶子节点之间还添加了指针形成了链表。
索引类型
在 InnoDB 里面,索引类型有三种,普通索引、唯一索引(主键索引是特殊的唯一 索引)、全文索引。
普通(Normal):也叫非唯一索引,是最普通的索引,没有任何的限制。
唯一(Unique):唯一索引要求键值不能重复。另外需要注意的是,主键索引是一 种特殊的唯一索引,它还多了一个限制条件,要求键值不能为空。主键索引用 primay key 创建。
全文(Fulltext):针对比较大的数据,比如我们存放的是消息内容,有几 KB 的数据的这种情况,如果要解决 like 查询效率低的问题,可以创建全文索引。只有文本类型的字段才可以创建全文索引,比如 char、varchar、text。
主键索引和唯一索引的区别?
主键索引是唯一索引的特殊类型。
数据库表通常有一列或列组合,其值用来唯一标识表中的每一行。该列称为表的主键。
在数据库关系图中为表定义一个主键将自动创建主键索引,主键索引是唯一索引的特殊类型。主键索引要求主键中的每个值是唯一的。当在查询中使用主键索引时,它还允许快速访问数据。
它们的一些比较:
- 对于主键, oracle/sql server/mysql等都会自动建立唯一索引,并且主键也是一种约束;
- 主键不一定只包含一个字段,所以如果你在主键的其中一个字段建唯一索引还是必要的;
- 主键可作外键,唯一索引不可;
- 主键不可为空,唯一索引可;
- 主键也可是多个字段的组合;
- 主键与唯一索引不同的是:
- 有not null属性;
- 每个表只能有一个;
- 主键可以用作外键,唯一索引不可以。
唯一索引
不允许具有索引值相同的行,从而禁止重复的索引或键值。系统在创建该索引时检查是否有重复的键值,并在每次使用 INSERT 或 UPDATE 语句添加数据时进行检查。
创建唯一索引的目的不是为了提高访问速度,而只是为了避免数据出现重复。唯一索引可以有多个但索引列的值必须唯一,索引列的值允许有空值。如果能确定某个数据列将只包含彼此各不相同的值,在为这个数据列创建索引的时候就应该使用关键字UNIQUE,把它定义为一个唯一索引
B+树比B树更适合做文件索引的原因
1. B+树空间利用率更高,可减少I/O次数
一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗。而因为B+树的内部节点只是作为索引使用,而不像B-树那样每个节点都需要存储数据。
也就是说:B+ 树相比 B 树,内部节点不存储数据,只存储键和指针。这使得每个节点可以存储更多的键,从而降低树的高度,减少磁盘 I/O 操作次数。因为每次读取一个节点的数据,可以利用磁盘块的全部空间,提高读取效率。
B 树 vs B+ 树的节点结构:
B 树:节点既包含键,也包含实际数据。
B+ 树:非叶子节点只包含键和指向子节点的指针,所有实际数据存储在叶子节点中。
2. 增删文件(节点)效率高。范围查询效率高
因为B+树的叶子节点包含所有关键字,并以有序的链表结构存储,这样可很好提高增删效率。
B+ 树的所有叶子节点通过指针链表连接,使得范围查询和顺序遍历非常高效。通过从一个范围的起始键开始,顺着链表遍历叶子节点,可以快速获取范围内的所有数据。
B 树 vs B+ 树的范围查询:
B 树:需要通过中序遍历来获取范围内的所有数据,性能较差。
B+ 树:通过链表连接的叶子节点,可以直接获取范围内的所有数据,性能更高。
3. B+树的查询效率更加稳定
因为B+树的每次查询过程中,都需要遍历从根节点到叶子节点的某条路径。所有关键字的査询路径长度相同,导致每一次查询的效率相当。
一般来说,B+树/B树一个节点的大小为1页,也就是16kB:
首先InnoDB的B+树中,非叶子节点存的是key + 指针;叶子节点存的是数据行。
对于叶子节点,如果一行数据大小为1k,那么一页就能存16条数据;对于非叶子节点,如果key使用的是bigint,则为8字节,指针在mysql中为6字节,一共是14字节,则16k能存放 16 * 1024 / 14 = 1170 个索引指针。于是可以算出,对于一颗高度为2的B+树,根节点存储索引指针节点,那么它有1170个叶子节点存储数据,每个叶子节点可以存储16条数据,一共 1170 x 16 = 18720 条数据。而对于高度为3的B+树,就可以存放 1170 x 1170 x 16 = 21902400 条数据(两千多万条数据),也就是对于两千多万条的数据,我们只需要高度为3的B+树就可以完成,通过主键查询只需要3次IO操作就能查到对应数据。所以在 InnoDB 中B+树高度一般为3层时,就能满足千万级的数据存储,所以一个节点为1页,也就是16k是比较合理的。