总的来说,事务的原子性是通过 undo log 来实现的,事务的持久性性是通过 redo log 来实现的,事务的隔离性是通过读写锁+MVCC 来实现的。
    事务的一致性通过原子性、隔离性、持久性来保证。也就是说 ACID 四大特性之中,C(一致性)是目的,A(原子性)、I(隔离性)、D(持久性)是手段,是为了保证一致性,数据库提供的手段。数据库必须要实现 AID 三大特性,才有可能实现一致性。同时一致性也需要应用程序的支持,应用程序在事务里故意写出违反约束的代码,一致性还是无法保证的,例如,转账代码里从 A 账户扣钱而不给 B 账户加钱,那一致性还是无法保证。
    至于 InnoDB 事务是如何通过日志来实现的,简单来说,因为事务在修改页**时,要先记 undo,在记 undo 之前要记 undo redo, 然后修改数据页,再记数据页修改的 redo。 Redo(里面包括 undo 的修改) 一定要比数据页先持久化到磁盘。
    当事务需要回滚时,因为有 undo,可以把数据页回滚到前镜像的状态,崩溃恢复时,如果 redo log 中事务没有对应的 commit 记录,那么需要用 undo把该事务的修改回滚到事务开始之前。如果有 commit 记录,就用 redo 前滚到该事务完成时并提交掉。
    更详细的回答是:
    redo 通常是物理日志,记录的是页的物理修改操作,用来恢复提交事务修改的页操作。而 undo 是逻辑日志,根据每行记录进行记录,用来回滚记录到某个特定的版本。
    当事务提交之后会把所有修改信息都会存到 redo 日志中。
    redo 日志由两部分组成,一个是在内存里的 redo log buffer,另一个是在磁盘里的 redo log 文件
    mysql 为了提升性能不会把每次的修改都实时同步到磁盘,而是会先存到Buffer Pool(缓冲池)里头,把这个当作缓存来用。然后使用后台线程去做缓冲池和磁盘之间的同步。
    系统重启后读取 redo log 恢复最新数据。虽然 redo log 会在事务提交前做一次磁盘写入,但是这种 IO 操作相比于 buffer pool 这种以页(16kb)为管理单位的随机写入,它做的是几个字节的顺序写入,效率要高得多。
    redo log buffer 中的数据,会在一个合适的时间点刷入到磁盘中。这个合适的时间点包括:
    1、MySQL 正常关闭的时候;
    2、MySQL 的后台线程每隔一段时间定时的讲 redo log buffer 刷入到磁盘, 默认是每隔 1s 刷一次;
    3、当 redo log buffer 中的日志写入量超过 redo log buffer 内存的一半时, 即超过 8MB 时,会触发 redo log buffer 的刷盘;
    4、当事务提交时,根据配置的参数 innodb_flush_log_at_trx_commit 来决定是否刷盘。要严格保证数据不丢失,必须得保证 innodb_flush_log_at_trx_commit 配置为 1。
    redo log 在进行数据重做时,只有读到了 commit 标识,才会认为这条 redo log 日志是完整的,才会进行数据重做,否则会认为这个 redo log 日志不完整, 不会进行数据重做**。
    undo log 和 redo log 记录物理日志不一样,它是逻辑日志。可以认为当 delete 一条记录时,undo log 中会记录一条对应的 insert 记录,反之亦然,当 update 一条记录时,它记录一条对应相反的 update 记录。当执行回滚时,就可以从 undo log 中的逻辑记录读取到相应的内容并进行回滚。
    而事务的隔离性,也可以通过 undo log 来实现的:当读取的某一行被其他事务锁定时,它可以从 undo log 中分析出该行记录以前的数据是什么,从而提供该行版本信息,帮助用户实现一致性非锁定读取,这也是 MVCC 的实现机制的组成部分。