MySQL-MVCC原理

MySQL-MVCC原理
MySQL-MVVC
 
一、事务
 
简介
 
只有Innodb数据库引擎支持事务
 
事务处理维护数据库的完整性,保证成批的SQL语句要么全部执行,要么全部不执行
 
事务用来管理insert、update、delete语句
 
事务特性
 
一般来说,事务是必须满足4个条件(ACID):原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。
 
**原子性:**一个食物中的所有操作,要么全部完成,要么全部不完成,不会结束在中间的某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。
 
**一致性:**在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
 
**隔离性:**数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
 
**持久性:**事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
 
在 MySQL 命令行的默认设置下,事务都是自动提交的,即执行 SQL 语句后就会马上执行 COMMIT 操作。因此要显式地开启一个事务务须使用命令 BEGIN 或 START TRANSACTION,或者执行命令 SET AUTOCOMMIT=0,用来禁止使用当前会话的自动提交。*
 
事务可能出现的问题
 
脏读(Dirty Read):
 
脏读又称无效数据的读出,是指在数据库访问中,事务T1将某一值修改,然后事务T2读取该值,此后T1因为某种原因撤销对该值的修改,这就导致了T2所读取到的数据是无效的,值得注意的是,脏读一般是针对于update操作的
 
不可重复读(Non-repeatable read):
 
已知有两个事务A和B,A 多次读取同一数据,B
 
在A多次读取的过程中对数据作了修改并提交,导致A多次读取同一数据时,结果不一致。
 
幻读(Phantom Read):
 
已知有两个事务A和B,A从一个表中读取了数据,然后B在该表中插入了一些新数据,导致A再次读取同一个表,
 
就会多出几行,简单地说,一个事务中先后读取一个范围的记录,但每次读取的记录数不同,称之为幻象读。
 
不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。
 
事务问题的四种解决方案
 
**Read uncommitted(读未提交):**可能出现脏读、不可重复读和幻读。
 
Read committed(读提交):可以避免脏读,但可能出现不可重复读和幻读。大多数数据库默认级别就是Read committed,比如Sql Server数据库和Oracle数据库。注意:该隔离级别在写数据时只会锁住相应的行。
 
Repeatable read(重复读):可以避免脏读和不可重复读,但可能出现幻读。注意:①、事务隔离级别为可重复读时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key锁;②、如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。
 
Serializable(序列化):可以避免脏读、不可重复读和幻读,但是并发性极低,一般很少使用。注意:该隔离级别在读写数据时会锁住整张表。
 
Oracle只提供Read commited 和Serializable 和自定义的Read only 级别,Mysql支持四种
 
事务控制语句:
 
BEGIN 或 START TRANSACTION 显式地开启一个事务;
 
COMMIT 也可以使用 COMMIT WORK,不过二者是等价的。COMMIT 会提交事务,并使已对数据库进行的所有修改成为永久性的;
 
ROLLBACK 也可以使用 ROLLBACK WORK,不过二者是等价的。回滚会结束用户的事务,并撤销正在进行的所有未提交的修改;
 
SAVEPOINT identifier,SAVEPOINT 允许在事务中创建一个保存点,一个事务中可以有多个 SAVEPOINT;
 
RELEASE SAVEPOINT identifier 删除一个事务的保存点,当没有指定的保存点时,执行该语句会抛出一个异常;
 
ROLLBACK TO identifier 把事务回滚到标记点;
 
SET TRANSACTION 用来设置事务的隔离级别。InnoDB 存储引擎提供事务的隔离级别有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ 和 SERIALIZABLE。
 
MYSQL 事务处理主要有两种方法:
 
1、用 BEGIN, ROLLBACK, COMMIT来实现
 
BEGIN 开始一个事务
 
ROLLBACK 事务回滚
 
COMMIT 事务确认
 
2、直接用 SET 来改变 MySQL 的自动提交模式:
 
SET AUTOCOMMIT=0 禁止自动提交
 
SET AUTOCOMMIT=1 开启自动提交
 
二、InnoDB锁
 
InnoDB隔离级别默认为(Repeatable Read)由于Innodb锁的特性,是解决了幻读的问题的。
 
行锁(共享锁与排它锁)
 
行锁也分为共享锁与排它锁。InnoDB的行锁是通过给索引上的==索引项加锁来实现的。只有通过索引条件进行数据检索InnoDB才使用行级锁,否则InnoDB将使用表锁(锁住索引的所有记录)==。
 
共享锁(Share Lock)
 
共享锁又称为读锁,是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排它锁。获得共享锁的事务只能读数据,不能修改数据。
 
**用途:**主要是用来确认记录是否存在,并确保没有人对这个记录进行是修改,如果当前食物需要对该记录进行修改则需要使用select … for update获取排它锁,否则容易造成死循环!
 
语法:selcet … lock in share mode
 
排它锁(exclusive lock)
 
排它锁又称为写锁,简称X锁,排它锁不能与其他锁并存,如一个事务获取了一个数据行的排它锁,其他事务就不能再获取该行的锁(共享锁、排它锁),只有该获取了排它锁的食物是可以对数据行进行读取和修改(其他事务要读取数据可来自快照)
 
语法:select … for update(update,delete,insert自动加了排它锁)
 
为了运行表锁与行锁共同存在,实现多粒度机制,InnoDB还有两种内部使用的意向锁
 
意向锁-表锁机别
 
意向锁属于表锁级别。InnoDB自动加的,不需要用户干预。
 
意向共享锁(IS) 意向排它锁(IX)
 
事务打算给数据行加行共享锁,必须先取得该表的IS锁,意向共享锁之间是可以相互兼容的 事务打算给数据行加行排他锁,必须先取得该表的IX锁,意向排它锁之间是可以相互兼容的
 
**意义:**当事务想去进行锁表时,可以先判断意向锁是否存在,存在时则可快速返回该表不能启用表锁。说白了就是这张表如果存在已经被行锁锁住了,就不能加表锁了。
 
表锁
 
InnoDB的行锁是通过给索引上的索引项加锁来实现的。只有通过索引条件进行数据检索InnoDB才使用行级锁,否则InnoDB将使用表锁(锁住索引的所有记录)。
 
也可以通过命令加锁 lock tables xx read/write;(和MyISAM设置表锁一样)
 
自增锁
 
针对自增列自增长的一个特殊的表级别锁,当当前的id自增后,但是在事务回滚后,这个id已经被使用过了,就会在表中出现id中间消失的状态。
 
可以通过 命令查询 show variables like 'innodb_autoinc_lock_mode';
 
默认取值1 ,代表连续,事务未提交ID
 
InnoDB锁的实现方式
 
Next-key Lock 临键锁,InooDB行锁默认算法
 
当sql执行按照索引进行数据的检索时,查询条件为范围查找(between and、<、>等)并有数据命中则此时SQL语句加上的锁为Next-key locks, 锁住索引的记录+区间。否则就是表锁哦。
 
临键锁也是解决了RR(Repeatable read)可重复读幻读的问题,原因是当前事务在这个区间做了查询就把当前区间给锁住了,其他事务就不能插入数据,也就不会出现幻读的情况了。
 
Gap Lock 间隙锁
 
当查询的记录不存在,临键锁就会退化为间隙锁。
 
Record Lock 记录锁
 
总结:
 
更新丢失:通过对当前修改线程加排他锁,其他线程就无法修改当前记录;
 
脏读:通过对修改记录的事务线程 加排他锁实现,当前线程加了排他锁,其他线程就不能查询当前的记录;
 
不可重复读:多次查询的事务线程,对这个线程加上共享锁,这样其他线程想修改这条记录是无法修改的;
 
幻读:通过加上Next-key锁,对于多次查询的线程有了临检索就不会让新的数据插入到这个区间来。

推荐阅读