MySQL知识回顾

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL知识回顾相关的知识,希望对你有一定的参考价值。

再来一篇mysql

索引:

相关概念

  1. 索引分类:普通索引(没有修饰)唯一索引(unique修饰)、全文索引(fulltext)、空间索引(spatial)

  2. 存储方式:B-Tree(数据存储是有序的,每个叶子离根的距离是相同的,不同的存储引擎实现B-Tree索引的时候不一样)、Hash

  3. 依赖列数:单列索引、组合索引

  4. 数据分布:聚簇索引(实际的数据行直接存储到了索引末集,默认主键索引的末集存储了所有的数据)二级索引(辅助索引)

  5. 回表情况:覆盖索引

    当一个索引包含(覆盖)了需要查询的字段的值时,称其为覆盖索引;

  6. 一个问题:

    聚簇索引包含了所有的数据,所以它也是一个覆盖索引。(对吗)

    (我认为是只有索引里包含查询的数据才是覆盖索引)

    不对,只有select、where中出现的列,被索引覆盖的情况才是覆盖索引,此时Extra会显示Using index;

最左前缀

table T, index (a,b,c)

-- 全值匹配
select * from T where a='' and b='' and c='';	-- Y
select * from T where c='' and b='' and a='';	-- Y(引擎会优化的)

-- 匹配左前缀
select * from T where a='';	-- Y
select * from T where b='';	-- N

-- 匹配列前缀
select * from T where a like 'x%';	-- Y
select * from T where a like '%x';	-- N
select * from T where b like 'x%';	-- N

-- 匹配范围值
select * from T where a between '' and '';	-- Y
select * from T where b between '' and '';	-- N

-- 全值匹配 + 范围匹配
select * from T where a='' and b between '' and '';	-- Y
select * from T where b='' and c between '' and '';	-- N
select * from T where a between '' and '' and b='';	-- N(a会走索引,b不行)

相当于按三个字段,依次排序,例如在X1范围内,M是有序的;M3范围内,S是有序的

慢查询分析

开启慢查询,可以改配置文件,也可以设置参数

首先查看慢查询开关:
off表示关闭,改成on打开,如果查询时间超过设定的时间,就会把这条查询语句记录下来,记录到下面那个文件中,默认是10s


设置开启,我这里设置了查询时间为1秒,但是一重启就会恢复10s,看来得在配置文件中改

退出mysql,重新登录一下,然后执行这个查询语句,表示睡眠3秒

然后查看慢查询日志:

查询怎么做优化的(在这个方面):
开启慢查询日志,看到某一个sql执行时间慢,然后用explain去看这条语句,发现索引问题,然后更改

事务

基于mysql的InnoDB引擎
MVCC既保证了事务,又增加了并发性
事务是怎么保证的,MVCC在事务中是怎么解决并发的问题,这是两个层面的问题

事务的特性都是怎么实现的?

隔离性

锁的粒度

锁的粒度:行锁和表锁
这里要注意:表锁在实现上并不是把整个表都锁住了,可能是锁住了一页,或几页,或者一个区;行锁也不是就锁一行,而是锁一个更大的范围,比如某一页,把这一行前后的数据都锁住了;
锁住了性能就一定差吗?不一定,是因为有MVCC的机制

锁的类型


IS锁和IX锁是兼容的

如果加了X锁,和所有锁都互斥,所以别人想读都不能读,那么肯定丧失了并发性,那么还怎么实现高性能呢,这个从何说起
这里MySQL是说支持这几种锁,但是这个锁怎么用,如果用的很笨拙,只要改就加X锁,那么并发性一定是差的;那么通过一些机制,使得加锁的时候也能保证并发,能做到吗?

锁的机制

其实底层是能不加锁就尽量不加锁,实在迫不得已才加锁,底层采用的机制是一个无锁的机制
无锁的实现就是MVCC,多版本并发控制
什么意思呢,就是给数据一个版本
比如要改一行数据,就需要加X锁,并不是去锁这行数据,而是将原始数据1copy一份,创建一个新的版本2,更新的时候是更新新的版本2,此时其他事务当然不能访问在更新的版本2的数据,而此时要读取的话还是可以读取旧的版本1中的数据

这个版本,不是简单的记个版本号,也可以是时间;而且有两个版本,一个数据的版本,一个用来记录删除的版本

此时会有一个疑问,每次更新的时候,是copy一个新的版本,那么这个操作不会影响性能吗?
其实这个版本的生成是基于日志实现的,每次事务提交的时候都会生成一个历史版本数据,是通过日志的方式去存的。而读的时候就是读这个版本的数据,也就是日志(undo log),undo log里记录的是历史版本的数据。
而日志是为了保证一致性的,肯定是存在的,所以并没有性能的损失

如果有两个事务同时要改数据可以吗?不行。只能保证可以读历史数据undo log,而改正式的数据只能有一个事务,是互斥的

如果查询的时候也希望显示的加锁,那么就可以加X锁或者S锁

锁的算法

InnoDB内置了三种算法实现锁,Record Lock加在行记录上,锁住一行数据,是加在聚簇索引上的,而主键索引就是聚簇索引

问题

脏读是要完全杜绝的,不可重复读有时候可以接受,最好杜绝;幻读往往可以接受,但是能杜绝更好,比如在统计的环节就不太合适

死锁

被动方式:超时回滚的时候,谁的锁的范围大,就释放谁的锁;如果一样,同时释放
这个方式会有一个问题,就是有的事务可能执行时间比较长,超过了死锁的阈值,那么也会被回滚,导致这个事务被错误的回滚

主动方式:死锁检测,就是画一个事务之间的关系图,应该就是操作系统中讲的资源分配图;一旦发现两个事务之间存在回路,就意味着可能会出现死锁

锁的升级

InnoDB存储引擎中不存在锁升级的问题
因为其不是根据每个记录来产生行锁的,相反,其根据每个事务访问的每个页面对锁进行管理的,采用的是位图的方式。因此不管一个事务锁住页中一个记录还是多个记录,其开销通常都是一致的。
所以锁底层实现不是加在一行,而是一页,本来就是加在页上,所以不存在升级问题。

那么加在页上范围大,会有影响吗?
加了互斥锁的同时,存在MVCC机制,并不影响别人读(一致性,非锁定:既能保证一致性,又是非锁定的);但前提是要看隔离级别,在某些级别下,加了锁可以读;而有些隔离级别,加了锁就不能读了

隔离级别

隔离级别高,性能差,所以要看场景选择;但是无论什么级别,更新的问题都被解决了,因为是基于IX表锁的,主要是读取的问题

读未提交没解决任何问题,一般不用
序列化在读的时候加了S锁,所以要改的话就互斥了,非常严格

读已提交,加了行锁,所以其他事务是无法读到正在改的这一行的数据了;但是可以读到历史数据,因为采用了MVCC(历史数据不是未提交的数据);是Record Lock算法解决了脏读问题,MVCC只是提高了并发性,不要MVCC也可以解决脏读问题

可重复读采用Next-Key Lock,Record Lock解决了脏读问题;锁住了范围,所以不会读到和原来记录不同个数的记录数目,解决了幻读问题

这里没有说清楚怎么解决的不可重复读问题,自己去看了一下:
总结而言,就是读已提交和可重复读这两个隔离级别对可读到的快照的要求不同,读已提交是可以读到最新的快照的;而可重复读在事务读取时,生成一个快照,后面读取操作都会读这个快照,别的事务在此期间提交了,是不不见的

一致性

undo log解决的是,事务执行到一半,回滚了
redo log有专门的文件存储,但是undo log没有,它是跟数据存储在一起,是在一个undo段中存储的
在存数据的时候,将磁盘分段,段分区,区分页,页里存行
redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog、undo log 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”,即sql语句

持久性

事务commit的时候,redo log会打个标记,表示事务提交了

数据不是直接存储到硬盘上的,是先存储到缓存页中的,如果突然断电,缓存会丢失,如何解决?
首先将数据写到redo log里,commit 的时候,数据才会被写入缓存
每次写日志,都会刷盘fsync,都会刷新到硬盘中
在磁盘上进行顺序读写的性能也是很高的,和内存几乎差不多,只是随机读取的时候比较慢,这是和操作系统有关的
redo log是物理日志,可以理解为是二进制的数据,恢复数据很快,但是解析很慢

redo log是记录所有数据,bin log也是记录所有数据,有什么区别吗?

  • bin log是数据库都有,redo log只有InnoDB有
  • bin log是逻辑日志,存储的是sql语句,而redo log是物理日志,存储很紧凑
  • bin log事务提交时才会去写入,而redo log在事务进行中就会被不断的写入
  • redo log 是循环写的,空间固定会用完;bin log 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志

以上是关于MySQL知识回顾的主要内容,如果未能解决你的问题,请参考以下文章

Python学习第97天(MySQL知识回顾)

Java知识回顾 (17)MySQL链接

MySQL基本操作知识回顾

MySQL基础知识回顾

MySQL知识回顾

mysql知识点回顾