1、事务的特性
事务具备以下四个特性,简称ACID属性。
原子性(Atomicity):
事务是一个完整的操作,事务的各步操作都是不可再分的,要么都执行, 要么都不执行。
一致性(Consistency):
当事务完成时,数据必须处于一致的状态。
隔离性(Isolation):
并发事务之间相互独立、隔离,它不应以任何方式依赖于或影响其他事 务。
持久性(Durability):
事务完成后,它对数据库的修改被永久保持。
2、 事务的隔离级别
① Read uncommitted (读未提交):最低级别,任何情况都无法保证。
② Read committed (读已提交):可避免脏读的发生。
③ Repeatable read (可重复读):可避免脏读、不可重复读的发生。
④ Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
3、 脏读、不可重复读、幻读
①脏读
脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。
②不可重复读
不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。
③虚读(幻读)
幻读是事务非独立执行时发生的一种现象。例如事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。
4、 事务的七种传播行为
什么是事务的传播行为:事务传播行为用来描述由某一个事务传播行为修饰的方法被嵌套进另一个方法的时事务如何传播。
事务传播行为类型 |
说明 |
PROPAGATION_REQUIRED |
如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 |
PROPAGATION_SUPPORTS |
支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY |
使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW |
新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED |
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER |
以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED |
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
5、锁机制
悲观锁:
总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。
乐观锁:(不能解决脏读的问题)
总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制或CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。
Mysql InnoDB引擎的锁机制(属于悲观锁)
(之所以以InnoDB为主介绍锁,是因为InnoDB支持事务,支持行锁和表锁用的比较多,Myisam不支持事务,只支持表锁)
1)按照锁的使用方式可分为:共享锁、排它锁、意向共享锁、意向排他锁
共享锁/读锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。(其他事务可以读但不能写该数据集)
排他锁/写锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。 (其他事务不能读和写该数据集)
意向共享锁(IS):通知数据库接下来需要施加什么锁并对表加锁。如果需要对记录A加共享锁,那么此时innodb会先找到这张表,对该表加意向共享锁之后,再对记录A添加共享锁。
意向排他锁(IX):通知数据库接下来需要施加什么锁并对表加锁。如果需要对记录A加排他锁,那么此时innodb会先找到这张表,对该表加意向排他锁之后,再对记录A添加排他锁。
注:
A、意向共享锁和意向排它锁是数据库主动加的,不需要我们手动处理;
B、对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);对于普通SELECT语句,InnoDB不会加任何锁,事务可以通过以下语句显示给记录集加共享锁或排他锁。
共享锁(S):SELECT * FROM table_name WHERE … LOCK IN SHARE MODE。
排他锁(X):SELECT * FROM table_name WHERE … FOR UPDATE。
2)按照锁的粒度可分为:行锁、页锁(间隙锁)、表锁
行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件来检索数据才会用到行锁,否则InnoDB将会使用表锁。
表锁:select * from table_nane where name = ‘小巷’ for update 。name字段不是唯一索引字段,所以是表锁。(表排他锁)
行锁:select * from table_name where id = 1 for update 。id 字段为唯一索引字段,所以使用的就是行锁,且是排它锁。
页锁(又叫Gap锁/间隙锁):所谓表锁锁表,行锁锁行,那么页锁折中,锁相邻的一组数据。
通过加锁控制,可以保证数据的一致性,但是同样一条数据,不论用什么样的锁,只可以并发读,并不可以读写并发(因为写的时候加的是排他锁所以不可以读),这时就要引入数据多版本控制来实现读写并发。
MVCC(数据多版本并发控制,属于乐观锁)
这项技术使得InnoDB的事务隔离级别下执行一致性读操作有了保证,换言之,就是为了查询一些正在被另一个事务更新的行,并且可以看到它们被更新之前的值。这是一个可以用来增强并发性的强大的技术,因为这样的一来的话查询就不用等待另一个事务释放锁。