数据库事务特征、数据库隔离级别,以及各级别数据库加锁情况-read uncommitted篇

1.前言

1.1 记录什么?

1.2 关于这篇分享对以上问题的解决

2.事务本质剖析

2.1 什么是事务?

2.2.1 如下表格所示:

对上述表格内容的解释msyql事务

应用层事务

个人理解这就是事务的本质。事务不应该只仅限于数据库。

2.2 关于ACID

举例子说明

3.数据库事务的隔离级别

3.1 为什么需要隔离级别?

现在我们知道数据库 隔离级别 的必要性,接下来讨论不同隔离级别会带来的问题。

3.2 不同隔离级别带来的问题(重要!含实操部分,最好可以实践下)

3.2.1 前置条件--几个概念的理解(重要)不同隔离级别带来的数据操作问题:

1.脏读:两个事务,t1事务可以读取到t2事务正在做更改的数据的中间状态(t2事务执行过程中),而这个数据的更改有可能不会被持久化(commit),而是rollback,导致t1在同一事务内的两次读取同一行数据得到结果不同。

2.不可重复读:t1事务在整个事务执行过程中读取某一条记录多次,发现读取的此条记录不是每次都一样。

3.幻读:t1事务在整个事务执行过程中读取某一范围内的数据,在第二次读取时发现多了几行或者少了几行。

3.2.2 数据库中的几种隔离级别

read uncommited--读未提交该隔离级别指即使一个事务的更新语句没有提交,但是别的事务可以读到这个改变,几种异常情况都可能出现。极易出错,没有安全性可言,基本不会使用。

read committed --读已提交该隔离级别指一个事务只能看到其他事务的已经提交的更新,看不到未提交的更新,消除了脏读和第一类丢失更新,这是大多数数据库的默认隔离级别,如Oracle,Sqlserver。

repeatable read --可重复读该隔离级别指一个事务中进行两次或多次同样的对于数据内容的查询,得到的结果是一样的,但不保证对于数据条数的查询是一样的,只要存在读改行数据就禁止写,消除了不可重复读和第二类更新丢失,这是Mysql数据库的默认隔离级别。

serializable --序列化读意思是说这个事务执行的时候不允许别的事务并发写操作的执行.完全串行化的读,只要存在读就禁止写,但可以同时读,消除了幻读。这是事务隔离的最高级别,虽然最安全最省心,但是效率太低,一般不会用。

3.2.3 数据库中的锁:

共享锁(Share locks简记为S锁):也称读锁,事务A对对象T加s锁,其他事务也只能对T加S,多个事务可以同时读,但不能有写操作,直到A释放S锁。

排它锁(Exclusivelocks简记为X锁):也称写锁,事务A对对象T加X锁以后,其他事务不能对T加任何锁,只有事务A可以读写对象T直到A释放X锁。

更新锁(简记为U锁):用来预定要对此对象施加X锁,它允许其他事务读,但不允许再施加U锁或X锁;当被读取的对象将要被更新时,则升级为X锁,主要是用来防止死锁的。因为使用共享锁时,修改数据的操作分为两步,首先获得一个共享锁,读取数据,然后将共享锁升级为排它锁,然后再执行修改操作。这样如果同时有两个或多个事务同时对一个对象申请了共享锁,在修改数据的时候,这些事务都要将共享锁升级为排它锁。这些事务都不会释放共享锁而是一直等待对方释放,这样就造成了死锁。如果一个数据在修改前直接申请更新锁,在数据修改的时候再升级为排它锁,就可以避免死锁。

接下来化繁为简,配合实操,来看看每种隔离级别场景。不要觉得繁琐,一定要读下去。

1.read uncommited--读未提交前置条件:1.开启两个 mysql 客户端终端2.查看当前客户端事务隔离级别

3.选择数据库,建立演示表test,并设置当前客户端事务隔离级别为read uncommitted.

注意:两个客户端都需执行set session transaction isolation level read uncommitted;

4.客户端1,客户端2设置事务提交模式为 set autocommit = 0;表示关闭默认的自动提交事务功能。

5.开启事务

6.客户端1 执行 如下脚本

结果如下图所示:

7.客户端2 执行如下脚本

结果如下图所示:8.切换到客户端1执行如下脚本

结果如下图所示:

我们发现此时客户端1再次读id = 1的记录时,name 已经从 ‘张三’ 更改为 ‘张八’。

我们继续执行下一步操作9.客户端2执行回滚操作,脚本如下所示

结果如下所示:

10.客户端1继续查看id = 1的记录,如下脚本

结果如下所示:

我们发现在客户端1的一次事务中id = 1 的记录的name 发生了变化,这种变化就称之为脏读。下面我们分析下 read uncommitted 情况下的加锁情况。吐槽一句,现在网上的博客对这个隔离级别的加锁分析五花八门。分为三大门派:

那么到底是什么样子呢,我们看一下:演示过程,打开3个命令行终端,其中两个做演示,最后一个客户端查询当前 innodb 锁状态 设置事务隔离级别为read uncommitted。做如下演示:1.客户端1做如下操作:

2.客户端2做与客户端相同操作,如下所示

我们发现update 操作并没有执行,而是静止了如下图所示我们分析了在客户端2锁等待情况下的加锁情况:命令为:

可以得出结论,read uncommitted 隔离级别下,写操作是有锁的,而且是 X 排他锁,可以灭掉上述两个门派。

并且我们看下上述客户端2情景下的事务状态如下图所示:

trxid 为208579的代表的就是客户端2的事务,trxstate代表的是锁状态,代表 客户端2的事务 处于锁等待状态,为什么是锁等待状态呢,因为 客户端2的事务在更改 id = 32 的记录时在主键上添加了 X(行级排他锁) 锁,你可能会有疑问,客户端1 的更新动作不是已经完成了么,那么 客户端1 肯定已经释放了在主键 id = 32 上的排他锁了呀,要不为什么客户端2 能读到客户端1 更改 id = 32 记录后的脏数据呢?但是真正的真相是客户端1在更新完后并没有释放排他锁,因为如果释放成功,那么客户端2的事务是能将 id = 32 的记录更新成功的,但是并没有。那既然客户端1在更新完后并没有释放排他锁,那客户端2为什么还能读到脏数据呢,这跟排他锁的属性是相悖的呀(排他锁会阻塞除当前操作外的其他事务的所有读写操作)。这就是最矛盾的问题,我在SqlServer的官网上找到这句话,事实上也正是这句话让我茅塞顿开,如下:

对应翻译:

看到了吧读取UNCOMMITTED事务也不被排他锁(排他锁将阻止当前事务读取已被修改但未被其他事务提交的行)阻止其实想想也对,应为排它锁对任何其他的事务开始之前申请的排它锁,共享锁都不兼容。但是如果我读不申请锁,就不会产生上述问题了呀。

所以最终结论是:read uncommitted 读不加锁,写加排他锁,并到事务结束之后释放。

博客搬家:大坤的个人博客欢迎评论哦~

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181030A1Z6DW00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券