数据库锁与隔离级别

并发控制主要是为了多线程操作时带来的资源读写问题。如果不加以空间可能会出现死锁,读脏数据、不可重复读、丢失更新等异常。

并发操作可以通过加锁的方式进行控制,锁又可分为乐观锁和悲观锁。

    悲观锁(Pessimistic Locking)并发模式假定系统中存在足够多的数据修改操作,以致于任何确定的读操作都可能会受到由个别的用户所制造的数据修改的影响。也就是说悲观锁假定冲突总会发生,通过独占正在被读取的数据来避免冲突。但是独占数据会导致其他进程无法修改该数据,进而产生阻塞,读数据和写数据会相互阻塞。

   乐观锁(Optimistic Locking)假定系统的数据修改只会产生非常少的冲突,也就是说任何进程都不大可能修改别的进程正在访问的数据。乐观并发模式下,读数据和写数据之间不会发生冲突,只有写数据与写数据之间会发生冲突。即读数据不会产生阻塞,只有写数据才会产生阻塞。

 

数据库隔离级别有四种,应用《高性能MySQL》一书中的说明:

READ UNCOMMITTED(读取未提交内容)

在READ UNCOMMITTED隔离级,所有事务都可以“看到”未提交事务的执行结果。在这种级别上,可能会产生很多问题,除非用户真的知道自己在做什么,并有很好的理由选择这样做。本隔离级很少用于实际应用,因它的性能也不比其他级别好多少,而别的级别还有其他更多的优点。读取未提交数据,也被称之为“脏读(Dirty Read)”。

READ COMMITTED(读取提交内容)

大多数数据库系统的默认隔离级别是 READ COMMITTED(但这不是MySQL默认的!)。它满足了隔离的早先简单定义:一个事务在开始时,只能“看见”已经提交事务所做的改变,一个事物从开始到提交前,所做的任何数据改变都是不可见的,除非已经提交。这种隔离级别也支持所谓的“不可重复读”(Nonrepeatable Read)。这意味着用户运行同一语句两次,看到的结果是不同的。

REPEATABLE READ(可重读)

REPEATABLE READ 隔离级解决了READ UNCOMMITTED隔离级导致的问题。它确保同一事务的多个实例在并发读取数据时,会“看到同样的”数据行。不过理论上,这会导致另一个棘手的问题:幻读(Phantom Read)。简单来说,幻读指当用户读取某一范围的数据行时,另一事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影”(Phantom)行。InnoDB和Falcon存储引擎通过多版本并发控制(Multiversion Concurrency Control)机制解决了幻读问题。

REPEATABLE READ 是MySQL的默认事务隔离级别。InnoDB和Falcon存储引擎也都遵循这种设置。

SERIALIZABLE(可串行化,序列化)

SERIALIZABLE是最高级别的隔离级,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,SERIALIZABLE是在每个读的数据行上加锁。在这个级别,可能导致大量的超时(Timeout)现象和锁竞争(Lock Contention)现象,性能很低,。一般很少看到有用户选择这种隔离级。但如果用户的应用为了数据的稳定性,需要强制减少并发的话,也可以选择这种隔离级。

 

MySQL修改事务隔离级别的方法:

1.全局修改,修改mysql.ini配置文件,在最后加上:

#可选参数有:READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE.

[mysqld]

transaction-isolation = REPEATABLE-READ

   这里全局默认是REPEATABLE-READ,其实MySQL本来默认也是这个级别。

2.对当前session修改,在登录mysql客户端后,执行命令:

set session transaction isolation level read uncommitted;

    要记住mysql有一个autocommit参数,默认是on,他的作用是每一条单独的查询都是一个事务,并且自动开始,自动提交(执行完以后就自动结束了,如果你要适用select for update,而不手动调用 start transaction,这个for update的行锁机制等于没用,因为行锁在自动提交后就释放了),所以事务隔离级别和锁机制即使你不显式调用start transaction,这种机制在单独的一条查询语句中也是适用的,分析锁的运作的时候一定要注意这一点。

锁机制

共享锁:由读表操作加上的锁,加锁后其它用户只能获取该表或行的共享锁,不能获取排它锁,也就是说只能读不能写。

排它锁:由写表操作加上的锁,加锁后其它用户不能获取该表或行的任何锁,典型是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会获取这一行数据的排它锁,需要等到前一个事务释放该排它锁才可以继续进行。

锁的范围

行锁: 对某行记录加上锁。

表锁: 对整个表加上锁。

这样组合起来就有:行级共享锁,表级共享锁,行级排他锁,表级排他锁。

为什么MySQL默认的隔离级别是repeatable read呢?

其实这是个历史原因:在MySQL5.0时,binlog_format只支持Statement,如果设置隔离级别为read committed会导致主从不一致。在MySQL5.1之后binlog支持ROW模式,在该模式下使用read committed上述情况则不成立。

 

Oracle数据库支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别,所以Oracle不支持脏读。SQL标准所定义的默认事务隔离级别是SERIALIZABLE,但是Oracle 默认使用的是READ COMMITTED。

设置隔离级别使用

SET TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]


数据库并发访问可能引起的问题:

二、数据库并发访问可能引起的问题
1. 脏读(事务没提交,提前读取,Dirty Read):脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。

2. 不可重复读(两次读的不一致,Unrepeatable Read,UR ) :是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取文档时,文档已更改。原始读取不可重复。如果只有在作者全部完成编写后编辑人员才可以读取文档,则可以避免该问题。  
3. 幻读 (Phantom Read,PR): 是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。例如,一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作者已将未编辑的新材料添加到该文档中。如果在编辑人员和生产部门完成对原始文档的处理之前,任何人都不能将新材料添加到文档中,则可以避免该问题。  
4.第一类更新丢失(回滚丢失): 
  当2个事务更新相同的数据源,如果第一个事务被提交,而另外一个事务却被撤销,那么会连同第一个事务所做的跟新也被撤销。也就是说第一个事务做的跟新丢失了。 
5.第二类更新丢失(覆盖丢失): 
  第二类更新丢失实在实际应用中经常遇到的并发问题,他和不可重复读本质上是同一类并发问题,通常他被看做不可重复读的特例:当2个或这个多个事务查询同样的记录然后各自基于最初的查询结果更新该行时,会造成第二类丢失更新。因为每个事务都不知道不知道其他事务的存在,最后一个事务对记录做的修改将覆盖其他事务对该记录做的已提交的跟新... 

下表是各隔离级别对各种异常的控制能力 :


LU丢失更新 DR脏读 NRR非重复读 SLU二类丢失更新 PR幻像读
未提交读 RU Y Y Y Y Y
提交读 RC N N Y Y Y
可重复读 RR N N N N Y
串行读 S N N N N Y




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值