MySQL
[Toc]
一、基础
MySQL知识普及
MySQL架构可以在多种不同场景中应用并发挥良好作用。主要体现在存储引擎的架构上,插件式的存储引擎架构将查询处理和其它的系统任务以及数据的存储提取相分离。
MySQL逻辑架构
-
最上层
最上层是一些客户端和连接服务,包含本地的sock通信和大多数基于客户端/服务端工具实现的类似于tcp/ip的通信,主要完成一些类似于连接处理、授权认证及相关的安全方案,在该层上引用了线程池的概念,为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于ssl的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作权限;
-
第二层
第二层架构主要完成大多数的核心服务功能。如sql接口,并完成缓存的查询。sql的分析和优化 以及部分内置函数的执行。所有跨存储引擎的功能也在这一层实现,如过程,函数等。在该层,服务器会解析查询并创建相应的内部解析树,并对其完成相应的优化如确定查询表的顺序,是否利用索引等。最后生成相应的执行操作。如select语句,服务器还会查询内部的缓存。如果缓存空间足够大,这样就解决大量读操作的环境中能够很好的提升系统的性能。
-
存储引擎层
存储引擎真正的负责MySQL中数据的存储和提取,服务器通过API与存储引擎进行通信,不同的存储引擎具有的功能不同,这样我们可以根据自己的实际需进行选取。
-
数据存储层
主要是将数据存储在运行于裸设备的文件系统之上,并完成于存储引擎的交互。
并发控制和锁的概念
当数据库中有多个操作需要修改同一数据时,不可避免的会产生数据的脏读。这时就需要数据库具有良好的并发控制能力,这一切在MySQL中都是由服务器和存储引擎来实现的。
解决并发问题最有效的方案是引入了锁的机制,锁在功能上分为共享锁(shared lock)和排它锁(exclusive lock)即通常说的读锁和写锁。当一个select语句在执行时可以施加读锁,这样就可以允许其它的select操作进行,因为在这个过程中数据信息是不会被改变的这样就能够提高数据库的运行效率。当需要对数据更新时,就需要施加写锁了,不在允许其它的操作进行,以免产生数据的脏读和幻读。锁同样有粒度大小,有表级锁(table lock)和行级锁(row lock),分别在数据操作的过程中完成行的锁定和表的锁定。这些根据不同的存储引擎所具有的特性也是不一样的。
MySQL大多数事务型的存储引擎都不只是简单的行级锁,基于性能的考虑,他们一般在行级锁基础上实现了多版本并发控制(MVCC)。
二、事务
简单的说事务就是一组原子性的SQL语句。可以将这组语句理解成一个工作单元,要么全部执行要么都不执行。默认MySQL中自动提交时开启的(start transaction)
事务具有ACID的特性
- 原子性:事务中的所有操作要么全部提交成功,要么全部失败回滚;
- 一致性:数据库总是从给一个一致性的状态转换到另一个一致性的状态;
- 隔离性:一个事务所做的修改在提交之前对其他事务是不可见的,两个以上的事务不会出现交错执行的状态,因为这样会导致数据不一致;
- 持久性:一旦事务提交,其所做的操作会永久的保存在数据库中;
事务的隔离级别
- 读未提交(read uncommitted): 事务的修改即使未提交也是对其他的事务不可见;
- 读提交(read committed):事务提交后所做的修改才会被其他事务看见,可能产生一个事务中两次查询结果不同;
- 可重复读(repeatable read):只有当前事务提交才能看见另一个事务的修改结果。解决了一个事务中两次查询的结果不同的问题;
- 串行化:只有一个事务提交后才会执行另一个事务;
三、存储引擎
1. InnoDB
- InnoDB 的存储文件有两个,后缀名分别是 .frm 和 .idb,其中 .frm 是表的定义文件,而 idb 是数据文件;
- InnoDB 中存在表锁和行锁,不过行锁是在命中索引的情况下才会起作用;
- InnoDB 支持事务,且支持四种隔离级别(读未提交、读已提交、可重复读、串行化),默认的为可重复读;
2. Myisam
- Myisam 的存储文件有三个,后缀名分别是 .frm、.MYD、MYI,其中 .frm 是表的定义文件,.MYD 是数据文件,.MYI 是索引文件;
- Myisam 只支持表锁,且不支持事务。Myisam 由于有单独的索引文件,在读取数据方面的性能很高 ;
四、存储结构
五、存储过程
六、日志文件
重做日志(redo log)
1. 基础
- 作用
- 确保事务的持久性;
- 防止在发生故障的时间点,尚有脏页未写入磁盘,在重启mysql服务的时候,根据redo log进行重做,从而达到事务的持久性;
- 内容
- 物理格式的日志,记录的是物理数据页面的修改的信息,其redo log是顺序写入redo log file的物理文件中去的;
- 产生:
- 事务开始之后就产生redo log,redo log的落盘并不是随着事务的提交才写入的,而是在事务的执行过程中,便开始写入redo log文件中;
- 释放:
- 当对应事务的脏页写入到磁盘之后,redo log的使命也就完成了,重做日志占用的空间就可以重用(被覆盖);
- 物理文件:
- 默认情况下,对应的物理文件位于数据库的data目录下的ib_logfile1&ib_logfile2;
- innodb_log_group_home_dir 指定日志文件组所在的路径,默认./ ,表示在数据库的数据目录下;
- innodb_log_files_in_group 指定重做日志文件组中文件的数量,默认2;
- 关于文件的大小和数量,由一下两个参数配置:
- innodb_log_file_size 重做日志文件的大小;
- innodb_mirrored_log_groups 指定了日志镜像文件组的数量,默认1;
2. 进阶
-
redo log是什么时候写盘的(事务开始之后逐步写入)?
重做日志是在事务开始之后逐步写入重做日志文件,而不一定是事务提交才写入重做日志缓存,原因就是,重做日志有一个缓存区Innodb_log_buffer,Innodb_log_buffer的默认大小为8M,Innodb存储引擎先将重做日志写入innodb_log_buffer中。然后会再通过三种方式将innodb日志缓冲区的日志刷新到磁盘。
-
三种将innodb日志缓冲区的日志刷新到磁盘的方式:
- Master Thread每秒一次执行刷新Innodb_log_buffer到重做日志文件;
- 每个事务提交时会将重做日志刷新到重做日志文件;
- 当重做日志缓存可用空间少于一半时,重做日志缓存被刷新到重做日志文件;
-
总结
由此可以看出,重做日志通过不止一种方式写入到磁盘,尤其是对于第一种方式,Innodb_log_buffer到重做日志文件是Master Thread线程的定时任务。因此重做日志的写盘,并不一定是随着事务的提交才写入重做日志文件的,而是随着事务的开始,逐步开始的。 即使某个事务还没有提交,Innodb存储引擎仍然每秒会将重做日志缓存刷新到重做日志文件。这一点是必须要知道的,因为这可以很好地解释再大的事务的提交(commit)的时间也是很短暂的。
回滚日志(undo log)
1. 基础
- 作用
- 保存了事务发生之前的数据的一个版本,可以用于回滚,同时可以提供多版本并发控制下的读(MVCC),也即非锁定读;
- 内容
- 逻辑格式的日志,在执行undo的时候,仅仅是将数据从逻辑上恢复至事务之前的状态,而不是从物理页面上操作实现的,这一点是不同于redo log的。;
- 产生:
- 事务开始之前,将当前是的版本生成undo log,undo 也会产生 redo 来保证undo log的可靠性;
- 释放:
- 当事务提交之后,undo log并不能立马被删除, 而是放入待清理的链表,由purge线程判断是否由其他事务在使用undo段中表的上一个事务之前的版本信息,决定是否可以清理undo log的日志空间;
2. 进阶
- undo是在事务开始之前保存的被修改数据的一个版本,产生undo日志的时候,同样会伴随类似于保护事务持久化机制的redolog的产生;
- 默认情况下undo文件是保持在共享表空间的,也即ibdatafile文件中,当数据库中发生一些大的事务性操作的时候,要生成大量的undo信息,全部保存在共享表空间中的;
- 因此共享表空间可能会变的很大,默认情况下,也就是undo 日志使用共享表空间的时候,被“撑大”的共享表空间是不会也不能自动收缩的;
- 因此,mysql5.7之后的“独立undo 表空间”的配置就显得很有必要了