mysql事务的概念及隔离级别

1 事务概念

        MySQL 事务主要用于处理操作量大,复杂度高的数据。比如说,在人员管理系统中,你删除一个人员,你既需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就构成一个事务。

1.1 事务特点

        数据库通过原子性(A)、隔离性(I)、持久性(D)来保证一致性(C):

  • Atomicity(原子性):一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作
  • Consistency(一致性):数据库总是从一个一致性状态转换到另一个一致状态。下面的银行列子会说到
  • Isolation(隔离性):通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的。注意这里的“通常来说”,后面的事务隔离级级别会说到
  • Durability(持久性):一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。(持久性的安全性与刷新日志级别也存在一定关系,不同的级别对应不同的数据安全级别)

        其中一致性是目的,原子性、隔离性、持久性是手段。因此数据库必须实现AID三大特性才有可能实现一致性。下表描述A、C、I、D的关系及实现手段:

目标Consistency
手段AtomicityIsolationDurability
实现undo log锁+mvccredo log

1.2 事务并发带来的读异常

  • 脏读:一个事务读到了另一个未提交事务修改过的数据
  • 不可重复读:一个事务只能读到另一个已经提交的事务修改过的数据,并且其他事务每对该数据进行一次修改并提交后,该事务都能查询得到最新值。(不可重复读在读未提交和读已提交隔离级别都可能会出现)
  • 幻读:一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来。(幻读在读未提交、读已提交、可重复读隔离级别都可能会出现)

1.3 不可重复读和幻读的区别

  • 不可重复读的重点是修改:在同一事务中,同样的条件,第一次读的数据和第二次读的数据不一样。(因为中间有其他事务提交了修改)
  • 幻读的重点在于新增或者删除:在同一事务中,同样的条件,,第一次和第二次读出来的记录数不一样。(因为中间有其他事务提交了插入/删除)

1.4 解决方案

1.4.1 加锁

        在读取数据前,对其加锁,阻止其他事务对数据进行修改

  • 共享锁与排他锁

    • 共享锁(读锁):其他事务可以读,但不能写

    • 排他锁(写锁) :其他事务不能读取,也不能写

  • 乐观锁与悲观锁

    • 乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。 乐观锁不能解决脏读的问题。

    • 悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。

  • 粒度锁

    • 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低

    • 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高

    • 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般

1.4.2 多版本并发控制(MVCC)

        不用加任何锁, 通过一定机制生成一个数据请求时间点的一致性数据快照 (Snapshot), 并用这个快照来提供一定级别 (语句级或事务级) 的一致性读取。

        MVCC只在读已提交可重复读两个隔离级别下工作,MVCC不能解决幻读,幻读还是得用锁。 MVCC主要解决几个问题:

        a. 读写之间阻塞的问题:通过 MVCC 可以让读写互相不阻塞,即读不阻塞写,写不阻塞读,这样就可以提升事务并发处理能力。

        b. 降低了死锁的概率:因为 InnoDB 的 MVCC 采用了乐观锁的方式,读取数据时并不需要加锁,对于写操作,也只锁定必要的行。

        c. 解决一致性读的问题:一致性读也被称为快照读,当我们查询数据库在某个时间点的快照时,只能看到这个时间点之前事务提交更新的结果,而不能看到这个时间点之后事务提交的更新结果。

1.4.3 幻读解决方案

        MVCC+间隙锁

2 事务隔离

        SQL标准定义了4类隔离级别,每一种级别都规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。

2.1 隔离级别

  • 读未提交
    • 所有事务都可以看到其他未提交事务的执行结果
    • 本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少
    • 引发问题——脏读(Dirty Read):读取到了未提交的数据
  • 读已提交
    • 大多数数据库系统的默认隔离级别,不是MySQL默认的
    • 满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变
    • 引发问题——不可重复读(Nonrepeatable Read)。导致这种情况的原因可能有:
      • 有一个交叉的事务有新的commit,导致了数据的改变
      • 一个数据库被多个实例操作时,同一事务的其他实例在该实例处理其间可能会有新的commit
  • 可重复读
    • MySQL的默认事务隔离级别
    • 它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行
    • 引发问题——幻读(Phantom Read):当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行
    • InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决幻读问题;InnoDB还通过间隙锁解决幻读问题
  • 可串行化
    • 最高的隔离级别,通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁
    • 可能导致大量的超时现象和读写锁竞争

        innoDB是mysql默认的存储引擎,默认的隔离级别是RR(Repeatable Read),并且在RR的隔离级别下更进一步,通过多版本并发控制(MVCC)解决不可重复读问题,加上间隙锁(也就是并发控制)解决幻读问题。因此innoDB的RR隔离级别其实实现了串行化级别的效果,而且保留了比较好的并发性能。

2.2 隔离级别对比

读未提交读已提交可重复读可串行读
脏读可能不会不会不会

不可

重复读

可能可能不会不会
幻读可能可能可能不会

3 事务日志

        mysql的日志文件有六种:

  • 重做日志(redo log)
  • 回滚日志(undo log)
  • 二进制日志(binlog)
  • 错误日志(errorlog)
  • 慢查询日志(slow query log)
  • 一般查询日志(general log)
  • 中继日志(relay log)

        其中重做日志和回滚日志与事务操作息息相关,二进制日志也与事务操作有一定的关系,这三种日志,对理解MySQL中的事务操作有着重要的意义。

3.1 redo日志(持久性)

        防止在发生故障的时间点,尚有脏页未写入磁盘,在重启mysql服务的时候,根据redo log进行重做,从而达到事务的持久性这一特性。

3.2 undo日志(原子性)

        保存了事务发生之前的数据的一个版本,可以用于回滚,同时可以提供多版本并发控制下的读(MVCC),也即非锁定读

3.3 binlog日志

        binlog日志主要作用:

  • 在主从复制中,从库利用主库上的binlog进行重播,实现主从同步
  • 用于数据库的基于时间点的还原

    binlog 日志有三种格式:

  • STATMENT : 基于SQL语句的复制,每一条会修改数据的sql语句会记录到 binlog 中 

    • 优点: 不需要记录每一行的变化,减少了binlog日志量,节约了IO, 从而提高了性能

    • 缺点: 在某些情况下会导致主从数据不一致,比如执行sysdate()  、 slepp() 等 
  • ROW : 基于行的复制,不记录每条sql语句的上下文信息,仅需记录哪条数据被修改了 

    • 优点: 不会出现某些特定情况下的存储过程、或function、或trigger的调用和触发无法被正确复制的问题 
    • 缺点: 会产生大量的日志,尤其是alter table的时候会让日志暴
  • MIXED : 基于 STATMENT 和 ROW 两种模式的混合复制( mixed-based replication, MBR ),一般的复制使用 STATEMENT 模式保存 binlog ,对于 STATEMENT 模式无法复制的操作使用 ROW 模式保存 binlog

3.4 redo log与binlog区别

redo logbinlog
文件大小redo log 的大小是固定的binlog可通过配置参数 max_binlog_size设置每个binlog文件的大小
实现方式redo logInnoDB 引擎层实现的,并不是所有引擎都有binlog 是 Server 层实现的,所有引擎都可以使用binlog日志
记录方式redo log 采用循环写的方式记录,当写到结尾时,会回到开头循环写日志binlog通过追加的方式记录,当文件大小大于给定值后,后续的日志会记录到新的文件上
适用场景redo log 适用于崩溃恢复(crash-safe)binlog 适用于主从复制和数据恢复

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值