MySQL知识点汇集
作者:互联网
索引的本质
索引是帮助MySQL高效获取数据的排好序的数据结构;
比如现在一百个数,那么如何快速获取中间值,常见的可以用二叉树,来快速定位数据,减少查询的次数;
索引的选择
常见的数据结构有:
https://www.cs.usfca.edu/~galles/visualization/Algorithms.html 这个网页可以帮助理解各种树
-
二叉树
- 如果次数有一万个数为(1,2,3....),那么二叉树会有一万层,更像一个链表结构。这种结构很显然是不便于快速定位数据的。因为如果要查询10000那么需要10000次io才能检索到索引;
-
红黑树
- 红黑树是一种平衡二叉树,较于二叉树会减少层次数量,但是随着数据量增加,红黑树的层级也会增加;
-
Hash表
- Hash表只能针对于单数据查询,如select * from user where id=888;这种查询会先hash(888)算出索引地址,然后拿到数据的地址。可以极快的查询到数据;
- 但是如果是范围查询,那么hash便无法支持;
以上,所以MySQL索引最好保证树的高度(14层),以此减少io的次数(14次),来达到快速查找的目的;
-
B树
- 比较矮胖,即缩小高度,将每层级的数据增多。
- 每个节点都有data,这样查询的时候一次性获取一个层级的节点放到内存,在内存中可以快速查找;
- 因为获取一个层级节点到内存是有大小限制的,即如果节点过大,那么获取的数量会减少;
- innodb_page_size的默认值是16kb, 即一个层级的节点最好16kb.
MySQL最终选择的是B+树
- B+树(B树的变种)
- 较于B树,B+树的非叶子节点不存储data。这样一次io可以获取更多的节点;
- 较于B树,B+树非叶子节点会冗余索引,即每层会有重复的,这样做事为了查询更快;
- 较于B树,B+树叶子节点中有指针连接,这样是为了范围查询的的时候可以直接往下查询,而不用从第一层索引开始重头开始,减少回表;
- 较于B树,B+树的非叶子节点不存储data。这样一次io可以获取更多的节点;
假如一个节点是16KB,且主键是Bigint(占用内存8B) ,主键间存储的内存地址假设占用内存B,那么一层可以存储1170个节点;依次类推,这样的高度为3的B+树大概可以存储2000W数据;
存储引擎
MySQL存储引擎是形容表的;
MyISAM存储引擎
文件存储格式为三个:
- frm 表结构
- MYD 数据
- MYI 索引
采用B+树 树的子节点存储的是数据的地址,获取到数据地址后再去MYD文件查询具体数据;
这种查询方式的索引也叫做非聚集索引
INNODB存储引擎
文件存储格式为两个:
- frm 表结构
- IBD 数据+索引
采用B+树 树的子节点存储的就是数据,减少了回表。根据索引可以直接查询到数据;
这种查询方式的索引叫做聚集索引 也叫聚簇索引
为什么INNODB要求必须有主键且推荐采用自增主键,而MyISAM却不需要?
- 因为InnoDB要求数据有序,如果插入的是非自增主键,随机插入现有索引页中间,可能会造成数据移动,增加开销。
- MyISAM 插入数据就是,如果中间数据页有空位就直接插入,没有就在现有数据页后面插入,不涉及数据移动。
联合索引是怎么样的?
-
如上图,可以看到图是联合索引(col1,col2,col3)
-
因为不是主键索引,所以叶子节点不存储data, 那么在非聚集索引中,即使复合列索引,非叶节点也存储三个值
-
非叶子节点也是存储三列,不过是先按照第一字段排序,相同按第二字段排序,依次类推
mysql(innodb)是索引组织表,叶子节点是存的表的主键
-
按照col1,col2,col3的顺序依次查找后可以找到主键id。再根据主键id进行索引查询;
-
如果where条件只有col2,那么很显然我们无法定位一个具体的索引区间,这种原则就是最左侧列匹配原则
理解以上联合索引后,我们可以很清晰的理解索引的匹配原则;
MySQL 索引匹配原则
在我们了解到MySQL底层数据结构B+树后,这些规则其实很好理解;
索引匹配原则如下:
- 等值匹配
- 最左侧列匹配
- 最左前缀匹配
- 范围查找
- 等值匹配+范围查找
假设现在有一张表student_score,表中有5个字段,id,class, name,course,score,其中id为主键,有四条数据
- id:1 class:1班 name:张三 course:数学 score 90
- id:2 class:1班 name:张三 course:语文 score 50
- id:3 class:3班 name:李四 course:数学 score 50
- id:4 class:3班 name:李四 course:语文 score 20
并且我们以class、name、course建立了联合索引;
等值匹配
假设现在有一条sql如下
select * from student_score where class = '1班' and name = '张三' and course = '数学'
这条语句中就会用到所有的索引字段,首先找class,然后查找name,最后查找course
最左侧列匹配
假设有一条sql如下
select * from student_score where course = '数学'
该SQL语句就不会用到索引,因为B+树中,索引的使用是从左到右的,不能跳过左边的,直接查找右边的,我们可以用explain来看一下
确定这里是没有用到索引的,这条SQL可以改成
select * from student_score where class = '1班' and name = '张三'
这样就可以用到索引了
因此在建立索引的时候,我们需要考虑表中的字段,到底哪些字段是最常被用于查找的
最左前缀匹配
这条原则适用于模糊匹配的时候,也就是需要用到like的时候,假设有一条SQL如下
select * from student_score where class like '1%'
这里是可以用到索引的,印在B+索引树中,数据都是按照字段来排序的,比如这里有联合索引 key(class、name、course) ,那么数据会按照class排序
但是如果你把SQL写成了下面这样,就用不到索引了,因为匹配不到最左前缀
select * from student_score where class like '%班'
范围查找
我们可以通过> 、 <这种范围比较来查找数据
select * from student_score where class > '1班' and class < '3班'
通过explain,可以看到,这条sql是用到了索引的
但是如果你把sql改成下面这样的,就不会用到索引了
select * from student_score where name > '张三'
因为该联合索引的B+树中只能根据class字段来进行范围查找,也就是联合索引中最左侧的字段
等值匹配+范围查找
假设有一条SQL如下
select * from student_score where class = '1班' and name > '张三' and course > '数学'
通过explain分析后,可以看到,这里也会用到索引
并发事务带来的问题
-
脏读(Dirty read): 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交 到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没 有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是 不正确的。
-
丢失修改(Lost to modify): 指在一个事务读取一个数据时,另外一个事务也访问了该数据, 那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据
-
指在一个事务内多次读同一数据。在这个事务还没有结束 时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修 改导致第一个事务两次读取的数据可能不太一样
-
幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接 着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了 一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
Mysql隔离级别
- READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导 致脏读、幻读或不可重复读。
- READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读 或不可重复读仍有可能发生。
- REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务 自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
- SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个 执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及 幻读。
MySQL 锁
表锁 & 行锁
- 只有明确指定主键,才会执行行锁,否则执行表锁;
- 上面意思就是只有可以找到具体的列才会执行行锁;
- 若匹配不到数据则无锁;
- 如数据表有id=1,2,3三条数据,select * form user where id=4,此时是没有锁的;
锁算法
行锁算法
-
Record Lock 普通行锁
- 键值在条件范围内
- 记录存在
-
Gap Lock 间隙所
- 锁定一个范围,不包括记录本身
- 对于键值不存在条件范围内,叫做间隙(GAP),引擎就会对这个间隙加锁;
- 比如: select * from user where id > 49 for update 那么遵循左开右闭原则,会增加(49, max]的间隙锁
-
Next-key Lock 行&间隙
-
锁定一个范围,包含记录本身
-
在键值范围条件内,同时键值又不存在条件范围内;
-
在可重复读隔离级别下才会生效;
-
表锁算法
-
意向锁(升级机制)
- 当一个事务带着表锁去访问一个加了行锁的资源,那么此时,这个行锁会升级成意向锁,将表锁住;
此时有两条数据,Id=10, name=jjj和Id=20, name=kkk
# 事务A(行锁)
select * from user where id = 10 for update;
# 事务B(表锁)
select * from user where name = 'kkk' for update;
此时执行事务B是拿不到锁的,因为在事务B是带着表锁去的,此时事务A的锁会升级成表锁(意向锁);
-
自增锁
- 如果一个事务正在往表里插入自增记录,其他事务都必须等待;
锁的实现
共享锁 & 排它锁
是行锁与表锁的一个具体实现
- 共享锁:
- 允许一个事务去读一行,阻止其他事务去获取该行的排它锁;
- 一般理解: 能读,不能写
- 排它锁(X): 写锁
- 允许持有排它锁的事务读写事务,阻止其他事务获取该资源的共享锁与排它锁;
- 不能获取任何锁,不代表不能读
某个事务获取数据的排它锁,其他事务不能获取该数据的任何锁,并不代表其他事务不能无锁读取该数据;
标签:知识点,事务,name,索引,where,汇集,MySQL,数据,class 来源: https://www.cnblogs.com/heyouxin/p/14127129.html