A事务的原子性(Atomicity):事务是一个不可分割的单元,事务中的全部操做,要么全作完,要么全不作html
C 事务的一致性(Consistency):一致性,即在事务开始以前和事务结束之后,数据库的完整性约束没有被破坏。好比用户A给用户B转帐,用户B给用户A转帐,而约束就是两我的的总金额仍是同样的mysql
I 事务的隔离性(Isolation):多个事务并发访问时,事务之间是隔离的,一个事务不该该影响其它事务运行效果。在下面会扩展MySQL事务隔离级别。laravel
D 事务的持久性(Durability):意味着在事务完成之后,该事务所对数据库所做的更改便持久的保存在数据库之中,并不会被回滚。sql
脏读: 对于两个事务A,B,事务A读取了已经被B更新但尚未提交的字段,以后,若B回滚,T1读取到的内容就是临时无效的内容。数据库
不可重复读: 在事务A中,屡次对同一数据进行读取,此时事务B也对该数据进行访问,也许事务B的修改,事务A屡次得到的数据可能不同。安全
幻读:事务A读取与搜索条件相匹配的若干行。事务B以插入或删除行等方式来修改事务A的结果集,而后再提交。这里幻读与不可重复读的差异在于不可重复读强调的是修改和删除,而幻读强调的是插入网络
MySQL中存在四种隔离等级:并发
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
未提交读(Read uncommitted) | 可能 | 可能 | 可能 |
已提交读(Read committed) | 不可能 | 可能 | 可能 |
可重复读(Repeatable read) | 不可能 | 不可能 | 可能 |
可串行读(Serializable) | 不可能 | 不可能 | 不可能 |
这里思考一下,为啥阻止幻读的隔离级别比不可重复读的高,由于InnoDB是行级锁,不可重复读是针对于对于正在修改或删除的数据行加锁,但仍是能够对表进行插入,因此可能出现幻读,要避免幻读就要把表的读写都变成表级锁,才能避免幻读,也所以变成了隔离等级为“可串行读”。数据库设计
PS:以上内容以 悲观锁 的概念能够更好理解,不过实际中出于性能考虑,是用以乐观锁 为理论基础的MVCC(多版本并发控制,Multi-Version Concurrency Control )高并发
悲观锁,正如其名,具备强烈的独占和排他特性。它指的是对数据被外界(包括本系统当前的其余事务,以及来自外部系统的事务处理)修改持保守态度,所以,在整个数据处理过程当中,将数据处于锁定状态。悲观锁的实现,每每依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,不然,即便在本系统中实现了加锁机制,也没法保证外部系统不会修改数据)。
即在事务A中数据在进行读取(共享锁)的时候,其余事务不能进行修改(排他锁),当在事务A的数据进行修改(排他锁)的时候,不能进行读取(共享锁)。
悲观锁大多数状况下依靠数据库的锁机制实现,以保证操做最大程度的独占性。但随之而来的就是数据库 性能的大量开销,特别是对长事务而言,这样的开销每每没法承受。
而乐观锁就很好的解决了这个问题,乐观锁能够有两种方式实现,一种是version,一种是时间戳,这里以version为例,假设数据通常状况下不会发生冲突,只有在提交的时候,才会进行加锁,并判断这提交的事务的版本与当前数据库的版本的对比,若是提交的数据版本号大于数据库表当前版本号,则予以更新,不然认为是过时数据,则把请求驳回。
乐观锁机制避免了长事务中的数据库加锁开销,大大提高了大并发量下的系统总体性能表现。
若是是在高并发下,不少用户容易出现冲突,即请求容易驳回
MVCC的实现没有固定的规范,每一个数据库中都会有不一样的实现,这里讨论InnoDB的MVCC,其内部原理是经过乐观锁,在InnoDB中,会在每行数据后面添加两个额外的隐藏的值来实现MVCC,一个是这个数据什么时候被建立,一个是这数据什么时候过时。在实际中,这里存储的是版本号,每开启一个新的事务,事务的版本号就回增长。在可重读Repeatable reads事务隔离级别下:
经过MVCC,虽然每行记录都须要额外的存储空间,更多的行检查工做以及一些额外的维护工做,但能够减小锁的使用,大多数读操做都不用加锁,读数据操做很简单,性能很好,而且也能保证只会读取到符合标准的行,也只锁住必要行。
咱们无论从数据库方面的教课书中学到,仍是从网络上看到,大都是上文中事务的四种隔离级别这一模块列出的意思,RR级别是可重复读的,但没法解决幻读,而只有在Serializable级别才能解决幻读。因而我就加了一个事务C来展现效果。在事务C中添加了一条teacher_id=1的数据commit,RR级别中应该会有幻读现象,事务A在查询teacher_id=1的数据时会读到事务C新加的数据。可是测试后发现,在MySQL中是不存在这种状况的,在事务C提交后,事务A仍是不会读到这条数据。可见在MySQL的RR级别中,是解决了幻读的读问题的。参见下图
这里又引伸出两个概念:快照读和当前读
快照读:就是普通的select
当前读:特殊的读操做(是要获取锁的),例如插入/更新/删除就是当前读
事务的隔离级别实际上都是定义了当前读的级别,MySQL为了减小锁处理(包括等待其它锁)的时间,提高并发能力,引入了快照读的概念,使得select不用加锁。事务的隔离只定义了读数据的要求,而写的要求固然是"当前读"。
这里注意,InnoDB默认的是行级锁,行级锁都是基于索引的。在当前读的查询语句中,若是一条SQL语句用不到索引是不会使用行级锁的,会使用表级锁把整张表锁住,这点须要注意。
在MySQL中,insert、update、delete语句默认会对涉及的数据集加排他锁,在Read committed等级下select语句默认是不会加的,若是要加的话,则须要显示在后面加 lock in share mode。在Repeatable read以及在Serializable隔离机制下,select是加共享锁的。
我是MySQL菜鸟,若是对于本文中有什么疑问或者问题,欢迎互相讨论提升
参考内容:
《深刻理解乐观锁与悲观锁》
《Innodb中的事务隔离级别和锁的关系》
《慕课网-数据库设计那些事》
《MySQL经常使用数据存储引擎区别》