Mysql事务原理
Mysql事务原理
为了解决多事务并发问题,数据库设计了事务隔离机制、锁机制、MVCC多版本并发控制隔离机制、日志机制,用一整套机制来解决多事务并发问题。
事务
MySQL事务是一组SQL语句的集合,它们作为一个工作单位执行。事务主要用于处理操作量大,复杂度高的数据。在同一个事务当中,这些操作最终要么全部执行成功,要么全部失败,不会存在部分成功的情况。
ACID
事务的ACID属性是数据库管理系统在写入或更新数据的过程中,为保证事务是正确可靠的。其具备有原子性、一致性、隔离性、持久性四个属性,简称事务的ACID属性。
- 原子性(Atomicity): 指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。由Undo Log来实现。
- 一致性(Consistency): 指事务必须使数据库从一个一致性状态变换到另外一个一致性状态。换一种方式理解就是:事务按照预期生效,数据的状态是预期的状态。
由其他三个属性以及业务代码正确逻辑来实现。 - 隔离性(Isolation): 指事务内部的操作与其他事务是隔离的,并发执行的各个事务之间不能互相干扰。由锁机制以及MVCC机制来实现。
- 持久性(Durability): 指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。由redo log来实现。
并发带来的事务问题
在并发场景下,MySQL事务可能会带来更新丢失、脏读、不可重复读、幻读问题。
- 更新丢失(Lost Update)或脏写: 两个或者多个事务同时选择同一行数据,都基于最初选定的值更新该行,由于每个事务都不知道其它事务的存在,就会发生更新丢失的问题。最后提交的更新覆盖了之前其它事务所做的更新。
- 脏读(Dirty Reads): 一个事务正在对一条记录进行修改,在这个事务完成并提交前,这条记录的数据就处于不一致的状态。此时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些“脏”数据,并依据此做了进一步的处理,就会产生对未提交的数据的依赖关系。
- 不可重复读(Non-Repeatable Reads): 一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了。
- 幻读(Phantom Reads): 一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据。
事务隔隔离级别
脏读、不可重复读、幻读其实都是数据库读一致性问题,可以由数据库提供的隔离机制来解决。数据库提供了读未提交、读已提交、可重复读、可串行化四种隔离机制。
- 读未提交(Read Uncommitted): 在读未提交隔离级别下,事务A可以读取到事务B修改过但未提交的数据。可能发生脏读、不可重复读和幻读问题,一般很少使用此隔离级别。
- 读已提交(Read Committed): 在读已提交隔离级别下,事务B只能在事务A修改过并且已提交后才能读取到事务B修改的数据。读已提交隔离级别解决了脏读的问题,但可能发生不可重复读和幻读问题,一般很少使用此隔离级别。
- 可重复读(Repeatable Read): 在可重复读隔离级别下,事务B只能在事务A修改过数据并提交后,自己也提交事务后,才能读取到事务B修改的数据。可重复读隔离级别解决了脏读和不可重复读的问题,但可能发生幻读问题。
- 可串行化(Serializable): 各种问题(脏读、不可重复读、幻读)都不会发生,通过加锁实现(读锁和写锁)。
隔离级别/事务问题 | 更新丢失 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|---|
读未提交 | 可以 | 不可以 | 不可以 | 不可以 |
读已提交 | 可以 | 可以 | 不可以 | 不可以 |
可重复读 | 可以 | 可以 | 可以 | 不可以 |
可串行化 | 可以 | 可以 | 可以 | 可以 |
数据库隔离级别越严格,并发问题越小,但是付出的代价也就越大,因为事务隔离实际上就是使事务在一定程度上“串行化”,这与“并发”是冲突的。同时,不同应用对读一致性和事务隔离程度的要求也是不同的。
Mysql默认的事务隔离级别是可重复读。对于RC和RR的选择,如果对并发要求高,可以选择RC,如果对同一时期的数据有要求,可以选择RR。
面试题:查询操作方法需要使用事务吗?
答:如果只查询一条语句,可以不加事务。
如果执行多条查询语句,假设现在使用的隔离级别是RR,最好加上事务。对于报表数据,如果使用RR,添加事务可以确保查询出来的数据是同一时间的数据,而对于RC或者不加事务,获取到的值一直都是最新的。
假设报表数据处理耗时1S,这期间有可能已经执行了很多的数据操作了,RR通过事务可以保证查出来的数据集是同一时间的,而RC或者不加事务有可能已经是不同时期的数据集。
大事务的影响
在使用事务的时候,应该尽量避免编写过大的事务,大事务在执行时可能会存在以下问题:
- 高并发场景下,由于事务操作是需要占用服务器链接的,大事务执行耗时过多,数据库连接池容易被撑爆
- 事务锁定的数据太多,容易造成大量的阻塞和锁超时
- 执行耗时过多,容易造成主从延迟
- 回滚需要的时间过长
- undo log膨胀
- 容易死锁
事务使用原则
- 将查询等数据准备操作放到事务外执行
- 事务中应避免远程调用或者远程调用设置超时,防止事务等待时间太久
- 事务中避免一次性处理太多数据,如果可以应拆分多个事务分次处理
- 更新等设计加锁操作的应尽可能的放在事务靠后的位置
- 能异步处理的尽量异步处理
- 应用端(业务代码)保证数据一致性,非事务执行