Site Overlay

数据库之事务隔离

什么是数据库事务?

可操作数据项的一组SQL语句操作,这些操作要么全部成功,要么全部失败(可能一部分成功,但有一步失败就要回滚所有操作),并且要满足ACID属性。

数据库的ACID属性

  1. 原子性(Atomicity):一组操作指令,要么都成功,要么都失败
  2. 一致性(Consistent):在事务开始和完成时,没有预期外的数据状态产生
  3. 隔离性(Isolation):多个事务操作,相互之间互不影响
  4. 持久性(Durable):事务完成后对于数据的修改是永久性的,即使系统崩溃也能保持
    主要了解一下事务隔离~

事务隔离要解决的问题

  1. 脏读:读取最终一定存在的数据,如果读到其他事务未提交的数据,可能会回滚
  2. 不可重复读:同一事务内,不同的时刻读到的同一批数据可能不一样
  3. 幻读:更改同一条记录行,事务B在事务A提交前进行更改操作,导致事务A提交时,数据更改好像未起作用

事务隔离级别

  1. 读未提交:不加锁,性能最好。事务A可以读到事务B修改过但未提交的数据
  2. 读已提交:事务B只能在事务A修改过并且已经提交后才能读到事务B修改的数据
  3. 可重复读:事务B只能在事务A修改过数据并提交后,自己也提交后才能读到事务B修改的数据
  4. 串行化:效率最低
    > 隔离越严格,并发副作用越小,但效率越低
事务隔离 脏读 不可重复读 幻读
读未提交 可能 可能 可能
读已提交 不可能 可能 可能
可重复读 不可能 不可能 可能
串行化 不可能 不可能 不可能

MySQL默认的事务隔离级别是可重复读

事务隔离如何实现?(以MySQL为例)

事务隔离是通过锁实现的。

锁:锁是计算机协调多个进程或线程并发访问某一资源的机制。

锁的分类

锁的两种策略
1. 乐观锁:不上锁,仅在提交数据时判断是否其他事务操作过此数据,如果有就回滚。适合并发量不大或者数据冲突的后果不严重的。
2. 悲观锁:每次读都会上锁,直到取出数据,开销大。适合并发量大的。

从读写角度分为:(针对InnoDB存储引擎)
InnoDB的行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件进行数据检索,InnoDB才使用行级锁;否则,将使用表锁。
1. 分享锁:也叫读锁。有读锁的事务只读不可写,其他事务可以加读锁不能加写锁

select * from table where id = 1 lock in share mode;
  1. 排他锁:也叫写锁。有写锁的事务可读可写,其他事务不允许加读锁或写锁
    delete update insert操作默认加排他锁
select * from table where id = 1 for update;

从颗粒度的角度分
1. 表级锁:整个表加锁,用户可进行读操作。如用户对表进行写操作,获得写锁且禁止其他用户读写操作,开销小,并发度最低。
2. 行级锁:对表中的一行记录加锁,表中的其他记录可进行读写操作。开销大,能支持高并发但会出现死锁。

死锁:事务A和B互相等对方完成,形成死循环

读未提交:不加锁,不隔离
读提交:读取数据时加行级读锁,读完该行后释放读锁;更新数据时加写锁,事务结束后释放。
可重复读:读取数据时加行级读锁,事务结束后释放读锁;更新数据时加写锁,事务结束后释放。
串行化:读的时候加读锁,其他事务可以并发读,但不能写。写的时候加写锁,其他事务不能并发写,也不能并发读,效率很低。

所以在只使用锁来实现隔离级别的控制的时候,需要频繁的加锁解锁,而且很容易发生读写的冲突。为了不加锁解决读写冲突的问题,引入了MVCC机制。