【MySQL】事件

打印 上一主题 下一主题

主题 969|帖子 969|积分 2907

一、什么是事件

我们先来看一个例子,比方有一个火车售票系统:

当客户端A检查另有一张票时,将票卖掉,还没有执行更新数据库的时间,客户端B检查了票数,发现大于0,于是又买了一次票。然后客户端A将票数更新回数据库。于是就出现了同一张票被卖了两次的情况。
以是数据库的 CURD 应该满足什么属性能办理上面的问题?

  • 买票的过程得是原子的吧
  • 买票互相应该不能影响吧
  • 买完票应该要永世有效吧
  • 买前,和买后都要是确定的状态吧


  • 什么是事件呢?
事件就是一组 DML 语句组成,这些语句在逻辑上存在干系性,这一组 DML 语句要么全部成功,要么全部失败,是一个整体。MySQL 提供一种机制,包管我们达到这样的效果。事件还规定不同的客户端看到的
数据是不雷同的。
事件就是要做的或所做的事情,重要用于处理操纵量大,复杂度高的数据。假设一种场景:我给某个人转账,数据库肯定需要将我账户上的金额 update ,然后给对方的账户做 add 操纵等等,这样,就需要多条 MySQL 语句构成,那么全部这些操纵合起来,就构成了一个事件。
正如我们上面所说,一个 MySQL 数据库,可不止一个事件在运行,同一时刻,甚至有大量的请求被包装成事件,在向 MySQL 服务器发起事件处理请求。而每条事件至少一条 SQL ,最多很多 SQL ,这样如果大家都访问同样的表数据,在不加保护的情况,就绝对会出现问题。甚至,由于事件由多条 SQL 构成,那么,也会存在执行到一半出错大概不想再执行的情况,那么已经执行的怎么办呢?
以是,一个完整的事件,绝对不是简朴的 SQL 聚集,还需要满足如下四个属性


  • 原子性:一个事件(transaction)中的全部操纵,要么全部完成,要么全部不完成,不会结束在中间某个环节。事件在执行过程中发生错误,会被回滚(Rollback)到事件开始前的状态,就像这个事件从来没有执行过一样。
  • 同等性:在事件开始之前和事件结束以后,数据库的完整性没有被粉碎。这表现写入的资料必须完全符合全部的预设规则,这包含资料的准确度、串联性以及后续数据库可以自发性地完成预定的工作。
  • 隔离性:数据库允很多个并发事件同时对其数据进行读写和修改的本事,隔离性可以防止多个事件并发执行时由于交叉执行而导致数据的不同等。事件隔离分为不同级别,包括读未提交( Read uncommitted )读提交( read committed )可重复读( repeatable read )串行化( Serializable )
  • 恒久性:事件处理结束后,对数据的修改就是永世的,即便系统故障也不会丢失。
上面四个属性,可以简称为 ACID ;原子性(Atomicity,或称不可分割性);同等性(Consistency);隔离性(Isolation,又称独立性);恒久性(Durability)。
以是总结,所谓的事件,就是在 ACID 四大属性的加持之下,由一条大概多条 SQL 共同构建成的,我们就称为事件。
二、为什么会出现事件

事件被 MySQL 编写者设计出来,本质是为了当应用程序访问数据库的时间,事件能够简化我们的编程模子,不需要我们去思量各种各样的匿伏错误和并发问题。可以想一下当我们使用事件时,要么提交,要么回滚,我们不会去思量网络异常了,服务器宕机了,同时更改一个数据怎么办对吧?因此事件本质上是为了应用层服务的,而不是伴随着数据库系统天生就有的。
备注:我们后面把 MySQL 中的一行信息,称为一行纪录。
三、事件的版本支持

MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事件, MyISAM 不支持。我们可以使用指令 show engines\G 查看各种引擎的属性:


四、事件提交方式

事件的提交方式常见的有两种:


  • 主动提交
  • 手动提交
查看事件提交方式:show variables like 'autocommit';

我们可以用 SET 来改变 MySQL 的主动提交模式:SET AUTOCOMMIT=0; 禁止主动提交。SET AUTOCOMMIT=1; 开启主动提交。
五、事件常见操纵方式

1. 准备工作



  • 设置隔离级别
我们将隔离级别设置成最低是为了方便我们观察征象。
为了便于演示,我们将 mysql 的默认隔离级别设置成读未提交。具体操纵我们后面会具体讲,现在以使用为主:
  1.                                 set global transaction isolation level read uncommitted;
复制代码

设置好后需要重启终端,进行查看:select @@tx_isolation;:



  • 创建测试表
    1.                           create table if not exists account(
    2.                                   id int primary key,
    3.                                   name varchar(50) not null default '',
    4.                                   blance decimal(10,2) not null default 0.0
    5.                           )ENGINE=InnoDB DEFAULT CHARSET=UTF8;
    复制代码
以上是一个简朴的员工工资表的表布局。
2. 事件的正常操纵

(1)事件的开始与回滚

起首我们已经开启主动提交:

我们开始一个事件的语句是:start transaction; 大概 begin,下面先使用第一个:

我们开始一个事件后,从该语句今后的全部 SQL 语句都是同一个事件。我们开启另一个终端同时也启动一个事件:

起首我们当前的表是空的,我们以左边的终端为主,我们先创建一个生存点 s1,对应的语句为 savepoint s1;;然后我们往表里插入一个数据;接着再创建一个生存点 s2,然后再插入一个数据,如下图:

然后我们在另一个终端查看该表,是可以瞥见另一个终端插入的数据的:

上面全部的创建生存点、插入数据的操纵都是一个事件,那么我们操纵失误了,想要撤回 Mike 的数据,我们就可以定向地回滚,可以回滚到指定的位置,比方我们想撤回 Mike 的数据,我们回滚到 s2 的生存点即可,对应的语句为:rollback to s2;,此时我们再从另一个终端查看该表时,就会发现 Mike 的数据已经没有了,如下:

如果我们直接 rollback;,会回滚到最开始的地方。那么表中的数据也就没有了。现在我们把该事件提交,对应的语句为 commit;。
如果我们插入数据后没有 rollback 而是 commit 那么数据就会恒久化地生存到数据库中,这时间 rollback 也没有用了。
(2)客户端瓦解未 commit

假设我们正常开始一个事件,正常插入数据,此时是可以看到插入的数据的:

但是如果当我们的 mysql 异常瓦解,还没有 commit 会怎样呢?下面我们让 mysql 异常瓦解,直接按下 *ctrl + * 即可:

如图,我们可以看到,数据会主动回滚。但是如果我们 commit 之后异常瓦解,数据不会再受影响,由于数据已经恒久化。
(3)begin 操纵会主动更改提交方式,不会受MySQL是否主动提交影响

我们上面手动启动一个事件并不会受 MySQL 是否主动提交影响,比方我们现在把主动提交关掉:

我们再启动一个事件,插入数据等,重复上面的操纵:

如上,异常瓦解会主动回滚。

如上,commit 后再异常瓦解数据已经恒久化。
(4)单条 SQL 与事件的关系

我们知道,当我们启动一个事件的时间删除一个数据再手动 commit 之后,数据一定会被删除。但是我们将主动提交关闭,不启动事件,像我们正常一样使用单 SQL 语句删除呢?再异常瓦解会怎样呢?下面我们验证一下:

如上,我们主动提交关闭后,执行单条 SQL 语句异常退出后,数据也会回滚回来!这是为什么呢?由于主动提交已经被关闭了!需要我们手动提交才气生存数据!下面我们验证一下是否需要我们手动提交:

如上,我们手动 commit 后数据确实被删除了!说明我们的单 SQL 语句也是事件,只是以前系统默认打开主动提交,执行完 SQL 语句后就主动 commit 了!
以是根据上面四个场景,我们得出以下结论


  • 只要输入 begin 大概 start transaction,事件便必须要通过 commit 提交,才会恒久化,与是否设置 set autocommit 无关;
  • 事件可以手动回滚,同时,当操纵异常,MySQL 会主动回滚;
  • 对于 InnoDB 每一条 SQL 语言都默认封装成事件,主动提交(select 有特殊情况,由于 MySQLMVCC );
  • 从上面的例子,我们能看到事件自己的原子性(回滚),恒久性(commit)。
事件操纵留意事项


  • 如果没有设置生存点,也可以回滚,只能回滚到事件的开始。直接使用 rollback (条件是事件还没有提交);
  • 如果一个事件被提交了(commit),则不可以回退(rollback);
  • 可以选择回退到哪个生存点;
  • InnoDB 支持事件, MyISAM 不支持事件;
  • 开始事件可以使 start transaction 大概 begin
六、事件隔离级别

1. 初识隔离性



  • MySQL 服务大概会同时被多个客户端进程(线程)访问,访问的方式以事件方式进行;
  • 一个事件大概由多条 SQL 构成,也就意味着,任何一个事件,都有执行前,执行中,执行后的阶段。而所谓的原子性,实在就是让用户层,要么看到执行前,要么看到执行后。执行中出现问题,可以随时回滚。以是单个事件,对用户表现出来的特性,就是原子性;
  • 但究竟全部事件都要有个执行过程,那么在多个事件各自执行多个 SQL 的时间,就还是有大概会出现互相影响的情况。好比:多个事件同时访问同一张表,甚至同一行数据;
  • 数据库中,为了包管事件执行过程中尽量不受干扰,就有了一个重要特性:隔离性
  • 数据库中,允许事件受不同程度的干扰,就有了一种重要特性:隔离级别
下面举一个例子,假设有人向数据库进行 update 操纵,另外一个人向数据库进行 select 数据,那么如果两个事件对同一个数据库操纵,数据库先执行谁的呢?我们开始想到的大概是先 updateselect 数据,由于需要包管数据是最新的,但是这是有问题的!为什么呢?好比我们在出生之前,能不能看到过去的世界呢?不能,我们也不应该看得到!又好比已颠末世的故人,能不能看到我们今天的世界呢?也不能!以是回到事件,事件也是一样的!它不需要看到旧的数据,也不应该看到最新的数据,只需要看到每个事件到来时,它应该看到的数据,这就是隔离性!
那么回到上面的问题,updateselect 谁先执行呢?这就要取决于它们谁先到来了,如果 update 先来,那么肯定先执行 update,由于要保持事件的原子性。如果 update 执行的很快,select 执行的好久,大概在 update 执行完毕之后,select 还在执行,那么此时 select 应不应该更新后的数据呢?不应该!由于要包管事件的隔离性!
2. 隔离级别



  • 读未提交【Read Uncommitted】: 在该隔离级别,全部的事件都可以看到其他事件没有提交的执行效果。(实际生产中不大概使用这种隔离级别的),但是相当于没有任何隔离性,也会有很多并发问题,如脏读,幻读,不可重复读等,我们上面为了更好地观察征象,用的就是这个隔离性。
  • 读提交【Read Committed】 :该隔离级别是大多数数据库的默认的隔离级别(不是 MySQL 默认的)。它满足了隔离的简朴定义:一个事件只能看到其他的已经提交的事件所做的改变。这种隔离级别会引起不可重复读,即一个事件执行时,如果多次 select, 大概得到不同的效果。
  • 可重复读【Repeatable Read】: 这是 MySQL 默认的隔离级别,它确保同一个事件,在执行中,多次读取操纵数据时,会看到同样的数据行。但是会有幻读问题。
  • 串行化【Serializable】: 这是事件的最高隔离级别,它通过强制事件排序,即事件需要一个一个排队处理,使之不大概相互辩论,从而办理了幻读的问题。它在每个读的数据行上面加上共享锁,但是大概会导致超时和锁竞争(这种隔离级别太极度,实际生产根本不使用)。
3. 事件隔离级别的设置与查看



  • 查看
查看全局隔级别:
  1.                                 select @@global.tx_isolation;
复制代码
查看此次会话(登录)全局隔级别:
  1.                                 select @@session.tx_isolation;
  2.                         或  select @@tx_isolation;
复制代码
以上两种查看隔离级别的区别在于,select @@global.tx_isolation; 是全局的隔离级别;select @@tx_isolation; 在此次登录时默认读取全局的隔离级别,然后拷贝一份给自己,它的生命周期是在当我们开始登录到退出客户端。


  • 设置隔离级别
    1.                           set [session | global] transaction isolation level { read uncommitted | read
    2.                           commited | repeatable read | serializable }       
    复制代码
此中 [session | global] 是设置当前会话大概全局隔离级别;{} 内部是对应的隔离级别。
设置当前会话隔离性,另起一个会话,不会影响另一个会话,只影响当前会话;而设置全局隔离性,另起一个会话,会被影响。
4. 读未提交 — RU

我们在上面也设置过了我们当前的隔离级别是 RU,如下:

接下来我们开启两个事件并发起来,我们在此中一个事件中插入数据、删除数据、修改数据等,还没有 commit 前,在另一个事件中都可以查看得到,如下:

这就是读未提交,我们在另一个事件中读到了别人还没提交的事件!一个事件在执行中,读到另一个执行中事件的更新(或其他操纵)但是未 commit 的数据,这种征象叫做脏读
5. 读提交 — RC

起首我们将隔离级别改成 RC

当我们将两个终端的隔离级别都设置为 RC 后,下面我们开始做一些实验。起首我们在两个终端分别启动事件,在此中一个终端插入、修改数据,观察另一个终端是否能见:

如上图,我们发现在一个事件在进行期间,另一个事件进行查看是不能瞥见它的增长或修改的,而当前事件自己可以瞥见吗?我们试一下:

是可以的;当我们将第一个终端的事件 commit 之后,看看另一个事件能否瞥见修改之后的表:

如上图,我们发现在它进行 commit 之后,另一个事件还没有 commit 也能看到对应的修改!这就是读提交
但是,第二个终端此时还在当前事件中,并未 commit,那么就造成了,同一个事件内,同样的读取,在不同的时间段(仍旧还在事件操纵中!),读取到了不同的值,这种征象叫做不可重复读
那么不可重复读是问题吗?是的!在和我们并发运行的事件中,它做了修改表的数据并 commit,而导致我在每次查看数据的时间都是不一样的,这会导致一些问题出现,比方我们在用使用这个事件查看表进行统计数据时,统计到了一半,突然再查一下数据发现数据不一样了,就会导致我们需要重新统计!这就是由于不可重复读大概会引发的问题!
6. 可重复读 — RR

由于 MySQL 默认的隔离级别就是 RR 级别,以是我们重新启动 MySQL 服务即可更换为 RR 级别:

下面我们也并发启动两个事件,此中一个进行修改、新增数据,观察另一个事件查看的情况:

我们看到, 另一个事件是看不见的,这也正常,我们在 RC 级别都看不见,RR 级别也应该看不见,但是下面我们将修改的数据 commit 呢?再看看会有什么变革:

如图,当我们 commit 之后再查看,还是看不见修改的数据。如果我们也将这个事件 commit,再查看,就可以看到修改的数据了:

如上图,这就是可重复读。
在可重复读中,我们假设第一个终端为终端A,第二个为终端B,多次查看,发现终端A在对应事件中 insert 的数据,在终端B的事件周期中,也没有什么影响,也符合可重复的特点。但是,一般的数据库在可重复读情况的时间,无法屏蔽其他事件 insert 的数据,为什么呢?由于隔离性实现是对数据加锁完成的,而 insert 待插入的数据由于并不存在,那么一般加锁无法屏蔽这类问题,以是会造成虽然大部门内容是可重复读的,但是 insert 的数据在可重复读情况被读取出来,导致多次查找时,会多查找出来新的纪录,就如同产生了幻觉。这种征象,叫做幻读(phantom read)。很显着,MySQLRR 级别的时间,是办理了幻读问题的,具体的办理方法我们就不研究了。
7. 串行化

串行化就是对全部操纵全部加锁,进行串行化,不会有问题,但是只要串行化,效率很低,险些完全不会被接纳。
接下来我们将隔离级别更换为串行化:

接下来我们启动两个事件,分别进行查看数据,是没有问题的,由于两个读取不会串行化,共享锁:

然后我们在终端A修改数据,在终端B读取,即进行读写操纵,终端A会卡住,由于终端B中的事件还没有结束:

当终端B中的事件结束,终端A中的事件才气继续:

反过来也一样,在终端A修改数据,在终端B读取,终端B中的读取也会卡住:

当终端A的事件结束终端B才气继续读取:

总结:


  • 此中隔离级别越严格,安全性越高,但数据库的并发性能也就越低,每每需要在两者之间找一个均衡点;
  • 不可重复读的重点是修改和删除:同样的条件,你读取过的数据,再次读取出来发现值不一样了;幻读的重点在于新增:同样的条件, 第1次和第2次读出来的纪录数不一样;
  • 说明: mysql 默认的隔离级别是可重复读,一般情况下不要修改;
  • 上面的例子可以看出,事件也有长短事件这样的概念。事件间互相影响,指的是事件在并行执行的时间,即都没有 commit 的时间,影响会比较大。
七、同等性



  • 事件执行的效果,必须使数据库从一个同等性状态,变到另一个同等性状态。当数据库只包含事件成功提交的效果时,数据库处于同等性状态。如果系统运行发生中断,某个事件尚未完成而被迫中断,而改未完成的事件对数据库所做的修改已被写入数据库,此时数据库就处于一种不正确(不同等)的状态。因此同等性是通过原子性来包管的;
  • 实在同等性和用户的业务逻辑强干系,一般 MySQL 提供技术支持,但是同等性还是要用户业务逻辑做支撑,也就是,同等性,是由用户和 MySQL 共同决定的;
  • 而技术上,通过 AID 包管 C,也就是有了原子性、恒久性、隔离性,就能包管同等性。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

飞不高

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表