侧边栏壁纸
  • 累计撰写 247 篇文章
  • 累计创建 16 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

MySQL 数据库事务简介与 ACID 特性

kaixindeken
2021-07-13 / 0 评论 / 0 点赞 / 160 阅读 / 2,919 字

数据库事务简介

数据库事务简单而言,就是一连串的数据库操作,这些操作是为了完成某个具体的功能,之所以要引入事务,是因为这些操作要么都执行成功,要么都执行失败,不能部分成功,部分失败(当然,对于只包含单个 SQL 语句的事务,显然也是符合事务概念的约束的)。

为了形象地解释数据库事务,我们举一个烂大街的例子:用户之间的转账。为了与时俱进,我们不用银行转账,而用支付宝钱包转账进行介绍。

假设中午小明和小强跟往常一样一起去公司楼下老娘舅吃饭,两人各点了一份标价 30 块钱的商务套餐,小强占位,小明排队点餐付的钱,由于天天一起吃饭,两人之间有个不成文的约定,吃完饭后谁付的钱另一个人支付宝转账给他。

我们梳理一下这个流程:小明支付宝钱包余额原来有 500 块钱,小强的是 300 块钱,小明付钱之后钱包余额是 500 - 30 x 2 = 440 元,吃完饭后小强要给小明转账 30 块钱,于是小强钱包余额还剩 300 - 30 = 270 元,而此时小明钱包余额则变成 440 + 30 = 470 元。

在整个流程中,如果从数据库世界的角度来看,只有小强给小明转账这一操作涉及到多条 SQL 语句,假设支付宝钱包对应的数据表是 wallets,对应的表结构如下:

CREATE TABLE `wallets` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `username` varchar(100) NOT NULL,
    `balance` int(10) unsigned DEFAULT 0,
    `created_at` timestamp NULL DEFAULT NULL,
    `updated_at` timestamp NULL DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `wallets_username_unique` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

在小强给小明转账的过程中,数据库世界对应的 SQL 操作如下:

UPDATE `wallets` SET `balance` = `balance` - 30 WHERE username = '小强';
UPDATE `wallets` SET `balance` = `balance` + 30 WHERE username = '小明';

这里对业务逻辑进行了简化,实际的逻辑可能更复杂,比如先要读取小强的钱包余额,如果不足 30 元,则不能往后执行扣减操作。我们以简化后的数据库操作进行介绍,对于这两条 SQL 语句而言,显然,它们要么都执行成功,要么都执行失败,不能第一条执行成功,第二条执行失败,这样就会造成小强钱包扣款成功,但小明没有收到转账的尴尬场面,在现实世界,这是不允许的,为了保证这一操作的原子性,需要借助数据库事务对这个流程进行封装。

数据库事务语法

数据表初始化

假设我们已经创建了 wallets 表并初始化了两条记录:

1.jpeg

注:为了避免数据精度问题,我们将余额通过分为单位转化为整型进行处理。

开启事务

现在我们要通过事务模拟转账操作,要使用数据库事务,需要先通过 BEGIN 或者 START TRANSACTION 语句开启事务:

执行 SQL 更新操作

然后我们执行对应的 SQL 更新语句:

1.jpeg

此时,如果我们另外开启一个 MySQL 客户端,是查询不到数据表记录变化的,因为数据更新还没有提交:

1.jpeg

提交事务

为了让 SQL 更新操作生效,必须通过 COMMIT 语句显式提交事务:

1.jpeg

回滚事务

如果在数据库事务操作序列中的某个数据库操作执行失败,则可以通过 ROLLBACK 语句回滚当前事务中前面的所有操作,这样就可以让数据表恢复到数据库事务开启之前的状态:

1.jpeg

以上就是 MySQL 数据库事务的基本语法及其使用。

ACID 特性

基于上述介绍,我们可以将一个数据库事务抽象为具备如下特性:

  • 原子性(Atomicity):即数据库事务中的一系列 SQL 操作是一个不可分割的整体,要么都执行成功,要么都执行失败,不存在部分执行成功部分执行失败的情况。
  • 一致性(Consistency):事务开始前和结束后,数据库的完整性约束和业务逻辑的一致性不能被破坏,事务提交成功后,意味着数据库从一个状态迁移到另一个状态,而事务执行失败回滚后,需要保持原有状态不被破坏。比如在上面的示例中,用户名和对应的钱包 ID 不能被篡改,钱包余额不能是负值,对于数据库的一致性,有些可以基于数据库本身的约束,比如我们设置 balance 字段为非负整数,有些则需要依赖业务代码进行保证,比如对钱包 ID 和用户名的篡改。
  • 隔离性(Isolation):不同事务之间的操作是相互隔离的,事务 A 不应该影响事务 B 的状态和操作,MySQL 数据库事务支持不同的隔离级别(默认是可重复读),并且通过锁机制来保证隔离性,这一块比较复杂,后面我们会详细介绍这一块。
  • 持久性(Durability):事务一旦提交,则其操作结果会永久性的保存下来,MySQL 会通过 redo 日志来保证即便事务提交之后出现宕机故障,也能恢复相应数据。

由于上面四个事务特性的英文首字母连起来正好是一个英文单词 ACID,所以数据库事务的四大特性又被称之为 ACID 特性。一个数据库事务必须同时满足这四个特性,反过来,如果一个 SQL 操作序列同时满足上面四个特性,我们也可以将其称之为一个数据库事务。

0

评论区