事务的特性以及延伸的议题
在讲事务之前,我想先说一说Mysql数据库锁的一些相关概念,如下图:
MySQL按锁的粒度划分可归纳为以下3种锁:
- 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
- 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
- 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
MySQL按锁的级别划分:
-
共享锁:又称读锁,读取操作创建的锁。一旦上锁,任何事务(包括当前事务)无法对其修改,其他事务可以并发读取数据,也可在对此数据再加共享锁
-
排它锁:又称写锁,如果事务对数据A加上排他锁后,则其他事务不可并发读取数据,也不能再对A加任何类型的锁。获准排他锁的事务既能读数据,又能修改数据。
-
意向锁:InnoDB所用的表级锁,其设计目的主要是为了在一个事务中揭示下一步将要被请求的锁的类型。
InnoDB中的两个表锁:
意向共享锁(IS):表示事务准备给数据行加入共享锁,也就是说一个数据行加共享锁前必须先取得该表的IS锁
意向排他锁(IX):类似上面,表示事务准备给数据行加入排他锁,说明事务在一个数据行加排他锁前必须先取得该表的IX锁。
意向锁是InnoDB自动加的,不需要用户干预。
我们需要清楚一个问题:并发控制
当程序中可能出现并发的情况时,我们就需要通过一定的手段来保证在并发情况下数据的准确性,通过这种手段保证了当前用户和其他用户一起操作时,所得到的结果和他单独操作时的结果是一样的。这种手段就叫做并发控制。并发控制的目的是保证一个用户的工作不会对另一个用户的工作产生不合理的影响。
我们常说的并发控制,一般都和数据库管理系统(DBMS)有关。在DBMS中的并发控制的任务,是确保在多个事务同时存取数据库中同一数据时,不破坏事务的隔离性和统一性以及数据库的统一性。
实现并发控制的手段
实现并发控制的主要手段大致可以分为乐观并发控制和悲观并发控制两种。
在开始介绍之前要明确一下:无论是悲观锁还是乐观锁,都是人们定义出来的概念,可以认为是一种思想。其实不仅仅是关系型数据库系统中有乐观锁和悲观锁的概念,像hibernate、tair、memcache等都有类似的概念。所以,不应该拿乐观锁、悲观锁和其他的数据库锁等进行对比。
悲观锁(Pessimistic Lock)
百度百科:
悲观锁,正如其名,具有强烈的独占和排他特性。它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度。因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
之所以叫做悲观锁,是因为这是一种对数据的修改抱有悲观态度的并发控制方式。我们一般认为数据被并发修改的概率比较大,所以需要在修改之前先加锁。
悲观锁主要用到共享锁或者排它锁
乐观锁( Optimistic Locking )
百度百科:
乐观锁机制采取了更加宽松的加锁机制。乐观锁是相对悲观锁而言,也是为了避免数据库幻读、业务处理时间过长等原因引起数据处理错误的一种机制,但乐观锁不会刻意使用数据库本身的锁机制,而是依据数据本身来保证数据的正确性。
相对于悲观锁,在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制。一般的实现乐观锁的方式就是记录数据版本。
乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。
说完这些锁,我接着说事务的特性以及它的隔离级别:
数据库事务(简称:事务)是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。
事务的四个特性(ACID)
原子性(Atomicity):事务的执行要么全部成功,要么全部失败
一致性(Consistency):数据库操作前后的状态保持一致,从一种正确的状态转换成另一种正确的状态
隔离性(Isolation):两个事务之间互不干涉
持久性(Durability):事务提交的操作是永久性的
先看一张图:
上图中,我们在处理并发控制时会出现一些问题:
脏读:事务A修改了某条记录,与此同时事务B读取了修改后的记录,由于某些原因,事务A进行RollBack之后,事务A读取到事务B未提交的数据,形成脏读。
不可重复读: 事务A读取数据的过程中,事务B对数据进行了修改,导致事务A读取两次的结果不一致。
幻读: 一个事务里面的操作发现了未被操作的数据. 事务A读取某个结果集记录,事务B此时增加或者删除了N条记录,导致事务A两次读取到的结果集不一致。
事务的隔离级别
通过设置事务的隔离级别可以有效的解决脏读,不可重复读和幻读.
读未提交:会发生脏读,不可重复读,幻读
读已提交:解决脏读, 会发生不可重复读,幻读
可重复读:解决脏读,不可重复读, 会发生幻读
串行化:每个事务只能按照顺序执行,避免了脏读,不可重复读,幻读,但是效率低下,一般不使用
延伸:
什么是分布式事务
分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。
什么是本地事务
本地事务也称为数据库事务或传统事务(相对于分布式事务而言)。它有几个特征:
- 一次事务只连接一个支持事务的数据库(一般来说都是关系型数据库)
- 事务的执行结果保证ACID
- 会用到数据库锁