1.mvcc多版本并发控制机制
mysql在可重复读级别下可以防止脏读和不可重复读,这个隔离性就是靠mvcc(Mulit-version concurrency control)机制来实现的,对一行数据的读和写默认是不会通过加锁来保证隔离性的,避免频繁的加锁互斥,而在串行化是对所有操作都加锁实现的。
mysql 在读已提交和可重复读的隔离级别都实现了mvcc机制。
undo日志版本链与read view机制详解
undo日志版本链是说一行数据被多个事务修改后,在每个事务修改完后,都会在undo日志记一条回滚的数据包括修改的事务id,并且会通过roll_pointer将这些修改历史串起来,形成一个版本链。
在可重复读级别,在开启事务后执行第一次select时会生成一个read view一致性视图,这个视图在事务结束前不会改变(如果是读已提交级别则是每执行一次select生成一个read view),这个视图由所有未提交的事务id数组(数组里最小的id为min_id)和已创建的最大的事务id(max_id)组成,事务里的任何查询都需要从对应的版本链里的最新数据开始逐条和read view做对比从而得到最终的快照数据。
版本链对比规则:
1. 如果 row 的 trx_id 落在绿色部分( trx_id可见的;
2. 如果 row 的 trx_id 落在红色部分( trx_id>max_id ),表示这个版本是由将来启动的事务生成的,是不可见的(若 row 的 trx_id 就是当前自己的事务是可见的);
3. 如果 row 的 trx_id 落在黄色部分(min_id
a. 若 row 的 trx_id 在视图数组中,表示这个版本是由还没提交的事务生成的,不可见(若 row 的 trx_id 就是当前自己的事务是可见的);
b. 若 row 的 trx_id 不在视图数组中,表示这个版本是已经提交了的事务生成的,可见。
注意:begin/start transaction 命令并不是一个事务的起点,在执行到它们之后的第一个修改操作或加排它锁操作(比如select...for update)的语句,事务才真正启动,才会向mysql申请真正的事务id,mysql内部是严格按照事务的启动顺序来分配事务id的。
总结:
MVCC机制的实现就是通过read-view机制与undo版本链比对机制,使得不同的事务会根据数据版本链对比规则读取同一条数据在版本链上的不同版本数据。相当于在第一次查询时做了一个快照,以后的每次查询都是这个快照的结果。如果有修改操作的话就会更新这个read view
2.innodb引擎sql执行的BufferPool
redo log重做日志关键参数
innodb_log_buffer_size:设置redo log buffer大小参数,默认16M ,最大值是4096M,最小值为1M。
show variables like '%innodb_log_buffer_size%';
set global innodb_log_buffer_size=16777216;
innodb_log_group_home_dir:设置redo log文件存储位置参数,默认值为"./",即innodb数据文件存储位置,其中的 ib_logfile0 和 ib_logfile1 即为redo log文件。
show variables like '%innodb_log_group_home_dir%';
innodb_log_files_in_group:设置redo log文件的个数,命名方式如: ib_logfile0, iblogfile1... iblogfileN。默认2个,最大100个。
show variables like '%innodb_log_files_in_group%';
innodb_log_file_size:设置单个redo log文件大小,默认值为48M。最大值为512G,注意最大值指的是整个 redo log系列文件之和,即(innodb_log_files_in_group * innodb_log_file_size)不能大于最大值512G。
show variables like '%innodb_log_file_size%';
redo log 写入磁盘过程分析:
redo log 从头开始写,写完一个文件继续写另一个文件,写到最后一个文件末尾就又回到第一个文件开头循环写,如下面这个图所示。
write pos 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。
checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件里。
write pos 和 checkpoint 之间的部分就是空着的可写部分,可以用来记录新的操作。如果 write pos 追上checkpoint,表示redo log写满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint 推进一下。
innodb_flush_log_at_trx_commit:这个参数控制 redo log 的写入策略,它有三种可能取值:
- 设置为0:表示每次事务提交时都只是把 redo log 留在 redo log buffer 中,数据库宕机可能会丢失数据。
- 设置为1(默认值):表示每次事务提交时都将 redo log 直接持久化到磁盘,数据最安全,不会因为数据库宕机丢失数据,但是效率稍微差一点,线上系统推荐这个设置。
- 设置为2:表示每次事务提交时都只是把 redo log 写到操作系统的缓存page cache里,这种情况如果数据库宕机是不会丢失数据的,但是操作系统如果宕机了,page cache里的数据还没来得及写入磁盘文件的话就会丢失数据。
InnoDB 有一个后台线程,每隔 1 秒,就会把 redo log buffer 中的日志,调用 操作系统函数 write 写到文件系统的 page cache,然后调用操作系统函数 fsync 持久化到磁盘文件。
redo log写入策略参看下图:
binlog二进制归档日志
binlog二进制日志记录保存了所有执行过的修改操作语句,不保存查询操作。如果 MySQL 服务意外停止,可通过二进制日志文件排查,用户操作或表结构操作,从而来恢复数据库数据。
启动binlog记录功能,会影响服务器性能,但如果需要恢复数据或主从复制功能,则好处则大于对服务器的影响。