可以在我的个人博客阅读文章,排版会美观一些:文章地址
PRIMARY KEY
SELECT id (name age) INDEX WHERE name AND age;
-- 建表语句:建表时,设置主键,自动创建主键索引
CREATE TABLE t_user (
id VARCHAR(20) PRIMARY KEY,
name VARCHAR(20)
);
-- 查看索引
SHOW INDEX FROM t_user;
-- 建表时创建单列索引:
-- 这种方式创建单列索引,其名称默认为字段名称:name
CREATE TABLE t_user (
id VARCHAR(20) PRIMARY KEY,
name VARCHAR(20),
KEY(name)
);
-- 建表后创建单列索引:
-- 索引名称为:name_index 格式---> 字段名称_index
CREATE INDEX name_index ON t_user(name)
-- 删除单列索引
DROPINDEX 索引名称 ON 表名
-- 建表时创建唯一索引:
CREATE TABLE t_user2 (
id VARCHAR(20) PRIMARY KEY,
name VARCHAR(20),
UNIQUE(name)
);
-- 建表后创建唯一索引:
CREATE UNIQUE INDEX name_index ON t_user2(name);
-- 建表时创建复合索引:
CREATE TABLE t_user3 (
id VARCHAR(20) PRIMARY KEY,
name VARCHAR(20),
age INT,
KEY(name,age)
);
-- 建表后创建复合索引:
CREATE INDEX name_age_index ON t_user3(name,age);
-- 复合索引查询的2个原则
-- 1.最左前缀原则
-- eg: 创建复合索引时,字段的顺序为 name,age,birthday
-- 在查询时能利用上索引的查询条件为:
SELECT * FROM t_user3 WHERE name = ?
SELECT * FROM t_user3 WHERE name = ? AND age = ?
SELECT * FROM t_user3 WHERE name = ? AND birthday = ?
SELECT * FROM t_user3 WHERE name = ? AND age = ? AND birthday = ?
-- 而其他顺序则不满足最左前缀原则:
... WHERE name = ? AND birthday = ? AND age = ? -- 不满足最左前缀原则
... WHERE name = ? AND birthday = ? -- 不满足最左前缀原则
... WHERE birthday = ? AND age = ? AND name = ? -- 不满足最左前缀原则
... WHERE age = ? AND birthday = ? -- 不满足最左前缀原则
-- 2.MySQL 引擎在执行查询时,为了更好地利用索引,在查询过程中会动态调整查询字段的顺序!
-- 这时候再来看上面不满足最左前缀原则的四种情况:
-- 不满足最左前缀原则,但经过动态调整顺序后,变为:name age birthday 可以利用复合索引!
... WHERE name = ? AND birthday = ? AND age = ?
-- 不满足最左前缀原则,也不能动态调整(因为缺少age字段),不可以利用复合索引!
... WHERE name = ? AND birthday = ?
-- 不满足最左前缀原则,但经过动态调整顺序后,变为:name age birthday 可以利用复合索引!
... WHERE birthday = ? AND age = ? AND name = ?
-- 不满足最左前缀原则,也不能动态调整(因为缺少name字段),不可以利用复合索引!
... WHERE age = ? AND birthday = ?
-- 建表:
CREATE TABLE t_emp(
id INT PRIMARY KEY,
name VARCHAR(20),
age INT
);
-- 插入数据:插入时,主键无序
INSERT INTO t_emp VALUES(5,'d',22);
INSERT INTO t_emp VALUES(6,'d',22);
INSERT INTO t_emp VALUES(7,'3',21);
INSERT INTO t_emp VALUES(1,'a',23);
INSERT INTO t_emp VALUES(2,'b',26);
INSERT INTO t_emp VALUES(3,'c',27);
INSERT INTO t_emp VALUES(4,'a',32);
INSERT INTO t_emp VALUES(8,'f',53);
INSERT INTO t_emp VALUES(9,'b',13);
-- 查询:自动排序,有序展示(因为主键是有主键索引的,因此会自动排序)
问题:为什么数据插入时,未按照主键顺序,而查询时却是有序的呢
?
问题:为什么要排序呢?
id = 3
的数据,只需要按照顺序去找即可,而如果不排序,就如同大海捞针,假如100W条数据,可能有时候需要随机查询100W次才找到这个数据,也可能运气好上来第1次就查询到了该数据,不确定性太高!上图这种分层树
结构查询效率较高,因为如果我需要查询 id=4
的数据,只需要在页目录中匹配,大于3且小于5,则去3对应的page=2
中查找数据,这样就不需要从第1页开始检索数据了,大大提高了效率!
从上图可得出,在只有2层的结构下,1page 可以存储记录总数为 1365 * 455 ≈ 62万条
,而如果再加1层结构,来存储page层分页目录数据的分页层PAGE的话,那么1PAGE可以存储总page数为:1365 * 1365 ≈ 186万条page
,而1PAGE存储的总记录数为 1365 * 1365 * 455 ≈ 8.5 亿条
。因此,我们平时使用的话,2层结构就已经足够了!实际上1个页存储的总数据树可能大于理论估计的,因为我们分配name字段的VARCHAR(20)
占20个字节,而实际上可能存储的name数据并没有20个字节,可能更小!
三层结构实例如图:
上图4.1 原理分析图中这种索引结构称之为B+树数据结构,那么什么是B+树呢?B树和B+树区别是什么呢?
详情参考文章:https://www.cnblogs.com/lianzhilei/p/11250589.html
问题4.2.1 为什么InnoDB底层使用B+树做索引而不用B树?
B树结构图:
16KB/(8B+8B)=1000
个键值(只是估计值,方便计算而已)。也就是说,一个深度为3的B+树索引可以维护10^3 * 10^3 * 10^3 = 10亿
条记录。在表中,聚簇索引实际上就是指的是主键索引!如果表中没有主键的话,则MySQL会根据该表生成一个RoleID,拿这个RoleId当做聚簇索引!
聚簇索引:将数据存储与索引放到一起
,索引结构的叶子节点保存了每行的数据。例如:4.1小结分析图中的data层一个单位就是聚簇索引存储数据的例子,主键id 字段就是聚簇索引,4.1小结分析图就是基于主键索引(聚簇索引)构成的B+树结构!聚簇索引不一定是主键索引,但是主键索引肯定是聚簇索引!
非聚簇索引:将数据与索引分开存储
,索引结构的叶子节点指向了数据对应的位置(聚簇索引的值)!非聚簇索引检索数据是在自己的 “树” 上进行查找,例如我们根据表中的非聚簇索引name字段去查找数据时,流程如下图:
再看一张比较正规的分析图:
注意:在InnoDB中,在聚簇索引之上创建的索引称之为辅助索引,例如:复合索引、单列索引、唯一索引。一个表中只能有1个聚簇索引,而其他索引都是辅助索引!辅助索引的叶子节点存储的不再是行的物理位置,而是主键的值,辅助索引访问数据总是需要二次查找的!
**问题5.1.1 **:为什么非聚簇索引(name字段的单列索引)构成的树,其叶子节点存储聚簇索引(主键id),而不直接存储行数据的物理地址呢?
换个方式问:非聚簇索引检索数据时,检索一次本树再去聚簇索引树中检索一次,这样二次检索树结构,那么为什么不直接在非聚簇索引树叶子节点中存放行数据物理地址,这样只需要检索一次树结构就拿到行数据呢?
这里画个图方便理解一些:
从上图得出,在做新增数据时,因为底层是需要基于主键索引进行排序的,那么就可能导致原来某些数据对应的物理地址发生了变化,而这时候由于我们的非聚簇索引树的叶子节点直接存储了数据的物理地址,所以为了保证能获取到数据,还需要同时对非聚簇索引树叶子节点的地址进行一遍更新修改!
同理,如果我们不做插入主键id为4这行记录的操作,而是将其删除的话,这个流程可以自己思考一下!
也就是说:之所以不在非聚簇索引树的叶子节点直接存放行数据的物理地址,是因为,存储数据的物理地址会随着数据库表的CRUD操作而不断变更,为了保证能获取到数据,这时必须要对非聚簇索引树相关叶子节点的地址进行一遍修改!而存主键,主键不会随着CRUD操作发生变化,宁愿多查一次树,也不要再修改一次树的结构!
InnoDB中:
WHERE id = 4
这样的条件查找主键,则按照B+树的检索算法即可查找对应的叶子节点,之后获得对应的行数据!MYISAM中:
MYISAM使用的是非聚簇索引,非聚簇索引的两颗B+树看上去没有什么不同,节点的结构完全一致,只是存储的内容不同,主键索引B+树的节点存储了主键,辅助索引B+树存储量辅助键。
表数据存储在独立的地方,这两颗B+树的叶子节点都使用一个地址指针指向真正的表数据,对于表数据来说,这两个键没有任何差别。
由于索引树是独立的,通过辅助键检索无需再次检索主键索引树!
问题:5.3.1 使用聚簇索引的优势
问题:每次使用辅助索引检索都需要经过2次B+树查找,看上去聚簇索引的效率明显要低于非聚簇索引,那么聚簇索引的优势何在呢?
-- 1.由于行数据和聚簇索引树的叶子节点存储在一起,同一页中会有多条行数据,首次访问数据页中某条行记录时,会把该数据页数据加载到Buffer(缓存器)中,当再次访问该数据页中其他记录时,不必访问磁盘而直接在内存中完成访问。
-- 注:主键id和行数据一起被载入内存,找到对应的叶子节点就可以将行数据返回了,如果按照主键id来组织数据,获取数据效率更快!
-- 2.辅助索引的叶子节点,存储主键的值,而不是行数据的存放地址。这样做的好处是,因为叶子节点存放的是主键值,其占据的存储空间小于存放行数据物理地址的储存空间
问题:5.3.2 使用聚簇索引需要注意什么?
-- 当使用主键为聚簇索引时,而不要使用UUID方式,因为UUID的值太过离散,不适合排序,导致索引树调整复杂度增加,消耗更多时间和资源。
-- 建议主键最好使用INT/BIGINT类型,且为自增,这样便于排序且默认会在索引树的末尾增加主键值,对索引树的结构影响最小(下面主键自增的问题会解释原因)。而且主键占用的存储空间越大,辅助索引中保存的主键值也会跟着增大,占用空间且影响IO操作读取数据!
问题:5.3.3 为什么主键通常建议使用自增id?
-- 聚簇索引树存放数据的物理地址(xx1,xx2,xx3,xxx5)与索引顺序(1,2,3,5)是一致的,即:
-- 1.只要索引是相邻的,那么在磁盘上索引对应的行数据存放地址也是相邻的。
-- 2.如果主键是自增,那么当插入新数据时,只需要按照顺序在磁盘上开辟新物理地址存储新增行数据即可。
-- 3.而如果不是主键自增,那么当新插入数据后,会对索引进行重新排序(重新调整B+树结构),磁盘上的物理存储地址也需要重新分配要存储的行数据!
问题:5.3.4 什么情况下无法利用索引呢?
-- 1. 查询语句中使用LIKE关键字:(这种情况主要是针对于单列索引)
-- 在使用LIKE关键字查询时,如果匹配字符串的第一个字符为'%',则索引不会被使用,而'%'不在最左边,而是在右边,则索引会被使用到!
-- eg:
SELECT * FROM t_user WHERE name LIKE 'xx%' -- 可以利用上索引,这种情况下可以拿xx到索引树上去匹配
SELECT * FROM t_user WHERE name LIKE '%xx%' -- 不可以利用上索引
SELECT * FROM t_user WHERE name LIKE '%xx' -- 不可以利用上索引
-- 2. 查询语句中使用多列索引:(这种情况主要是针对于聚合索引)
-- 多索引是在表的多个字段创建索引,只有查询条件中使用了这些字段中的第一个字段,索引才会被使用。即:最左前缀原则,详情查看3.4小结聚合索引中的介绍!
-- 3. 查询语句中使用OR关键字:
-- 查询条件中有OR关键字时,如果OR前后的两个条件列都具有索引,则查询中索引将被使用,而如果OR前后有一个或2个列不具有索引,那么查询中索引将不被使用到!
约束:
作用:是为了保证数据的完整性而实现的摘自一套机制,即(约束是针对表中数据记录的)
MySQL中的约束:
主键约束= 非空约束 + 唯一约束
保证某列数据不能为空且唯一;示例:
create table member(
id int(10),
phone int(15) unsigned zerofill,
name varchar(30) not null,
constraint uk_name unique(name),
constraint pk_id primary key (id),
constraint fk_dept_id foreign key (dept_id,字段2)
references dept(主表1)(dept_id)
);
答案参考:第2小结
考察点:复合索引的最左前缀原则
-- 假设构成复合索引的字段为 name,age,birthday
-- 则下面那种情况可以使用成功利用复合索引查询?
... WHERE name = ? -- 可以利用
... WHERE name = ? AND age = ? -- 可以利用
... WHERE name = ? AND birthday = ? -- 可以利用
... WHERE name = ? AND age = ? AND birthday = ? -- 可以利用
... WHERE name = ? AND birthday = ? AND age = ? -- 不满足最左前缀原则,但经过动态调整后可以利用
... WHERE birthday = ? AND age = ? AND name = ? -- 不满足最左前缀原则,但经过动态调整后可以利用
... WHERE age = ? AND birthday = ? -- 不满足最左前缀原则,不能动态调整,不能利用复合索引
MySQL索引数据结构: B + Tree
B+树,聚簇索引~
MySQL索引与约束
答案参考: 4.2小结B+树结构分析中的问题4.2.1
答案参考:5.1小结聚簇索引和非聚簇索引分析
答案参考:5.1小结的问题5.1.1
答案参考:5.3小结的问题5.3.2
答案参考:5.3小结的问题5.3.3
答案参考:5.3小结的问题5.3.4
答案参考:5.3小结的问题5.3.1