Mysql事物隔离性

数据库为了维护4个性质,一般使用加锁的方式。 但是数据库又是高并发的应用,如果是加锁过度,并发的处理能力会下降。
所以加锁能力,是数据库对于事物处理的精髓所在。下面的内容主要就是介绍,在事物处理过程中,数据库到底做了什么。

mysql的4个性质:

  • 一致性
  • 原子性
  • 隔离性
  • 持久性

数据库两段锁协议

数据库有个两段锁协议,事物就是分成这2个阶段,加锁阶段和解锁阶段。

加锁阶段: 在该阶段加锁操作。执行表锁的时候,进行读操作之前要申请并获得S锁(共享锁,其他事物可以继续加共享锁, 但是不能加排它锁。); 在进行写操作之前要申请获得X锁(排它锁, 其他的事物不能再获得任何锁)。 加锁不成功,则事物进入等待状态;直到加锁成功才继续执行。
解锁阶段:当事物释放一个锁之后,事物进入解锁阶段,在该阶段只能进行解锁操作不能进行加锁操作。

事物 加锁/解锁处理
begin;
insert into …. insert加锁
uodate …. uodate加锁
delete …. delete加锁
commit; 事物提交时,同时释放insert, uodate, delete对应的锁

两段锁协议保证了事物的并发调度是串行化的。
串行化: 在数据恢复和备份的时候很重要。

事物隔离性作用

事物如果没有隔离性,则会出现以下问题:

脏读: 事物t1处理的过程中,读取了另一个事物t2未提交的的数据。


不可重复读: 一个事物范围内多次查询却是返回了不同结果,因为这间隔中另一个事物修改并提交修改了。 实际上我们要做到的是,在事物t1查询的时候,其他的事物虽然也有提交,但是还是要返回事物t1所查询的结果。


幻读: 事物t1对某数据做了修改,但是事物2插入了一行数据。而操作事物t1的用户查看刚才修改的数据,可能就会发现貌似之前事物t1修改的数据并没有被修改掉。实际这么可能是事物t2提交的数据。所以就造成了数据的看似没有被改变的样子。


注意:

1. 脏读是读取还没提交的数据; 不可重复读是读取了前一事物提交的数据。
2. 不可重复读查询的是同一个数据项,而幻读是针对一批数据整体。

四种隔离级别

为了保证并发读取数据的正确性,提出了事物的4种隔离级别。数据库的锁,也是为了构建隔离级别存在的。
所以,针对上述的3个问题,设置了4种隔离级别,来解决:

  1. 读未提交:允许脏读。数据库一般都不会用,任何操作都不会加锁。
  2. 读已提交: 只读取已经提交的,oracle等有些数据库是默认这个级别的。
  3. 可重复读: 可重复读,在同一事物内,查询的都是事物开始时刻一致的,innodb默认级别。但是还存在幻读。
  4. 串行化: 完全串行化读,每次读都需要获得表级的共享锁,读写会堵塞。

mysql锁的种类

常见的有行锁和表锁,metadata lock等。表锁是对一张表上锁,也是分为读锁和写锁,但是并发能力低下,做ddl处理时才使用。

行锁是锁住数据行,枷锁的方式比较复杂,但是只是锁住部分的数据对于其他的数据无影响,并发能力强。mysql一般也是用行锁处理并发事物。

Read Commited (读取提交内容)

在使用行锁的时候,如果没有使用索引,mysql会给整个表的数据行加锁。因为在sql运行时候,mysql并不知道哪些数据行是所想要的数据行,因为没有索引嘛。将所有的数据行就行加锁返回后,再由mysql server层进行过滤。

但是,实际mysql并不是这样的,还是做了一些的改进。在msql server过滤条件发现不满足的时候,会调用unlock_row方法,吧不满足条件的记录释放锁。(这样就会违背两段锁协议的约束)。 最后,只有满足条件的数据行会上锁。但是,重点是,在没有索引的情况下,还是会对所以的数据行进行加锁的,然后才会释放。 可见即使是mysql,为了效率也会违背规范的。 【这部分结论得出参见《高性能mysql》第三版】

不可重复读和幻读的区别

首先,不可重复读的重点是在update和delete,而幻读的重点在于insert。 这是区别。

在串行化隔离级别中,读用读锁,写用写锁,读锁和写锁互斥,这么做是有效的避免了幻读,不可重复读,脏读问题,但是数据库的并发能力大大的降低。
所以说,对于这个二个问题,是使用锁的机制解决问题。
在mysql,oracle, pgsql数据库中,都是采用以乐观锁为理论基础的MVCC(多版本并发控制)来避免这个问题的。

悲观锁

悲观锁为了保证事物的隔离性,需要一致性锁定读。读取数据加上锁,其他事物无法修改数据了;修改数据是加锁,同样其他事物无法修改数据。

乐观锁

乐观锁加锁机制较宽松。
乐观锁大多是基于数据版本记录机制实现的。数据版本指的是, 为数据增加一个版本标识。在基于数据表的版本解决方案中,一般通过为数据表增加一个‘version’字段实现的。

后面太复杂,暂时不写了。。。。。。