如果对事务的基本知识不是太了解,可以先去看这篇文章【MySQL】事务知识小结
MySQL只有innodb引擎支持事务,因此这篇文章也是以innodb为背景写的。
一、事务并发会带来什么问题?
- 脏读:读到了其他事务未提交的数据
- 不可重复读:当前事务先进行了一次数据读取,然后再次读取到的数据是别的事务修改成功的数据,导致两次读取到的数据不一致(update、delete)
- 幻读:当前事务读第一次取到的数据比后来读取到数据条目少(insert)
解决上面的问题就需要设置相应的事务隔离性
- Read Uncommitted(未提交读) :事务中的修改,即使没有提交,其他事务也可以看得到,会导致“脏读”、“幻读”和“不可重复读取”。
- READ COMMITTED (提交读):保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”,但不能避免“幻读”和“不可重复读取”。该级别适用于大多数系统。
- REPEATABLE READ(重复读) :默认隔离级别,保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但不能避免“幻读”,但是带来了更多的性能损失。
- Serializable (串行化):最严格的级别,事务串行执行,资源消耗最大,解决了所有问题。
需要注意,在innoDB引擎中,当隔离级别为可重复读就可以解决幻读的情况,因为innodb
- 在快照读读情况下,mysql通过mvcc来避免幻读。
- 在当前读读情况下,mysql通过next-key来避免幻读
因此innodb的默认隔离级别就是可重复度.
二、如何实现事务隔离级别?
首先有两种方式:
- 加锁:在读取数据前,对其加锁,防止其他事务对数据进行修改。
- MVCC:多版本并发控制,指的是一种提高并发的技术,生成一个数据请求时间点的一致性数据快照,并用这个快照来提供一定级别的一致性读取.
最早的数据库系统,只有读读之间可以并发,读写,写读,写写都要阻塞。引入多版本之后,只有写写之间相互阻塞,其他三种操作都可以并行,这样大幅度提高了InnoDB的并发度。在内部实现中,InnoDB是在undolog中实现的,通过undolog可以找回数据的历史版本。
三、锁内容详解
1、锁的分类
1.1表锁和行锁
首先来看看表锁和行锁,顾名思义,表锁是锁住整张表,行锁是锁住一行的数据,下面是它们的区别
- 锁定粒度:表锁>行锁
- 加锁效率:表锁>行锁
- 冲突概率:表锁>行锁
- 并发性能:表锁<行锁
MyISAM存储引擎仅支持表锁;innodb存储引擎既支持行锁又支持表锁。
1.2共享锁和排他锁(行锁)
共享锁:又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同意数据可以共享一把锁,都能访问到数据,但是只能读不能修改。
共享锁的加锁释放锁方式:
-
BEGIN;
-
-
select *
from student
where
id =
1
LOCK INSHARE
MODE;
-
-
commit/
rollback;
排他锁:又称为写锁,排他锁不能与其他锁并存,如一个事务获取了一个数据行的排他锁,其他事物就不能再获取该行的锁,只有该获取了排他锁的事务才能对数据进行读取和修改。
排他锁的加锁释放锁方式:
- 自动:delete/update/insert 默认加上排他锁
- 手动:select * from student where id = 1 FOR UPDATE;
1.3意向共享锁和意向排他锁(表锁)
首先需要知道意向锁是由数据引擎自己维护的,用户无法手动操作意向锁。
意向共享锁:表示事务准备给数据行加入共享锁,也就是说一个数据行加共享锁前必须先取得该表的意向共享锁。
意向排他锁:表示事务准备给数据行加入排他锁,说明事务在一个数据行加排他锁前必须先取得该表的意向排他锁。
思考:为什么需要(表级别的)意向锁?
因为在加表锁时,如果此时表中已经有其他事务锁定了这张表的任一行,表锁就不能成功,因此在加表锁时会先去判断每一行有没有加锁,然后再看加不加表锁,但是判断每一行加没加锁显然效率太低了,因此就需要意向锁,使用意向锁就会在加表锁时直接判断是否有意向锁即可,提高了效率。
2、锁的作用
锁是为了解决资源竞争并发的问题,在Java中资源就是对象,在Mysql中就是数据表、数据行
3、锁到底锁住了什么?
当数据库中没有索引时,加锁会锁住整张表.
其实数据库中的锁是锁住索引项实现的!对于innodb而言,如果没有索引它定义一个主键来作为聚簇索引。那也就是在我们没有创建索引时,对innodb存储引擎而言,我们加锁表中的数据,锁住的就是这个隐式聚簇索引,InnoDB行锁对唯一索引或主键索引的数据生效,若我们没有手动的去加索引,那么对于加锁时只有sql语句能用到默认的聚簇索引也就是默认的主键索引时才会锁住行,因此我们常说如果没有建主键索引,使用innodb时会锁整张表。
4、行锁的算法
分为三种:记录锁、间隙锁、临键锁
记录锁:总是会锁住索引记录,如果InnoDB存储引擎表在建立的时候没有设置任何一个索引,那么这时InnoDB存储引擎会使用隐式的主键来进行锁定。
间隙锁: 只在RR级别存在,是为了解决Phantom Problem(幻象/幻读),利用这种锁技术,锁定的不是单个值,而是一个范围。
临键锁: 是结合了记录锁和间隙锁的一种锁定算法,在此种算法下,InnoDB对于行的查询都是采用这种锁定算法。