事务定义
一组原子性SQL查询,独立的工作单元
ACID(atomicity-原子性,consistency-一致性,isolation-隔离性,durability-持久性)
- 事务不可分割
- 从一个一致性状态转到另一个一致性状态
- 事务修改提交之前对其他事务不可见
- 一旦提交,永远保存
根据业务是否需要事务,选择合适的储存引擎,提升性能
若存储引擎不支持事务也可以使用locktable提供一定保护
隔离级别
- 脏读(dirty read):事务读取了未提交的数据
- 不可重复读(nonrepeatable read):事务T1读取一行记录,紧接着事务T2修改了T1刚才读取的那一行记录,然后T1又再次读取这行记录,发现与刚才读取的结果不同
- 幻像读(phantom read):当某个事务在读某个范围内的记录时,另一个事务又在该范围内加入了新的记录,当之前的事务再读取该范围的记录时,会产生幻行。InnoDB和XtraDB存储引擎通过MVCC解决了幻像读的问题
READ UNCOMMITTED(未提交读)
: 事务的修改即使未提交,对其他事务也可见,性能略好,但有很多问题,不建议使用
READ COMMITTED(提交读)
: 事务所做的修改在提交前对其他事务不可见,大多数数据库默认隔离级别
REPEATABLE READ(可重复读)
: 同一事务多次读取的记录结果是一致的,Mysql默认级别
SERIALIZABLE(序列化)
: 强制事务串行执行,会对读取的每一行数据加锁,会导致大量的超时和锁争用,用在非常需要确保数据的一致性并且可以接受没有并发的情况下
事务的隔离级别|脏读可能性|不可重复读可能性|幻像读可能性|备注
--|--|--|---
READ UNCOMMITTED未提交读|YES|YES|YES
READ COMMITTED提交读|No|YES|YES
REPEATABLE READ可重复读|No|No|YES|Mysql默认级别
SERIALIZABLE序列化|No|No|No|会在读取的每一行数据上都加锁
死锁
多个事务在同一资源上互相占用,并请求锁定对方占用的资源,从而导致恶性循环
- 多个事务以不同的顺序锁定资源
- 多个事务同时锁定同一资源
解决方式:1. 检测到死锁时,立刻返回错误 2. 当查询的时间达到锁等待超时的设定后放弃锁请求
InnoDB处理方式
: 将持有最少行级排他锁的事务进行回滚
发生死锁后,只有部分或者完全回滚其中一个事务,才能打破死锁。发生死锁无法避免,程序设计时需要考虑如何处理死锁,通常重新执行因死锁回滚的事务即可
事务日志
提高事务的效率
:
- 存储引擎在修改表的数据时只需要修改其内存拷贝,再把该修改行为记录到持久在硬盘上的事务日志中,而不用将修改的数据本身持久到磁盘。
- 事务日志采用的是追加的方式,写日志的操作是在磁盘上的一小块顺序I/O,不像随机I/O需要在多次移动磁头。
事务日志持久后,内存被修改的数据在后台慢慢刷回到磁盘。称为预写式日志(Write-Ahead Logging),修改数据需要写两次磁盘.
若数据的修改已经记录到日志并持久化,即使内存的修改没及时写到磁盘,也可以在存储引擎重启时自动恢复
MySQL事务
官方提供的事务存储引擎:InnoDB和NDB Cluster
第三方:XtraDB和PBXT
自动提交
mysql默认会启用自动提交,每个查询都会被当作一个事务执行提交操作,在当前连接中可以设置AUTOCOMMIT 1/ON或0/OFF。
导致大量数据改变的操作ALTER TABLE,还有LOCK TABLES等也会导致自动提交
事务中混合使用存储引擎
- 在非事务型的表上,回滚操作无效,且不会报错,只会有警告信息
为每张表选择合适的存储引擎
隐式、显式锁定
- InnoDB使用两段锁定协议,事务执行时,可随时执行锁定,只要到COMMIT或ROLLBACK时才会同时释放所有锁,InnoDB会根据隔离级别自动加锁(隐式)
- SELECT ... LOCK IN SHARE MODE和 SELECT ... FOR UPDATE 显式锁定,但不建议使用
LOCK TABLES、UNLOCK TABLES
服务器层实现,不能代替事务,且影响性能
MVCC多版本并发控制
- 行级锁变种 能尽量避免加锁 开销低
此处介绍InnoDB对MVVC的实现
实现原理
: 在每行记录后保存两个隐藏的列,一个保存行创建时间,一个保存过期(删除)时间,此处的时间指的是系统版本号,它会在创建事务时递增,
具体操作
:
- SElECT
- 只查找版本号小号或等于当前事务的版本号,确保读取的行,或是在事务开始之前,或是事务自己插入的
- 行的删除版本要么未定义,要么大于当前事务版本号,确保事务读到行在事务开始之前未被删除
- INSERT
- 插入的每一行保存当前系统版本号作为行版本号
- DELETE
- 插入的每一行保存当前系统版本号作为行版本号
- UPDATE
- 为插入的新记录保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识