一、事务
事务是一组SQL语句组成的逻辑处理单元,而MySQL的事务想必大家都已经非常的清楚了,既然这里提到了它,顺便讲讲什么是ACID:
原子性(Atomicity):事务是一个原子操作单元。在当时原子是不可分割的最小元素,其对数据的修改,要么全部成功,要么全部都不成功。
一致性(Consistent):事务开始到结束的时间段内,数据都必须保持一致状态。
隔离性(Isolation):数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的”独立”环境执行。
持久性(Durable):事务完成后,它对于数据的修改是永久性的,即使出现系统故障也能够保持。
二、MySQL的事务实现原理
MySQL中的事务主要通过redo log和undo log,以及锁实现,其中redo log主要用于保证事务的原子性和持久性,而undo log主要用于保证事务的一致性,锁则是用于实现隔离性。undo log中记录的主要是一些逻辑日志,如一条条的SQL语句,对于更新语句,每一条插入对应着删除,每一条修改对应着修改为最初版本的语句。而redo log主要记录的则是一些物理日志,是记录的对页(非连续存储导员)实实在在物理修改操作,其中主要分为两部分,一部分是redo log buffer存储于缓存之中,是易丢失的,一部分是redo log file存储于磁盘中,是持久化的。下面主要讲讲redo log的一些实现策略。
三、innodb_flush_method分析
这里提到操作系统中的一个fsync函数,操作系统用它将内存中所有发生了修改的文件同步到存储设备之中。
在默认情况之下,MySQL使用innodb存储引擎之时,为了保证所产生的日志都能够刷新到redo log file之中,在每次将redo log buffer中的记录写入到redo log file之时,都需要调用一次fsync函数。策略是现将重做日志缓存中的记录写入到文件系统缓存,再调用一次fsync操作,将重做日志刷新到磁盘之中。可以通过innodb_flush_method参数进行调整,如下:
mysql> show variables like "innodb_flush_method";
+---------------------+-------+
| Variable_name | Value |
+---------------------+-------+
| innodb_flush_method | |
+---------------------+-------+
1 row in set (0.00 sec)
其中innodb_flush_method主要有三种取值,第一种则是默认的fsync,即上文所提到的策略;第二种是O_DIRECT直接将重做日志缓存中的记录刷新到磁盘,不经过系统缓存,切记,这里同样会调用一次fsync函数,下文分晓;最后一种则是O_DSYNC表示以同步io的方式打开文件,任何写操作都将阻塞到数据写入物理磁盘后才会返回。详细的解释请参考:INNODB_FLUSH_METHOD理解
四、刷新策略
由于fsync需要将内存中的修改同步到磁盘之中,因此它的效率与磁盘的性能密切相关,那么innodb是如何控制fsync的周期呢?
innodb中通过innodb_flush_log_at_trx_commit控制刷新的策略,如下:
mysql> show variables like "innodb_flush_log_at_trx_commit";
+--------------------------------+-------+
| Variable_name | Value |
+--------------------------------+-------+
| innodb_flush_log_at_trx_commit | 1 |
+--------------------------------+-------+
1 row in set (0.01 sec)
其中主要有三个取值,0:表示事务提交的时候不进行刷新到重做日志文件的操作,而是在master thread之中每隔1秒对其进行一次fsync操作。1:默认为1,表示事务每次提交则进行一次fsync操作。2:表示事务提交之时,仅仅将重做日志缓存写入到系统缓存之中,不进行fsync操作,只要操作系统不发生宕机,则事务不会丢失。很明显效率上是2>0>1,但由于2不能避免脏数据的出现,0仅仅限制于master thread之中,因此选择哪一个参数应当仔细斟酌。
五、设置为O_DIRECT之后依然会进行fsync
当把innodb_flush_method参数单单设置为O_DIRECT以后,也许并不会让缓存的写入速度发生质的飞越,因为这里同样会进行一次fsync操作。在linux中,一切数据皆是文件,一个文件的身份识别是通过一个INODE,称为I节点,它主要有两部分组成,元数据和数据块。其中,元数据主要存储以下信息:
- 文件的字节数
- 文件的时间戳
- 文件的链接(软、硬链接)
- 指向数据块的链接
由于元数据相当于文件的身份标识符,因此一旦文件发生了更改,那么元数据也必须要做相应的改变。在linux中,元数据主要存储于INODE CACHE之中,而数据块则主要存于系统缓存之中,因此一旦文件发生了更新,则必须调用一次fsync更新INODE_CACHE中的元数据信息,否则将找不到访问文件的入口了。