1 事务
定义:事务(Transaction)是数据库应用中构成单一逻辑工作单元的操作集合。
特性:ACID
- 原子性Atomicity
- 保证事务包含的一组更新操作是原子不可分的,要么不做,要么全做
- 一致性Consistency
- 要求事务执行完成后将数据库从一个一致性状态转变到另一个一致性状态
- 一致性状态是指数据库中的数据满足完整性约束,是一种以一致性状态为基础的逻辑属性
- 隔离性Isolation
- 一个事务的执行不能被其他事务干扰,即一个事务内部的操作即使用的数据对并发的其他事务是隔离的,并发执行的事务之间不能相互干扰
- 持久性Durability
- 事务一旦提交,那么对数据库所做的修改将是持久的
- 即使系统故障或崩溃也保证是持久的
MySQL事务的隔离级别
MySQL的事务隔离级别共有四个,分别是读未提交、读已提交、可重复读以及可串行化
MySQL的事务隔离级别的作用就是让事务之间相互隔离,互不影响,这样可以保证事务的一致性。
隔离级别/隔离级别对性能的影响:可串行化>可重复读>读已提交>读未提交
由此可见,隔离级别越高,所需消耗的MySQL性能越大(如事务并发严重性),为了平衡二者,一般建议设置的隔离级别为可重复读,MySQL默认的隔离级别也是可重复读。
幻读是指一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来。(幻读在读未提交、读已提交、可重复读隔离级别都可能会出现)
读未提交
在读未提交隔离级别下,事务A可以读取事务B修改过但未提交的数据,不加锁
在这种级别下,可能发生脏读、不可重读和幻读问题,一般很少用此级别
读已提交
在读已提交隔离级别下,事务B只能在事务A修改过并且已提交后才能读取到事务B修改的数据
解决了脏读问题,但可能发生不可重复读和幻读问题,一般很少使用此隔离级别
可重复读
在可重复读的级别下,保证了同一事务读取相同数据,MySQL使用行级锁,其他事务不能对该行修改,但是可以插入一行数据。
解决了脏读和不可重复读的问题,但可能发生幻读问题。
可串行化
各种问题都不会发生,通过加锁实现(MySQL行锁和间隙锁),耗费数据库性能,一般不使用,MySQL默认是可重复读。
总结:
- 读未提交:A可读B修改且未提交的数据
- 读已提交:A可读B修改且已提交的数据
- 可重复读:A在提交后,才能读取B修改且已提交的数据
- 可串行化:最高级别 ,加锁
2 并发控制
2.1 并发操作与数据的不一致性
- 丢失更新
- 事务2执行时可能丢失事务1对数据库的更新
- T1读->T2读->T1修改->T2修改 T2读取了错误数据
- 事务2执行时可能丢失事务1对数据库的更新
- 不可重读
- 事务1读取数据后,事务2对数据进行了更新,使事务1无法再现前一次读取结果
- 事务1读取数据后,事务2对数据进行更新,当事务1再次读取时得到与前一次不同的值
- 事务1读取数据后,事务2对数据进行删除,当事务1再次读取时,发现数据消失了
- 事务1读取数据后,事务2对数据进行插入,当事务1再次读取时,发现多了一些数据(幻读)
- 事务1读取数据后,事务2对数据进行了更新,使事务1无法再现前一次读取结果
- 污读/脏读
- 一个事务读到了另一个未提交事务修改过的数据
- 读脏数据是指事务1修改某一数据,并将其写回磁盘,事务2读取同一数据后,事务1被撤销了,事务1修改的数据恢复原值,事务2读到的数据就与原来的数据不一致,则事务2读到的数据就是脏数据,即不正确的数据
例如:数据原来是A,事务1是将A改为B,最后改为C
数据变化为A->B->C
则:事务2读数据时,
- 读的是B,为脏读,实际应读为A或C
- 第一次读的是A,再次需要读时是B,两次不一致,为不可重复读
- 第一次读的是A,再次需要读时是A和C,多读出来C,则为幻读
2.2 封锁
实现并发控制的方法主要有两种,即封锁(Lock)技术和时标(Time Stamping)技术
封锁类型有:
- 排他锁/ X锁/写锁
- 若事务T对数据对象加上X锁,则只允许T读取和修改A,其他任何事物都不能再对A加任何类型的锁,直到事物T释放A上的锁,保证了其他事务在T释放A上的锁之前不能再读取和修改A
- 提问:为什么上了写锁,别的事务还可以读操作
- 因为InnoDB有MVCC机制(多版本并发控制),可以使用快照读,而不会堵塞
- 共享锁/ S锁/读锁
- 若事务T对数据对象A加上S锁,则其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。
2.3 活锁和死锁
活锁:多个事务并发执行过程中可能存在某个机会获得锁的事务去永远无法获得锁的现象,这种现象称为活锁。可以采用先来先服务的策略预防。
死锁:同操作系统
只有出现并发操作时才有可能出现死锁