一.事务的定义
事务是"一组原子性的SQL命令",或者说一个独立的工作单元。如果数据库引擎能够成功地对数据库应用该组SQL的全部语句,那么就执行该组SQL。如果其中有任何一条语句因为崩溃或其他原因无法执行,那么所有的语句都不会执行。也就是说,事务内的语句,要么全部执行成功,要么全部执行失败。
二.事务四大特征(ACID)
- 原子性:
原子性是指事务的原子性操作,对数据的修改要么全部执行成功,要么全部失败 - 一致性:
一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。 - 隔离性:
一个事务所做的修改在最终提交以前,对其他事务是不可见的; - 持久性:
一旦事务提交,则其所做的修改就会永久保存到数据库中;
三.事务四大特性之一————隔离性
隔离性有隔离级别(4个)
- 读未提交会读到另一个事务的未提交的数据,产生脏读问题
- 读已提交则解决了脏读的,出现了不可重复读,即在一个事务任意时刻读到的数据可能不一样,可能会受到其它事务对数据修改提交后的影响,一般是对于update的操作。
- 可重复读解决了之前不可重复读和脏读的问题,但是由带来了幻读的问题,幻读一般是针对insert操作
- 串行化对于同一行记录,写会加“写锁”,读会加“读锁”,当出现锁冲突时,后访问的事务需要等前一个事务执行完成,才能继续执行。
在实际应用中,读未提交在并发时会导致很多问题,而性能相对于其他隔离级别提高却很有限,因此使用较少。可串行化强制事务串行,并发效率很低,只有当对数据一致性要求极高且可以接受没有并发时使用,因此使用也较少。因此在大多数数据库系统中,默认的隔离级别是读已提交(如 Oracle)或可重复读(后文简称RR)
四.事务的并发问题
-
丢失更新是另一个锁导致的问题,简单来说其就是一个事务的更新操作会被另一个事务的更新操作所覆盖,从而导致数据的不一致。 例如:
1.事务T1将行记录r更新为v1,但是事务T1并未提交 2.与此同时,事务T2将行记录r更新为v2,事务T2未提交。 3.事务T1提交。 4.事务T2提交。
解决更新丢失办法:
悲观锁:在更新前读数据时加锁,使其他事务无法对其数据更新,待当前事务更新后释放锁。 乐观锁:在数据行增加版本号,更新时更新条件传入版本号
-
脏读:当前事务(A)中可以读到其他事务(B)未提交的数据(脏数据),这种现象是脏读
-
不可重复读:一事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
脏读与不可重复读的区别在于:前者读到的是其他事务未提交的数据,后者读到的是其他事务已提交的数据。举例如下:
解决不可重复读的方法:在SQL语句中加锁,也许是纪录锁,间隙锁,又或者是表锁。
这样可避免当前事务读起到其他事务修改的数据,因为其他事务已经被阻塞了,只有当前事务释放了锁,
其他事务才能对其数据修改。
- 幻读:是指在同一事务下,连续执行两次同样的SQL语句可能导致不同的结果,第二次的SQL语句可能会返回之前不存在的行。举例如下
InnoDB 默认的隔离级别是RR(可重复读)。需要注意的是,在 SQL 标准中,RR是无法避免幻读问题的,但是 InnoDB
实现的RR避免了幻读问题。
MVCC
RR解决脏读、不可重复读、幻读等问题,使用的是 MVCC:MVCC 全称Multi-Version Concurrency Control,即多版本的并发控制协议。下面的例子很好的体现了 MVCC 的特点:在同一时刻,不同的事务读取到的数据可能是不同的(即多版本)—— 在 T5 时刻,事务A和事务C可以读取到不同版本的数据。
总结
MVCC 最大的优点是读不加锁(乐观锁为基础),因此读写不冲突,并发性能好。InnoDB 实现 MVCC,多个版本的数据可以共存,主要是依靠数据的隐藏列(也可以称之为标记位)和undo log。其中,数据的隐藏列包括了该行数据的版本号、删除时间、指向undo log的指针等等;当读取数据时,MySQL 可以通过隐藏列判断是否需要回滚并找到回滚需要的undo log,从而实现 MVCC;隐藏列的详细格式不再展开。