事务(transaction):最小的不可再分的工作单元;通常一个事务对应一个完整的业务(例如银行账户转账业务,该业务就是一个最小的工作单元)
事务只和DML语句有关,或者说只有DML语句才有事务。
在事物进行过程中,未结束之前,DML语句不会更改底层数据,它只是将历史操作记录一下,在内存中完成记录。只有在事物结束的时候,而且是成功的结束的时候,才会修改底层硬盘文件中的数据。
在MySQL中,默认情况下事务是自动提交的。
事务的基本要素(ACID)- 原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,同一个事务中的多条语句是不可分割的。
- 一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。
- 隔离性(Isolation):指多线程环境下,一个线程中的事务不能被其他线程中的事务打扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。
- 持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。
-
脏读:指一个线程中的事务读取到了另外一个线程中未提交的数据。比如:线程A读取了线程B更新前的数据,然后B回滚操作,那么A读取到的数据是脏数据。
示例
: 张三的工资为5000,事务A中把他的工资改为8000,但事务A尚未提交。 与此同时,事务B正在读取张三的工资,读取到张三的工资为8000。随后,事务A发生异常,而回滚了事务。张三的工资又回滚为5000。 最后,事务B读取到的张三工资为8000的数据即为脏数据,事务B做了一次脏读。 -
不可重复读:指一个线程中的事务读取到了另外一个线程中提交的
update
数据。具体来说:是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该数据。那么,在第一个事务中两次读数据之间,有可能第二个事务对该数据做了修改,那么第一个事务两次读到的的数据就是不一样的。在一个事务中前后两次读取的结果并不致,导致了不可重复读。 比如:线程 A 多次读取同一数据,线程B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。示例
: 在事务A中,读取到张三的工资为5000,操作没有完成,事务还没提交。 与此同时, 事务B把张三的工资改为8000,并提交了事务。 随后,在事务A中,再次读取张三的工资,此时工资变为8000,两次读取到的结果不一致。 -
幻读:指一个线程中的事务读取到了另外一个线程中提交的
insert
的数据。就好像发生了幻觉一样,比如:明明当前事务查询没有这条结果,理应可以在当前事务进行新增操作,但是却提示记录已存在不能插入相同主键记录,这就是幻读。再比如:第一个事务对表中的数据进行了全表update,同时,第二个事务向表中插入一行新数据。执行后,第一个事务的用户发现表中还有一条记录没有更新成功,就好象发生了幻觉一样,这也是幻读。示例
: 目前工资为5000的员工有10人,事务A读取所有工资为5000的人数为10人。 此时,事务B插入一条工资也为5000的记录。 这时,事务A再次读取工资为5000的员工,记录为11人。此时产生了幻读。
注意:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。
事务相关术语- 开启事务:Start Transaction,任何一条DML语句(insert、update、delete)执行,标志事务的开启
- 结束事务:End Transaction
- 提交事务:Commit Transaction
- 回滚事务:Rollback Transaction
- 悲观锁:假定所有不同事务的处理一定会出现干扰,数据库中最严格的并发控制策略,如果一个事务正在对数据处理,那么在整个事务过程中,其他事务都无法对这个数据进行更新操作,直到该事务释放了这个锁。
- 乐观锁:假定所有不同事务的处理不一定会出现干扰,所以在大部分操作里不许加锁,但是既然是并发就有出现干扰的可能。在乐观锁中当提交更新请求之前,要先去检查读取这个数据之后该数据是否发生了变化,如果有变化,那么你此次的提交就要放弃,如果没有变化就可以提交。