日常开发中,很多操作,不是通过一个SQL就能完成的,往往需要多个SQL配合完成
当执行多个SQL操作的时候,如果中间出现了特殊的情况(程序崩溃,网络断开,主机掉点了...),可能就会出现,前面的SQL执行成功,后面的SQL执行失败了,这时就需要事务
事务:把多个操作,打包成一个整体
使用事务
sql
start transaction;
...
rollback/commit;
事务的基本特性
- 原子性
- 一致性
- 持久性
- 隔离性
原子性
能够保证,这个整体要么都执行成功,要么就一个都不执行,能够有效避免,部分执行,部分未执行,产生的一些"中间状态引起的问题",把多个操作,打包成一个整体,称为"原子性"
(这里并非一个都不执行,而是事务可以保证当执行到某一条的时候出现问题了,数据库就能自动的把前面SQL造成的影响,给恢复回去,恢复如初,看起来就好像一条SQL都没执行的样子,这里的"翻新"的操作,称为**"回滚"(rollback)**)
事务的原子性 就是通过回滚机制来保证的,为了实现回滚机制,数据库会在执行事务的时候,记录日志,println...就是日志,数据库的日志是写入到硬盘的文件中了,当事务最终都执行完毕,中间没有差错,这些记录就可以不要了,但是如果执行事务的过程中出现问题了,MySQL就可以根据日志中记录的内容来进行恢复操作了:
之前进行的新增 操作,就把数据删除掉
之前进行的删除 操作,就把数据新增上来
之前进行的修改操作,就把数据改回去
之前进行的查询操作,不影响,不需要进行任何恢复行为
- 程序崩溃:应用程序(Java写的程序),数据库是正常的,直接进行回滚操作即可
- 系统崩溃:MySQL服务器崩溃,挂了的时候无法立即进行回滚,数据库重新启动的时候,就能够发现上次有个事务出问题了,按照之前记录日志进行回滚
- 网络断开:与程序崩溃一致,等待网络连接,直接进行回滚
- 主机断电:与系统崩溃处理方法一致
一致性
执行事务之前,和执行事务完毕之后数据是一致的
一致性也与回滚有关,一旦触发了回滚,回滚回去的数据得是对的,如果顺利执行没有触发回滚,数据也是要符合要求的
持久性
把数据存储在硬盘上,此处的持久,程序重启/主机重启,数据依然能存在(不仅仅是数据库,但凡见到"持久性"都是这个意思)
不丢失 = 持久性 + 一致性
执行事务对数据库产生的修改,就会在硬盘上持久保存,重启之后仍然存在
隔离性
隔离性描述的是数据库并发(多个客户端,同时给服务器,发起事务)执行事务时,产生的情况比较复杂,若是多个客户端操作的是同一张表,则要保证:尽可能保证不出错,并且提高效率,隔离性就是要一起处理的情况下不出错 + 效率提高
脏读问题
MySQL服务器读到的是个临时数据,临时数据可能会被随时修改掉
数据库中,如果有事务A和事务B,事务A针对某个表做出了一些修改,在事务A提交之前事务B就对这里的数据进行了读取,最终就可能出现A后续的操作又把上述数据进行了修改,导致最终B读到的数据和A提交的数据,是不同的
如何解决脏读问题?对写操作加锁,也就是说只有一个事务才能对某张表修改,写操作进行时其他事务不得进行写操作,这种方式就可以解决脏读问题,引入写加锁之后,执行A的过程中,B就不能执行了,要等待,这就相当于降低了"并发能力",也就会降低数据库服务器的处理效率,提高了"隔离性",也提高了数据的准确性
不可重复读问题
存在三个事务ABC,事务A针对数据进行修改,提交,接下来事务B进行读取数据(事务B多个SQL都要进行读操作),在执行B的过程中又有一个事务C,又针对数据进行了修改,就会使B里面的不同读操作,读出来的结果不一样
如何解决不可重复读问题?在写操作加锁的情况下,给读操作也加锁
- 写操作加锁:我写的时候,别人不能读
- 读操作加锁:别人读的时候,我也不能写
此时再次引入锁,就会使"并发程度"进一步降低,效率也随之降低,"隔离性"又进一步提高,数据的准确性也会提高,这个时候,ABC都不能并发了
幻读问题
此时已经进行了读操作和写操作,幻读问题和不可重复读问题类似,事务B读的过程中,事务C没有修改数据内容,而是修改了"结果集",导致B内部不同的读操作读到的结果集合不同
如何解决幻读问题?串行化:所有的事务都是串行执行的,此时就不会有这样的问题
MySQL事务的隔离性具体是怎么体现的?
- read uncommitted:允许读取其他事务未提交的数据 =>
脏读 + 不可重复读 + 幻读 并发程度最高,隔离性最低
- read committed:只能读取其他事务提交后的数据 =>
解决了脏读,存在不可重复读 + 幻读 并发程度降低,隔离性提高
- repeatable read:针对读操作和写操作都加锁了 =>
解决了脏读 + 不可重复读,存在幻读 并发程度又降低,隔离性又提高
- 串行化(serializable):所有的事务都是串行执行的 =>
解决了脏读 + 不可重复读 +幻读 并发基本没有,隔离性最高
修改MySQL配置文件my.ini,使服务器处于某个隔离级别中来运行;比如先做做一个和钱有关的系统,可以设置成串行化;做一个短视频点赞系统,就可以设置成读未提交,追求最大的效率
以上为事务常见的特性以及重要问题解决方案,下一篇博客介绍JDBC!