一、事务概念
事务是由多个 SQL 语句构成,主要为了完成任务;例如:删除一名的学生的信息,就要删除这名学生的学号、班级信息、电话号码等,那么删除一名学生的信息由要执行多条 SQL,这就是事务;
事务的四大属性(ACID):
1)原子性:要么删除完学生的信息,要么不删除;
2)一致性:事务的执行复合我们用户的预期;
3)隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Readuncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)
4)永久性:事务完成之后,数据的修改是永久的,即使系统故障也不会修改;
注意:只要保证了原子性、隔离性、永久性就保证了一致性;
为什么会出现事务?
答:一开始是没有事务的,程序员后面使用多次数据库时发现多个用户使用(连接)数据库时,会造成并发访问造成的数据不一致性的问题,后来在数据库里面添加了事务,事务就是解决方案;在之前由程序员来手动来保证事务,现在由数据库来保证,所以事务本质是为应用层服务的;
二、事务的版本支持
只有 innodb 支持:

三、事务的提交方式
常见的就两种:
1)手动提交
2)自动提交

四、事务常见操作方式
前提工作:

要退出 MySQL 之后才能显示:

目的:降低隔离性,方便观察事务的并发访问产生的问题;
事务的开始与回滚:


回滚:

事务的提交:

没有打保存点,也能直接回滚:

注意:一旦 commit 之后,回滚是不起作用的;

注意:我们事务开启之后,一旦出现异常或者崩溃没有 commit 就会自动回滚;
autocommit 开不开都不会影响事务如果不 commit 就会自动回滚:


如果我们把 autocommit 关闭了,影响的是单句 SQL(不开启事务 begin),此时我们没有开启 begin 但是 MySQL 会自动把 SQL 当成事务,没有 commit 就会自动回滚;
事务的回滚体现了原子性,commit 体现了持久性;
五、事务隔离级别
隔离性:多个事务同时运行,互不影响;
隔离级别:事务之间影响的程度;
隔离级别:
1)读为提交【Read Uncommitted】:一个事务还没有 commit ,另外一个事务就能读到该是事务的运行结果;
2)读提交【Read Committed】:大多数数据库的默认级别(除了 MySQL),只有 commit 了,另外一个事务才能看到;
3)可重复读【Repeatable Read】:MySQL的默认级别,不管事务是否 commit,另外一个事务只有结束该事务才能看到 commit 运行的结果 ;
4)串行化【Serializable】:这是事务的最高隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决了幻读的问题。它在每个读的数据行上面加上共享锁,。但是可能会导致超时和锁竞争(这种隔离级别太极端,实际生产基本不使用)。
隔离级别如何实现:隔离,基本都是通过锁实现的,不同的隔离级别,锁的使用是不同的。常见有,表锁,行锁,读锁,写锁,间隙锁(GAP),Next-Key锁(GAP+行锁)等。不过,我们目前现有这个认识就行,先关注上层使用。


设置会话的隔离级别不会影响其他会话;只有设置全局的隔离级别会影响所有的会话;
退出会话重新登录会话的隔离级别和全局隔离级别一样;
读未提交的实验:

两个事务同时运行,一个事务还未 commit ,另外一个会话的事务能看到我事务运行中的操作结果;这个过程叫做脏读;
read uncommitted 不建议使用,几乎没有加锁,既然效率高,但是问题多;
读提交实验:

两个事务同时运行,只有当其中一个事务 commit 了,另外一个事务才能看到该事务运行的结果;
右侧事务运行中重复的 select 看到的结果不一样,这叫不可重复读,如果重复读会导致用户的决策不一样;
不可重复读是有问题的,例如:A 事务查看一批的工资,其中 tom 的工资在1000-2000 之间,同时 B 事务在修改 tom 的工资,修改成 2500,那么一开 A 事务查其他人的工资是没有问题的,当 B事务未 commit 时,A 事务查到的 tom 的工资是在 1000-2000 之间,如果 B 事务此时 commit 之后,A 事务查找 2000-3000 工资的人里面也看到了 tom,此时 1000-2000 和 2000-3000 都有 tom,这就是由于不可重复读的造成的问题;
既然读提交是有问题的,那么读未提交也是有问题的;
可重复读实验:

两个事务同时运行,A 事务中的操作,B 事务看不到,即使 A 事务 commit 了,也看不到,只有 B 事务 commit 之后才看到 A 事务运行的结果;
串行化实验:

串行化,当两个事务同时运行而且访问的是同一个内容,那么如果 A 事务要修改该内容,此时 B 事务同时要查看该内容,只要 B 事务不 commit ,A 事务的修改操作就会阻塞在那里,如果 B 事务长时间不 commit ,A事务的修改操作就会超时;串行化的效率是非常慢的;
注意:此时的 A 阻塞在那里,但是 B 事务可进行修改操作,只有一方 commit ,另外一方才能修改;
其中隔离级别越严格,安全性越高,但数据库的并发性能也就越低,往往需要在两者之间找一个平衡点。
不可重复读的重点是修改和删除:同样的条件,你读取过的数据,再次读取出来发现值不一样了幻读的重点在于新增:同样的条件,第1次和第2次读出来的记录数不一样
说明:mysq1默认的隔离级别是可重复读,一般情况下不要修改
上面的例子可以看出,事务也有长短事务这样的概念。事务间互相影响,指的是事务在并行执行的时候,即都没有commit的时候,影响会比较大。

一致性:
事务执行的结果,必须使数据库从一个一致性状态,变到另一个一致性状态。当数据库只包含事务成功提交的结果时,数据库处于一致性状态。如果系统运行发生中断,某个事务尚未完成而被迫中断,而改未完成的事务对数据库所做的修改已被写入数据库,此时数据库就处于一种不正确(不一致)的状态。因此一致性是通过原子性来保证的。
其实一致性和用户的业务逻辑强相关,一般MySQL提供技术支持,但是一致性还是要用户业务逻辑做支撑,也就是,一致性,是由用户决定的。而技术上,通过AID保证C。
六、MVCC 机制
1.每个事务都有自己的事务ID,可以根据事务 ID 的大小来决定事务到来的先后顺序(谁小谁先来);
2.mysqld 可能面临处理多个事务的情况,事务也有自己的生命周期,mysqld 要对个事务进行管理:先描述,在组织,用结构体或者类来描述,用数据结构来管理;那么 msyqld 对事务的管理就变成了增删查改;
数据库并发的场景有三种:
读-读:不存在任何问题,也不需要并发控制
读-写:有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读,幻读,不可重复读
写-写:有线程安全问题,可能会存在更新丢失问题,比如第一类更新丢失,第二类更新丢失(后面补充)
1)读写
读---写(大部分都是读写冲突):
多版本并发控制(MVCC)是一种用来解决读-写冲突的无锁并发控制,,为事务分配单向增长的事务ID,为每个修改保存一个版本,版本与事务ID关联,读操作只读该事务开始前的数据库的快照。所以MVCC可以为数据库解决以下问题:
在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能。
同时还可以解决脏读,幻读,不可重复读等事务隔离问题,但不能解决更新丢失问题。
MVCC 的三个前提知识:
3个记录隐藏字段(其实是4个):
DB_TRX_ID:6 byte,最近修改(修改/插入)事务ID,记录创建这条记录/最后一次修改该记录的事务
IDDB_ROLL_PTR:7 byte,回滚指针,指向这条记录的上一个版本(简单理解成,指向历史版本就行,这些数据一般在 undo 1og中)
DB_ROw_ID:6 byte,隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引,如果表中有主键,就不会有这个隐藏主键,而是以主键产生聚簇索引;
补充:实际还有一个删除flag隐藏字段,既记录被更新或删除并不代表真的删除,而是删除flag变了。
undo日志:一段内存的缓冲区;
Read View:一个描述事务的结构体,看课件具体实现(一定要看);
