1. 数据库架构
尽量减少 IO 的读取,可以使用缓存数据
可以和 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 树 :一种多路自平衡的搜索树
这里的 17 / 35 为数据项,也就是第几行数据,通过判断数据项对应行的数据,判断下一步,每一次判断都是一次 I O 操作。
2.3 B+ 树的使用(Innodb 的 默认实现)
https://blog.csdn.net/yin767833376/article/details/81511377
1. B 树

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

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

这样的话,可以进行两种查找,一种:对于主键进行范围和分页查找(利用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 索引分类:

# 建立一个表这个系统有一个会员表有下列字段:会员编号 INT会员姓名 VARCHAR(10)会员身份证号码 VARCHAR(18)会员电话 VARCHAR(10)会员住址 VARCHAR(50)会员备注信息 TEXT
- 建立一个用户表
那么这个 会员编号,作为主键,使用 PRIMARY (主键索引)create table Person(-> vip_id int,-> vip_name varchar(20),-> vip_number varchar(18),-> vip_phone varchar(10),-> vip_position varchar(50),-> vip_text varchar(100),-> primary key (vip_id)-> );
会员姓名 如果要建索引的话,那么就是普通的 INDEX (姓名可重复,不具有唯一性)
会员身份证号码 如果要建索引的话,那么可以选择 UNIQUE (唯一的,不允许重复) (唯一索引)
除此之外还有全文索引,即FULLTEXT
会员备注信息 , 如果需要建索引的话,可以选择全文搜索。
用于搜索很长一篇文章的时候,效果最好。
用在比较短的文本,如果就一两行字的,普通的 INDEX 也可以。
但其实对于全文搜索,我们并不会使用MySQL自带的该索引,而是会选择第三方软件如Sphinx,专门来做全文搜索。
其他的如空间索引SPATIAL,了解即可,几乎不用
3.2 索引与无索引的对比:
// 创建一个索引// 这是表的结构mysql> desc test;+-------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+-------+-------------+------+-----+---------+-------+| name | varchar(20) | YES | | NULL | || sex | varchar(20) | YES | | NULL | |+-------+-------------+------+-----+---------+-------+2 rows in set (0.00 sec)// 为 user 创建一个索引// mysql 中索引的数据结构采用 b 树mysql> create index index_name on test(name);Query OK, 0 rows affected (0.08 sec)Records: 0 Duplicates: 0 Warnings: 0// 查询索引mysql> show index from test;+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+| test | 1 | index_name | 1 | name | A | 2 | NULL | NULL | YES | BTREE | | | YES | NULL || test | 1 | sex_s | 1 | sex | A | 4 | NULL | NULL | YES | BTREE | | | YES | NULL |+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
// 实验一下 索引#1.创建一个实验表create table s1(id int,name varchar(20),gender char(6),email varchar(50));#2. 创建存储过程,实现批量插入记录delimiter $$ #声明存储过程的结束符号为$$create procedure auto_insert1()BEGINdeclare i int default 1;while(i<3000000)doinsert into s1 values(i,concat('egon',i),'male',concat('egon',i,'@oldboy'));set i=i+1;end while;END$$ #$$结束delimiter ; #重新声明分号为结束符号#3. 查看存储过程show create procedure auto_insert1\G#4. 调用存储过程call auto_insert1();
// 假如 进行查询mysql> select id from s1 where email='egon555555@oldboy';+--------+| id |+--------+| 555555 |+--------+1 row in set (1.70 sec)// 假如索引create index idx on s1(id);
说明索引能过够大大提升查询效率,因为假如没有索引,Mysql 必须从表的第一列开始从头遍历一次。
4.索引常见名词:
https://blog.csdn.net/BorisCao/article/details/105570143
4.1 回表:
select name, age from s1 where name = ?
这个过程中,我们要先查询到id,再根据 id 查到对应项
效率较低,尽量避免回表操作。
4.2 覆盖索引:
通过某一个索引 例如 name 获取 id 之后,再通过 id 查找对应项,这就是 sql 命中了索引但是没有覆盖索引。
以上的查询过程,根据 name 的值,通过索引查找到对应的 name 和 id, 这个时候由于 子节点为 name,id ,包含了 id 值,所以不用再次进行回表查询,这就是索引覆盖。如果进行索引的优化,可以考虑进行组合索引以及索引覆盖。
mysql> select * from s1 where id = 123;+------+---------+--------+----------------+| id | name | gender | email |+------+---------+--------+----------------+| 123 | egon123 | male | egon123@oldboy || 123 | egon123 | male | egon123@oldboy |+------+---------+--------+----------------+2 rows in set (0.04 sec)mysql> select id from s1 where id = 123;+------+| id |+------+| 123 || 123 |+------+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 的 数据项。
举个例子:
#在其中 id 为 主键, name 和 age 组成列和索引#请问一下的哪个语句会走联合索引select * from table where name = 'zhangsan' and age = 12; (1)select * from table where name = 'zhangsan';(2)select * from table where age = 12;select * from table where age = 12 and name = 'zhangsan';(4)(1)(2) (4)会走 , (4) 优化器会把 age 和 name 进行交换位置(3) 不会
mysql> create index ne on s1(name ,email);Query OK, 0 rows affected (17.00 sec)Records: 0 Duplicates: 0 Warnings: 0说明我们需要先匹配 name ,然后再 匹配 email# 没有name 的情况下mysql> select * from s1 where email='egon300000@oldboy';+--------+------------+--------+-------------------+| id | name | gender | email |+--------+------------+--------+-------------------+| 300000 | egon300000 | male | egon300000@oldboy |+--------+------------+--------+-------------------+1 row in set (4.04 sec)# 有name的情况下mysql> select * from s1 where name='egon300000';+--------+------------+--------+-------------------+| id | name | gender | email |+--------+------------+--------+-------------------+| 300000 | egon300000 | male | egon300000@oldboy |+--------+------------+--------+-------------------+1 row in set (0.00 sec)
说明在创建索引时,最好创建唯一性索引,并将 唯一项放在左侧,这样查询速度较快,就算有一部分数据不知道,也能在最快时间内获取匹配的数据。
4.4 索引下移
https://www.jianshu.com/p/31ceadace535 可以去看下数据的组成,什么是 server 层, 什么是存储引擎
ICP(Index Condition Pushdown)是在MySQL 5.6版本上推出的查询优化策略,把本来由Server层做的索引条件检查下推给存储引擎层来做,以降低回表和访问存储引擎的次数,提高查询效率。
本来是在存储引擎进行拉取后,在 server 层进行数据过滤。
进行索引下推后,使用 name 和 age 一起进行数据筛选,将筛选结果返回给 server。
可通过 show index 来查看表中所有的 index

