MYSQL为什么能做到数据的一致性?

架构小魔方 2024-04-26 03:55:34
什么是事务?

众所周知,MYSQL是一个事务性的数据库,它可以绝对可靠保证数据一致性,目前也广泛用于线上实时在线业务,目前各大互联网公司均采用MYSQL,就算是一些公司自研的数据库也是基于开源MYSQL基础进行优化和改造,那么MYSQL到底是如何保证数据的一致性的呢?

要说到MYSQL的一致性,那就不得不提事务,事务有一个ACID特性(原子性、隔离性、持久性以及一致性)。

原子性(Atomicity):一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节,而且事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样,就好比买一件商品,购买成功时,则给商家付了钱,商品到手;购买失败时,则商品在商家手中,消费者的钱也没花出去。

一致性(Consistency):是指事务操作前和操作后,数据满足完整性约束,数据库保持一致性状态。比如,用户 A 和用户 B 在银行分别有 800 元和 600 元,总共 1400 元,用户 A 给用户 B 转账 200 元,分为两个步骤,从 A 的账户扣除 200 元和对 B 的账户增加 200 元。一致性就是要求上述步骤操作后,最后的结果是用户 A 还有 600 元,用户 B 有 800 元,总共 1400 元,而不会出现用户 A 扣除了 200 元,但用户 B 未增加的情况(该情况,用户 A 和 B 均为 600 元,总共 1200 元)。

隔离性(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致,因为多个事务同时使用相同的数据时,不会相互干扰,每个事务都有一个完整的数据空间,对其他并发事务是隔离的。也就是说,消费者购买商品这个事务,是不影响其他消费者购买的。

持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失.

MYSQL的存储架构

首先来看下MYSQL的存储架构,它分为三个主要的文件,undo log(回滚日志)、redo(重做日志)和binlog(归档日志),而事务中的原子性主要是通过undo log 来保证的,持久性是通过redo log 来保证,隔离性是通过MVCC(多版本并发控制)来保证的,一致性是通过持久性+原子性+隔离性来保证,而数据库的主从数据一致是通过binlog日志来保证的。详细来说,这个四个文件的作用如下:

undo log(回滚日志):是 Innodb 存储引擎层生成的日志,实现了事务中的原子性,主要用于事务回滚和 MVCC。

redo log(重做日志):是 Innodb 存储引擎层生成的日志,实现了事务中的持久性,主要用于掉电等故障恢复;

binlog(归档日志):是 Server 层生成的日志,主要用于数据备份和主从复制;

MYSQL如何保证数据的一致性?

当写入一条数据时,先会记录 undo log 日志,然后 redo log 会记录事务执行后的数据, 事务提交后,会同步写入binlog 日志,在通过二阶段来保证redolog和binlog的一致性,避免主从数据不一致,事务提交后数据会更新到buffer pool 缓冲,在由innodb_flush_log_at_trx_commit参数控制刷盘的频率,undolog记录的是事务执行之前的数据,如果事务执行过程中失败,根据undolog进行事务回滚。

Undo log 是逻辑日志,可以理解为:

当delete一条记录的时候,Undo log会记录下一条insert语句,当insert一条记录的时候,Undo log会记录下一条delete语句,当update一条记录时,Undo log会记录下一条相反的update语句

Redo log日志与Undo log恰恰相反

Redo log记录的是新数据的备份,在事务提交前,只需要将 Undo log持久化即可,不需要将数据持久化。当系统崩溃时,虽然数据没有持久化,但是 Undo log已经持久化。系统可以根据 Undo log的内容,将数据恢复到最新的状态。

为什么要采用二阶段来保证一致性?

当需要向表中插入一条记录X的时候。

情况一:

如果先写binlog再写redo log,那么假设binlog写完后崩溃后,此时redo log还没写。那么重启恢复的时候就会出现问题,binlog中已经有X的记录,当从主机同步数据的时候,或者使用binlog恢复数据的时候,就会同步X这条记录。但是redo log中没有关于X的记录,所以恢复崩溃之后,插入X记录的这个事务是无效的,即数据库中没有该行的记录,这就造成了数据不一致的情况。

情况二:

如果是先写redo log再写 binlog,那么假设redo log写完后崩溃了,此时binlog还没写。那么重启恢复的时候也会出现问题,redo log中已经有X的记录了,所以崩溃恢复之后,插入X记录的这个事务是有效的,通过该记录可以将数据恢复到数据库中;但是binlog中还没有关于X的记录,所以当从机从主机同步数据的时候,或者使用binlog恢复数据的时候,就不会同步到X这条记录,这就造成了数据不一致。

两阶段提交如何保证数据一致性?

情况一:一阶段提交之后崩溃了,即已写入 redo log,处于 prepare 状态的时候崩溃了,此时由于 binlog 还没写,redo log 处于 prepare 状态还没提交,所以崩溃恢复的时候,这个事务会回滚,此时 binlog 还没写,所以也不会传到备库。

情况二:假设写完 binlog 之后崩溃了,此时redo log 中的日志是不完整的,处于 prepare 状态,还没有提交,那么恢复的时候,首先检查 binlog 中的事务是否存在并且完整,如果存在且完整,则直接提交事务,如果不存在或者不完整,则回滚事务。

情况三:假设 redo log 处于 commit 状态的时候崩溃了,那么重启后的处理方案同情况二。

由此可见,两阶段提交能够确保数据的一致性。

1 阅读:54

架构小魔方

简介:感谢大家的关注