事务及其特性

数据库事务(简称:事务)是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。事务的使用是数据库管理系统区别文件系统的重要特征之一。

事务拥有四个重要的特性:原子性()、一致性()、隔离性()、持久性(),人们习惯称之为 ACID 特性。下面我逐一对其进行解释。

事务的隔离级别

SQL 标准定义的四种隔离级别被 ANSI(美国国家标准学会)和 ISO/IEC(国际标准)采用,每种级别对事务的处理能力会有不同程度的影响。

我们分别对四种隔离级别从并发程度由高到低进行描述,并用代码进行演示,数据库环境为 MySQL 5.7。

READ (读未提交)

该隔离级别的事务会读到其它未提交事务的数据,此现象也称之为脏读

准备两个终端,在此命名为 mysql 终端 1 和 mysql 终端 2,再准备一张测试表 test,写入一条测试数据并调整隔离级别为 READ ,任意一个终端执行即可。 @@.n = 'READ-'; test;use test; table test(id int key); into test(id) (1);登录 mysql 终端 1,开启一个事务,将 ID 为 1 的记录更新为 2。; test set id = 2 where id = 1; * from test; — 此时看到一条ID为2的记录登录 mysql 终端 2,开启一个事务后查看表中的数据。 test;begin; * from test; — 此时看到一条 ID 为 2 的记录

最后一步读取到了 mysql 终端 1 中未提交的事务(没有 提交动作),即产生了脏读,大部分业务场景都不允许脏读出现,但是此隔离级别下数据库的并发是最好的。

READ (读提交)

一个事务可以读取另一个已提交的事务,多次读取会造成不一样的结果,此现象称为不可重复读问题, 和 SQL 的默认隔离级别。

准备两个终端,在此命名为 mysql 终端 1 和 mysql 终端 2,再准备一张测试表 test,写入一条测试数据并调整隔离级别为 READ ,任意一个终端执行即可。 @@.n = 'READ-'; test;use test; table test(id int key); into test(id) (1);登录 mysql 终端 1,开启一个事务,将 ID 为 1 的记录更新为 2,并确认记录数变更过来。; test set id = 2 where id = 1; * from test; — 此时看到一条记录为 2登录 mysql 终端 2,开启一个事务后,查看表中的数据。 test;begin; * from test; — 此时看一条 ID 为 1 的记录登录 mysql 终端 1,提交事务。;切换到 mysql 终端 2。 * from test; — 此时看到一条 ID 为 2 的记录

mysql 终端 2 在开启了一个事务之后,在第一次读取 test 表(此时 mysql 终端 1 的事务还未提交)时 ID 为 1,在第二次读取 test 表(此时 mysql 终端 1 的事务已经提交)时 ID 已经变为 2,说明在此隔离级别下已经读取到已提交的事务。

READ(可重复读)

该隔离级别是 MySQL 默认的隔离级别,在同一个事务里, 的结果是事务开始时时间点的状态,因此,同样的 操作读到的结果会是一致的,但是,会有幻读现象。MySQL 的 引擎可以通过 next-key locks 机制(参考下文”行锁的算法”一节)来避免幻读。

准备两个终端,在此命名为 mysql 终端 1 和 mysql 终端 2,准备一张测试表 test 并调整隔离级别为 READ,任意一个终端执行即可。 @@.n = '-READ'; test;use test; table test(id int key,name (20));登录 mysql 终端 1,开启一个事务。; * from test; — 无记录登录 mysql 终端 2,开启一个事务。; * from test; — 无记录切换到 mysql 终端 1,增加一条记录并提交。 into test(id,name) (1,'a');;切换到 msyql 终端 2。 * from test; –此时查询还是无记录通过这一步可以证明,在该隔离级别下已经读取不到别的已提交的事务,如果想看到 mysql 终端 1 提交的事务,在 mysql 终端 2 将当前事务提交后再次查询就可以读取到 mysql 终端 1 提交的事务。我们接着实验,看看在该隔离级别下是否会存在别的问题。此时接着在 mysql 终端 2 插入一条数据。 into test(id,name) (1,'b'); — 此时报主键冲突的错误

也许到这里您心里可能会有疑问,明明在第 5 步没有数据,为什么在这里会报错呢?其实这就是该隔离级别下可能产生的问题,MySQL 称之为幻读。注意我在这里强调的是 MySQL 数据库, 数据库对于幻读的定义可能有所不同。

(序列化)

在该隔离级别下事务都是串行顺序执行的,MySQL 数据库的 引擎会给读操作隐式加一把读共享锁,从而避免了脏读、不可重读复读和幻读问题。

准备两个终端,在此命名为 mysql 终端 1 和 mysql 终端 2,分别登入 mysql,准备一张测试表 test 并调整隔离级别为 ,任意一个终端执行即可。 @@.n = ''; test;use test; table test(id int key);登录 mysql 终端 1,开启一个事务,并写入一条数据。; into test(id) (1);登录 mysql 终端 2,开启一个事务。; * from test; — 此时会一直卡住立马切换到 mysql 终端 1,提交事务。;

一旦事务提交,msyql 终端 2 会立马返回 ID 为 1 的记录,否则会一直卡住,直到超时,其中超时参数是由 eout 控制。由于每条 语句都会加锁,所以该隔离级别的数据库并发能力最弱,但是有些资料表明该结论也不一定对,如果感兴趣,您可以自行做个压力测试。

表 1 总结了各个隔离级别下产生的一些问题。

表 1. 各个隔离级别下产生的一些问题

隔离级别脏读不可重复读幻读读未提交可以出现可以出现可以出现读提交不允许出现可以出现可以出现可重复读不允许出现不允许出现可以出现序列化不允许出现不允许出现不允许出现

MySQL 中的锁

锁也是数据库管理系统区别文件系统的重要特征之一。锁机制使得在对数据库进行并发访问时,可以保障数据的完整性和一致性。对于锁的实现,各个数据库厂商的实现方法都会有所不同。本文讨论 MySQL 中的 引擎的锁。

锁的类型

实现了两种类型的行级锁:

S 锁和 S 锁是兼容的,X 锁和其它锁都不兼容,举个例子,事务 T1 获取了一个行 r1 的 S 锁,另外事务 T2 可以立即获得行 r1 的 S 锁,此时 T1 和 T2 共同获得行 r1 的 S 锁,此种情况称为锁兼容,但是另外一个事务 T2 此时如果想获得行 r1 的 X 锁,则必须等待 T1 对行 r 锁的释放,此种情况也成为锁冲突。

为了实现多粒度的锁机制, 还有两种内部使用的意向锁,由 自动添加,且都是表级别的锁。

意向锁的主要目的是为了使得行锁和表锁共存。表 2 列出了行级锁和表级意向锁的兼容性。

表 2. 行级锁和表级意向锁的兼容性

锁类型冲突冲突冲突冲突IX冲突兼容冲突兼容S冲突冲突兼容兼容IS冲突兼容兼容兼容

行锁的算法

存储引擎使用三种行锁的算法用来满足相关事务隔离级别的要求。

———END———
限 时 特 惠: 本站每日持续更新海量各大内部创业教程,永久会员只需109元,全站资源免费下载 点击查看详情
站 长 微 信: nanadh666

声明:1、本内容转载于网络,版权归原作者所有!2、本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。3、本内容若侵犯到你的版权利益,请联系我们,会尽快给予删除处理!