Server 层 所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。
存储引擎层负责数据的存储和提取。其架构模式是插件式的,在 create table 语句中使用 engine=memory, 来指定使用内存引擎创建表
如果用户名密码认证通过,连接器会到权限表里面查出你拥有的权限。之后,这个连接里面的权限判断逻辑,都将依赖于此时读到的权限。
权限表:
大致就是mysql库中的user表和db表
db 表中的权限列和 user 表中的权限列大致相同,只是user 表中的权限是针对所有数据库的,而 db 表中的权限只针对指定的数据库。如果希望用户只对某个数据库有操作权限,可以先将 user 表中对应的权限设置为 N,然后在 db 表中设置对应数据库的操作权限。
连接完成后,如果你没有后续的动作,这个连接就处于空闲状态,你可以在 show processlist 命令中看到它。Command 列显示为“Sleep”的这一行,就表示现在系统里面有一个空闲连接。客户端如果太长时间没动静,连接器就会自动将它断开。
建立连接的过程通常是比较复杂的,尽量使用长连接。
但是全部使用长连接后,你可能会发现,有些时候 MySQL 占用内存涨得特别快,这是因为 MySQL 在执行过程中临时使用的内存是管理在连接对象里面的。这些资源会在连接断开的时候才释放。所以如果长连接累积下来,可能导致内存占用太大,被系统强行杀掉(OOM),从现象看就是 MySQL 异常重启了。
两种解决方案:
重连 or 重置连接
定期断开长连接。使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开连接,之后要查询再重连。
如果你用的是 MySQL 5.7 或更新版本,可以在每次执行一个比较大的操作后,通过执行 mysql_reset_connection 来重新初始化连接资源。这个过程不需要重连和重新做权限验证,但是会将连接恢复到刚刚创建完时的状态。
不要使用查询缓存
查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空。
MySQL 8.0 版本直接将查询缓存的整块功能删掉
MySQL 需要知道你要做什么,因此需要对 SQL 语句做解析
优化器是在表里面有多个索引的时候,决定使用哪个索引;
或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序
优化器阶段完成后,这个语句的执行方案就确定下来,通过优化器知道了该怎么做
开始执行的时候,要先判断一下你对这个表 T 有没有执行查询的权限,如果没有,就会返回没有权限的错误
Q:为什么对权限的检查不在优化器之前做?
A:有些时候,SQL语句要操作的表不只是SQL字面上那些。比如如果有个触发器,得在执行器阶段(过程中)才能确定。优化器阶段前是无能为力的
如果有权限,就打开表继续执行。
打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。
select * from T where ID=10;
比如我们这个例子中的表 T 中,ID 字段没有索引,那么执行器的执行流程是这样的:
对于有索引的表,执行的逻辑也差不多。
第一次调用的是“取满足条件的第一行”这个接口,之后循环取“满足条件的下一行”这个接口,这些接口都是引擎中已经定义好的。
MySQL 的慢查询日志是 MySQL 提供的一种日志记录,它用来记录在 MySQL 中响应时间超过阀值的语句,具体指运行时间超过 long_query_time 值的SQL,则会被记录到慢查询日志中。
# 查看慢查询日志是否开启,默认情况下关闭mysql> SHOW VARIABLES LIKE '%slow_query_log%';+---------------------+--------------------------------------+| Variable_name | Value |+---------------------+--------------------------------------+| slow_query_log | OFF || slow_query_log_file | /var/lib/mysql/iZ251fpy8x9Z-slow.log |+---------------------+--------------------------------------+2 rows in set (0.00 sec)
https://blog.csdn.net/chengqiuming/article/details/120402562
Q:如果表 T 中没有字段 k,而你执行了这个语句 select * from T where k=1, 那肯定是会报“不存在这个列”的错误: “Unknown column ‘k’ in ‘where clause’”。这个错误是在哪个阶段报出来的呢?
A:分析器。Oracle会在分析阶段判断语句是否正确,表是否存在,列是否存在等,MySQL确实在设计上受Oracle影响颇深。
与查询流程不一样的是,更新流程还涉及两个重要的日志模块:redo log(重做日志)和 binlog(归档日志)
https://blog.csdn.net/weixin_51261234/article/details/124908426
先在粉板上记下这次的账,等打烊以后再把账本翻出来核算。
在 MySQL 里也有这个问题,如果每一次的更新操作都需要写进磁盘,然后磁盘也要找到对应的那条记录,然后再更新,整个过程 IO 成本、查找成本都很高。为了解决这个问题,MySQL 的设计者就用了类似酒店掌柜粉板的思路来提升更新效率。
存储表空间ID,页号,偏移量以及需要更新的值,所需的存储空间是很小的,刷盘快。
WAL 技术,WAL 的全称是 Write-Ahead Logging,它的关键点就是先写日志,再在系统比较空闲的时候写磁盘。只有日志写入成功,才算是事务提交成功。
当发生宕机且数据未刷新到磁盘的时候,可以通过redo log来恢复,保证ACID中的D,这就是redo log的作用。
在执行事务的过程中,每执行一条语句,就可能产生若干条redo日志,这些日志是按照产生的顺序写入磁盘的,也就是说使用顺序IO,效率比随机IO快
redo log是存储引擎层产生的,而bin log是数据库层产生的。假设一个事务,对表做十万行的记录插入,在这个过程中,一直不断的往redo log顺序记录,记录的是页面的变化,而bin log不会记录,直到这个事务提交,才会一次写入到bin log文件中。
redo log是物理日志,记录的是在具体某个数据页上做了什么修改,做了什么改动;
binlog是逻辑日志,有三种模式,statement 格式的话是记sql语句, row格式会记录行的内容,记两条,更新前和更新后都有,mixed根据sql语句特点,由系统决定某个修改使用row还是statement格式进行存储。
若sql语句可能引起主备不一致,那么使用row格式,否则使用statement格式。
Redo log可以简单分为以下两个部分:
InnoDB 的 redo log 是固定大小的,比如可以配置为一组 4 个文件,每个文件的大小是 1GB,那么这块“粉板”总共就可以记录 4GB 的操作。从头开始写,写到末尾就又回到开头循环写,如下面这个图所示。
write pos 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。
write pos 和 checkpoint 之间的是“粉板”上还空着的部分,可以用来记录新的操作。如果 write pos 追上 checkpoint,表示“粉板”满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint 推进一下
以一个更新事务为例,redo log 流转过程,如下图所示:
第1步:先将原始数据从磁盘中读入内存中来,修改数据的内存拷贝
第2步:生成一条重做日志并写入redo log buffer,记录的是数据被修改后的值
第3步:当事务commit时,将redo log buffer中的内容刷新到 redo log file,对 redo log file采用追加 写的方式
第4步:定期将内存中修改的数据刷新到磁盘中
redo log的写入并不是直接写入磁盘的,InnoDB引擎会在写redo log的时候先写redo log buffer,之后以 一定的频率 刷入到真正的redo log file 中。这里的一定频率怎么看待呢?这就是我们要说的刷盘策略。
注意,redo log buffer刷盘到redo log file的过程并不是真正的刷到磁盘中去,只是刷入到 文件系统缓存(page cache)中去,真正的写入会交给系统自己来决定(比如page cache足够大了)。那么对于InnoDB来说就存在一个问题,如果交给系统来同步,同样如果系统宕机,那么数据也丢失了(虽然整个系统宕机的概率还是比较小的)。
针对这种情况,InnoDB给出 innodb_flush_log_at_trx_commit 参数,该参数控制 commit提交事务时,如何将 redo log buffer 中的日志刷新到 redo log file 中。它支持三种策略:
InnoDB存储引擎有一个后台线程,每隔一秒,就会把redo log buffer中的内容写到文件系统缓存(page cache),然后调用刷盘操作。也就是说,一个没有提交事务的redo log记录,也可能刷盘。因为在事务执行过程中redo log记录是会写入redo log buffer中,这些redo log记录会被后台线程刷盘
除了后台线程每秒1次轮询操作,还有一种情况,当redo log buffer占用的空间即将达到innodb_log_buffer_size(这个参数默认是16M)的一半的时候,后台线程会主动刷盘。
归档日志
执行器和 InnoDB 引擎在执行这个简单的 update 语句时的内部流程
图中浅色框表示是在 InnoDB 内部执行的,深色框表示是在执行器中执行的。
将 redo log 的写入拆成了两个步骤:prepare 和 commit,这就是"两阶段提交"。
bin log在之间写
为什么日志需要“两阶段提交”
反证法:
redolog和binlog具有关联行,在恢复数据时,redolog用于恢复主机故障时的未更新的物理数据,binlog用于备份操作。每个阶段的log操作都是记录在磁盘的,在恢复数据时,redolog 状态为commit则说明binlog也成功,直接恢复数据;如果redolog是prepare,则需要查询对应的binlog事务是否成功,决定是回滚还是执行。
简单说,redo log 和 binlog 都可以用于表示事务的提交状态,而两阶段提交就是让这两个状态保持逻辑上的一致。
Q:定期全量备份的周期“取决于系统重要性,有的是一天一备,有的是一周一备”。那么在什么场景下,一天一备会比一周一备更有优势呢?或者说,它影响了这个数据库系统的哪个指标?
A:一天一备binlog比较小,恢复时间比较短 一般做法是从库做延迟复制,binlog 一周全量备份
原文出处:MySQL实战45讲 1,2