0%

初识事务

提到数据库,相信很多人第一个想到的就是事务,今天就从事务开始,对数据库的基本特征以及背后的实现逻辑进行一个简单的说明。

事务

在写这篇文章的时候,笔者特意又重新查了下事务的定义。维基百科中关于事务的定义是:“事务是数据库系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成”。事务的概念本身并没有什么好展开的,事务之所以成为数据库系统最重要的特征之一,是因为事务的 ACID 属性。

ACID

Atomicity(原子性) :事务中的一组操作,要么全部成功,要么全部失败,不存在部分成功的情况。原子性的定义相对是比较直观的,可以类比编程语言中的原子操作,从这个角度上来说,事务是数据库系统处理用户操作的最小单位(自动提交模式可以看作是一条语句一个事务)。数据库系统内部主要通过 Undo(回滚日志、前进项)来保证原子性。每一个操作在执行时,都会记录下执行前的状态到 Undo 中,当事务需要回滚时,就可以通过 Undo 恢复到事务执行前的状态。

Consistency(一致性) :数据库总是从一个一致性状态转移到另一个一致性状态。一致性的定义比较模糊,什么叫从一个一致性状态转移到另一个一致性状态?其实这是由于中文的翻译往往只翻译了一半,关于一致性有一段非常最要的补充:

1
2
Any data written to the database must be valid according to all defined rules, 
including constraints, cascades, triggers, and any combination thereof.

总结起来就是:数据库在任意时刻的状态都必须符合所有的验证规则。一个简单的例子:表A记录了用户的账户余额,表B记录了用户的消费记录。从数据库的角度,A中的值与B中记录的和应该总是一个固定值,这就是一条验证规则。另外一个例子:表A存了用户信息,表B存了地址信息,表B中的地址被表A引用(外键),那么删除表B中的记录时,必须检查表A中的记录,这也是一条验证规则。关于一致性的判断,更多的需要与关系间的内在逻辑相结合。

Isolation(隔离性) :不同事务间的操作是相互隔离的,一个事务的操作不受其他事务的影响,也不会影响其他事务。关于隔离性,不同的数据库有不同的实现逻辑,按照可见性进行划分,可以分为四种不同的隔离级别。关于隔离级别,下个部分会进行详细介绍。

Durability(持久性) :一个事务只要提交成功就必须完整保存,不管发生任何软件或者硬件故障都必须保证数据不会丢失。持久性可以说是数据库系统的基础,在真实的业务场景中,数据丢失也往往是最无法容忍的。数据库系统主要通过 Redo(重做日志、后进项)来保证持久性。Redo 是一种典型的 WAL,每一个操作在执行时,都会记录这个操作的内容,并持久化到 Redo 中。当系统恢复时,如果发现之前的操作没有生效,可以通过 Redo 进行重做,保证已提交的操作有效。

事务隔离级别

前面提到,按照可见性进行划分,隔离性可以分为四种不同的事务隔离级别,下面逐一进行说明。

Read Uncommit(读未提交) :一个事务的修改可以立即被其它事务读取。读未提交的问题是会出现脏读,举例来说:事务1修改表A的数据,未提交,此时事务2读到了修改项,随后事务1进行了回滚。读未提交是最弱的一种隔离级别,在生产环境中,基本上不会使用。

Read Committed(读已提交) :一个事务的修改在事务提交后可以立即被其它事务读取到。读已提交避免了脏读的问题,这也是当前主流的数据库默认的事务隔离级别。

Repeatable Read(可重复读) :一个事务内部,前后多次的查询结果保持一致。回到隔离性的定义:一个事务的操作不受其他事务的影响。可重复读要解决的就是这个问题。在内部实现上,不管是读已提交还是可重复读,都需要借助 Readview 和 Undo 来实现,只是开启 Readview 的时间点有区别。Readview 和 Undo 也是 MySQL 实现 MVCC 的基础,更多细节在后续的文章单独介绍。

Serializable(串行化) :所有的事务,按顺序执行,任意时刻最多只有一个事务在执行。串行化从本质上来说完全限制了并发,在真实环境中一般不会使用。

总结

这篇文章主要从事务入手,简单的介绍了事务的基本特性和实现逻辑,引入了数据库系统中几个非常重要的概念:Undo、Redo、Readview、MVCC。这些概念会在后续的文章中详细说明。