Mysql事务笔记

1. 事务简介

事务具有ACID特性

  • A 原子性,原子性指整个数据库事务是不可分割的工作单位。
  • C 表示一致性,也就是说一致性指事务将数据库从一种状态转变为另一种一致的状态,在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。
  • I 表示隔离性,隔离性也叫做并发控制、可串行化或者锁。
  • D 表示持久性,表示事务一旦提交了,其结果就是永久性的。

2. 事务分类

  • 扁平事务
  • 带有保存点的扁平事务
  • 链事务
  • 分布式事务
  1. 扁平事务
    扁平事务是最简单的事务,所有操作在同一层次,扁平事务的主要缺点是不能提交或回滚事务的某一部分.
BEGIN WORK
  Operation 1
  Operation 2
  ...
  Operation n
COMMIT WORK
  1. 带有保存点的扁平事务
    这种事务除了支持扁平事务支持的操作外,允许在事务执行过程中回滚到同一事务中较早的一个状态,这是因为可能某些事务在执行过程中出现的错误并不会对所有的操作都无效,放弃整个事务不合乎要求,开销也太大。保存点用来通知系统应该记住事务当前的状态,以便以后发生错误时,事务能回到该状态。
  2. 链事务
    链事务,就是指回滚时,只能恢复到最近一个保存点;而带有保存点的扁平事务则可以回滚到任意正确的保存点。
  3. 嵌套事务
BEGIN WORK
    SubTransaction1:
     BEGIN WORK 
      SubOperationX 
     COMMIT WORK
    SubTransaction2: 
      BEGIN WORK 
        SubOperationY 
      COMMIT WORK 
    ...
    SubTransactionN: 
      BEGIN WORK 
        SubOperationN 
      COMMIT WORK 
COMMIT WORK
  1. 分布式事务
    分布式事务通常是指在一个分布式环境下运行的扁平事务,因此需要根据数据所在位置访问网络中的不同节点。

3. 隐含的commit操作

  • DDL语句:ALTER DATABASEALTER EVENTALTER PROCEDUREALTER TABLEALTER VIEWCREATE TABLEDROP TABLERENAME TABLETRUNCATE TABLE
  • 修改MYSQL架构的的语句:CREATE USERDROP USERGRANTRENAME USERREVOKESET PASSWORD
  • 管理语句: ANALYZE TABLECACHE INDEXCHECK TABLELOAD INDEX INTO CACHEOPTIMIZE TABLEREPAIRE TABLE

4. 事务的隔离级别

隔离级别 脏读(Dirty Read) 不可重复读(NotReplacable Read) 幻读(Phantom Read)
未提交读(Read uncommited) 可能 可能 可能
已提交读(Read commited) 不可能 可能 可能
可重复读(Repeatable Read) 不可能 不可能 可能
可串行化(Serializable) 不可能 不可能 不可能
  • 脏读: 一个事务读取到另一个事务没有提交的数据
    比如:事务T1更新了一行记录的内容,但是并没有提交所做的修改。事务T2读取到了T1更新后的行,然后T1执行回滚操作,取消了刚才所做的修改。现在T2所读取的行就无效了;
  • 不可重复读: 在同一事务中,两次读取同一数据,得到内容不同;
    比如:事务T1读取一行记录,紧接着事务T2修改了T1刚才读取的那一行记录。然后T1又再次读取这行记录,发现与刚才读取的结果不同。这就称为“不可重复”读,因为T1原来读取的那行记录已经发生了变化;
  • 幻读:同一事务中,用同样的操作读取两次,得到的记录数不相同;
    比如:事务T1读取一条指定的WHERE子句所返回的结果集。然后事务T2新插入 一行记录,这行记录恰好可以满足T1所使用的查询条件中的WHERE子句的条件。然后T1又使用相同的查询再次对表进行检索,但是此时却看到了事务T2刚才插入的新行。这个新行就称为“幻像”,因为对T1来说这一行就像突然出现的一样。

隔离级别越低,事务请求的锁越少或保持锁的时间就越短。InnoDB存储引擎默认的支持隔离级别是REPEATABLE READ;在这种默认的事务隔离级别下已经能完全保证事务的隔离性要求,即达到SQL标准的SERIALIZABLE级别隔离。

我们可以可以用SET TRANSACTION语句改变单个会话或者所有新进连接的隔离级别。它的语法如下:

SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}

注意:默认的行为(不带session和global)是为下一个(未开始)事务设置隔离级别。如果使用GLOBAL关键字,语句在全局对从那点开始创建的所有新连接(除了不存在的连接)设置默认事务级别。你需要SUPER权限来做这个。使用SESSION 关键字为将来在当前连接上执行的事务设置默认事务级别。 任何客户端都能自由改变会话隔离级别(甚至在事务的中间),或者为下一个事务设置隔离级别。


在RC模式下,数据的读取都是不加锁的,但是数据的写入,修改和删除是需要加锁的。


锁的概念

  • 共享锁 (S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
  • 排它锁 (X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。
    • 共享锁和排他锁都是行级锁,行级锁对系统开销较大,但处理高并发较好。
    • 对于 insert,update,delete 语句 InnoDB 会自动给对应数据加排它锁,对于 select 语句 InnoDB 默认不会加任何锁(因为 MySQL 引入了一种快照读的机制,减少了锁的处理,提升了并发能力),除非显示的给记录加锁。

事例

事务主要处理操作量大,复杂度高的数据。(事务要么全执行,要么全不执行)

开始:START TRANSACTION或BEGIN开始一项事务
提交:COMMIT,提交事务,使变更称为永久变更
回滚:ROLLBACK可以回滚当前事务,取消变更
此外,SET AUTOCOMMIT = {0|1},可以禁用或启用默认的autocommit模式

分析网上购书例子

某书(book_id=123)在数据库中只有一本,而这个时候甲、乙几乎同时对这本书发出购买请求;分析过程。
1、对于用户甲来说,他的动作比乙稍快,其触发的过程如下
  * SELECT book_num FROM book WHERE bood_id = 123;
  * 当book_num的值大于,确定购买行为并更新book_num
  * UPDATE book SET book_num = book_num-1 WHERE book_id = 123;
  * 购书成功
2、对于用户乙
  * SELECT book_num FROM book WHERE book_id = 123;
  * 此时甲刚执行第一步操作,还没有来得及做第二步操作,所以book_num还是 >0
  * UPDATE book SET book_num=book_num-1 WHERE book_id = 123
  * 购书成功

表面上看来甲,乙购书都成功了,但是数据库都只有一本书,此时怎么可能都买到书呢?看下数据库,
发现book_num=-1,这当然是不允许的。

这个时候我们可以用事务

BEGIN;
SELECT book_num FROM book WHERE book_id = 123;
....
UPDATE book SET book_num=book_num-1 WHERE book_id=123;
COMMIT;

此时可以解决问题吗?当然不能。实际应该如下

BEGIN;
SELECT book_num FROM book WHERE book_id = 123 FOR UPDATE;
....
UPDATE book SET book_num=book_num-1 WHERE book_id=123;
COMMIT;
加上FOR UPDATE的时候会加上一个行锁,如果此时事务没有完全结束,那么其他的事务在使用SELECT .... FOR UPDATE请求的时候就会处于等待状态。

悲观锁和乐观锁

  • 悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。
  • 乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。
  1. 在数据库中,悲观锁的流程如下:

在对任意记录进行修改前,先尝试为该记录加上[排他锁](exclusive locking)。
如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。 具体响应方式由开发者根据实际需要决定。
如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。
其间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或直接抛出异常。

  1. 乐观锁

在关系数据库管理系统里,乐观并发控制(又名“乐观锁”,Optimistic Concurrency Control,缩写“OCC”)是一种并发控制的方法。它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回滚。
乐观锁基本上是基于版本号实现的,一般通过为数据库表中添加一个"version"字段来实现。读取出数据时,将版本号一同读出,之后更新时,对此版本号➕1,此时,将提交数据的版本数据与数据库表对应记录的当前版本号进行比较,如果提交的数据版本号>数据库当前版本号,则更新数据,否则则认为是过期数据。

MVCC在MySQL的InnoDB中的实现
在InnoDB中,会在每行数据后添加两个额外的隐藏的值来实现MVCC,这两个值一个记录这行数据何时被创建,另外一个记录这行数据何时过期(或者被删除)。 在实际操作中,存储的并不是时间,而是事务的版本号,每开启一个新事务,事务的版本号就会递增。 在可重读Repeatable reads事务隔离级别下:

  • SELECT时,读取创建版本号<=当前事务版本号,删除版本号为空或>当前事务版本号。
  • INSERT时,保存当前事务版本号为行的创建版本号
  • DELETE时,保存当前事务版本号为行的删除版本号
  • UPDATE时,插入一条新纪录,保存当前事务版本号为行创建版本号,同时保存当前事务版本号到原来删除的行。

通过MVCC,虽然每行记录都需要额外的存储空间,更多的行检查工作以及一些额外的维护工作,但可以减少锁的使用,大多数读操作都不用加锁,读数据操作很简单,性能很好,并且也能保证只会读取到符合标准的行,也只锁住必要行。

Mysql的InnoDB设置锁属性

  • Mysql的InnoDB默认使用的是RR级别,我们可以设置某个session的级别
SET session transaction isolationg level read commited;
SET session binlog_format= 'ROW'

读和读的区别

MySQL中的读,和事务隔离级别中的读,是不一样的。
在RR级别中,通过MVCC机制,虽然让数据变得可重复读,但我们读到的数据可能是历史数据,是不及时的数据,不是数据库当前的数据!这在一些对于数据的时效特别敏感的业务中,就很可能出问题。
对于这种读取历史数据的方式,我们叫它快照读 (snapshot read),而读取数据库当前版本数据的方式,叫当前读 (current read)。很显然,在MVCC中:

  • 快照读:就是select
select * from table ....;
  • 当前读:特殊的读操作,插入/更新/删除操作,属于当前读,处理的都是当前的数据,需要加锁。
select * from table where ? lock in share mode;  #共享锁
select * from table where ? for update;          #排他锁
insert;
update ;
delete;

事务的隔离级别实际上都是定义了当前读的级别,MySQL为了减少锁处理(包括等待其它锁)的时间,提升并发能力,引入了快照读的概念,使得select不用加锁。而update、insert这些“当前读”,就需要另外的模块来解决了。

写("当前读")
为了解决当前读中的幻读问题,MySQL事务使用了Next-Key锁。
Next-Key锁是行锁和GAP(间隙锁)的合并,行锁上文已经介绍了,接下来说下GAP间隙锁。
行锁可以防止不同事务版本的数据修改提交时造成数据冲突的情况。但如何避免别的事务插入数据就成了问题。

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

推荐阅读更多精彩内容