一、数据库事务隔离级别
SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。
1、Read Uncommitted(读取未提交内容)
在该隔离级别中,所有事务可以看到其他未提交事务的执行结果。本级别隔离很少用于实际应用,比其他隔离级别性能也好不了多少。读取未提交的数据成为“脏读”(Dirty Read)。
2、Read Committed(读取提交内容)
这是大多数数据库默认的隔离级别,但是,不是MySQL的默认隔离级别。它满足了隔离简单的定义:一个事务只能看到已提交事务所做的改变。这种隔离会出现“不可重复读”的现象(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit(包括update和delete),导致目标数据被修改,所以同一select可能返回不同结果。
3、Repeatable Read(可重读)
这是MySQL默认的隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行(目标数据行不会被修改)。不过,会出现“幻读”的现象。幻读是由insert引起的,简单来说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。
4、Serializable(可串行化)
这是最高的事务隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简而言之,它是在每个读的数据行上,加了共享锁,在这个级别,可能导致大量的超时现象和锁竞争。
5、总结
脏读(Drity Read)
某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。不可重复读(Non-repeatable read)
在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。(update 和 delete 引起)幻读(Phantom Read)
在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。(由 insert 引起)
二、锁机制
1、共享锁、排它锁
共享锁
由读表操作加上的锁,加锁后其他用户只能获取该表或行的共享锁,不能获取排它锁,也就是说只能读不能写排它锁
由写表操作加上的锁,加锁后其他用户不能获取该表或行的任何锁,典型是mysql事务中
start transaction;
select * from user where userId = 1 for update;
执行完这句以后
1)当其他事务想要获取共享锁,比如事务隔离级别为Serializable的事务,执行select * from user;
将会被挂起,因为Serializable的select语句需要获取共享锁
2)当其他事务执行
select * from user where userId = 1 for update;
update user set userAge = 100 where userId = 1;
也会被挂起,因为for update会获取这一行数据的排它锁,需要等到前一个事务释放该排它锁才可以继续进行。
2、行锁、表锁
行锁
对某行记录加上锁表锁
对整个表加上锁
这样组合起来就有:行级共享锁、表级共享锁、行级排他锁、表级排他锁。
3、乐观锁、悲观锁
- 悲观锁(Pessimistic Lock)
顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。
传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
- 乐观锁(Optimistic Lock)
顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,如果期间被修改那么, 则给用户做出提示。可以使用版本号等机制记录状态。
乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。