什么是事务?
在MySQL中,事务(Transaction)是一组要么全部执行成功、要么全部不执行的SQL语句序列。事务主要用于确保数据的一致性和完整性,特别是在多个用户并发访问和修改数据库时。事务具有四个关键特性,通常称为ACID特性:
-
原子性(Atomicity):
- 原子性意味着事务是一个不可分割的工作单元。事务中的所有操作要么全部完成,要么全部不执行。如果事务中的某个操作失败,那么事务中已经完成的操作必须被回滚(撤销),使数据库返回到事务开始之前的状态。
-
一致性(Consistency):
- 一致性确保事务将数据库从一个一致状态转换到另一个一致状态。事务执行前后,数据库中的数据必须满足所有定义的完整性约束、触发器、级联回滚等规则。
-
隔离性(Isolation):
- 隔离性保证事务并发执行时,一个事务的内部操作对其他并发事务是隔离的。这意味着一个事务的中间状态对其他事务是不可见的,除非该事务已经提交。MySQL支持几种隔离级别,包括读未提交(READ UNCOMMITTED)、读已提交(READ COMMITTED)、可重复读(REPEATABLE READ)和串行化(SERIALIZABLE)。
-
持久性(Durability):
- 持久性确保一旦事务提交,它对数据库的影响是永久的,即使系统发生崩溃。这意味着事务一旦提交,其修改的数据必须被永久保存到数据库中,不会被丢失。
为什么要有事务?
多用户是可以并发访问数据库的,这时数据库中的数据就相当于是临界资源,这时使用者必须写一些无关的代码来保证线程安全,使用起来很麻烦。所以,事务就是该部分的封装。访问数据库时,事务能够简化编程模型。本质上事务是为应用层服务的。
MySQl中一定有大量的事务,这些事务需要被管理。
如何管理?
先描述,再组织。
事务的本质就是一个类。
事务的版本支持
在使用事务时,应确保数据库引擎支持事务。例如,InnoDB是MySQL的默认存储引擎,支持事务;而MyISAM则不支持事务。
show engines;
事务的提交方式
- 手动提交
- 自动动提交
- 查看事务提交方式
- 修改提交方式
事务的常见操作
注意下面的操作的隔离级别都是读未提交(read uncommitted)
-
创建测试表
create table if not exists account(
id int primary key,
name varchar(50) not null default '',
blance decimal(10,2) not null default 0.0
)ENGINE=InnoDB DEFAULT CHARSET=UTF8;
mysql> show variables like 'autocommit'; -- 查看事务是否自
操作流程
commit可以理解为,结束该事务,也就是完成了整个过程,MySQL的数据此时应该是持久化,不再受该事务的影响。
没有commit事务出现异常,直接将表回滚到处理该事务前表的状态。
回滚rollback
begin与autocommit
上面的操作都是在自动提交下完成的。
为什么还需要commit来完成手动提交呢?
autocommit影响不了手动开始的事务。begin开始事务后,必须手动进行提交。
autocommit影响的是CURD的操作。mysql将单个SQL语句封装为了事务(包含了autocommit)。
- 手动提交
- 自动提交
结论
- 只要输入begin或者start transaction,事务便必须要通过commit提交,才会持久化,与是否设置set autocommit无关。
- 事务可以手动回滚,同时,当操作异常,MySQL会自动回滚
- 对于 InnoDB 每一条 SQL 语言都默认封装成事务,自动提交。(select有特殊情况,因为MySQL 有 MVCC )从上面的例子,我们能看到事务本身的原子性(回滚),持久性(commit)
那么隔离性?一致性?
事务操作注意事项
- 如果没有设置保存点,也可以回滚,只能回滚到事务的开始。直接使用 rollback(前提是事务还没有提交)
- 如果一个事务被提交了(commit),则不可以回退(rollback)
- 可以选择回退到哪个保存点
- InnoDB 支持事务, MyISAM 不支持事务
- 开始事务可以使 start transaction 或者 begin
事务的隔离级别
理解隔离级别和隔离性
- 事务的执行阶段可以分为三个阶段,执行前,执行中,执行后。
- 并发的访问一个数据库的事务,在执行中会相互影响。
- 隔离性:在事务运行中,不会相互影响
- 隔离级别:在事务运行中,不会相互影响的程度
修改隔离级别
四种隔离级别
-
读未提交【Read Uncommitted】: 在该隔离级别,所有的事务都可以看到其他事务没有提交的执行结果。(实际生产中不可能使用这种隔离级别的),但是相当于没有任何隔离性,也会有很多并发问题,如脏读,幻读,不可重复读等,我们上面为了做实验方便,用的就是这个隔离性。
就是事务中未提交的操作后的表的内容也可被看到。
读未提交就是没有任何进行任何隔离处理。
-
读提交【Read Committed】 :该隔离级别是大多数数据库的默认的隔离级别(不是 MySQL 默认的)。它满足了隔离的简单定义:一个事务只能看到其他的已经提交的事务所做的改变。这种隔离级别会引起不可重复读,即一个事务执行时,如果多次 select, 可能得到不同的结果。
意思大致就是只能看到已经提交的事务的表。
读提交也叫做不可重复读
在事务中多次读到的数据不一致,就是不可重复读。推此即彼,可重复读读到的数据是一致的。
-
可重复读【Repeatable Read】: 这是 MySQL 默认的隔离级别,它确保同一个事务,在执行中,多次读取操作数据时,会看到同样的数据行。但是会有幻读问题。
在事务中读到的表的数据始终是一样的。
-
串行化【Serializable】: 这是事务的最高隔离级别,它通过强制事务排序,使之不可能相互冲突,
从而解决了幻读的问题。它在每个读的数据行上面加上共享锁,。但是可能会导致超时和锁竞争(这种隔离级别太极端,实际生产基本不使用)
这里插入失败是因为等待时间有点长
重新操作一遍。
总结:在上面的实验中两个事务在读取数据库时,不会阻塞,只有进行其他操作时,会阻塞,需要等待另一个进程提交才会继续执行。
一致性由原子性,隔离性和持久性保证。
数据库并发的三种场景
这三种情况,读读没有线程安全问题,不需要处理,剩下的两种情况都有线程安全问题,但读写是占大多数的。
隔离性的原理
这里针对的主要是读写并发的情况。
MySQL数据库实现隔离性主要采用的技术是MVCC(多版本控制) ,这种控制是不需要锁的,也就是无锁并发控制。
一些认知:
1.MySQL如何区分事务的先后-->每个事务都有一个事务ID,事务ID越小,则事务来的更早
2.MySQL要对事务进行管理--->一套对象/结构体(对事务进行建模)
3个隐藏字段
DB_TRX_ID:记录最近被修改的事务ID
DB_ROLL_PTR:指向上一个版本
DB_ROW_ID:在没有给表设置索引是会被使用
flag:标识该条记录是否被删除
上面表的更具体描述是:
undo log
undo log理解为MySQL中的一段内存缓冲区
理解表中数据的更新(版本更新)
1.将张三改为李四
2.继续,将李四的年龄改为28
这样表中每条记录就形成了一条版本链
3.如果删除该条记录呢?
并不是真正的删除该条记录,只需要将flag进行修改就可以了,然后将老的版本放入undo log就可以了,跟上面的步骤差不多。
当前读和快照读
当前读:就是读最新被修改的记录
快照读:就是读版本链中的某一个版本,读哪一个版本取决于隔离级别。
如何使不同的事务看到不同的内容?如何实现隔离级别的呢?
通过Read View来实现的。
Read View是判断事务可见性的一个类,不是事务被创建出来后,就接着创建Read View,只有在进行快照读的时候,才会创建Read View。
Read View
源代码:
可重复读(RR)和不可重复读(RC)的本质区别
可以读到被提交的内容
在提交之前读
- Read View形成的时机(首次快照读的时候)不同可能影响快照读的结果。
- RR 的Read View是一旦创建就不会改变。
- RC的Read View是每次快照读的时候,都会更新。
写-写
直接理解为当前读就可以。