[每天进步一点点]mysql笔记整理(二):事务与锁
一. 事务
定义
事务是数据库管理系统执行过程中一个逻辑单位,由一个有限的数据库操作序列构成。
说明:
- 它是数据库最小的工作单元。
- 它包含一个或者多个DML语句,包括:insert、update、delete。单条的DDL(create drop)和DCL(grant revoke)也会有事务。
事务是和存储引擎对应的,引用mysql官网
特性
- Atomicity原子性
对数据库的操作要么全部成功,要么全部失败,不可能出现部分成功或者失败。如果前边的操作成功了,后边的操作失败了,这个时候会回滚。
在InnoDB中,回滚是是通过undo log来实现的,undo log记录了数据修改之前的值(逻辑日志),如果发生异常,就可以用undo log来实现回滚操作。
- Consistent一致性
一致性是指数据库的完整性约束没有被破坏,事务执行的前后都是合法的数据状态。比如主键必须是唯一的。
- Isolation隔离性
在多个事务对表或者行进行并发操作,应该互相不干扰的,通过这种方式,也就是最终保证业务数据的一致性。
- Durable持久性
对数据的操作,如insert、update、delete,只要事务提交成功,那么数据结果就是永久性的,不能因为系统的宕机或者数据库的重启恢复到原来的初始状态。
持久性是通过redo log和double write双写缓冲来实现的。我们操作数据的时候,首先会将数据写到buffer pool中,同时记录redo log,如果在刷盘(刷盘:将数据写入磁盘)之前出现异常,在重启之后就可以读取redo log里面的内容,然后将数据写入磁盘来保证数据的持久性。
参数
手动开始事务有两种方式
- 用begin开启事务。
- 用start transaction开启事务
结束事务也有两种方式
- 用commit提交一个事务
- 用rollback回滚一个事务
说明:在客户端的连接中断时,事务也会中断。
在InnoDB中,autocommit参数默认是on,代表自动开启事物和自动提交事务。
事务隔离级别
名称解释
- 脏读
在一个事务里面,由于其他的时候修改了数据并且没有提交,而且导致了前后两次读取的数据不一致的情况,这种事物并发的问题,我们通常称为脏读。也可以理解为一次事务读取到了其他事务未提交的数据的情况。
- 不可重复读
同样在两个事务的情况下,一个事务读取到了其它事务已提交的数据导致前后两次读取的数据不一致的情况,我们通常情况下称为不可重复读。
- 幻读
由于其它事务插入数据,导致另一事务前后读取到的数据不一致的情况我们称它为幻读。
隔离级别
- Read Uncommitted未提交读
一个事务读取到其它事务未提交的数据,这种情况会出现脏读,它没有解决任何的问题。
- Read Committed已提交读
一个事务只能读取到其它事务已提交的数据,不能读取到其它事务未提交的数据,它解决了脏读的问题,但是会出现不可重复读的问题。
- Repeatable Read可重复读
在同一个事务里面多次读取同样的数据结果都是一样的,它解决了不可重复读的问题,但是没有解决幻读问题。
- Serializable串行化
所有的事务都是串行执行的,所有的数据操作都是需要等待之前操作完成才能执行,这样就不存在事务的并发操作。它解决了所有并发事务导致的问题。
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read Uncommitted | 可能 | 可能 | 可能 |
Read Committed | 不可能 | 可能 | 可能 |
Repeatable Read | 不可能 | 不可能 | InnoDB存储引擎不可能 |
Serializable | 不可能 | 不可能 | 不可能 |
说明:mysql InnoDB默认使用RR(Repeatable Read)。事务隔离级别越高,事务的并发度就越低,InnoDB在RR的级别就解决了幻读问题,这也是在InnoDB存储引擎的情况下默认使用RR的原因,它既保证数据的一致性,又支持较高的并发。
解决方案
如果要解决读一致性的问题,保证一个事务前后两次读取的数据结果一致,实现事务的隔离,一般都是采用加锁来解决这种问题。
LBCC
Lock Based Concurrency Control:基于锁的并发控制,在我们要读取数据的时候,锁定我们要操作的数据,不允许其它事务来修改。这样其实就是意味着不支持并发的读写操作,会影响操作数据的效率。
MVCC
在LBCC存在效率问题的情况下,就出现了另一种解决方案MVCC。
Multi Version Concurrency Control:核心就是们可以查询在我们这个事务开始之前已经存在的数据,即使它在后面被修改或者删除。在我们这个事务之后新增的数据是查询不到的。
InnoDB为每行记录都实现了两个隐藏字段:
- DB_TRX_ID:6字节,插入或更新行的最后一个事务的事务的id,事务编号是自动递增的。(可以理解为创建版本号)
- DB_ROLL_PTR:7字节,回滚指针。(可以理解为删除版本号)
锁
共享锁
Shared Locks:我们获取了一行数据的读锁以后,可以用来读取数据,所以它也叫做读锁,而且多个事务可以共享一个把读锁。可以通过lock in share mode手动加一把读锁。例如:select * from table lock in share moe;
释放锁有两种方式,它包括:提交事务和结束事务。只要事务结束,锁就会自动释放。
排它锁
Exclusive Locks:它是用来操作数据的,所以也叫做写锁。只要一个事务获取了一行数据的排它锁,其它事务就不能在获取这行数据的共享锁和排它锁。可以通过for update给一行数据加上一个排它锁,例如:select * from table where ID = 1 for update;
释放锁同共享锁一样。
意向锁
意向锁其实是由数据库自己维护的。当我们给一行数据加上共享锁之前,数据库会自动在这张表上面加一个意向锁。当我们给一行数据加上排它锁之前,数据库会自动在这张表上面加一个意向排它锁。
意向锁存在的意义是当我们准备给一张表加上锁的时候,我们首先要判断是否有其他事务锁定了其中某些行,如果有的话,肯定就不能加表锁,那么这个时候我们会去扫描整个表,如果数据量比较大的话,加锁的效率就变的非常低。有了意向锁后,我们只要判断这张表上有没有意向锁就可以了。意向锁也可以简单的理解为一个标志。
记录锁
当我对于唯一性的索引(唯一索引和主键索引)使用等值查询,能够精确匹配到一条记录的时候,这种情况使用的就是记录锁。
间隙锁
当我们查询的记录不存在,没有命中任何一条记录的时候,无论是用等值查询还是范围查询,它使用的都是间隙锁。
间隙锁主要是阻塞insert插入操作,相同的间隙锁之间不冲突。间隙锁只有在RR的情况下存在。
临键锁
当我们使用范围查询的时候,不仅仅命中了多行记录,而且还包含了Gap Lock(间隙锁),这种情况下我们使用的就是临键锁。它是mysql里面默认的行锁算法,相当于记录锁+间隙锁。
说明:还有表锁和行锁,这两个比较简单,一个就是锁定整张表,一个是锁定特定的行。
总结
了解了锁之后,我们可以回过头来看看事务的隔离级别;
- RU(Read Uncommitted):不加锁
- RR(Repeatable Read):普通的select使用快照读(snapshot read),底层使用的是MVCC来实现。加锁的select(select …… lock in share mode/select …… for update)以及更新删除操作(update、delete)等语句使用的是当前读(current read),底层使用记录锁、间隙锁或者临键锁。
- RC(Read Committed):普通方式下和RR相同。加锁的select操作都是使用的记录锁,因为RC隔离级别下没有Gap Lock。除了两种特殊情况下,外键约束检查(foreign-key constraint checking)以及重复键检查(duplicate-key checking)时会使用间隙锁封锁区间。所以RC会出现幻读的问题。
- Serializable:所有的select语句都会被隐式的转化为select …… lock in share mode,会和update、delete互斥。
整理不易,转载请注明出处,喜欢的小伙伴可以关注公众号查看更多喜欢的文章。
联系方式:4272231@163.com
QQ:95472323
微信:ffj2000