一个数据库事务一般包含了一个序列的对数据库的读/写操做。它的存在包含有如下两个目的:html
当事务被提交给了DBMS(数据库管理系统),则DBMS须要确保该事务中的全部操做都成功完成且其结果被永久保存在数据库中,若是事务中有的操做没有成功完成,则事务中的全部操做都须要被回滚,回到事务执行前的状态;同时,该事务对数据库或者其余事务的执行无影响,全部的事务都好像在独立的运行。mysql
数据库中事务有4种隔离级别:sql
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read Uncommitted | 可能 | 可能 | 可能 |
Read Committed | 不能 | 可能 | 可能 |
Repeatable Read | 不能 | 不能 | 可能 |
Serializable | 不能 | 不能 | 不能 |
数据库数据以下:数据库
select * from isolation; +----+------+ | id | name | +----+------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +----+------+ 3 rows in set
可能读取到其余会话中未提交事务修改的数据。并发
---------------------------------------------------------------------------------- 事务A | 事务B ---------------------------------------------------------------------------------- begin | begin ---------------------------------------------------------------------------------- | update isolation set name = '-1' where id = 1 ---------------------------------------------------------------------------------- select name where id = 1 | ---------------------------------------------------------------------------------- commit | commit ----------------------------------------------------------------------------------
Console:1性能
在一个事务中,屡次读取的数据不同。主要指另外一个事务update、delete操做后会影响该事务select.net
---------------------------------------------------------------------------------- 事务A | 事务B ---------------------------------------------------------------------------------- begin | begin ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- select name where id = 2 | ---------------------------------------------------------------------------------- | update isolation set name = '-1' where id = 2 ---------------------------------------------------------------------------------- | commit ---------------------------------------------------------------------------------- select name where id = 2 | ---------------------------------------------------------------------------------- commit | ----------------------------------------------------------------------------------
Console:2 Console:-1线程
在一个事务中,屡次读取的数据不同。主要指另外一个事务insert操做后会影响该事务selectcode
---------------------------------------------------------------------------------- 事务A | 事务B ---------------------------------------------------------------------------------- begin | begin ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- select count(1) where name = '3' | ---------------------------------------------------------------------------------- | insert into isolation(name) values('3') ---------------------------------------------------------------------------------- | commit ---------------------------------------------------------------------------------- select count(1) where name = '3' | ---------------------------------------------------------------------------------- commit | ----------------------------------------------------------------------------------
Console:1 Console:2htm
不可重复读和幻读的类似之处在于结果上都是在一个事务中屡次查询结果不一致,其不一样在于形成其结果的缘由不一样,不可重复读由于update、delete操做而幻读由于insert操做。
能够从锁机制实现隔离来看这两个的区别:可重复读中,该sql第一次读取到数据后,就将这些数据加锁,其它事务没法修改这些数据,就能够实现可重复读了。但这种方法却没法锁住insert的数据,因此当事务A先前读取了数据,或者修改了所有数据,事务B仍是能够insert数据提交,这时事务A就会发现莫名其妙多了一条以前没有的数据,这就是幻读,不能经过行锁来避免。
数据库经过加锁来实现上诉数据库事务。Mysql的InnoDB数据库引擎经过乐观锁、悲观锁实现事务隔离,实现数据库的并发控制。
百度百科中这样介绍悲观锁,悲观锁在数据库理论中根据锁的互斥性把锁分为共享锁和排它锁:
正如其名,它指的是对数据被外界(包括本系统当前的其余事务,以及来自外部系统的事务处理)修改持保守态度,所以,在整个数据处理过程当中,将数据处于锁定状态。悲观锁的实现,每每依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,不然,即便在本系统中实现了加锁机制,也没法保证外部系统不会修改数据)。
上诉悲观锁,咱们能够看出有点像Java中的Synchronized、ReentranLock的感受,是同步阻塞的。在两个事务同时进行锁操做时,后一个事务会被阻塞直至获取锁或者超时。
下面使用Mysql的Update操做来看悲观锁机制,事务A进行更新操做,可是不提交:
// 设置Mysql不用自动提交 mysql> set autocommit=0; Query OK, 0 rows affected // Mysql Update操做会形成加锁 mysql> update isolation set name = '-1' where id=1; Query OK, 1 row affected Rows matched: 1 Changed: 1 Warnings: 0 // 这里不Commit
事务B同时也进行相同的更新操做,结果会操做该线程阻塞:
mysql> update isolation set name = '-1' where id=1 // 会一直阻塞在这里直至获取锁或者超时
百度百科中这样介绍乐观锁:
乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁机制采起了更加宽松的加锁机制。悲观锁大多数状况下依靠数据库的锁机制实现,以保证操做最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销每每没法承受。而乐观锁机制在必定程度上解决了这个问题。乐观锁,大可能是基于数据版本( Version )记录机制实现。何谓数据版本?即为数据增长一个版本标识,在基于数据库表的版本解决方案中,通常是经过为数据库表增长一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,以后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,若是提交的数据版本号大于数据库表当前版本号,则予以更新,不然认为是过时数据。
上诉乐观锁,咱们能够看出来有点像Java中的CAS操做,是同步非阻塞的。
https://tech.meituan.com/innodb-lock.html
http://chenzhou123520.iteye.com/blog/1860954
https://blog.csdn.net/sadfishsc/article/details/51027734