索引
索引是数据结构。索引(Index)是帮助 MySQL 高效获取 数据的数据结构。
**索引的作用是对数据排序、提高查询速度。**类比图书馆藏书类别,跳过全部书籍扫描,精准定位到某小区域的数据。
不同的索引会影响到 OrderBy(排序) 与 Where(条件查询) 运行效率。
数据库除维护数据以外,还维护着拥有特定查找算法的数据结构,这些数据结构存储 数据 地址来指向真实数据 =》索引
案例图解
左侧 16进制: 是 索引【数据结构】存储的 真实数据的物理地址【指针】
Col1:为 索引【数据结构】的全局唯一 ID【主键】
Col2:为 索引【数据结构】的真实数据的别名/代称【关键字】
由此,为了加速 Col2 的查找可以维护一个右边所示的 二叉查找树,每个节点分别包含:关键字 Key 与 指向真实数据的物理地址 Address,这样就可用 二叉查找 在一定的 时间复杂度中 查询到 相应真实数据地址,从而获取数据值,而不再是 全数据扫描检索。
特性
- 索引本质是对数据的简化(由地址值代替真实值)排序,索引自身不小,也就不可能全部存储在内存中,因此索引也通常以索引文件的形式存储在磁盘中。
- 其次,索引为了维持自身指向的有效性,也要随 真实数据的 增删改 发生改变(无效数据,新增数据,越来越臃肿)也就是——索引查询会随数据量增加变的越来越慢。
- 最后,仍是因为维持索引的有效性,那么 就会在真实数据更新操作的同时更新索引指向,故此索引会降低更新效率。
也就是——索引 会 多次重新构建,去除 已删除数据的索引、无效的地址指向等(通常情况下,我们只是逻辑删除,为了保证完整历史操作)
优劣
索引优势
1.提高数据查找速度,减少磁盘IO次数
2.通过索引来对数据排序,降低查询时 OrderBy 子句对 CPU 消耗,提高 OrderBy 子句 的查询速度。
索引劣势
1.索引 实际上也是个表,存储 唯一主键 、字段值别值、真实字段值地址,所以 索引 也会占用 内存磁盘资源。
2.索引 提升查询速度,同时也会降低更新表速度。(更新 真实数据的同时也要更新索引表)
3.索引 不可能为每个字段及其他们的所有组合都建立一个索引,故此,就要花时间研究建立 最有效索引 或 优化索引。
类别
单值索引:一个索引只包含单个列,一个表可有多个单列索引【频繁查询的单个列,很少】
唯一索引:索引列的值必须唯一,但允许有空值
主键索引:设定为主键后数据库会自动建立主键索引,innodb为聚簇索引
复合索引:一个索引包含多个列【大多数是 多个列 作为查询条件】
数据结构
平常未特别说明的 “索引” 结构指代都是 B-Tree(多路搜索树,不一定是二叉树,如三叉)结构的索引。 聚集索引,次要索引,前缀索引,唯一索引,默认都是使用 B+树索引,统称索引。除 B+Tree 结构外,还有哈希 结构索引(不是重点)等。
B-Tree
上图为 一颗 B-Tree(2叉),一个节点拥有3个属性,一个为 黄色指针(指向下一个节点),一个为 蓝色 数据项(数值),一个为 真实数据的 红色地址(磁盘地址)。浅蓝色为 磁盘Block(代指一个真实的物理磁盘区域/扇面/磁道)。
特性:
叶子与非叶子节点 均存储 真实数据的地址,叶子节点只存储 数据项 及 地址,
二叉对 P1,P2,P3 的定义为:P1 [ -∞,叉1),P2(叉1,叉2),P3(叉2,+∞]
查询过程:
如果查询 17 / 35 则只会 进行1次 磁盘IO,第一层根节点 即可找到。如果查询 26 则只会 则只会 进行2次 磁盘IO,第一次 磁盘IO 加载磁盘块1,然后在内存中使用二分查找,确定 26 在(17,35)间,进行第二次 磁盘IO,在内存中使用二分查找,找到 26。依次类推。
B+Tree
上图为 一颗 B+Tree(3叉),一个节点拥有2个属性,一个为 黄色指针(指向下一个节点),一个为 蓝色 数据项(数值)
浅蓝色为 磁盘Block(代指一个真实的物理磁盘区域/扇面/磁道)。
特性:
真实数据 仅存在于 叶子节点,非叶子节点仅存储 指向真实数据的 指针(黄) 及 指引搜索方向的 数据项(蓝),
三叉对 P1,P2,P3 的定义为:P1 [ 叉1,叉2),P2(叉2,叉3),P3(叉3,+∞]
查询过程:
查找数据项 28 的真实数据值。第一次 磁盘IO 加载 磁盘块1,在内存中使用二分查找,确定 29 在(17,35)间,然后进行 第二次 磁盘IO 加载 磁盘块3,再在内存中使用二分查找,确定 29 在(26,30)间,然后进行 第三次 磁盘IO 加载 磁盘块8,再在内存中做二分查找找到29,结束查询,总计三次IO。这对于百万行数据来说,相较全表扫描是巨大提升。
B-Tree/B+Tree 区别
1)B-Tree 关键字、指针、记录 是放在一起的;B+Tree 非叶子节点中只有 关键字、指针,叶子节点只有 记录。
2)B-Tree,越靠近根节点的记录查找时间越短;而B+树中每个记录的查找时间基本是一样的,都需要从根节点走到叶子节点,而且在叶子节点中还要再比较关键字。
B+Tree 为何更适合作为索引结构
因为,B+Tree 非叶子节点 只存储 关键字 与 指针,没有B-Tree的记录,也就是,将B-Tree非叶子节点的空间也用于 存储更多的 关键字与指针,那么 B+Tree 的非叶子节点 就比 B-Tree 非叶子节点 能存放更多的 关键字与指针 ——》也就是,B+Tree 的层级要少于 B-Tree 的层级。这样带来的好处是减少 磁盘IO次数。(磁盘IO一次耗时要远远大于 内存中做 二分查找运算)
故此,尽管B+Tree找到一个记录所需的比较次数要比B-Tree多,但是一次磁盘访问的时间相当于成百上千次内存比较的时间,因此实际测试中B+树的性能可能还会更好,而且B+树的叶子节点使用指针连接在一起,方便顺序遍历 查询全表,这也是很多数据库和文件系统使用B+树的缘故。
总结: B+树的磁盘读写次数更少(代价更低), B+树的查询效率更加稳定。
补充·聚簇索引 与 非聚簇索引
聚簇索引,真实的含义是,根据索引相近而聚簇存放的数据行 —— 一种数据存储规则/方式
术语 ‘聚簇’:表示索引列Key的相邻数据行的Value聚成一簇存储在一起。
优点
按照聚簇索引排列顺序查询范围数据时,由于数据都是紧密相连,不用从多个磁盘块中提取数据,所以减少大量磁盘IO
限制
MySQL目前只有 Innodb 数据引擎支持聚簇索引,而 Myisam 并不支持。
由于数据的物理存储排序方式只能有一种,所以每个表 只能有一个聚簇索引。一般情况下就是该表的主键
注:为了充分利用聚簇索引的特性,所以 Innodb 表的主键列尽量选用有序的顺序id,而不建议用无序的如 uuid 这种。
索引建立条件
不建立
1.表记录太少
2.经常增删改的表或者字段
3.Where条件里用不到的字段不创建索引
4.过滤性能不好的字段不适合建索引
建立索引
1.主键自动建立唯一索引
2.频繁作为查询条件的字段应该创建索引
3.查询中与其它表关联的字段如外键应建立索引
4.组合索引性价比更高
5.查询中 OrderBy 进行排序的字段,排序字段若通过索引去访问将大大提高排序速度
6.查询中统计或者分组字段