PDD 面试之数据库ACID的原理

去不了拼多多了。

现在分享一下面试的问题:

  • 三次握手的原理

  • 数据库ACID的原理。

  • 设计模式。

  • Java类加载的时机

  • 线程常见的问题。

##数据库ACID的原理。

Atomicity

Transactions are often composed of multiple statements. Atomicity guarantees that each transaction is treated as a single “unit”, which either succeeds completely, or fails completely: if any of the statements constituting a transaction fails to complete, the entire transaction fails and the database is left unchanged. An atomic system must guarantee atomicity in each and every situation, including power failures, errors and crashes.

原子性保证每个事务都是最小不可分割的单元,要么全部成功,要么全部失败并恢复至事务未开始的样子。

Consistency

Consistency ensures that a transaction can only bring the database from one valid state to another, maintaining database invariants: any data written to the database must be valid according to all defined rules, including constraints, cascades, triggers, and any combination thereof. This prevents database corruption by an illegal transaction, but does not guarantee that a transaction is correct.

一致性指事务总是能使得数据库从一个一致性状态转移到另一种一致性状态

Isolation

Transactions are often executed concurrently (e.g., reading and writing to multiple tables at the same time). Isolation ensures that concurrent execution of transactions leaves the database in the same state that would have been obtained if the transactions were executed sequentially. Isolation is the main goal of concurrency control; depending on the method used, the effects of an incomplete transaction might not even be visible to other transactions.

隔离性指即使在并发的执行事务时,事务之间不会造成相互影响

Durability

Durability guarantees that once a transaction has been committed, it will remain committed even in the case of a system failure (e.g., power outage or crash). This usually means that completed transactions (or their effects) are recorded in non-volatile memory.

持久性是一旦事务提交,那么这个事务一定是生效的,即使后续遇到系统崩溃

网上很多文章提到数据库的乐观锁、悲观锁。而在我看来,这种分类过于浅显,没有触及到问题的本质核心。锁只是数据库为了达到ACID的一小部分,除此之外MVCC(多版本并发控制)及WAL(write ahead log)才是数据库事务的核心。一个数据系统中,数据应该是其次的,最重要的是记录数据的变更,即log。

Mysql怎么保证一致性的?

从数据库层面,数据库通过原子性、隔离性、持久性来保证一致性。也就是说ACID四大特性之中,C(一致性)是目的,A(原子性)、I(隔离性)、D(持久性)是手段,是为了保证一致性,数据库提供的手段。数据库必须要实现AID三大特性,才有可能实现一致性。
从应用层面,通过代码判断数据库数据是否有效,然后决定回滚还是提交数据。

Mysql怎么保证原子性的?

利用Innodb的undo log。

undo log名为回滚日志,是实现原子性的关键,当事务回滚时能够撤销所有已经成功执行的sql语句,他需要记录你要回滚的相应日志信息。

例如

  1. 当你delete一条数据的时候,就需要记录这条数据的信息,回滚的时候,insert这条旧数据
  2. 当你update一条数据的时候,就需要记录之前的旧值,回滚的时候,根据旧值执行update操作
  3. 当年insert一条数据的时候,就需要这条记录的主键,回滚的时候,根据主键执行delete操作undo log记录了这些回滚需要的信息,当事务执行失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。

Mysql怎么保证持久性的?

利用Innodb的redo log(重做日志)。

正如之前说的,Mysql是先把磁盘上的数据加载到内存中,在内存中对数据进行修改,再刷回磁盘上。如果此时突然宕机,内存中的数据就会丢失。

怎么解决这个问题?

简单啊,事务提交前直接把数据写入磁盘就行啊。 (WAL)

这么做有什么问题?

只修改一个页面里的一个字节,就要将整个页面刷入磁盘,太浪费资源了。毕竟一个页面16kb大小,你只改其中一点点东西,就要将16kb的内容刷入磁盘,听着也不合理。
毕竟一个事务里的SQL可能牵涉到多个数据页的修改,而这些数据页可能不是相邻的,也就是属于随机IO。显然操作随机IO,速度会比较慢。
采用redo log解决上面的问题。当做数据修改的时候,不仅在内存中操作,还会在redo log中记录这次操作。当事务提交的时候,会将redo log日志进行刷盘(redo log一部分在内存中,一部分在磁盘上)。当数据库宕机重启的时候,会将redo log中的内容恢复到数据库中,再根据undo log和binlog内容决定回滚数据还是提交数据。

采用redo log的好处?

redo log进行刷盘比对数据页刷盘效率高,具体表现如下

  • redo log体积小,毕竟只记录了哪一页修改了啥,因此体积小,刷盘快。
  • redo log是一直往末尾进行追加,属于顺序IO。效率显然比随机IO来的快。

Isolation实现之 (锁 OR undo log+MVCC)

总是能在各种数据库事务文章中看到 未提交读、提交读、可重复读等字眼,而大部分文章将这个功能的实现归功于读写锁。锁是一种实现方式,但熟悉操作系统就会知道,锁是一种开销很大的并发控制手段,如无必要,尽量采用其他方式实现。

1)锁实现的Isolation

未提交读:一个update事务A只有在对数据修改时才加write lock,一旦写完马上释放write lock,即使事务A还没有提交。因此事务B在读取同一行时,才能读到事务A修改过的数据。

提交读:一个update事务A只有在对数据修改时才加write lock,但直到事务A commit时才释放写锁。因此,同时进行的事务B希望读取同一行数据时,会被事务A的write lock堵塞,所以解决了脏读的问题。

可重复读:这个隔离等级的条件下,除了执行提交读的写锁方式,还会在读取一行数据后,为这行数据添加read lock直至事务commit。例如,事务A读取ID=1这一行数据,然后为ID=1添加read lock。事务B同时希望update ID=1,此时获取写锁失败,因此在事务A执行完之前,没有其他任何事务可以对ID=1这一行做修改,因此解决了重复读的问题

虽然读写锁解决了Isolation问题,但锁会导致大量的堵塞,性能下降。某些时候会造成死锁,为了解决死锁,还要添加死锁探测机制,性能进一步下降,因此需要更高效的方式实现Isolation。

2)MVCC实现的Isolation

MVCC,多版本并发控制,的思路是:对每一行数据用log记录多个版本,每个版本的数据可能都不相同,然后根据事务的ID去寻找到适合他的那个版本数据,以此达到不同事务之间的隔离性。
如果一个事务读取的行正在做DELELE或者UPDATE操作,读取操作不会等行上的锁释放,而是读取该行的快照版本。

WAL、redo log与Durability

数据库的最终数据是要落盘的,试想如果我想修改100张表里的10000条数据,如果每次修改都直接以落盘的形式处理,那么必定有大量的随机IO,如果使用机械磁盘那么一定存在大量的disk seek时间,数据库性能极低。为了能提高性能,我们希望能将这些改动缓存下来,统一处理,但问题是:如果这期间数据库崩溃了,那么这部分修改就丢失了,Durability就无法得到满足。

针对于此问题,InnoDB存储引擎引入了WAL技术(write-ahead logging)和redo log。

1.每次commit事务之前,先将要修改的数据信息存入redo log buffer,然后根据innodb_flush_log_at_trx_commit 的设置将redo log刷新至磁盘,写redo log至磁盘属于磁盘顺序写(因为只需append log到下一位置),因此redo log的落盘速度是非常快的。

2.MySQL在内存中执行此次update操作,即修改数据。

3.MySQL会周期或不周期性的刷新内存值到磁盘。

可以看到,一次update事务并不会立刻修改磁盘中的数据,而是记录修改信息,后期刷新至磁盘。所以步骤一以后,即使数据库崩溃,还可以根据落盘的redo log恢复已执行的事务,使得我们能获得Durability。
示例

参考文章:
数据库ACID与实现机制
Mysql中ACID的原理
真心感谢两位大佬!!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值