数据库锁、隔离级别

来源:互联网 发布:联通有网络机顶盒吗 编辑:程序博客网 时间:2024/06/05 17:17

 

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

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

    悲观锁(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 0