2019-03-18

InnoDB原理以及索引优化


数据库事务设计遵循ACID原则 原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)

mysql默认的4种隔离级别

读未提交(read-uncommitted)、读已提交(或不可重复读)(read-committed)、可重复读(repeatable-read)、串行化(serializable)。

MySQL的默认隔离级别是RR。

一 共享锁 独占锁

mysql提供两种行锁 共享锁(shared locks,S锁)   独占锁(exclusive locks,X锁)

共享锁运行当前事务读取行 独占锁运行当前事务更新和删除行

共享锁  事务T1获取行x的共享锁,其他事务同时也可获取行x的共享锁

独占锁  事务T1获取行x的独占锁,其他事务必须等到T1释放锁才能获得该锁

如果事务T1在行r上保持S锁,则另一个事务T2对行r的锁的请求按如下方式处理:

         T2可以同时持有S锁;

         T2如果想在行r上获取X锁,必须等待其他事务对该行添加的S锁或X锁的释放。

二  意向锁-Intention Locks

InnoDB支持多种粒度的锁,允许行级锁和表级锁的共存。

InnoBD使用意向锁来实现多个粒度级别的锁定。意向锁是表级锁,表示table中的row所需要的锁(S锁或X锁)的类型。

意向锁分为意向共享锁(IS锁)和意向排它锁(IX锁)。IS锁表示当前事务意图在表中的行上设置共享锁,下面语句执行时会首先获取IS锁,因为这个操作在获取S锁:

SELECT ... LOCK IN SHARE MODE

IX锁表示当前事务意图在表中的行上设置排它锁。下面语句执行时会首先获取IX锁,因为这个操作在获取X锁:

SELECT ... FOR UPDATE

事务要获取某个表上的S锁和X锁之前,必须先分别获取对应的IS锁和IX锁。


InnoDB中的锁

记录锁Record

Record Lock是对索引记录的锁定。

记录锁有两种模式:S模式和X模式。例如:SELECT id FROM test WHERE id = 10 FOR UPDATE; 表示防止任何其他事务插入、更新或者删除id =10的行。

记录锁始终只锁定索引。即使表没有建立索引,InnoDB也会创建一个隐藏的聚簇索引(隐藏的递增主键索引),并使用此索引进行记录锁定。

间隙锁Gap Locks

间隙锁作用在索引记录之间的间隔,又或者作用在第一个索引之前,最后一个索引之后的间隙。不包括索引本身。

例如:

SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;

这条语句阻止其他事务插入10和20之间的数字,无论这个数字是否存在。

Next-key锁

Next-key锁实际上是Record锁和gap锁的组合。Next-key锁是在下一个索引记录本身和索引之前的gap加上S锁或是X锁(如果是读就加上S锁,如果是写就加X锁)。

默认情况下,InnoDB的事务隔离级别为RR,系统参数innodb_locks_unsafe_for_binlog的值为false。InnoDB使用next-key锁对索引进行扫描和搜索,这样就读取不到幻象行,避免了幻读的发生。

插入意向锁

插入意向锁在行插入之前由INSERT设置一种间隙锁,是意向排它锁的一种。在多事务同时写入不同数据至同一索引间隙的时,不会发生锁等待,事务之间互相不影响其他事务的完成,这和间隙锁的定义是一致的。

假设一个记录索引包含4和7,其他不同的事务分别插入5和6,此时只要行不冲突,插入意向锁不会互相等待,可以直接获取。

自增锁

自增锁(AUTO-INC Locks)是事务插入时自增列上特殊的表级别的锁。最简单的一种情况:如果一个事务正在向表中插入值,则任何其他事务必须等待,以便第一个事务插入的行接收连续的主键值。


三 幻读

在同一个事务中,执行同一个sql,得到不同的结果

幻读在RR条件下是不会出现的。

RC(Read Commit)隔离级别可以避免脏读,事务内无法获取其他事务未提交的变更,但是由于能够读到已经提交的事务,因此会出现幻读和不重复读。也就是说,RC的快照读是读取最新版本数据,而RR的快照读是读取被next-key锁作用区域的副本。

乐观锁与悲观锁

乐观锁:在UPDATE的WHERE子句中加入版本号信息来确定修改是否生效;

悲观锁:在UPDATE执行前,SELECT后面加上FOR UPDATE来给记录加锁,保证记录在UPDATE前不被修改。SELECT ... FOR UPDATE是加上了X锁,也可以通过SELECT ... LOCK IN SHARE MODE加上S锁,来防止其他事务对该行的修改。

死锁

   无论是哪种场景,万变不离其宗,都是由于某个区间上或者某一个记录上可以同时持有锁,例如不同事务在同一个间隙gap上的锁不冲突;不同事务中,S锁可以阻塞X锁的获取,但是不会阻塞另一个事务获取该S锁。这样才会出现两个事务同时持有锁,并互相等待,最终导致死锁。

其中需要注意的点是,增、删、改的操作都会进行一次当前读操作,以此获取最新版本的数据,并检测是否有重复的索引。这个过程除了会导致RR隔离级别下出现死锁之外还会导致其他两个问题:

第一个是可重复读可能会因为这次的当前读操作而中断,(同样,幻读可能也会因此产生);

第二个是其他事务的更新可能会丢失(解决方式:悲观锁、乐观锁)。




























记录锁Record记录锁Record

你可能感兴趣的:(2019-03-18)