一、事务sql
1.1 事务的四大特性数据库
- 原子性(Atomicity):原子性是指事务包含的全部操做要么所有成功,要么所有失败回滚。
- 一致性(Consistency):一致性是指事务必须使数据库从一个一致性状态变换到另外一个一致性状态,也就是说一个事务执行以前和执行以后都必须处于一致性状态。拿转帐来讲,假设用户A和用户B二者的钱加起来一共是5000,那么无论A和B之间如何转帐,转几回帐,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
- 隔离性(Isolation): 隔离性是当多个用户并发访问数据库时,好比操做同一张表时,数据库为每个用户开启的事务,不能被其余事务的操做所干扰,多个并发事务之间要相互隔离。
- 持久性(Durability):持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即使是在数据库系统遇到故障的状况下也不会丢失提交事务的操做。
1.2 并发事务带来的问题并发
- 脏读:指在一个事务处理过程里读取了另外一个未提交的事务中的数据。好比用户A的帐户里有500元,事物T1:将money-100=400,可是该事物还未提交。 事物T2:读取用户A的帐户,查看到A的帐户有400。而后事物1回滚,用户A的帐户又变回了原来的500。 事物T2就产生了脏读。
- 不可重复读:不可重复读是指A事务读取了B事务已经提交的更改数据。假设A在取款事务过程当中查询帐户余额,B往该帐户转帐100后,A再次查询帐户余额,会发现两次读取帐户的余额发生不一致。不可重复读和脏读的区别是,脏读是某一事务读取了另外一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。
- 幻读:A事务读取到了B事务的新增数据。A事务统计存款帐户的总金额为10000,这时B事务新增了一个帐户存款为100,A事务再次统计总金额10100(产生了幻读)。幻读与不可重复读的区别,幻读读取了其余事务新增的数据,不可重复读,读取了其余事务的更改(或者删除)数据。
1.3 事务的隔离级别性能
为了达到上述事务特性,数据库定义了几种不一样的事务隔离级别:spa
- READ_UNCOMMITTED(未提交读): 最低的隔离级别,容许读取还没有提交的数据变动,可能会致使脏读、幻读或不可重复读
- READ_COMMITTED(提交读): 容许读取并发事务已经提交的数据,能够阻止脏读,可是幻读或不可重复读仍有可能发生
- REPEATABLE_READ(可重复读): 对同一字段的屡次读取结果都是一致的,除非数据是被自己事务本身所修改,能够阻止脏读和不可重复读,但幻读仍有可能发生(Mysql innodb存储引擎经过一些特殊的处理,在该隔离级别解决了幻读问题)。
- SERIALIZABLE(串行): 最高的隔离级别,彻底服从ACID的隔离级别。全部的事务依次逐个执行,这样事务之间就彻底不可能产生干扰,也就是说,该级别能够防止脏读、不可重复读以及幻读。可是这将严重影响程序的性能。一般状况下也不会用到该级别。
这里须要注意的是:Mysql 默认采用的 REPEATABLE_READ隔离级别。版本控制
二、Innodb的锁blog
Innodb 与 MyISAM 最大的不一样在于:一是支持事务,二是采用行级锁。索引
共享锁:又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同一数据能够共享一把锁,都能访问到数据,可是只能读不能修改;
加锁释锁方式:
select * from procuct WHERE id=1 LOCK IN SHARE MODE;事务
排他锁:
又称为写锁,简称X锁,排他锁不能与其余锁并存,如一个事务获取了一个数据行的排他锁,其余事务就不能再获取该行的锁(共享锁、排他锁),只有获取了排他锁的事务才能够对数据行进行读取和修改,(其余事务要读取数据可来自于快照)。ci
意向共享锁(IS):表示事务准备给数据行加入共享锁,即一个数据行加共享锁前必须先取得该表的IS锁,意向共享锁之间是能够相互兼容的
意向排它锁(IX):表示事务准备给数据行加入排他锁,即一个数据行加排他锁前必须先取得该表的IX锁,意向排它锁之间是能够相互兼容的
意向锁(IS、IX)是InnoDB数据操做以前自动加的,不须要用户干预。
为何须要意向锁?
假如事务A锁住表中的一行(写锁),事务B锁住整个表(写锁)。若是没有意向锁,事务A既然锁住了某一行,其余事务就不可能修改这一行。这与”事务B锁住整个表就能修改表中的任意一行“造成了冲突。因此,没有意向锁的时候,行锁与表锁共存就会存在问题!有了意向锁以后,事务A在申请行锁(写锁)以前,数据库会自动先给事务A申请表的意向排他锁。当事务B去申请表的写锁时就会失败,由于表上有意向排他锁以后事务B申请表的写锁时会被阻塞。
加锁方式:
delete / update / insert 默认加上X锁
SELECT * FROM table_name WHERE ... FOR UPDATE
InnoDB的行锁实现方式:
经过给索引上的索引项加锁来实现的,若是没有索引,InnoDB将对表中的全部记录加锁,实际效果跟表锁同样。
InnoDB的行锁分为三种状况:
- Next-key locks:锁住记录+区间(左开右闭),当sql执行按照索引进行数据的检索时,查询条件为范围查找(between and、<、>等)并有数据命中则此时SQL语句加上的锁为Next-key locks,锁住索引的记录+区间(左开右闭)。若是有一张表,用sql “SELECT * FROM product WHERE id > 3 AND id < 6 FOR UPDATE;” 去查询,那么该条语句会锁住(5,7] 这之间的数据。 这时候向表中插入id为6的数据,是会被阻塞。


- Gap locks:锁住数据不存在的区间(左开右开),当sql执行按照索引进行数据的检索时,查询条件的数据不存在,这时SQL语句加上的锁即为Gap locks,锁住索引不存在的区间(左开右开)。当用“SELECT * FROM product WHERE id > 2 AND id < 4 FOR UPDATE;”去查询,查不到数据,此时会锁住(1,5)区间的数据。
- Record locks:锁住具体的索引项,当sql执行按照惟一性(Primary key、Unique key)索引进行数据的检索时,查询条件等值匹配且查询的数据是存在,这时SQL语句加上的锁即为记录锁Record locks,锁住具体的索引项。当使用SELECT * FROM product WHERE id =2 FOR UPDATE 去查询,它会锁住id为2的这一行记录。
三、MVCC并发版本控制
MVCC就是同一条数据能够同时存在多个版本:更新数据时,先插入一条新记录,而后把旧记录标记为删除;查询时只查询事务开始前就已存在的记录。
- 快照读:select语句默认,不加锁,MVCC实现可重复读,使用的是MVCC机制读取undo中的已经提交的数据。因此它的读取是非阻塞的。
- 当前读:select语句加S锁或X锁;全部的修改操做加X锁。
RR隔离级别下的快照读,不是以begin开始的时间点做为snapshot创建时间点,而是以第一条select语句的时间点做为snapshot创建的时间点。