MySQL事务
刷过八股文的应该都知道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元,数据如下
原子性
我们使用转账来举例子,当用户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 保存点名称;
当我们想回滚到某个保存点时,可以使用下边这个语句(下边语句中的单词WORK和SAVEPOINT是可有可无的):
ROLLBACK [WORK] TO [SAVEPOINT] 保存点名称;