MySQL本身也是在文件系统的基础上发展而来,因为锁的存在使之有所不同。
MySQL
作为一种数据库软件,难免会存在对其共享资源
的并发访问
,为了协调和管理
不同资源的并发访问,也就产生了锁机制
,因为锁机制的存在为数据库提供了数据的完整性
和一致性
。
从锁的级别
来分锁可分为:行级锁、表级锁、页级锁。
从锁的类型
来分锁可分为:共享锁、排它锁(独占锁)。
为了协调行锁、表锁
产生了:意向锁(表级锁)。
共享锁
,允许事务去读取
数据。排它锁
,允许事务去修改或删除
数据。意向锁
,获取行级锁的时候,自动添加的表级锁,包含:意向共享锁、意向排它锁。
对于MyISAM
存储引擎,只支持表锁
,而InnoDB
存储引擎则支持行锁、表锁
。
MyISAM
存储引擎修改、删除数据的时候,会产生排它锁,锁定的整张表
,并发写入性能较差,而读取的时候产生的是共享锁,不会锁定表,读取性能就比较好。
InnoDB
存储引擎修改、删除数据的时候,会产生排它锁,锁定的特定索引记录
,一般不会影响表中的其它行,并发写入性能较好,而读取的时候产生的是共享锁,不会锁定表和行,读取性能较好。
行锁锁定的是索引记录,而不是记录行,如果没有索引,则使用隐式索引进行锁定。
当一张表某些行
已经获取了排它锁
,在表中会产生一个意向排它锁
,如果此时有一个事务要来锁定整张表,那么一看有意向排它锁
的存在,该事务就会被阻塞
,通过意向锁
直接就可以知道能不能锁定表,不需要逐行去遍历检测是否有排它锁
,通过意向锁高效地协调了行锁和表锁的关系。
行级锁
按照锁定范围来分,又分为三种:
Record Lock
单行记录上的锁。Gap Lock
间隙锁,锁定一个范围,不包含记录本身。Next-Key Lock
锁定一个范围,包含记录本身,用于解决幻读问题。
当然,锁也是有利有弊的,也可能出现死锁
的情况。
当两个或两个以上
的事务在执行过程中,因争夺资源
而造成一种相互等待
的现象,称为死锁
。
最后,也是因为锁的存在,丰富了后续事务的功能。
MySQL通过设计一种机制,使得数据能够完整地从一种一致性状态切换到另一种一致性状态,这种机制称为事务。
事务包含有四大特性:原子性(A)、一致性(C)、隔离性(I)、持久性(D),简称酸性。原子性
:事务中的操作,要么全部成功,要么全部失败,不可切分。一致性
:事务将数据库从一种一致性状态转变成另外一种一致性状态,并且保证数据的完整性。隔离性
:又称并发控制,事务在提交之前对于其它事务是处于不可见的状态的。持久性
:事务一旦提交,结果就是永久性的,不会因为数据库宕机而丢失数据。
原子性
、持久性
是通过redo
日志实现的,一致性
是通过undo
日志实现的,隔离性
是通过锁机制
实现的。
从本质上来说,原子性
也是为了配合持久性
而存在的,当事务的一部分写入redo日志
后,发生了崩溃、断电
,那么根据原子性
来说,该次事务应当恢复
,那么对于已经持久化到日志文件中的数据,就必须要通过回溯
来撤销。在InnoDB
存储引擎中,redo
重做日志对应的就是ib_logfile0
、ib_logfile1
。
接着,事务要进行回滚
,那就需要通过一致性
来保障,而undo
日志就是用来实现一致性
的,在undo
日志中保存了多个版本的事务的一些信息,通过undo
日志,将事务rollback
到修改之前的样子。
在此,不得不提的是MySQL的MVCC
多版本并发控制,它也是通过undo
日志来实现的。
MVCC是通过在每一数据行后头添加2个隐藏字段create version
、delete version
以及每次开启一个事务会初始化一个事务id
。新增一条数据的时候,create version
的值就等于事务id
,删除数据的时候,delete version
就等于事务id
,更新数据的时候会先删后增,在undo
日志中就会存在2条数据,一条delete version
就等于事务id
,一条create version
的值等于事务id
。
在事务执行过程中,可能会同时存在其它的事务,而多个
事务之前需要相互隔离
,也就是要做到并发控制
,锁就是用来实现隔离性
的。MySQL的事务的隔离级别
包含:Read Uncommitted
读未提交、Read Committed
读已提交、Read Repeatable
可重复读、Serializable
串行化。其中,读已提交
、可重复读
是基于MVCC
多版本并发控制来实现的。
锁,为事务的并发控制带来了好处,同时也带来了坏处,包括:脏读、不可重复读、幻读。
脏读
,指的是一个事务读到了另一个事务未提交的内容,一旦另一个事务回滚了,就出现了脏数据
。不可重复读
,指的是同一个事务使用同一句SQL进行多次读取
,返回不同的结果。幻读
,指的是一个事务在进行增删
的时候,某些已经确定不会出现的记录突然出现。
要解决脏读,那就需要至少设置隔离级别为:Read Committed
读已提交。
要解决不可重复读,那就需要至少设置隔离级别为:Read Repeatable
可重复读。
要解决幻读,那就需要设置隔离级别为:Serializable
串行化或者采用Next-Key Lock
间隙锁。