小王:我肯定付款了啊,不然怎么下单。
老板说:我没收到钱啊。你把付款的截图发给我。
小王说:我吃饭还能不付钱吗,你等着。
于是小王给老板截图了,老板拿着截图去找了美团技术,美团技术一查,转账失败。跟老板说不好意思,今天这代码是实习生写的,我们马上开除他,稍后转给你。这时候老板一颗悬着的心才放下,可不能一天就卖一份水饺还没收到钱,这不亏大了呢!
以上纯属虚构,没有诋毁美团实习生的意思。
从上面的问题看,付款成功了,转账失败了,这时候用户吃到了饭,但是老板没收到钱。放在正常的堂食,你不先付款,估计人儿就的赶你出去,一手交钱一手交货买卖不变的道理。
我们引申出一个概念:最小操作单元。即我们人为定义了一个业务场景,这个场景中的操作要么全部成功,要么全部失败。
英语原文中把这种最小操作单元定义为:transaction ,在英语中的解释是:
an occasion when someone buys or sells something, or when money is exchanged or the activity of buying or selling something:
- a business transaction
- Each transaction at the foreign exchange counter seems to take forever
- We need to monitor the transaction of smaller deals.
通俗的说就是我们做某事所发生的这个时机或这个场景,代指这整个的发生过程。在 MySQL 中我们把 transaction 翻译为 事务,个人感觉中文意思总和英文有点不搭。
上面这个例子中我们可以了解到 transaction 存在的主要意图:
- 在最小操作单元中保持稳定的操作,即使在故障时也能恢复到操作之前的状态保持数据一致性。
- 保持各个最小操作单元之前互相隔离,以防止互相交互产生的覆盖性错误。
一般需要事务来控制的场景发生在:
更新–插入–选择–插入–
即一个最小操作单元中保持两个及以上的非查询操作。
事务结束的两种可能方式:
- commit:提交最小操作单元中的所有操作。
- terminate:操作终止,最小操作单元中所有修改无效。
数据库操作的环境:
- 共享-多用户并发访问
- 不稳定-潜在的硬件/软件故障
事务所需环境:
- 不共享 - 一个事务内的操作不受其他事务影响
- 稳定 - 即使面对系统故障,当前事务的操作也能保留现场
一个事务一旦开始,则必须确保:
- 所有操作必须可回溯
- 所有操作对后续操作的影响必须是可见的
一个事务开始的过程中必须确保:
在该事务结束之前其他事务看不到它的结果。
如果事务中止:
必须确保当前事务所有可能影响数据一致性的操作都会被清理。
如果系统出现故障:
必须确保重新启动时所有未提交的事务都会被清理。
针对以上事务操作过程中可能会出现的问题,抽象出事务如果满足以下条件,则可以保证数据完整性:
- Automicity(原子性)
要么事务中的所有任务都必须发生,要么都不发生。
- Consistency(一致性)
每个事务都必须保留数据库的完整性约束(已声明的一致性规则)。它不能使数据处于矛盾状态。在执行期间,一系列数据库操作不会违反任何完整性约束。
- Isolation(隔离性)
两个同时进行的事务不能互相干扰。交易中的中间结果必须对其他交易不可见。其他一系列数据库操作无法看到一系列数据库操作的中间状态。
- Durability(持久性)
已完成的事务以后不能中止或放弃其结果。它们必须在崩溃后通过(例如)重新启动DBMS持续存在。保证已提交的一系列数据库操作将永久保留。
特意查证了一下,关于事务四大特性的提出最早是在 1983 年由 Andreas Reuter 和 Theo Haerder 两位关系型数据库研发的鼻祖在论文:Principles of transaction-oriented database recovery
中提出。论文链接,感兴趣的可以下载来看看。
事务的 ACID 特性概念简单,但不是很好理解,主要是因为这几个特性不是一种平级关系:
- 只有满足一致性,事务的执行结果才是正确的。
- 在无并发的情况下,事务串行执行,隔离性一定能够满足。此时只要能满足原子性,就一定能满足一致性。 在并发的情况下多个事务并行执行,事务不仅要满足原子性,还需要满足隔离性,才能满足一致性。
- 事务满足持久化是为了能应对数据库崩溃的情况。
InnoDB 如何实现事务
鉴于 MyISAM 引擎不支持事务,支持事务的引擎只有 InnoDB,所以下面关于事务的讲解都是基于 InnoDB引擎。
在 InnoDB引擎中实现事务最重要的东西就是日志文件,保证事务的四大特性主要依靠这两大日志:
- redo log :保证事务持久性
- undo log:回滚日志,保证事务原子性
两大日志系统分别保证了持久性和原子性,另外还有两大特性是通过什么来保证的呢?
一致性 和 隔离性 是通过 MVCC 机制 和 锁机制来一起控制。先提前介绍,后面我们详解讨论。
典型的事务操作会遵循如下流程:
start transaction;
… # do your business
commit;
start transaction
标识事务的开始,直到遇到 commit
才会提交事务。在该事务过程中如果出现问题,会自动调用 rollback 逻辑回滚该事物已完成的 sql。
非显式开启事务
MySQL 中默认采用的是自动提交的模式:
mysql > show variables like ‘autocommit’;
±-----------------±------+
| Variable_name | Value |
±-----------------±------+
| autocomment | ON |
±-----------------±------+
自动模式下,你无需显式的输入 start transaction
作为开头和使用 commit
作为结尾来标识一个事务。每个sql 语句都会被作为一个事务提交。
当然你也可以关闭自动提交事务机制:
mysql > set autocommit = 0;
需要注意的是:autocommit
参数的修改指只针对当前连接,在一个连接中修改该属性并不会影响别的连接。
不被 autocommit 影响的操作
MySQL 中提供了一些不会被 autocommit 属性值所影响的特殊指令,这些指定即使在事务中执行,他们也会立刻执行而不是等到 commit 语句之后再提交,这些特殊指令包括:DDL(create table / drop table / alter table)
、lock tables
等等。
我们探讨事务到底在探讨什么?
事务的定义我们已经了解,无非就是把几个有上下文关联的 sql 放在一起操作要么全部成功,要么全部失败。道理很简单,那我们分析这么多到底在分析什么呢?貌似难的点不在于打包执行,在于如果让这些打包命中不互相影响&