引言
事务就是能够把多个SQL给打包在一起,变成一个整体。因为有些操作是需要作为一个整体来完成的,比如转账操作,A给B转账100,那么A的账户余额就会减100,B的账户余额就会加100。如果在A转账的过程中,因为某些原因只执行到一半,那么A的账户减100,B的账户却没有加100,显然这种是不正确的状态。事务就是要保证避免出现这种不科学的状态,把整个转账操作打包成一个整体,要么全部执行,要么一个都不执行。关于事务把操作打包成一个整体,就叫做“原子性”,这也是事务最核心的特性。
关于在执行过程中出错,就一条都不执行,这里并不代表一条都没有执行,而是出错之后,自动恢复到执行之前的样子,在外界看来就是一条都没执行的样子。这里面涉及到了一个关键操作,“回滚”。回滚就是把执行过的操作逆向恢复回去,类似于ctrl+Z。数据库会把执行的每个操作都记录下来,如果某个操作出错,就会把事务中前面的操作进行回滚。根据之前进行的操作,进行逆向操作,前面是插入,回滚就删除,前面是删除,回滚就插入,前面是修改,回滚就改回去。关于保存操作记录是有很大开销的,一般地就把正在执行的事务的操作给记录下来。
事务的使用
1.开启事务:start transaction;
2.执行多条SQL语句
3.回滚或提交:rollback/commit; (开启事务之后,中间的这些sql不会立即就执行,而是先等commit再统一执行,保证原子性,rollback主动进行回滚)
rollback即是全部失败,commit即是全部成功。
示例:
start transaction;
-- A账户减少100
update accout set money=money-100where name = 'A';
-- B账户增加100
update accout set money=money+100where name = 'B';
commit;
数据库的事务主要有四大特征:
1.原子性:这是事务的初心,⼀个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执⾏过程中发⽣错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执⾏过⼀样。
2.一致性:事务执行前执行后都必须是数据合法的状态。一致性是事务要解决的问题,也是要依赖原子性来实现的,在事务开始之前和事务结束以后,数据库的完整性没有被破坏。
3.持久性:事务产生的修改,都是会写入硬盘的,即使程序重启或主机重启或掉电,事务都是可以正常工作的,保证修改是有效的。
4.隔离性:一个数据库服务器同时执行多个事务的时候,事务之间的相互影响程度。数据库允许多个并发事务同时对其数据进⾏读写和修改的能⼒,隔离性可以防⽌多个事务
并发执⾏时由于交叉执⾏⽽导致数据的不⼀致。事务隔离分为不同级别,包括读未提交(
Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串⾏化
(Serializable)。
谈谈脏读、不可重复读以及幻读问题
关于MySQL提供的四个隔离级别:
mysql服务器要同时给多个客户端提供服务,此时多个客户端之间,可能会同时发起事务,尤其是这多个事务在操作同一个数据库的同一个表的时候,就很有可能引起一些麻烦。如果隔离性越高,就意味着事务之间的并发程度越低,执行效率是越慢,但是数据的准确性是越高的。如果隔离性越低,就意味着事务之间的并发程度越高,执行效率是越快,但是数据的准确性是越低的。
MySQL提供的四个隔离级别,根据实际需求选择不同的隔离级别。
1.如果事务B正在写数据,然而事务A也在读取数据,因为针对的是同一份数据,那么这种情况就属于“脏读”问题,在这个场景下,事务间是完全并发的,没有任何限制,这个级别就是read uncommitted。为了解决“脏读”问题,就是降低并发性,提高隔离性,也就是给“写操作”加锁。比如事务B在写数据的时候,事务A是不能读取的。
2.为了解决“脏读”问题,给“写操作”加锁了,所以A只能在B写入完成之后才能读取,这个级别就是read committed。但是又引入了新的问题,当A正在读取数据的时候,这个时候B又开始写入数据了,导致A读取数据一半的时候后面的数据就变了。在一个事务中,连续两次读取的数据的结果不一致,这就是“不可重复读”问题。为了解决“不可重复读”问题,那么给“读操作”也加锁了,在A读取数据的时候,是不能写入数据的。这两个事务之间的并发程度被进一步降低了,隔离性进一步的提高了,运行速度进一步降低了,但是数据准确性进一步提高了。
3.为了解决“不可重复读”问题,给“写操作”和“读操作”都加锁了,这个级别就是repeatable read,针对同一个代码文件来说,事务B在写入数据的时候,事务A不能进行读取,同样,事务A在进行读取数据的时候,事务B也是不能进行写入数据的。但是随着又引入了新的问题,既然B在写入数据的时候,A不能进行读取,同样A在读取数据的时候,B不能写入数据,但是他们都不是一直傻等着的,他们各自也会去做其他的事情,比如新增或删除其他文件。此时,虽然读取的代码内容没有变,但是会发现文件的数量有变化,例如这种情况,在同一个事务中,两次读到的结果集不相同,这就是“幻读”问题。为了解决“幻读”问题,就只有实现串行化,完全舍弃并发,比如B在写入数据的时候,A什么都不干,就干等着,这个级别就是serializable。
总结:MySQL提供的四个隔离级别
1.read uncommitted(读未提交):不做任何限制,事务之间都是随意并发执行的,并发程度最高,隔离性最低,会产生脏读+不可重复读+幻读。
2.read committed(读已提交):对写操作加锁了,并发程度降低了,隔离性提高了。解决了脏读问题,仍然存在不可重复读+幻读。
3.repeatable read(可重复读):对写和读都加锁了,并发程度又降低了,隔离性又提高了.解决了脏读+不可重复读,可能存在幻读问题。
4.serializable(串行化):严格串行化,并发程度最低(串行执行的)隔离性最高。解决了脏读+不可重复读+幻读问题,执行速度最慢的。
MySQL的隔离级别是通过MySQL的配置文件来进行调整的,在开发中,根据实际问题的需求场景来决定选用哪一个级别。其中MySQL的默认级别是repeatable read。