MySQL数据库

Mysql事务笔记

2016-07-09  本文已影响253人  yaya_pangdun

1. 事务简介

事务具有ACID特性

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操作

4. 事务的隔离级别

隔离级别 脏读(Dirty Read) 不可重复读(NotReplacable Read) 幻读(Phantom Read)
未提交读(Read uncommited) 可能 可能 可能
已提交读(Read commited) 不可能 可能 可能
可重复读(Repeatable Read) 不可能 不可能 可能
可串行化(Serializable) 不可能 不可能 不可能

隔离级别越低,事务请求的锁越少或保持锁的时间就越短。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模式下,数据的读取都是不加锁的,但是数据的写入,修改和删除是需要加锁的。


锁的概念

事例

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

开始: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事务隔离级别下:

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

Mysql的InnoDB设置锁属性

SET session transaction isolationg level read commited;
SET session binlog_format= 'ROW'

读和读的区别

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

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间隙锁。
行锁可以防止不同事务版本的数据修改提交时造成数据冲突的情况。但如何避免别的事务插入数据就成了问题。

上一篇 下一篇

猜你喜欢

热点阅读