刷过八股文的应该都知道mysql事务的特性有4个 原子性,隔离性,一致性,持久性。但是具体到解释就一塌糊涂了,这里附上自己的理解,相当于给自己提个醒。

什么是事务

我们需要保证原子性隔离性一致性持久性的一个或多个数据库操作称之为一个事务

什么是原子性隔离性一致性持久性

我们的例子里需要一张表,表示某人的银行账户中的财富数量

CREATE TABLE account (
    id INT NOT NULL AUTO_INCREMENT COMMENT '自增id',
    name VARCHAR(100) COMMENT '客户名称',
    balance INT COMMENT '余额',
    PRIMARY KEY (id)
) Engine=InnoDB CHARSET=utf8;

其中,有两条数据,用户A有11元,用户B有2元,数据如下

id

name

balance

1

用户A

11

2

用户B

2

原子性

我们使用转账来举例子,当用户A向用户B转账10元,那么数据库会执行下面两个update语句

UPDATE account SET balance = balance - 10 WHERE id = 1;
UPDATE account SET balance = balance + 10 WHERE id = 2;

这时候就有问题了,如果第一条语句执行完后,服务器就断电了,那么虽然用户A的钱已经减了,但是用户B并没有收到,这时候就用到了原子性,也就是让这两条语句不可分割,要么全成功,要么全失败,不能存在转了一半的这种情况。

隔离性

假如用户A使用两台ATM机,向用户B进行了两次5元的转账,那么正确的结果应该是用户A剩余1元,用户B剩余12元。

但是在数据库中,这个操作可能会复杂一点,我们把一次转账简化为以下几个步骤:

  • 步骤一:读取用户A的账户余额 read(A)

  • 步骤二:用户A的余额减去5 A=A-5

  • 步骤三:用户A的余额写入数据库 write(A)

  • 步骤四:读取用户B的账户余额 read(B)

  • 步骤五:用户B的账户余额加5 B= B+5

  • 步骤六:将用户B的账户余额写入数据库 write(B)

正常情况下 一次转账结束后再进行下一次转账是没有问题的,但是避免不了并发转账的情况,比如说用户A在两台ATM机上同时操作,此时数据库的步骤就可能变成了

ATM1:read(A) = 11

ATM2:read(A) = 11

ATM1:A=A-5 此时A=6

ATM1:write(A) 此时A=6

ATM1:read(B) 此时B=2

ATM1:B=B+5 此时B=7

ATM1:write(B) 此时B=7

ATM2:A=A-5 此时A=6

ATM2:write(A) 此时 A=6

ATM2:read(B) 此时 B=7

ATM2:B=B+5 此时B=12

ATM2:write(B) 此时B=12

流程完毕后 A账户里有6元 B账户里有12元 得了 银行亏死

所以这肯定是有问题的,我们需要保证每次转账,或者说每条数据的状态变化的时候,不能影响本次状态转换。有点类似于java中多线程的加锁方式,当前线程正在修改这条数据的时候,其他线程就不要修改这条数据了。

一致性

一致性就是保证数据的合理,是一个追求的结果,原子性和隔离性就是为了保证一致性。

持久性

当数据状态变化之后,这个结果要永久保留。比如不能转账成功后,服务器断电重启,发现数据没了。

开启事务

在mysql中 BEGIN 和 START TRANSACTION 都可以开启事务 START TRANSACTION语句后可以跟随几个修饰符:

  • READ ONLY:标识当前事务是一个只读事务,也就是属于该事务的数据库操作只能读取数据,而不能修改数据。

  • READ WRITE:标识当前事务是一个读写事务,也就是属于该事务的数据库操作既可以读取数据,也可以修改数据。

  • WITH CONSISTENT SNAPSHOT:启动一致性读

提交事务

COMMIT

手动回滚事务

ROLLBACK

保存点

如果你开启了一个事务,并且已经敲了很多语句,忽然发现上一条语句有点问题,你只好使用ROLLBACK语句来让数据库状态恢复到事务执行之前的样子,然后一切从头再来,总有一种一夜回到解放前的感觉。所以数据库提出了一个保存点(英文:savepoint)的概念,就是在事务对应的数据库语句中打几个点,我们在调用ROLLBACK语句时可以指定会滚到哪个点,而不是回到最初的原点。定义保存点的语法如下:

SAVEPOINT 保存点名称;

当我们想回滚到某个保存点时,可以使用下边这个语句(下边语句中的单词WORKSAVEPOINT是可有可无的):

ROLLBACK [WORK] TO [SAVEPOINT] 保存点名称;