1. 数据库架构

尽量减少 IO 的读取,可以使用缓存数据
image.png
可以和 springboot 项目相比较,思考一下项目中的数据库架构设计

2. 什么是索引

https://www.cnblogs.com/bypp/p/7755307.html
https://blog.csdn.net/weixin_51486343/article/details/113351092

2.0 索引的原理

可以把索引的原理比作字典,属于范围查询,先定位到章,再定位小节,然后找到页数。
本质都是:通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据。即 key - value 形式
因为数据在数据库中都是以页的方式存储,最后使用分段的方式对数据进行查询,因此我们可以想到树这种数据结构。

由于每次查找都需要消耗磁盘IO,因此需要尽量减小树的高度。显然二分树 和 avl 树不适用

对于树,阶数表示 此树的节点 最多 有 多少个子节点, 一般使用 M 表示
关键字,是唯一一个能表示一个记录的数据项(多为主键)

2.1 降低树的高度,使用 B-Tree

B 树 :一种多路自平衡的搜索树
image.png
这里的 17 / 35 为数据项,也就是第几行数据,通过判断数据项对应行的数据,判断下一步,每一次判断都是一次 I O 操作。
image.pngimage.png

2.3 B+ 树的使用(Innodb 的 默认实现)

https://blog.csdn.net/yin767833376/article/details/81511377

1. B 树

mysql 索引 - 图6

  1. 每个节点中根据实际情况会包含大量的关键字信息和分支,即 主键 对应的 value,不仅包含 数据的 key 还有 data 值。

2. B+ Tree

mysql 索引 - 图7

  1. 除了叶子节点中,只储存关键字和对应的指针。
  2. 叶子节点间是链式环结构,B+ tree 存在两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点

mysql 索引 - 图8
这样的话,可以进行两种查找,一种:对于主键进行范围和分页查找(利用sqt 做顺序查找)
另一种,从头指针开始,进行类似二分的随机查找。

2.4 二者的区别:

假设在 Innodb 下,每页默认大小为 16 KB:
对于 B 树 :假设一条数据(数据 + 地址 + 主键) 为 1k ,则根据 b 树 ,所能存储的数据量为 16k / 1k _ 16 _16 = 4096条
对于 B+ 树,由于存放的是 (地址 + 主键)为 10 字节,则可存储 (16 * 1024 / 10)^3 的数据量。
说明B+树存储的数据条数远比 B 树多,对于 千万级别的查询仅需要 1~3次 IO 即可。

3. 索引管理

3.1 索引分类:

image.png

  1. # 建立一个表
  2. 这个系统有一个会员表
  3. 有下列字段:
  4. 会员编号 INT
  5. 会员姓名 VARCHAR(10)
  6. 会员身份证号码 VARCHAR(18)
  7. 会员电话 VARCHAR(10)
  8. 会员住址 VARCHAR(50)
  9. 会员备注信息 TEXT
  • 建立一个用户表
    1. create table Person(
    2. -> vip_id int,
    3. -> vip_name varchar(20),
    4. -> vip_number varchar(18),
    5. -> vip_phone varchar(10),
    6. -> vip_position varchar(50),
    7. -> vip_text varchar(100),
    8. -> primary key (vip_id)
    9. -> );
    那么这个 会员编号,作为主键,使用 PRIMARY (主键索引)
    会员姓名 如果要建索引的话,那么就是普通的 INDEX (姓名可重复,不具有唯一性)
    会员身份证号码 如果要建索引的话,那么可以选择 UNIQUE (唯一的,不允许重复) (唯一索引)

除此之外还有全文索引,即FULLTEXT
会员备注信息 , 如果需要建索引的话,可以选择全文搜索。
用于搜索很长一篇文章的时候,效果最好。
用在比较短的文本,如果就一两行字的,普通的 INDEX 也可以。
但其实对于全文搜索,我们并不会使用MySQL自带的该索引,而是会选择第三方软件如Sphinx,专门来做全文搜索。
其他的如空间索引SPATIAL,了解即可,几乎不用

3.2 索引与无索引的对比:

  1. // 创建一个索引
  2. // 这是表的结构
  3. mysql> desc test;
  4. +-------+-------------+------+-----+---------+-------+
  5. | Field | Type | Null | Key | Default | Extra |
  6. +-------+-------------+------+-----+---------+-------+
  7. | name | varchar(20) | YES | | NULL | |
  8. | sex | varchar(20) | YES | | NULL | |
  9. +-------+-------------+------+-----+---------+-------+
  10. 2 rows in set (0.00 sec)
  11. // 为 user 创建一个索引
  12. // mysql 中索引的数据结构采用 b 树
  13. mysql> create index index_name on test(name);
  14. Query OK, 0 rows affected (0.08 sec)
  15. Records: 0 Duplicates: 0 Warnings: 0
  16. // 查询索引
  17. mysql> show index from test;
  18. +-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
  19. | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
  20. +-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
  21. | test | 1 | index_name | 1 | name | A | 2 | NULL | NULL | YES | BTREE | | | YES | NULL |
  22. | test | 1 | sex_s | 1 | sex | A | 4 | NULL | NULL | YES | BTREE | | | YES | NULL |
  23. +-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
  1. // 实验一下 索引
  2. #1.创建一个实验表
  3. create table s1(
  4. id int,
  5. name varchar(20),
  6. gender char(6),
  7. email varchar(50)
  8. );
  9. #2. 创建存储过程,实现批量插入记录
  10. delimiter $$ #声明存储过程的结束符号为$$
  11. create procedure auto_insert1()
  12. BEGIN
  13. declare i int default 1;
  14. while(i<3000000)do
  15. insert into s1 values(i,concat('egon',i),'male',concat('egon',i,'@oldboy'));
  16. set i=i+1;
  17. end while;
  18. END$$ #$$结束
  19. delimiter ; #重新声明分号为结束符号
  20. #3. 查看存储过程
  21. show create procedure auto_insert1\G
  22. #4. 调用存储过程
  23. call auto_insert1();
  1. // 假如 进行查询
  2. mysql> select id from s1 where email='egon555555@oldboy';
  3. +--------+
  4. | id |
  5. +--------+
  6. | 555555 |
  7. +--------+
  8. 1 row in set (1.70 sec)
  9. // 假如索引
  10. create index idx on s1(id);

1184802-20170912214329375-1453229056.png

说明索引能过够大大提升查询效率,因为假如没有索引,Mysql 必须从表的第一列开始从头遍历一次。

4.索引常见名词:

https://blog.csdn.net/BorisCao/article/details/105570143

4.1 回表:

  1. select name, age from s1 where name = ?

这个过程中,我们要先查询到id,再根据 id 查到对应项
mysql 索引 - 图11
效率较低,尽量避免回表操作。

4.2 覆盖索引:

通过某一个索引 例如 name 获取 id 之后,再通过 id 查找对应项,这就是 sql 命中了索引但是没有覆盖索引。mysql 索引 - 图12
以上的查询过程,根据 name 的值,通过索引查找到对应的 name 和 id, 这个时候由于 子节点为 name,id ,包含了 id 值,所以不用再次进行回表查询,这就是索引覆盖。如果进行索引的优化,可以考虑进行组合索引以及索引覆盖。

  1. mysql> select * from s1 where id = 123;
  2. +------+---------+--------+----------------+
  3. | id | name | gender | email |
  4. +------+---------+--------+----------------+
  5. | 123 | egon123 | male | egon123@oldboy |
  6. | 123 | egon123 | male | egon123@oldboy |
  7. +------+---------+--------+----------------+
  8. 2 rows in set (0.04 sec)
  9. mysql> select id from s1 where id = 123;
  10. +------+
  11. | id |
  12. +------+
  13. | 123 |
  14. | 123 |
  15. +------+
  16. 2 rows in set (0.00 sec)

这就是覆盖索引的情况,命中索引之后,直接从索引的数据结构中就获取了id在硬盘的地址,速度很快。

4.3 最左匹配原则 (**):

https://juejin.cn/post/6844903966690508814
为列创建索引时,可以选择多个列组成索引,即联合索引,遵循最左匹配原则

索引的最左匹配原则,对于 Mysql 的 B+ 树,当 数据项是复合的数据结构时,树时根据从左到右的顺序来建立数据项,比如(name, age, sex),B+树会优先比较 name 来确定下一步,但是假如数据为(张三,F)而非 (张三,20,F)时,由于没有第一个搜索因子,只能等把名字等于张三的数据都找到(线性遍历之后),再匹配 sex = F 的 数据项。

举个例子:

  1. #在其中 id 为 主键, name 和 age 组成列和索引
  2. #请问一下的哪个语句会走联合索引
  3. select * from table where name = 'zhangsan' and age = 12; (1)
  4. select * from table where name = 'zhangsan';(2)
  5. select * from table where age = 12;
  6. select * from table where age = 12 and name = 'zhangsan';(4)
  7. (1)(2) (4)会走 , (4) 优化器会把 age name 进行交换位置
  8. (3) 不会
  1. mysql> create index ne on s1(name ,email);
  2. Query OK, 0 rows affected (17.00 sec)
  3. Records: 0 Duplicates: 0 Warnings: 0
  4. 说明我们需要先匹配 name ,然后再 匹配 email
  5. # 没有name 的情况下
  6. mysql> select * from s1 where email='egon300000@oldboy';
  7. +--------+------------+--------+-------------------+
  8. | id | name | gender | email |
  9. +--------+------------+--------+-------------------+
  10. | 300000 | egon300000 | male | egon300000@oldboy |
  11. +--------+------------+--------+-------------------+
  12. 1 row in set (4.04 sec)
  13. # 有name的情况下
  14. mysql> select * from s1 where name='egon300000';
  15. +--------+------------+--------+-------------------+
  16. | id | name | gender | email |
  17. +--------+------------+--------+-------------------+
  18. | 300000 | egon300000 | male | egon300000@oldboy |
  19. +--------+------------+--------+-------------------+
  20. 1 row in set (0.00 sec)

说明在创建索引时,最好创建唯一性索引,并将 唯一项放在左侧,这样查询速度较快,就算有一部分数据不知道,也能在最快时间内获取匹配的数据。

4.4 索引下移

https://www.jianshu.com/p/31ceadace535 可以去看下数据的组成,什么是 server 层, 什么是存储引擎
ICP(Index Condition Pushdown)是在MySQL 5.6版本上推出的查询优化策略,把本来由Server层做的索引条件检查下推给存储引擎层来做,以降低回表和访问存储引擎的次数,提高查询效率。
mysql 索引 - 图13
本来是在存储引擎进行拉取后,在 server 层进行数据过滤。
进行索引下推后,使用 name 和 age 一起进行数据筛选,将筛选结果返回给 server。

可通过 show index 来查看表中所有的 index