【学习笔记】 mysql45讲 Part2

9-45

09 | 普通索引和唯一索引,应该怎么选择?

9.1 查询过程

  •    假设,执行查询的语句是 select id from T where k=5。这个查询语句在索引树上查找的过程,先是通过 B+ 树从树根开始,按层搜索到叶子节点(回表),也就是图中右下角的这个数据页,然后可以认为数据页内部通过二分法来定位记录。
     1)对于唯一索引来说,由于索引定义了唯一性,查找到第一个满足条件的记录后,就会停止继续检索。
     2)对于普通索引来说,查找到满足条件的第一个记录 (5,500) 后,需要查找下一个记录,直到碰到第一个不满足 k=5 条件的记录。
    

9.2 更新过程

首先我们得明白:使用数据时,内存向磁盘读取数据!
当更改数据页时(二级索引对应的数据页)。
若数据页在内存中——直接更新。
若数据页在磁盘中——就直接将需改操作写在内存中的 change buffer中。当从磁盘读取数据,结合内存中的 change buffer进行修改后(merge操作!),将修正后的数据传给用户。

  • change buffe,先在内存写修改操作 --> 磁盘读数结合merge修正后返回数据给用户
  • change buffe的修改操作最终也会修改数据,然后存储到磁盘上。
  • 只有普通索引才有change buffe(明白为什么?)

9.3 change buffer 的使用场景

  • 普通索引的所有场景,使用 change buffer 都可以起到加速作用吗?
  • 核心时将修改操作写下来,所以对于刚写就读的事务就没有意义。(适合写多读少!)

9.4 索引选择和实践

  • 建议使用普通索引

9.5 change buffer 和 redo log

  • change buffer在内存上,当利用普通索引进行数据的更新的时候,发生以下步骤:
  1. 数据页不在内存时,将修改操作填入change buffer,当用户读取数据时,利用磁盘读取的数据和change buffer中的操作,将数据修订后返给用户。
    2)将1的操作写入redo log(磁盘)中,防止数据丢失。

14 | count(*)这么慢,我该怎么办?(计算非null)

14.1 count(*) 的实现方式

  • 不同引擎中count(*)实现方式

    • MyISAM 引擎把一个表的总行数存在了磁盘上,因此执行 count(*) 的时候会直接返回这个数,效率很高;(当然家里where条件,这效率就每那么快了)
    • InnoDB 引擎就麻烦了,它执行 count(*) 的时候,需要把数据一行一行地从引擎里面读出来,然后累积计数
  • 为什么 InnoDB 不跟 MyISAM 一样,也把数字存起来呢?

    • InnoDB使用的时多版本控制(默认是可重复读),MVCC会使放回行,变得不确定
  • InnoDB 是索引组织表,主键索引树的叶子节点是数据,而普通索引树的叶子节点是主键值。所以,普通索引树比主键索引树小很多。对于 count(*) 这样的操作,遍历哪个索引树得到的结果逻辑上都是一样的。因此,MySQL 优化器会找到最小的那棵树来遍历

    B+树只有叶子结点上有数据,全部遍历其实就是对叶子结点的链表进行遍历。此时如果遍历主键索引树,由于其叶子结点上存放的是完整的行信息,对于一个数据页而言其行密度会比较小,最终导致要扫描的数据页较多,进而IO开销也比较大。如果遍历第二索引树,其叶子结点只存放主键信息,其数据页的行密度比较大,最终扫描的数据页较少,节省了IO开销。

14.2 用缓存系统保存计数

14.3 在数据库保存计数1

14.4 不同的 count 用法

  • 对于 count(主键 id) 来说,InnoDB 引擎会遍历整张表,把每一行的 id 值都取出来,返回给 server 层。server 层拿到 id 后,判断是不可能为空的,就按行累加。

  • 对于 count(1) 来说,InnoDB 引擎遍历整张表,但不取值。server 层对于返回的每一行,放一个数字“1”进去,判断是不可能为空的,按行累加。

  • 对于 count(字段) 来说:

    • 如果这个“字段”是定义为 not null 的话,一行行地从记录里面读出这个字段,判断不能为 null,按行累加;
    • 如果这个“字段”定义允许为 null,那么执行的时候,判断到有可能是 null,还要把值取出来再判断一下,不是 null 才累加。
  • count() 是例外,并不会把全部字段取出来,而是专门做了优化,不取值。count() 肯定不是 null,按行累加。

16讲“orderby”是怎么工作的

select city,name,age from t where city=‘杭州’ order by name limit 1000 ;

全字段排序

rowid排序

上面全字段排序的话,如果单行很大(耗内存大),这个方法效率不够好。所以就有了rowid排序!

全字段排序 VS rowid排序(mysql核心思想:如果内存够,就要多利用内存,尽量减少磁盘访问)

  • 优化:正常是取出来的数据进行排序后,将数据返回给用户,又没有办法使取出来的数据就是有序的?
  • 方法一:使用索引(city,name),这个需要回表操作
  • 方法二:使用索引(city, name, age),覆盖式索引直接返回数据给用户

34讲到底可不可以使用join

Index Nested-Loop Join: 驱动表全局扫描(存储引擎一条调获取),被驱动表树搜索

Simple Nested-Loop Join:驱动表和被驱动表都是全局扫描

Block Nested-Loop Join: 方便比较,将驱动表数据放内存中(不用来回读取磁盘)

用join?小表大表?

  • 如果可以使用被驱动表的索引,join语句还是有其优势的;
  • 不能使用被驱动表的索引,只能使用Block Nested-Loop Join算法,这样的语句就尽量不要使用;
  • 在使用join的时候,应该让小表做驱动表。

35讲join语句怎么优化

优化Index Nested-Loop Join(NLJ)——Batched Key Access:

  • 解决被驱动表匹配时,多次遍历查表得问题!
    1. 与被驱动表匹配时,对索引获得主键id进行排序后,在根据id查找记录。

优化Block Nested-Loop Join(BNL) :

因为都要全局遍历驱动表和被驱动表,尽可能将其变为NLJ

扫描二维码关注公众号,回复: 13124830 查看本文章
    1. 直接在被驱动表上建索引,这时就可以直接转成BKA算法了。
    1. 在使用临时表
    1. 扩展-hash join
      在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/The_dream1/article/details/115259730
今日推荐