InnoDB组件结构:
假设现在有一条更新语句:
update users set name = 'lisi' where id = 1
需要更新到数据库,InnoDB会执行哪些操作呢?
首先,InnoDB会判读缓冲池里是否存在 id = 1 这条数据,如果不存在则从磁盘中加载到缓冲池中,而且还会对这行数据加独占锁,防止多个sql同时修改这行数据。
假设 id = 1 这条数据name原来的值 name = ‘zhangsan’,现在我们要更新为 name = ‘lisi’ , 那么我们就需要把旧值name='zhangsan’和id=1这些信息写入到undo日志文件中。
对于熟悉数据库的同学来说都了解事务的概念,在事务未提交之前,所有操作都有可能进行回滚,即可以把 name = ‘lisi’ 回滚到 name = ‘zhangsan’,所以将更新前的值写到undo日志文件。
在undo日志文件写入完毕之后,便开始更新内存中的这条数据。把 id = 1 的 name = ‘zhangsan’ 更新为 name = ‘lisi’。这时内存中的数据已经更新完毕,但磁盘上的还没有变化,此时出现了不一致的脏数据。
这时可能有一个疑问,万一事务提交完成,但MySQL服务宕机了,而内存中的数据还没写入到磁盘,是不是会造成数据丢失而造成sql执行数据前后不一致?
在InnoDB结构中,有一个 redo log buffer 缓冲区存放redo日志,所谓redo日志,例如 把id=1,name='zhangsan’修改为name=‘lisi’ 便是一条日志。
但这时redo log buffer 还仅仅存在内存中,没能实现MySQL宕机后的数据恢复。
其实并没有影响,事务没有提交,意味着执行没有成功,就算MySQL崩溃或者宕机后,内存中的 buffer pool 和 redo log buffer 修改过的数据都会丢失,也并不影响数据前后的一致性。
如果事务提交失败,那数据库的数据更加不会改变。
在提交事务时,redo日记会根据策略实现把redo日志从 redo log buffer 里写入磁盘。策略通过 innoDB_flush_log_at_trx_commit 来配置。
此时就算buffer pool 的数据没有刷进磁盘,也可以从redo log 中得知修改过哪些数据,MySQL宕机重启后,可以从redo日志中恢复修改的数据。
看完这几种相信为了保证数据安全,参数为1是最佳策略。
binlog其实是属于MySQL Server 的日志文件,而在这出提出是因为与redo log有着很大的关联。
在执行更新的同时,innoDB与执行器一直在交互,包括加载数据到缓冲池,写入undo日志文件,更新内存数据,写redo日志和刷入磁盘等。而对binlog的写入也是由执行器执行。
其中 1、2、3、4步骤为执行更新语句做的事,而 5、6是提交事务开始做的事。
sync_binlog参数控制binlog的刷盘策略
binlog写入磁盘后,会把binlog日志文件所在的位置和文件名称都写入redo log日志文件中,同时在redo log日志文件里写入一个commit标记。
commit 标记意义着保持redo log 和 binlog 日志一致。
如果在步骤5或者步骤6,事务提交开始,MySQL宕机了,redo log 中并没有commit标记,都算事务提交失败。
意味着 commint 标记是事务最终提交成功。
脏数据刷入磁盘是由后台IO线程随机刷入磁盘的。
这时候考虑到,在刷入磁盘之前,MySQL宕机怎么办?这时候,事务已经提交成功,redo log 中也有commit标记,就算宕机了,重启后,也会根据redo日志文件把数据更新到内存中,等待IO线程的刷盘。
通过更新语句执行分析之后,了解到InnoDB存储引擎中包含了 buffer pool 缓冲池、redo log buffer 缓冲区等缓存数据,undo、reod log等日志文件,同时也有MySQL Server 的日志文件。
在执行更新语句的时候,会修改buffer pool、写undo日志文件、 写redo log buffer等操作;提交事务时,会将redo log 刷盘,binlog刷盘,写入binlog文件名称和位置,写入commit标记,最后等待IO线程将buffer pool的脏数据随机刷盘。