mysql怎么保证隔离性 mysql4种隔离级别操作过程

mysql事务隔离级别

mysql事务隔离级别如下:

创新互联公司成立十多年来,这条路我们正越走越好,积累了技术与客户资源,形成了良好的口碑。为客户提供网站制作、成都网站建设、网站策划、网页设计、申请域名、网络营销、VI设计、网站改版、漏洞修补等服务。网站是否美观、功能强大、用户体验好、性价比高、打开快等等,这些对于网站建设都非常重要,创新互联公司通过对建站技术性的掌握、对创意设计的研究为客户提供一站式互联网解决方案,携手广大客户,共同发展进步。

1.读取未提交(READ-UNCOMMITTED):最低的隔离级别,允许读取尚未提交的数据变更,可能造成脏读、不可重复读、幻读。

2.读取已提交(READ-COMMITTED):允许读取并发事务已经提交的数据,可以避免脏读,但是可能造成不可重复、幻读。

3.可重复读(REPEATABLE-READ):对同一字段多次读取的结果都是一致的,除非本身事务修改,可以避免脏读和不可重复读,但是可能造成幻读。

4.可串行化(SERIALIZABLE):最高的隔离级别,完全服从ACID的隔离级别,所以的事务依次执行,可以避免脏读、不可重复读、幻读。

事务的特性:

1.原子性:事务最小的执行单位,不允许分割。事务的原子性确保动作要么全部执行,要么全部不执行。

2.一致性:执行事务的前后,数据保持一致。例如转账的业务中,无论事务是否成功,转账者和收款人的总额应该是不变的。

3.隔离性:并发访问数据库时,一个用户的事务不应该被其他事务所影响,各并发事务之间数据库是独立的。

4.持久性:一个事务被提交后,它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有影响。

Mysql隔离级别之MVCC的ReadView的理解

比较常用的两种分别是读已提交、可重复读,那么Mysql是如何保证多个事务读取一条数据的隔离性的?

当我们读取一条被其他事务变更的数据时,会在undo Log中产生一条变更前的日志.这个日志可以专门用于回滚。

我们大概来看一下这个日志的大概结构:

前面三个字段属于变更前的,另外:

trx_id : 代表是哪个事务编号修改的。

roll_pointer : 相当于一个链表,往下查找就是上一次更改前的。当一条数据被更改了多次之后,由该字段构建成一个链表俗称 版本链 。

有了undo Log的话可以很快追溯到更改之前的数据,有了这个之后,假设多个事务都在读同一条记录,并且还发生了修改,这个时候

多版本并发控制,指的就是在使用读提交和可重复读隔离级别的事务,在执行普通select操作时,访问记录版本链的过程;使不同事务的读写、写读操作并发执行,提高系统性能;

基于当前活跃事务列表构成的ReadView,当某个事务创建ReadView时,会将当前活跃的事务也加入其中。

我们来看一下大概结构:

readview 中四个比较重要的概念:

m_ids :表示在生成readview时,当前系统中 活跃的读写事务id列表 ;

min_trx_id :表示在生成readview时,当前系统中活跃的读写事务中最小的事务id,也就是m_ids中最小的值;

max_trx_id :表示生成readview时,系统中应该分配给下一个事务的id值;

creator_trx_id :表示生成该readview的事务的事务id;

有了readview,在访问某条记录时,按照以下步骤判断记录的某个版本是否可见:

下面是对于同一条数据的多个事务读取流程:

ReadView( 简称RV )一旦创建是不可变的,即便其中某个线程事务提交了,也不会影响当前线程创建的ReadView,你可以理解为一个副本快照。

总的来说判断就三个条件:

基于上述规则,很好的解决了一致性读的问题;当前线程创建完RV之后,读到的数据都是相同的;不会读到其他事务未提交和后提交的数据。

可重复读的RV是以一个事务的开始和结束作为它的生命周期的

读提交级别是能够读到其他事务提交的数据的,那么这个时候上面的流程是不是满足不了呀?因为假设ABC都在一个RV之中,C提交了数据,但是B看不到呀,因为条件2就满足不了呀;

这个时候Mysql就把这个级别的RV做了调整, 每次读取数据的时候会创建一个新的ReadView

当RV中的事务B提交了事务的时候,A每次会创建一个新的RV来查看数据版本,新的RV的m_ids肯定是不包含已经提交的事务B的,这个时候就能够读到事务B的数据了。

之前一直以为可重复读没有解决幻读的问题,现在基于这个流程另外加上命令行调试之后发现应该是解决了的。

因为一旦创建RV的话,当前活跃事务快照已经生成,这个时候如果新来的事务或者快照内的事务新增了数据也不会读到:

如有问题,欢迎留言交流。

mysql默认事务隔离级别

SQL标准中支持4种事务隔离级别,READ_UNCOMMITTED(读未提交),READ_COMMITTED(读已提交),REPEATABLE_READ(可重复读),SERIALIZABLE(串行读),MySQL innodb引擎支持全部这4种事务隔离级别。

工具/原料:

联想Y7000P

Windows10

MySQL6.0

1、启动命令行窗口,连接MySQL数据库

图示,通过MySQL提供的客户端命令mysql连接MySQL数据库。

2、通过系统变量查询数据库当前事务隔离级别

图示,通过查询数据库提供的系统变量 tx_isolation 或 transaction_isolation 的值即可获取当前的事务隔离级别。MySQL数据库默认的事务隔离级别是REPEATABLE_READ (可重复读)。

3、设置本次会话的事务隔离级别

图示,通过命令set session transaction isolation level可以设置本次会话的事务隔离级别,该设置不会影响其他会话,并且设置会随着当前会话的结束而结束。

4、设置全局会话的事务隔离级别

图示,通过命令set global transaction isolation level可以设置全局会话的事务隔离级别,该设置不会影响当前已经连接的会话,设置完毕后,新打开的会话,将使用新设置的事务隔离级别。

5、设置一次操作的事务隔离级别

图示,通过命令set transaction isolation level可设置下一次事务操作的隔离级别,该设置会随着下一次事务的提交而失效。

mysql的事务四个特性以及事务的四个隔离级别

分别是原子性、一致性、隔离性、持久性。

原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。

一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。举例来说,假设用户A和用户B两者的钱加起来一共是1000,那么不管A和B之间如何转账、转几次账,事务结束后两个用户的钱相加起来应该还得是1000,这就是事务的一致性。

隔离性是当多个用户并发访问数据库时,比如同时操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。关于事务的隔离性数据库提供了多种隔离级别,稍后会介绍到。

持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。例如我们在使用JDBC操作数据库时,在提交事务方法后,提示用户事务操作完成,当我们程序执行完成直到看到提示后,就可以认定事务已经正确提交,即使这时候数据库出现了问题,也必须要将我们的事务完全执行完成。否则的话就会造成我们虽然看到提示事务处理完毕,但是数据库因为故障而没有执行事务的重大错误。这是不允许的。

在数据库操作中,在并发的情况下可能出现如下问题:

正是为了解决以上情况,数据库提供了几种隔离级别。

数据库事务的隔离级别有4个,由低到高依次为Read uncommitted(未授权读取、读未提交)、Read committed(授权读取、读提交)、Repeatable read(可重复读取)、Serializable(序列化),这四个级别可以逐个解决脏读、不可重复读、幻象读这几类问题。

虽然数据库的隔离级别可以解决大多数问题,但是灵活度较差,为此又提出了悲观锁和乐观锁的概念。

悲观锁,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度。因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制。也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统的数据访问层中实现了加锁机制,也无法保证外部系统不会修改数据。

商品t_items表中有一个字段status,status为1代表商品未被下单,status为2代表商品已经被下单(此时该商品无法再次下单),那么我们对某个商品下单时必须确保该商品status为1。假设商品的id为1。

如果不采用锁,那么操作方法如下:

但是上面这种场景在高并发访问的情况下很可能会出现问题。例如当第一步操作中,查询出来的商品status为1。但是当我们执行第三步Update操作的时候,有可能出现其他人先一步对商品下单把t_items中的status修改为2了,但是我们并不知道数据已经被修改了,这样就可能造成同一个商品被下单2次,使得数据不一致。所以说这种方式是不安全的。

在上面的场景中,商品信息从查询出来到修改,中间有一个处理订单的过程,使用悲观锁的原理就是,当我们在查询出t_items信息后就把当前的数据锁定,直到我们修改完毕后再解锁。那么在这个过程中,因为t_items被锁定了,就不会出现有第三者来对其进行修改了。需要注意的是,要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。我们可以使用命令设置MySQL为非autocommit模式: set autocommit=0;

设置完autocommit后,我们就可以执行我们的正常业务了。具体如下:

上面的begin/commit为事务的开始和结束,因为在前一步我们关闭了mysql的autocommit,所以需要手动控制事务的提交。

上面的第一步我们执行了一次查询操作: select status from t_items where id=1 for update; 与普通查询不一样的是,我们使用了 select…for update 的方式,这样就通过数据库实现了悲观锁。此时在t_items表中,id为1的那条数据就被我们锁定了,其它的事务必须等本次事务提交之后才能执行。这样我们可以保证当前的数据不会被其它事务修改。需要注意的是,在事务中,只有 SELECT ... FOR UPDATE 或 LOCK IN SHARE MODE 操作同一个数据时才会等待其它事务结束后才执行,一般 SELECT ... 则不受此影响。拿上面的实例来说,当我执行 select status from t_items where id=1 for update; 后。我在另外的事务中如果再次执行 select status from t_items where id=1 for update; 则第二个事务会一直等待第一个事务的提交,此时第二个查询处于阻塞的状态,但是如果我是在第二个事务中执行 select status from t_items where id=1; 则能正常查询出数据,不会受第一个事务的影响。

使用 select…for update 会把数据给锁住,不过我们需要注意一些锁的级别,MySQL InnoDB默认Row-Level Lock,所以只有「明确」地指定主键或者索引,MySQL 才会执行Row lock (只锁住被选取的数据) ,否则MySQL 将会执行Table Lock (将整个数据表单给锁住)。举例如下:

1、 select * from t_items where id=1 for update;

这条语句明确指定主键(id=1),并且有此数据(id=1的数据存在),则采用row lock。只锁定当前这条数据。

2、 select * from t_items where id=3 for update;

这条语句明确指定主键,但是却查无此数据,此时不会产生lock(没有元数据,又去lock谁呢?)。

3、 select * from t_items where name='手机' for update;

这条语句没有指定数据的主键,那么此时产生table lock,即在当前事务提交前整张数据表的所有字段将无法被查询。

4、 select * from t_items where id0 for update; 或者 select * from t_items where id1 for update; (注:在SQL中表示不等于)

上述两条语句的主键都不明确,也会产生table lock。

5、 select * from t_items where status=1 for update; (假设为status字段添加了索引)

这条语句明确指定了索引,并且有此数据,则产生row lock。

6、 select * from t_items where status=3 for update; (假设为status字段添加了索引)

这条语句明确指定索引,但是根据索引查无此数据,也就不会产生lock。

乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以只会在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回用户错误的信息,让用户决定如何去做。实现乐观锁一般来说有以下2种方式:

MySQL 是如何实现四大隔离级别的

SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。

Read Uncommitted(读取未提交内容)

在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。

Read Committed(读取提交内容)

这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。

Repeatable Read(可重读)

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

Serializable(可串行化)

这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

这四种隔离级别采取不同的锁类型来实现,若读取的是同一个数据的话,就容易发生问题。例如:

脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。

不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。

幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。

在MySQL中,实现了这四种隔离级别,分别有可能产生问题如下所示:


名称栏目:mysql怎么保证隔离性 mysql4种隔离级别操作过程
转载源于:http://myzitong.com/article/dooshjs.html