数据库事务和锁

事务的概念

一个数据库事务一般包含了一个序列的对数据库的读/写操做。它的存在包含有如下两个目的:html

  1. 为数据库操做序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即便在异常状态下仍能保持一致性的方法。
  2. 当多个应用程序在并发访问数据库时,能够在这些应用程序之间提供一个隔离方法,以防止彼此的操做互相干扰。

当事务被提交给了DBMS(数据库管理系统),则DBMS须要确保该事务中的全部操做都成功完成且其结果被永久保存在数据库中,若是事务中有的操做没有成功完成,则事务中的全部操做都须要被回滚,回到事务执行前的状态;同时,该事务对数据库或者其余事务的执行无影响,全部的事务都好像在独立的运行。mysql

事务的ACID性质

  • 原子性(Atomicity):事务做为一个总体被执行,包含在其中的对数据库的操做要么所有被执行,要么都不执行。
  • 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另外一个一致状态。一致状态的含义是数据库中的数据应知足完整性约束。
  • 隔离性(Isolation):多个事务并发执行时,一个事务的执行不该影响其余事务的执行。
  • 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中。

事务的隔离级别

数据库中事务有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操做,是同步非阻塞的。

Reference

https://tech.meituan.com/innodb-lock.html
http://chenzhou123520.iteye.com/blog/1860954
https://blog.csdn.net/sadfishsc/article/details/51027734

相关文章
相关标签/搜索