MySQL学习笔记

428 阅读8分钟

聚簇索引

InnoDB引擎会自动为主键(没有主键的表,InnoDB会自动生成一个主键)生成索引,叫聚簇索引,该B+树的叶子节点会保存所有信息,而其他不是主键的字段生成的索引,都是二级索引。

二级索引

二级索引生成的B+树,叶子节点只保存该字段的值和主键的值,在二级索引的B+树查询完之后,还需要回到聚簇索引中再次进行查询,查出所有的信息,这个叫做回表

联合索引

同时为多个列建立索引,先按照第一个列的大小进行排序,然后再按照第二个列的大小进行排序,以此类推,只建立一颗B+树,本质上也是二级索引

索引的代价

  • 空间上的代价

每建立一个索引,都会生成一个B+树,每一个B+树的每一个节点都是一个数据页,一个页默认会占用16KB的存储空间

  • 时间上的代价

每次对表中的数据进行增删改操作时,都需要去修改B+树索引,去进行记录移位、页面分裂、页面回收之类的操作。因为不论是叶子节点中的记录,还是内节点中的记录(也就是不论是用户记录还是目录项记录)都是按照从小到大的顺序,形成的单向链表。

全值匹配

搜索条件中的列和联合索引中的列一致,然后是用=来判断的,比如:

SELECT * FROM person_info WHERE name = 'Ashburn' AND birthday = '1990-09-27' AND phone_number = '15123983239';

值得注意的是,where子句中的搜索条件的顺序对查询结果没有影响,因为MySQL有个叫查询优化器的东西。

匹配左边的列

也可以不用包含全部联合索引中的列,只包含左边的就行,或者多个左边的列也行。 如果我们想使用联合索引中尽可能多的列,搜索条件中的各个列必须是联合索引中从最左边连续的列。

字符串排序

本质是比较大小,就用到了该列的字符集和比较规则。一般的比较规则都是逐个比较字符的大小,先比较字符串的第一个字符,第一个字符小的那个字符串就比较小,如果第一个字符相同,那就再比较第二个字符,以此类推。

匹配列前缀

所以一个排好序的字符串列,前n个字符,也就是前缀,都是排好序的,所以对于字符串类型的索引列来说,我们只匹配它的前缀也是可以快速定位记录的,比如:

SELECT * FROM person_info WHERE name LIKE 'As%';

但是如果只给出后缀或者中间的某个字符串,比如:

SELECT * FROM person_info WHERE name LIKE '%As%';

这样就没办法快速定位记录位置了,因为字符串中间有'As'的字符串并没有排好序,所以只能全表扫描了。 有的时候必须要后缀查询,可以将表中的数据逆序存储,然后再进行前缀查询。

精确匹配某一列并范围匹配另外一列

左边的列是精确查找,右边的列可以进行范围查找,比如: SELECT * FROM person_info WHERE name = 'Ashburn' AND birthday > '1980-01-01' AND birthday < '2000-12-31' AND phone_number > '15100000000';

索引可以用来排序

order by 之后的列,按照联合索引建立的时候的顺序,就可以使用该联合索引进行排序。如果顺序颠倒了,就无法使用索引了。

不可以使用索引进行排序的几种情况

  • ASC DESC混用

要么各个列都是ASC规则排序,要么都是DESC规则排序

  • where子句中出现非排序使用到的索引列

如果where子句中出现了非排序使用到的索引列,那么排序依然是使用不到索引的

  • 排序列包含非同一个索引的列

有时候用来排序的多个列不是一个索引里的,这种情况也不能使用索引进行排序

用于分组

分组列的顺序也需要和索引列的顺序一致,也可以只使用索引列中左边的列进行分组

覆盖索引

为了彻底告别回表操作带来的性能损耗,最好在查询列表里只包含索引列。 不推荐用*作为查询列表,最好把我们需要查询的列依次标明。

如何挑选索引

  • 只为用于搜索、排序或分组的列创建索引

也就是说,只为出现在where子句中的列、连接子句中的连接列,或者出现在order by 或 group by子句中的列创建索引。

  • 考虑列的基数

列的基数指的是某一列中不重复数据的个数。在记录行数一定的情况下,列的基数越大,该列中的值越分散,列的基数越小,该列中的值越集中。最好为那些列的基数大的列建立索引,为基数太小的列建立索引效果可能不好

  • 索引列的类型尽量小

以整数为例,在表示的整数范围允许的情况下,尽量让索引列使用较小的类型,能用INT就不要使用BIGINT

索引字符串值的前缀

字符串如果太长,存储就占据太大的存储空间,也会耗费很长的时间。所以可以只对字符串的前几个字符进行索引,也就是说在二级索引的记录中只保留字符串前几个字符。

这样在查找记录时虽然不能精确的定位到记录的位置,但是能定位到相应前缀所在的位置,然后根据前缀相同的记录的主键值回表查询完整的字符串值。

比方说我们在建表语句中只对name列的前10个字符进行索引可以这么写:

    name VARCHAR(100) NOT NULL,
    birthday DATE NOT NULL,
    phone_number CHAR(11) NOT NULL,
    country varchar(100) NOT NULL,
    KEY idx_name_birthday_phone_number (name(10), birthday, phone_number)
);

name(10)就表示在建立的B+树索引中只保留记录的前10个字符的编码,这种只索引字符串值得前缀的策略使我们非常鼓励的,尤其是在字符串类型能存储的字符比较多的时候。

让索引列在比较表达式中单独出现

如果索引列在比较表达式中不是以单独列的形式出现,而是以某个表达式,或者函数调用形式出现的话,是用不到索引的。

冗余和重复索引

注意不要重复或冗余,比方说某个列既是主键、又是唯一索引,还有普通索引。这种情况要避免。

B+树索引适用于以下情况

  • 全值匹配
  • 匹配左边的列
  • 匹配范围值
  • 精确匹配某一列并范围匹配另外一列
  • 用于排序
  • 用于分组

使用索引时需要注意以下事项

  • 只为用于搜索、排序或分组的列创建索引
  • 为列的基数大的列创建索引
  • 索引列的类型尽量小
  • 可以只对字符串值的前缀建立索引
  • 只有索引列在比较表达式中单独出现才可以适用索引
  • 主键要有AUTO_INCREMENT属性
  • 定位并删除表中的重复和冗余索引
  • 尽量适用覆盖索引进行查询,避免回表带来的性能损耗

InnoDB存储

使用页为基本单位来管理存储空间,默认的页大小为16KB

每个索引都对应一颗B+树,该B+树的每个节点都是一个数据页,数据页之间不必要是物理连续的,因为数据页之间有双向链表来维护这些页的顺序

InnoDB的聚簇索引的叶子节点存储了完整的用户记录,也就是所谓的索引即数据,数据即索引

为了更好地管理页,InnoDB有个表空间(文件空间)的概念,每个表空间可以被划分为很多个页,我们的表数据就存放在某个表空间下的某些页里

InnoDB表空间分类

  • 系统表空间
  • 独立表空间

包含.frm和.ibd两个文件,.ibd文件就用来存储表中的数据和索引

MyISAM引擎

MyISAM中索引全部是二级索引,数据和索引是分开存放的。

MyISAM并没有表空间一说,表数据都存放在对应的数据库子目录,该目录会有.frm .MYD .MYI三个文件。

.MYD存储表的数据文件,.MYI存储表的索引文件

InnoDB 区

对于16KB的页来说,连续的64个页就是一个区,也就是说一个区默认占用1MB空间大小。

单标访问方法

  • const

通过主键或者唯一二级索引,与常数的等值比较

SELECT * FROM single_table WHERE id = 1438;

  • ref

二级索引列与常数等值比较

SELECT * FROM single_table WHERE key1 = 'abc';

  • ref_or_null

不仅想找出某个二级索引列的值等于某个常数的记录,还想把该列的值为null的记录也找出来:

SELECT * FROM single_table WHERE key1 = 'abc' OR key1 IS NULL;

  • range

索引列需要匹配某个或某些范围的值

SELECT * FROM single_table WHERE key2 IN (1438, 6328) OR (key2 >= 38 AND key2 <= 79);

  • index

搜索条件在联合索引中,但是不是最左列,然后查询列表在联合索引中

  • all

全表扫描