4月1日
基础架构
大体来说是可以分为server层和存储引擎层两个部分。
Server 层包括连接器、分析器、查询缓存、优化器、执行器等,涵盖了MySQL 大多数核心服务,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器和视图等。
存储引擎层负责数据的存储和提取,服务器通过API与存储引擎进行通信,这些借口就屏蔽了不同的存储引擎之间的差异。存储引擎是插件式的,支持InnoDB、MyISAM、Memory等,最常见的就是InnoDB,从Mysql5.5.5版本开始,就成了默认的存储引擎。
连接器
客户端与连接器建立连接、获取权限、维持和管理连接。连接的建立是通过TCP握手的方式。
当一个用户成功建立连接后,即使管理员账号对这个用户的权限做了修改,也不会影响已经连接的权限。修改完成后,只有重新建立连接时才会使用新的权限设置。
客户端如果长时间没有操作,连接器会自动断开连接(时间参数适用wait_timeout
控制的,默认值时8小时)。
查询缓存
对于SELECT语句,查询缓存是第二步。
MySQL拿到查询请求后,会先到查询缓存看看,是否之前已经执行该语句。如果存在,那么就直接返回查询结果,否则就执行后面的阶段,并将执行结果存入查询缓存中。
查询缓存往往弊大于利,不建议使用。因为查询缓存的失效非常频繁,只要对一个表更新,这个表所有的查询缓存都会被清空。对于更新压力大的数据库来说,查询缓存的命中率非常低。
MySQL8.0版本直接将查询缓存整块功能删掉了。
分析器
先做词法分析,在做语法分析。
词法分析:判断你输入的多个字符串和空格组成的一条sql语句,里面的字符串分别是什么,代表什么。
语法分析:根据语法规则,判断你输入的sql语句是否满足MySQL的语法。
优化器
在开始执行之前进行优化,优化器在表里面有多个索引的时候,决定使用哪个索引;或者在一个语句中有多表关联的时候,决定各个表的连接顺序。
优化后执行的效率会不同。
优化器不关心表使用的是什么存储引擎,但是存储引擎对于优化查询是有影响的。
执行器
执行的时候,首先要判读用户对表的操作权限,如果没有权限,则返回错误,如果有权限,就打开表继续执行,打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。
日志系统
和查询流程不同,更新流程还会设计到两个重要的日志模块,redo log 和binlog。
redo log 重做日志
redo log 日志是InnoDB引擎特有的。
当有一条记录需要更新时,InnoDB引擎就会先把记录写到redo log里面,并更新内存,这个时候就算更新完成了,同时InnoDB引擎会在适当的时候将这个操作记录更新到磁盘中。这个更新往往在系统比较空闲的时候。
同时InnoDB的redo log 的大小是固定的。从头开始写,到末尾又回到开头循环写。有了redo log,InnoDB就可以保证数据库发生异常重启,之前提交的记录都不会丢失,这个能力成为crash-safe。
binlog 归档日志
binlog日志属于MySQL的Server层实现的,所有引擎都可以使用。binlog是追加写入。binlog文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
两种日志的三个不同点:
1 redo log 是InnoDB引擎特有的;binlog是MySQL的Server层实现的,所有引擎都可以使用。
2 redo log 是物理日志,记录的是“在某个数据页做了什么修改”;binlog是逻辑日志,记录的是这个语句的原始逻辑,比如“给ID=2这一行的c字段加1”
3 redo log 是循环写,空间固定会用完;binlog是可以追加写入。
日志的写入流程
redo log的写入可以拆分为两个步骤: “prepare” 和“commit”,这就是“两阶段提交”。
举例说明:
给ID=2这一行的c字段加1
redo log 用于保证crash-safe能力,innodb_flush_log_at_trx_commit
这个参数设置成1的时候,表示每次事务的redo log 都直接持久化到磁盘。这个参数建议这是成1,这样可以保证MySQL异常重启之后数据不会丢失。sync_binlog
参数设置为1时,表示每次事务的binlog都直接持久化到磁盘。
4月9日
全局锁和表级锁
MySQL中锁分为三种:全局锁、表级锁和行级锁。
全局锁
全局锁是对整个数据库加锁,会让整个数据库处于只读的状态。
全局锁的使用命令:
1 | flush tables with read lock; # 加锁 |
全局锁多用于全库逻辑备份。
表级锁
表级锁分为表锁和元数据锁。
表锁:用法是 lock tables ... read/write;
,lock tables 除了会限制别的线程的读写之外,也限定了本线程接下来的操作对象。例如:
1 | lock tables t1 read, t2 write; |
那么在unlock tables;
之前,本线程只能对t1进行读,对t2进行写,不能完成其他操作,访问其他表。
全局锁和表锁都会在客户端断开连接时自动解锁。
表锁解决的的数据库引擎不支持行锁的时候才使用,在实际中其他情况下不建议使用,影响较大。在引擎支持的情况下,升级代码中出现的lock tables
和ublock tables
改成begin
和commit
就可以了。
元数据锁(MDL)
元数据锁不需要显示使用,在访问一个表的时候就会被自动加上,用来确保读写的正确性。
- 读锁:对一个表做增删改查操作时,加MDL读锁
- 写锁:对表做了结构变更操作的时候,加MDL写锁
读锁之间不互斥,可以多线程。
写写、写读锁之间互斥,需要等先执行的完成。
在事务中:
事务中的MDL锁,会在执行语句开始时申请,但是执行完成之后,不会立即释放,而是等到整个事务的提交之后再释放。