一. 引出事务
在日常开发中, 很多操作不是通过一个SQL完成的, 往往需要多个SQL配合完成
但是当多个SQL操作时, 如果中间出现了特殊的情况(程序崩溃, 系统崩溃, 网络断开, 主机掉电......), 那么可能就会出现, 前面的SQL执行成功, 后面的SQL执行失败了
考虑一个场景: 转账
如果a要给b转账500, 那么a的余额-500, b的余额+500, 那么如果在a的余额-500之后, 就出现了上述的特殊情况, 那么这个钱就消失了
这个时候就要使用到事务
二. 概念
事务, 指把多个操作, 打包成一个整体, 就能够保证, 这个整体要么都执行成功, 要么就一个都不执行, 有效避免, 部分执行, 部分未执行, 产生的一些"中间状态引起的问题"
其实, 如果中间出现问题, 并不是真的一个都没有执行, 事务中的若干个SQL必然是要一条一条执行的, 但是事务能够保证, 当执行到某一条的时候如果出现问题了, 数据库就能够自动把前面的sql造成的影响, 给恢复回去, 恢复完成, 看起来就好像一条sql都没执行的样子
像这样的"翻新"操作, 称为"回滚"(rollback)
像这样的"翻新"操作, 称为"回滚"(rollback)
为了实现回滚机制, 数据路在执行事务的时候, 记录日志
(数据库的日志是写入到硬盘的文件中)
当事务最终都执行完毕, 中间没有差错, 那么这些记录内容就可以不要了
但是如果执行事务的过程中, 出现问题了, mysql就可以根据日志中记录的内容, 来进行恢复操作
1)之前进行了新增操作, 就把数据删除掉
2)之前进行了删除操作, 就把数据新增回来
3)之前进行了修改操作, 就把数据改回去
4)之前进行了查询操作, 不需要任何恢复行为
三. 事务的基本特性
1. 原子性
上述把多个操作打包成一个整体, 有回滚机制, 能够出发还原的这种特性, 称为"原子性"
2. 一致性
执行事务之前和执行事务完毕之后, 数据是一致的, 不会出现对不上的情况, 其实和回滚是有关的, 一旦触发回滚了, 回滚回去的数据得是对的, 如果顺利执行没有触发回滚, 数据也是符合要求的
3. 持久性
但凡提到持久性, 就要想要: 把数据存储在硬盘上
此处的持久, 指的是程序重启/主机重启, 数据仍然存在
执行事务产生的修改, 会在硬盘上持久保存
4. 隔离性
隔离性主要考虑的是, 数据库并发执行事务时, 产生的情况
并发是指: 多个客户端, 同时给服务器, 发起事务,
此时就需要数据库服务区都能够给处理
如果同时处理, 又可能会出现问题:
1) 脏读问题
数据库中, 如果有事务A和事务B, 事务A针对某个表做出了一些修改, 但是在事务A提交之前(即事务完毕之前), 事务B就对这里的数据进行了读取, 最终就可能出现A后续的操作又把上述数据进行了修改, 导致最终B读到的数据和A提交的数据是不同的, 于是就出现了"脏读问题"
为了避免这样的情况出现, 我们可以针对 "写操作"加锁
本来是执行事务A的时候, B事务可以执行, 但是引入写加锁后, 执行A的过程中, B就不能执行了, 要等待
但是这样就相当于降低了"并发能力", 也就会降低数据库服务器的处理效率, 同时提高了"隔离性", 也提高了数据库的准确性
并发执行事务的过程中, 相互之间影响越小, 隔离性就越高, 影响越大, 隔离性就越低
2) 不可重复读问题
存在三个事务ABC, 事务A针对数据进行修改, 提交, 接下来事务B进行读取数据, 在B执行的过程中, 又有一个事务C, 又针对数据进行修改, 而此时事务B还在对数据进行读取, 就会出现前后读取的数据不一致
为了避免这样的情况出现, 我们可以针对" 读操作"加锁
本来是执行事务B的时候, C事务可以执行, 但是引入读加锁后, 执行B的过程中, C就不能执行了, 要等待
同样这样就相当于降低了"并发能力", 也就会降低数据库服务器的处理效率, 同时提高了"隔离性", 也提高了数据库的准确性
3) 幻读问题
事务A先修改并提交了数据, 事务B进行读数据, 此时事务C, 没有修改B读的数据, 但是给对应的表进行了新增数据/删除数据等操作, 导致事务B中, 读到的数据集不同, 就会出现"幻读问题"
解决幻读的方式, 是 "串行化", 时所有的事务都严格的按照"一个接一个"的方式进行, 完全没有并发了
此时执行的效率是最低的, 隔离性也是最高的, 数据也是最准确的
总结一下:
1)脏读 ==> 写加锁
2) 不可重复读 ==> 读加锁
3) 幻读 ==> 串行化
mysql事务的隔离性具体是怎么实现呢? mysql给程序猿提供了四个隔离级别, 可以在mysql配置文件中进行设置
1) read uncommitted: 允许读取其他事务未提交的数据
2) read committed: 不允许读取其他事务未提交的数据
3) repeatable read: 针对读操作和写操作都加锁了
4) serializable(串行化): 所有事物都是串行执行的
在实际开发中, 我们要根据具体的应用场景来选择不同的隔离级别
四. 使用事务
1) 开始事务:
start transaction;
2) 回滚或提交:
rollback/commit;
例:
start transaction;
-- 阿里巴巴账户减少2000
update accout set money=money-2000 where name = '阿里巴巴';
-- 四十大盗账户增加2000
update accout set money=money+2000 where name = '四十大盗';
commit;
中间的SQL语句, 就被打包成一个整体了