文章目录
- 事务隔离级别
- [MySQL 中是如何实现事务隔离的](#MySQL 中是如何实现事务隔离的)
- 什么是存储引擎
- 如何定位慢查询
- 分析慢查询原因
- MySQL超大分页怎么处理
- 索引失效
- 什么时候建立唯一索引、前缀索引、联合索引?
- redolog与binlog是如何保证一致的
- redolog刷盘时机
- undo日志什么时候删除?
- [binlog 、redolog分别在什么层](#binlog 、redolog分别在什么层)
- 意向锁
- MySQL数据类型
- 关键字书写顺序和执行顺序
- MySQL查询缓存
- [Buffer Pool中是如何管理Page页](#Buffer Pool中是如何管理Page页)
- [SELECT COUNT(*) 在哪个引擎执行更快?](#SELECT COUNT(*) 在哪个引擎执行更快?)
- [count(*) 和 count(1)和count(列名)区别](#count(*) 和 count(1)和count(列名)区别)
- binlog记录的一定是sql语句吗
- [Redo Log 的刷盘时机](#Redo Log 的刷盘时机)
- [怎么将一条 SQL 加载到缓冲池和记录哪些日志的](#怎么将一条 SQL 加载到缓冲池和记录哪些日志的)
- 如何优化mysql分页查询
- MySQL加锁的基本单位
- [on 和 where 的区别](#on 和 where 的区别)
- 为什么不建议为区分度不高字段建立索引
- 一条update更新语句的执行流程
- MySQL执行一条Select语句是怎么运行的?
- 为什么不用memory
链接
MySQL基础篇
事务隔离级别
需要解决的问题
脏读
脏读指的是读到了其他事务未提交的数据
可重复读可重复读指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据都是一致的。通常针对数据更新(UPDATE)操作。
不可重复读对比可重复读,不可重复读指的是在同一事务内,不同的时刻读到的同一批数据可能是不一样的,可能会受到其他事务的影响,比如其他事务改了这批数据并提交了。通常针对数据更新(UPDATE)操作。
幻读幻读是针对数据插入(INSERT)操作来说的。假设事务A对某些行的内容作了更改,但是还未提交,此时事务B插入了与事务A更改前的记录相同的记录行,并且在事务A提交之前先提交了,而这时,在事务A中查询,会发现好像刚刚的更改对于某些数据未起作用,但其实是事务B刚插入进来的,让用户感觉很魔幻,感觉出现了幻觉,这就叫幻读。
事务隔离级别
读未提交
MySQL事务隔离其实是依靠锁来实现的,加锁自然会带来性能的损失。而读未提交隔离级别是不加锁的,所以它的性能是最好的,没有加锁、解锁带来的性能开销。
读提交读提交就是一个事务只能读到其他事务已经提交过的数据,也就是其他事务调用 commit命令之后的数据。那脏数据问题迎刃而解了。读提交事务隔离级别是大多数流行数据库的默认事务隔离界别,比如 Oracle,但是不是 MySQL 的默认隔离界别。每个 select 语句都有自己的一份快照,而不是一个事务一份 ,所以在不同的时刻,查询出来的数据可能是不一致的。
可重复读可重复读是指,事务不会读到其他事务对已有数据的修改,及时其他事务已提交,也就是说,事务开始时读到的已有数据是什么,在事务提交前的任意时刻,这些数据的值都是一样的。但是,对于其他事务新插入的数据是可以读到的,这也就引发了幻读问题。
串行化串行化是4种事务隔离级别中隔离效果最好的,解决了脏读、可重复读、幻读的问题,但是效果最差,它将事务的执行变为顺序执行
MySQL 中是如何实现事务隔离的
首先说读未提交,它是性能最好,因为没加锁。
再来说串行化。读的时候加共享锁,也就是其他事务可以并发读,但是不能写。写的时候加排它锁,其他事务不能并发写也不能并发读。
最后说读提交和可重复读。
实现可重复读
为了解决不可重复读,或者为了实现可重复读,MySQL 采用了 MVVC (多版本并发控制) 的方式。
我们在数据库表中看到的一行记录可能实际上有多个版本,每个版本的记录除了有数据本身外,还要有一个表示版本的字段,记为 row trx_id
,而这个字段就是使其产生的事务的 id,事务 ID 记为 transaction id
,它在事务开始的时候向事务系统申请,按时间先后顺序递增。
按照上面这张图理解,一行记录现在有 3 个版本,每一个版本都记录这使其产生的事务 ID,比如事务A的transaction id 是100,那么版本1的row trx_id 就是 100,同理版本2和版本3。
在上面介绍读提交和可重复读的时候都提到了一个词,叫做快照 ,学名叫做一致性视图,这也是可重复读和不可重复读的关键,可重复读是在事务开始的时候生成一个当前事务全局性的快照,而读提交则是每次执行语句的时候都重新生成一次快照。
对于一个快照来说,它能够读到那些版本数据,要遵循以下规则:
- 当前事务内的更新,可以读到;
- 版本未提交,不能读到;
- 版本已提交,但是却在快照创建后提交的,不能读到;
- 版本已提交,且是在快照创建前提交的,可以读到;
利用上面的规则,再返回去套用到读提交和可重复读的那两张图上就很清晰了。还是要强调,两者主要的区别就是在快照的创建上,可重复读仅在事务开始是创建一次,而读提交每次执行语句的时候都要重新创建一次。
什么是存储引擎
存储引擎是数据库底层的组件,是数据库的核心。
使用存储引擎可以创建、查询、更新、删除数据库。存储引擎可以理解为数据库的操作系统,不同的存储引擎提供的存储方式、索引机制等也不相同
如何定位慢查询
方案一:开源工具
- 调试工具:Arthas
- 运维工具:Prometheus、Skywalking
方案二:MySQL自带慢日志
查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有SQL语句的日志如果要开启慢查询日志,需要在MySQL的配置文件(/etc/my.cnf)中配置如下信息:
sql
# 开启MySQL慢日志查询开关
slow_query_log=1
# 设置慢日志的时间为2秒,SQL语句执行时间超过2秒,就会视为慢查询,记录慢查询日志
long_query_time=2
分析慢查询原因
可以采用 EXPLAIN 或者 DESC 命令获取 MySQL 如何执行 SELECT 语句的信息。语法
sql
-- 直接在select语句之前加上关键字 explain/desc
explain select 字段列表 from 表名 where 条件;
可以采用MySQL自带的分析工具 EXPLAIN 去查询这条sql的执行情况
- 通过key和key_len检查是否命中了索引(索引本身存在、是否有失效的情况)
- 通过type字段查看sql是否有进一步的优化空间,是否存在全索引扫描或全盘扫描
- 通过extra建议判断,是否出现了回表的情况,如果出现了,可以尝试添加索引或修改返回字段来修复
MySQL超大分页怎么处理
可以使用覆盖索引解决。
问题:在数据量比较大时,limit分页查询,需要对数据进行排序,效率低
因为,当在进行分页查询时,如果执行 limit 9000000,10 ,此时需要MySQL排序前9000010 记录,仅仅返回
9000000 - 9000010 的记录,其他记录丢弃,查询排序的代价非常大 。
解决方案:覆盖索引+子查询。先分页查询数据的id字段,确定了id之后
再用子查询来过滤,只查询这个id列表中的数据就可以了。因为查询id的时候 走的覆盖索引,所以效率可以提升很多
索引失效
什么时候建立唯一索引、前缀索引、联合索引?
前缀索引
考虑一个文章表articles,其内容字段content非常大:
为了减少索引空间占用并加速对title列的模糊查询,我们可以只对title的前几个字符创建前缀索引:
唯一索引
列已经是一个主键,自动带有唯一约束。如果还需要确保逐渐的唯一性,可以添加一个唯一索引:
联合索引
- 减少开销。
建一个联合索引(col1,col2,col3),实际相当于建了(col1),(col1,col2),(col1,col2,col3)三个索引。每多一个索引,都会增加写操作的开销和磁盘空间的开销。对于大量数据的表,使用联合索引会大大的减少开销! - 覆盖索引。
对联合索引(col1,col2,col3),如果有如下的sql: select col1,col2,col3 from test where col1=1 and col2=2。那么MySQL可以直接通过遍历索引取得数据,而无需回表,这减少了很多的随机io操作。减少io操作,特别的随机io其实是dba主要的优化策略。所以,在真正的实际应用中,覆盖索引是主要的提升性能的优化手段之
redolog与binlog是如何保证一致的
InnoDB存储引擎使用两阶段提交方案。
原理很简单,将redo log的写入拆成了两个步骤prepare和commit,这就是两阶段提交。
使用两阶段提交后,写入binlog时发生异常也不会有影响,因为MySQL根据redo log日志恢复数据时,发现redo log还处于prepare阶段,并且没有对应binlog日志,就会回滚该事务。
redolog刷盘时机
理想情况下,事务一提交就会进行刷盘操作,但是实际上是刷盘的时机是根据策略来决定的。 InnoDB存储引擎为redo
log的刷盘策略提供了innodb_flush_log_at_trx_commit参数,它支持三种策略:
- 0:设置为0的时候,每次提交事务时不刷盘。
- 1:设置为1的时候,每次提交事务时刷盘。
- 2:设置为2的时候,每次提交事务时都只把redo log buffer写入page cache。
innodb_flush_log_at_trx_commit参数默认为1,当事务提交的时候会调用fsync对redolog进行刷盘,将redo log buffer写入redo log文件中。
另外,Innodb存储引擎有一个后台线程,每隔1秒,就会把会redo log buffer中的内容写入到文件系统缓存page
cache,然后调用fsync刷盘。
undo日志什么时候删除?
insert类型的,在事务提交之后就可以清除掉了。
update类型的,事务提交之后不能立即清除掉,这些日志会用于mvcc。只有当readview中不存在这个事务的事务id时才能被删除。
binlog 、redolog分别在什么层
redo log(重做日志)是InnoDB存储引擎独有的
binlog日志是逻辑日志,记录内容是语句的原始逻辑,属于MySQL Server层
意向锁
innodb的意向锁主要用于解决多粒度的锁并存的情况。
要作用是处理行锁和表锁之间的矛盾,能够显示"某个事务正在某一行上持有了锁,或者准备去持有锁"
MySQL数据类型
关键字书写顺序和执行顺序
- 书写顺序
sql
SELECT <字段名>
FROM <表名>
JOIN <表名>
ON <连接条件>
WHERE <筛选条件>
GROUP BY <字段名>
HAVING <筛选条件>
UNION
ORDER BY <字段名>
LIMIT <限制行数>;
- 执行顺序
1.FORM:选择from后面跟的表,产生虚拟表1。
2.ON:ON是JOIN的连接条件,符合连接条件的行会被记录在虚拟表2中。
3.JOIN:如果指定了LEFT JOIN,那么保留表中未匹配的行就会作为外部行添加到虚拟表2中,产生虚拟表3。如果有多个JOIN链接,会重复执行步骤1~3,直到处理完所有表。
4.WHERE:对虚拟表3进行WHERE条件过滤,符合条件的记录会被插入到虚拟表4中。
5.GROUP BY:根据GROUP BY子句中的列,对虚拟表2中的记录进行分组操作,产生虚拟表5。
6.HAVING:对虚拟表5进行HAVING过滤,符合条件的记录会被插入到虚拟表6中。
7.SELECT:SELECT到一步才执行,选择指定的列,插入到虚拟表7中。
8.UNION:UNION连接的两个SELECT查询语句,会重复执行步骤1~7,产生两个虚拟表7,UNION会将这些记录合并到虚拟表8中。
9.ORDER BY: 将虚拟表8中的记录进行排序,虚拟表9。
10.LIMIT:取出指定行的记录,返回结果集。
MySQL查询缓存
一、查询缓存的基本概念
MySQL 的查询缓存是一种用于存储查询结果的内存区域。当一个查询被执行时,MySQL首先检查查询缓存中是否已经存在相同的查询结果。如果存在,直接从查询缓存中返回结果,而无需再次执行查询语句,从而大大提高查询性能。
二、查询缓存的工作原理
- 缓存存储
查询缓存存储的是完整的查询语句和对应的查询结果。当一个查询被执行时,MySQL会将查询语句进行哈希计算,生成一个唯一的哈希值。这个哈希值作为查询缓存的键,查询结果作为值存储在缓存中。 例如,对于查询 SELECT *FROM users WHERE age > 30,MySQL 会计算这个查询语句的哈希值,并将查询结果与这个哈希值一起存储在查询缓存中。- 缓存命中判断
当一个新的查询到来时,MySQL同样会对查询语句进行哈希计算,然后在查询缓存中查找是否存在相同的哈希值。如果找到,并且查询语句和缓存中的查询完全一致(包括大小写、注释等),则认为是缓存命中,直接返回缓存中的结果。
例如,如果再次执行 SELECT * FROM users WHERE age > 30,MySQL会计算哈希值并在查询缓存中查找,发现有相同的哈希值且查询语句一致,就会从缓存中返回结果。- 缓存失效
查询缓存并不是永久有效的,当以下情况发生时,查询缓存会失效:
- 表数据发生变化:当查询涉及的表中的数据被插入、更新或删除时,与该表相关的所有查询缓存都会被标记为无效,并从缓存中移除。这是因为表数据的变化可能导致查询结果不再准确。
- 查询语句发生变化:即使是微小的变化,如添加注释、改变大小写等,也会导致查询缓存不命中。因为 MySQL 对查询语句的一致性要求非常严格。
- 缓存空间不足:当查询缓存的内存空间不足时,MySQL 会根据一定的算法淘汰一些旧的缓存数据,为新的查询结果腾出空间。
Buffer Pool中是如何管理Page页
Page页分类:在BP的底层采用的是链表数据结构管理Page。Page根据状态可以分为3中类型
- free Page:空闲Page,未被使用的Page。
- clean Page:被使用的Page,但是数据没有修改过。
- dirty Page:脏页,被使用过的Page,并且数据被修改了,缓存页中的数据与磁盘的数据不一致的。
上面说的三种Page类型,InnoDB采用三种链表结构进行维护和管理。
free list :表示空闲缓冲区,管理free Page
flush list:表示的是需要刷新到磁盘的缓冲区,管理dirty page,内部page是按照修改时间排序。
lru ist :表示正在使用的缓冲区,管理cean page和dity pae,该缓冲区以 midpoint 为基点,前面的链表称为new 列表区,存放经常被访问的数据,占63%,后面的链表称为old列表区,存放的是使用较少的数据占37%。
注意:脏页在fush链表和LRU链表中都存在,但是两者互不影响,LRU链表负责管理page的可用性和释放,而fush list负责管理脏页的刷盘操作
SELECT COUNT(*) 在哪个引擎执行更快?
select count(*)常用于统计表的总行数,在MyISAM存储引擎中执行更快,但前提是不加where条件。
因为MyISAM对于表的行数做了优化,内部用了一个变量存储表的行数,如果查询条件没有where条件,则MyISAM可以迅速返回结果,如果加上where语句就不行了。
InnerDB存储引擎也有一个存储了表行数的变量,但这个值是一个估计值,所以并没有什么实际意义。
count(*) 和 count(1)和count(列名)区别
执行效果上:
- count(*)包括了所有的列,相当于行数,在统计结果的时候,不会忽略为NULL的值。
- count(1)包括了忽略所有列,用1代表代码行,在统计结果的时候,不会忽略为NULL的值。
- count(列名)只包括列名那一列,在统计结果的时候,会忽略列值为空(这里的空不是指空字符串或者0,而是表示null)的计数,即某个字段值为NULL时,不统计。
执行效率上:
- 列名为主键,count(列名)会比count(1)快
- 列名不为主键,count(1)会比count(列名)快
- 如果表多个列并且没有主键,则 count(1 的执行效率优于 count(*)
- 如果有主键,则 select count(主键)的执行效率是最优的
- 如果表只有一个字段,则 select count(*)最优。
binlog记录的一定是sql语句吗
binlog 有三种格式:
- Statement(Statement-Based Replication,SBR):每一条会修改数据的 SQL 都会记录在 binlog 中。
- Row(Row-Based Replication,RBR):不记录 SQL 语句上下文信息,仅保存哪条记录被修改。
- Mixed(Mixed-Based Replication,MBR):Statement 和 Row 的混合体。
Redo Log 的刷盘时机
- 事务提交
当事务提交时,log buffer 中的 redo log 会被刷新到磁盘。这可以通过参数 innodb_flush_log_at_trx_commit 来控制,具体细节将在后文提到。 - log buffer 空间不足
当 log buffer 中缓存的 redo log 占满了 log buffer 总容量的大约一半时,需要将这些日志刷新到磁盘。 - 事务日志缓冲区满
InnoDB 使用一个事务日志缓冲区(transaction log buffer)来暂时存储事务的重做日志条目。当缓冲区满时,会触发日志刷新,将日志写入磁盘。 - Checkpoint(检查点)
InnoDB 定期执行检查点操作,将内存中的脏数据(已修改但尚未写入磁盘的数据)刷新到磁盘,并将相应的重做日志一同刷新,以确保数据的一致性。 - 后台刷新线程
InnoDB 启动了一个后台线程,负责周期性(每隔 1 秒)地将脏页(已修改但尚未写入磁盘的数据页)刷新到磁盘,并将相关的重做日志一同刷新。 - 正常关闭服务器
当 MySQL 服务器正常关闭时,redo log 会被刷新到磁盘,以确保所有未持久化的数据都被写入磁盘。 - 后台线程主动刷盘
当 redo log buffer 占用的空间接近 innodb_log_buffer_size 一半时,后台线程也会主动刷盘。
怎么将一条 SQL 加载到缓冲池和记录哪些日志的
- 准备更新:准备执行一条 SQL 更新语句。
- 查找数据:MySQL(InnoDB 引擎)会先在缓冲池(Buffer Pool)中查找这条数据。如果缓冲池中没有找到,则会从磁盘中查找,并将数据加载到缓冲池中。
- 保存原始记录:在加载到缓冲池的同时,会将这条数据的原始记录保存到 Undo 日志文件中。
- 执行更新:InnoDB 在缓冲池中执行更新操作,更新后的数据会记录在 Redo Log Buffer 中。
- 写入 Redo Log:MySQL 提交事务时,会将 Redo Log Buffer 中的数据写入到 Redo 日志文件中。
- 刷盘策略:刷磁盘可以通过 innodb_flush_log_at_trx_commit 参数来设置刷盘策略。
- 重启恢复:MySQL 宕机重启时,会将 Redo 日志恢复到缓冲池中。
如何优化mysql分页查询
-
最大id查询法
扫描意思呢?举个例子,我查询第一页的时候是limit 0,10 查询到的最后一条id是10,那么下一页的查询只需要查询id大于10的19条数据即可。
-
BETWEEN ... AND
这种方式也只能适用于自增主键,并且id没有断裂,否者不推荐这种方式,我们发现使用BETWEEN AND的时候查询出来11条记录,也就是说BETWEEN AND包含了两边的边间条件。使用的时候需要特别注意一下。
-
limit id
这种查询方式就是先扫描4000010行,但是只取出id,然后再查询id大于这个值的前10条,这样虽然也是扫描了400多w行记录,由于id是主键,拥有者主键索引,所以查询 查询一个id的limit速度会快很多,我们可以对比一下一开始的limit 4000000,10,效率相差了3倍多。
-
延迟关联(个人推荐)
什么叫延迟关联,他让mysql扫描尽可能少的记录,获取到需要访问的记录后再根据关联列回到远表查询需要的所有列,这样听起来是不是很拗口,我们用sql来实现一下。
-
分表查询
mysql推荐一张表的存储不要超过500w数据,查询400w不到1秒对于一般的查询来说已经可以了,如果还要更快的话,我建议使用分表存储,分表又分两种情况,水平分表于垂直分表。
水平分表:假如一张表的原始数据有1000w条数据,我可分三张表存储,一张表300的万,这样查询的时候压力就会小很多,并且效率也很高很多,那问题来了,如何这个水平水表如何实现呢?像可以借助mycat之类的中间件,阿里云也提供了数据库的分表技术,当然,你也可以自己手写分表,但是自己手写分表的时候需要注意id重复以及如何定义搭配当前id在那张表中,算法推荐使用hash值。
垂直分表:假如张彪的记录有100w,按正常来说查询速度应该不会太慢,但是由于这张表的字段超多,而且还有很多text类型的字段,这个时候我们可以将占用空间比较小的字段分在一张表,占用空间比较大的字段分在另一张表,两张表一一关联,这样,查询的时候就会快很多了。
MySQL加锁的基本单位
加锁单位是 next-key lock ( 间隙锁 + 行锁)
原则1:加锁的基本单位是next-key lock 。next-key lock是前开后闭区间。
原则2:查找过程中访问到的对象才会加锁。
原则3:索引上的等值查询,给唯一索引加锁的时候,next-key lock退化为行锁。
原则4:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock退化为间隙锁。
原则5:唯一索引上的范围查询会访问到不满足条件的第一个值为止。(在 mysql 45 讲中有说明,丁老师认为这是一个 bug,且在 8
版本的时候已经修复,待会也会举例说明)
on 和 where 的区别
1、on 条件是在生成临时表时使用的条件,它不管 on 中的条件是否为真,都会返回左边表中的记录。
2、where 条件是在临时表生成好后,再对临时表进行过滤的条件。这时已经没有 left join的含义(必须返回左边表的记录)了,条件不为真的就全部过滤掉。
为什么不建议为区分度不高字段建立索引
- 索引维护的开销对于区分度不高的字段,虽然索引可以加快查询速度,但是维护索引所需的开销可能会超过索引带来的性能提升。因为当插入、更新或删除数据时,需要更新索引。
- 索引占用的空间索引占用了磁盘空间,而区分度不高的字段往往只包含少量不同的值。如果为这些字段建立索引,那么索引所占用的空间可能会超过字段本身的数据。这样就会浪费存储空间,并且可能导致磁盘的随机I/O增加。
- 不适用于某些查询对于区分度不高的字段,如果查询条件中包含该字段,使用索引可能并不高效。因为在这种情况下,使用全表扫描可能更加高效。
一条update更新语句的执行流程
核心组成部分
Redo Log、Undo Log、BinLog分别存的什么内容,分别是做什么用的? Redo Log
在引擎层实现,用来恢复数据的,保障已提交事务的持久化特性,记录的是物理级别的数据页(包括data page和undo page)做的修改
Undo Log 在引擎层实现的逻辑日志,用于数据回滚到之前状态,对于每个UPDATE语句,对应一条相反的UPDATE的undo log
BinLog 是Server实现的逻辑日志,用于复制和恢复数据,记录了所有的 DDL 和 DML
语句(除了数据查询语句select、show等)
WAL是什么,哪里用到了它? WAL全称为Write-Ahead Logging,预写日志系统。主要写undo log、redolog、binlog这些用到了。
真正使用WAL的原因是:磁盘的写操作是随机IO,比较耗性能,所以如果把每一次的更新操作都先写入log中,那么就成了顺序写操作,实际更新操作由后台线程再根据log异步写入
UndoLog会存储在哪些地方? 我们从图中也可以清晰的知道UndoLog在两个地方:Buffer Pool中的undopage页,和磁盘中的表共享空间的Undo log
详解执行流程
Client客户端:
客户端通过tcp/ip发送一条sql语句到server层
Server层:
- 接收客户端过来的请求,进行权限验证
- 权限验证通过后,解析器会对SQL语句进行词法、语法分析等
- 经过验证解析的SQL语句会在优化器生成选择最优执行计划
- 然后执行器将会执行经过优化的SQL语句
对于Server层来说,它是不知道存储引擎的实现细节的,而是通过定义的API接口和存储引擎通信。可以理解为存储引擎是一个类,然后每个实例(InnoDB)都通过一个特殊的处理程序接口与MySQL服务器通信。
InnoDB引擎层:
- 调用存储引擎接口后,会先从Buffer Pool获取数据页,如果没有就从磁盘中读入Buffer Pool,然后判断更新前后的记录是否一样
- 开启事务,修改数据之前先记录undo log,写入Buffer Pool的undo page
- 开始更新page data中的记录,被修改的数据页称为脏页,修改会被记录到内存中的 redo log buffer中,再刷盘到磁盘的redo log文件,此时事务是 perpare阶段
- 这个时候更新就完成了,当时脏页不会立即写入磁盘,而是由后台线程完成,这里会用double write来保证脏页刷盘的可靠性
-还没结束呢,这时候可以通知Server层,可以正式提交数据了, 执行器记录binlog cache,事务提交时才会将该事务中的binglog刷新到磁盘中 - 这个时候Update语句完成了Buffer Pool中数据页的修改、undo日志、redo log缓存记录,以及记录binlog cache缓存
- commit阶段,这个阶段是将redo log中事务状态标记为commit
- 此时binlog和redo log都已经写入磁盘,如果触发了刷新脏页的操作,先把脏页copy到double write buffer里,Double Write Buffer 的内存数据刷到磁盘中的共享表空间 ibdata,再刷到数据磁盘上数据文件 ibd
什么是二阶段提交?
二阶段提交,顾名思义,会包含 2 个阶段:
prepare 阶段,协调器会询问所有执行器,是否可以提交事务
commit 阶段,协调器会通知执行器进行提交操作
update的执行阶段也用到了,比如:
- prepare 阶段:将 redo log 对应的事务状态设置为 prepare,然后将 redo log 刷新到硬盘;
- commit阶段:将 binlog 刷新到磁盘,接着调用引擎的提交事务接口,将 redo log 状态设置为 commit(将事务设置为 commit状态后,刷入到磁盘 redo log 文件)
MySQL执行一条Select语句是怎么运行的?
连接器 :建立连接,管理连接、校验用户身份;
查询缓存 :查询语句如果命中查询缓存则直接返回,否则继续往下执行。MySQL 8.0已删除该模块;
解析 SQL :通过解析器对 SQL 查询语句进行词法分析、语法分析,然后构建语法树,方便后续模块读取表名、字段、语句类型;
执行 SQL:执行 SQL 共有三个阶段:
- 预处理阶段:检查表或字段是否存在;将 select * 中的 * 符号扩展为表上的所有列。
- 优化阶段:基于查询成本的考虑, 选择查询成本最小的执行计划;
- 执行阶段:根据执行计划执行 SQL 查询语句,从存储引擎读取记录,返回给客户端
为什么不用memory
-
这个缓存是本地缓存,如果有多台服务器,sql语句会随机发送给其中一台,无法保证相同的请求会发送给同一台,这样本地缓存命中率就比较低,本地缓存(即查询缓存)就没有什么用了
-
从查询缓存中取出结果或者将查询结果添加进查询缓存中,会带来额外性能消耗
-
当表的结构或内容发生变化时,使用这个表的缓存查询将不再有效,查询缓存值的相关条目将被清空。常见修改表结构和表内容的语句,如delete、insert、altertable很多。使用redis作为缓存将受这个影响很小,因为它是基于键值对保存缓存的,只有key对应的缓存内容发生变化,才会受到影响,其他key对应缓存内容发生变化对其几乎是没有影响的