2020-03-01

MYSQL事务-隔离级别

事务是什么?

事务简言之就是一组SQL执行要么全部成功,要么全部失败。MYSQL的事务在存储引擎层实现。

事务都有ACID特性:

[if !supportLists]· [endif]

原子性(Atomicity):一个事务必须被视为一个不可分割的单元;

[if !supportLists]· [endif]

[if !supportLists]· [endif]

一致性(Consistency):数据库总是从一种状态切换到另一种状态;

[if !supportLists]· [endif]

[if !supportLists]· [endif]

隔离性(Isolation):通常来说,事务在提交前对于其他事务不可见;

[if !supportLists]· [endif]

[if !supportLists]· [endif]

持久性(Durablity):一旦事务提交,所做修改永久保存数据库;

[if !supportLists]· [endif]

事务最常用的例子就是银行转账。假设polo需给lynn转账1000元,如下步骤:

[if !supportLists]· [endif]

确认polo账户余额高于1000元;

[if !supportLists]· [endif]

[if !supportLists]· [endif]

从polo的账户余额减去1000元;

[if !supportLists]· [endif]

[if !supportLists]· [endif]

将lynn的账户余额增加1000元;

[if !supportLists]· [endif]

SQL语句如下:

mysql> BEGIN;mysql> SELECT balance FROM bank_account WHERE uid=10001;mysql> UPDATE bank_account SET balance=balance-1000 WHERE uid=10001;mysql> UPDATE bank_account SET balance=balance+1000 WHERE uid=10002;mysql> COMMIT;

注:mysql启动事务可使用BEGIN或者START TRANSACTION;

上述三个步骤执行在一个事务中就能够保证数据的完整性,要么全部成功,要么全部失败。

MYSQL提供两种事务型引擎:Innodb和NDBCluster。默认采用自动提交模式,执行一条语句自动COMMIT。通过AUTOCOMMIT变量可启用或者禁用自动提交模式:

mysql> SHOW VARIABLES LIKE "AUTOCOMMIT";

+---------------+-------+

| Variable_name | Value |

+---------------+-------+

| autocommit    | ON    |

+---------------+-------+1 row in set (0.00 sec)


mysql> SET AUTOCOMMIT=1

AUTOCOMMIT=1表示开启默认提交,0表示关闭默认提交需要手动提交。

事务隔离级别

事务隔离性的解释:通常情况下,事务在提交之前对于其他事务不可见。

数据库有四种隔离级别,当然MYSQL也是如此。分别为:

[if !supportLists]· [endif]

READ UNCOMMITED(未提交读)

[if !supportLists]· [endif]

[if !supportLists]· [endif]

READ COMMITED(已提交读)

[if !supportLists]· [endif]

[if !supportLists]· [endif]

EPEATABLE READ(可重复读)

[if !supportLists]· [endif]

[if !supportLists]· [endif]

SEAIALIZABLE(可串行化)

[if !supportLists]· [endif]

本人理解 : 隔离级别就是决定一个事务的修改另一个事务什么情况下可见。

书本解释 : 隔离级别都规定了一个事务中所做修改,哪些在事务内和事务间是可见的。

上面两段理解的区别在于是否存在事务内可见性的规定。我在各个级别似乎没有看到

下面开始说明MYSQL的四种隔离级别,先准备一张学生表:

mysql> CREATE TABLE `student` (

 `id` int(11) NOT NULL AUTO_INCREMENT,

 `name` varchar(32) NOT NULL DEFAULT '',

 PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

只有id(主键自增)与name字段

[if !supportLists]· [endif]

READ UNCOMMITTED(未提交读)

[if !supportLists]· [endif]

事务中修改没有提交对其他事务也是可见的,俗称脏读。非常不建议使用。

[if !supportLists]· [endif]

实例演示

[if !supportLists]· [endif]

客户端A和B设置隔离级别为未提交读

[if !supportLists]· [endif]

mysql> SET SESSION TX_ISOLATION='READ-UNCOMMITTED';

[if !supportLists]· [endif]

客户端A与B开启事务并查询student

[if !supportLists]· [endif]

mysql> BEGIN;mysql> SELECT * FROM student;

Empty set (0.00 sec)

[if !supportLists]· [endif]

客户端A和B都是空数据

[if !supportLists]· [endif]

客服端B插入一条新的数据

[if !supportLists]· [endif]

mysql> INSERT INTO student(name) VALUES("polo");Query OK, 1 row affected (0.00 sec)

[if !supportLists]· [endif]

此时事务未提交,客服端A查看student表

[if !supportLists]· [endif]

$ SELECT * FROM student;

mysql> SELECT * FROM student;

+----+------+

| id | name |

+----+------+

|  1 | polo |

+----+------+1 row in set (0.00 sec)

[if !supportLists]· [endif]

客户端A看到B未提交的修改

[if !supportLists]· [endif]

客户端B执行回滚操作

[if !supportLists]· [endif]

mysql> ROLLBACK

[if !supportLists]· [endif]

成功之后,客户端A查看student表

[if !supportLists]· [endif]

mysql> SELECT * FROM student;Empty set (0.00 sec)

[if !supportLists]· [endif]

客户端A查看数据为空

[if !supportLists]· [endif]

以上可以看出未提交读隔离级别的危险性,对于一个没有提交事务所做修改对另一个事务是可见状态,容易造成脏读。非特殊情况不得使用此级别

[if !supportLists]· [endif]

[if !supportLists]· [endif]

READ COMMITTED(提交读)多数数据库系统默认为此级别(MYSQL不是)。已提交读级别即为一个事务只能已提交事务所做的修改,也就解决了未提交读的问题,即脏读的问题。

[if !supportLists]· [endif]

实例演示

[if !supportLists]· [endif]

客户端A和B设置隔离级别为已提交读

[if !supportLists]· [endif]

mysql> SET SESSION TX_ISOLATION='READ-COMMITTED';

[if !supportLists]· [endif]

客户端A与B开启事务并查询student

[if !supportLists]· [endif]

mysql> BEGIN;mysql> SELECT * FROM student;

Empty set (0.00 sec)

[if !supportLists]· [endif]

客户端A和B都为空

[if !supportLists]· [endif]

客服端B插入一条新的数据,不提交

[if !supportLists]· [endif]

mysql> INSERT INTO student (name) VALUES('polo');

[if !supportLists]· [endif]

客户端A查看student

[if !supportLists]· [endif]

mysql> SELECT * FROM student;Empty set (0.00 sec)

[if !supportLists]· [endif]

注意这里与上面不同了,在客户端B没有提交事务情况下无数据

[if !supportLists]· [endif]

下面客户端B提交事务

[if !supportLists]· [endif]

mysql> COMMIT;

[if !supportLists]· [endif]

客户端A再次查看student表。

[if !supportLists]· [endif]

mysql> SELECT * FROM student;

+----+------+

| id | name |

+----+------+

|  1 | polo |

+----+------+1 row in set (0.00 sec)

[if !supportLists]· [endif]

成功读取到客户

[if !supportLists]· [endif]

从上面的示例可以看出,提交读没有了未提交读的问题,但我们可以看到在客户端A的一个事务中执行两次同样的SELECT语句得到不同结果,因此已提交读又被称为不可重复读。同样筛选条件可能得到不同的结果。

[if !supportLists]· [endif]

[if !supportLists]· [endif]

REPEATABLE READ(可重复读)

[if !supportLists]· [endif]

如其名也,解决已提交读不可重复读取的问题。

[if !supportLists]· [endif]

示例演示

[if !supportLists]· [endif]

客户端A和B设置隔离级别为可重复读

[if !supportLists]· [endif]

mysql> SET SESSION tx_isolation='REPEATABLE-READ'

[if !supportLists]· [endif]

客户端A与B开启事务并查看

[if !supportLists]· [endif]

mysql> BEGIN;

mysql> SELECT * FROM student;

+----+------+

| id | name |

+----+------+

|  1 | polo |

+----+------+1 rows in set (0.00 sec)

[if !supportLists]· [endif]

客服端B更新polo为jeff,并提交事务

[if !supportLists]· [endif]

mysql> UPDATE student SET name='jeff' WHERE id=1;mysql> COMMIT

[if !supportLists]· [endif]

客户端A查看student表

[if !supportLists]· [endif]

mysql> SELECT * FROM student;

+----+------+

| id | name |

+----+------+

|  1 | jeff |

+----+------+1 rows in set (0.00 sec)

[if !supportLists]· [endif]

注意客户端A查看数据未变,没有不可重复读问题

[if !supportLists]· [endif]

客户端A提交事务,并查看student表

[if !supportLists]· [endif]

mysql> COMMIT;

mysql> SELECT * FROM student;

+----+------+

| id | name |

+----+------+

|  1 | polo |

+----+------+1 rows in set (0.00 sec)

[if !supportLists]· [endif]

上面实例可知,可重复读两次读取内容一样。数据库这级别并没有解决幻读的问题。但是MYSQL在可重复读基础上增加了MVCC机制解决了此问题,实例无法演示幻读效果。

[if !supportLists]· [endif]

那什么是幻读?首先,可重复读锁定范围为当前查询到的内容,如执行

[if !supportLists]· [endif]

mysql> SELECT * FROM student WHERE id>=1

[if !supportLists]· [endif]

锁定的即id>=1查到的行,为行级锁。如另一事务执行并默认提交以下语句

[if !supportLists]· [endif]

mysql> INSERT INTO student (name) VALUES ('peter');

[if !supportLists]· [endif]

新增的这行并没有被锁定,此时读取student

[if !supportLists]· [endif]

mysql> SELECT * FROM student WHERE id>=1;

+----+---------+| id | name    |

+----+---------+

|  1 | polo    ||  2 | peter   |

+----+---------+

2 rows in set (0.00 sec)

[if !supportLists]· [endif]

便出现了幻读

[if !supportLists]· [endif]

除了使用MYSQL的MVCC机制,还可以使用可串行化隔离级别解决此问题。

[if !supportLists]· [endif]

[if !supportLists]· [endif]

SEAIALIZABLE(可串行化)

[if !supportLists]· [endif]

可串行化是最高隔离级别,强制事务串行执行。执行串行了也就解决了一切的问题,这个级别只有在对数据一致性要求非常严格且没用并发的情况下使用

[if !supportLists]· [endif]

实例演示

[if !supportLists]· [endif]

客户端A和B设置隔离级别为可串行化

[if !supportLists]· [endif]

mysql> SET SESSION tx_isolation='SERIALIZABLE';

[if !supportLists]· [endif]

客户端A执行查询

[if !supportLists]· [endif]

mysql> SELECT * FROM student WHERE id<4;

+----+---------+| id | name    |

+----+---------+

|  1 | polo    ||  2 | peter   |

+----+---------+

2 rows in set (0.00 sec)

[if !supportLists]· [endif]

客户端B执行新增

[if !supportLists]· [endif]

mysql> INSERT INTO student (name) VALUES('tiger');

[if !supportLists]· [endif]

好的!效果出现了,此时我们会发现INSERT语句被阻塞执行,原因就是A执行了查询表student同时满足id<4,已被锁定。如果查询表student条件为id<3,则新增语句可正常执行。

[if !supportLists]· [endif]

隔离级别对照图

隔离级别                                             脏读   不可重复读  幻读     加锁读

未提交读  read uncommited             YES       YES          YES        NO

提交读  read commited                    NO            YES      YES          NO

可重复读 repeatable read                NO            NO         YES          NO

线性化 seaializable                          NO           NO           NO            YES

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,752评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,100评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,244评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,099评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,210评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,307评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,346评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,133评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,546评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,849评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,019评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,702评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,331评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,030评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,260评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,871评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,898评论 2 351

推荐阅读更多精彩内容

  • 1、7月9号天还没亮,愚耕就从隐蔽的角落里走了出来,要早早地赶到那职介所那里去等着,也还可以好好感受一下广州清晨的...
    秩堂阅读 338评论 0 0
  • 序:我相信笨功夫的力量 *文案是一门功夫,他需要热情和恒心 *我相信笨功夫的力量,它让我能够坦然面对艰苦的磨砺,它...
    一泓读书阅读 361评论 0 3
  • 1.错误日志(log_error) 记录启动\关闭\日常运行过程中,状态信息,警告,错误 错误日志配置 默认就是开...
    桑正阅读 218评论 0 0
  • 学习笔记-模拟实验 MySQL 数据库增量数据恢复案例 一、场景概述 MySQL数据库每日零点自动全备 某天上午1...
    博客用户阅读 161评论 0 0
  • title:关系型数据库date:2020-03-02 15:49:00 一、定义 关系型数据库,是指采用了关系模...
    城管懋_1b25阅读 328评论 0 0