Mysql系列(十一)—Msql之锁

锁是数据库系统区别于文件系统的一个关键特性,锁机制用于管理对共享资源的并发访问。

Lock与latch

latch一般称为闩锁,又可以分为mutex(互斥量)和rwlock(读写锁)。其目的是用来保证并发线程操作临界自资源的正确性,通常没有死锁检测机制。

lock的对象是事务,一般在commit或rollback之后进行释放。其是有死锁机制的。

              

InnoDB存储引擎中的锁

锁的类型

行锁分为:共享锁(S Lock)、排他锁(X Lock)。如果一个事务已经获得了行r的共享锁,那么另外的事务可以立即获得r的共享锁,称为锁兼容。如果两个是排他锁,那么后一个事务必须等待前一个事务执行完成释放锁,该情况为锁不兼容。

           

意向锁

InnoDB支持多粒度锁定,支持行级和表级的同时锁定。InnoDB提供了意向锁,意向锁分为多个层次,意向锁意味着希望在更细粒度上进行加锁。如果要对r记录上X锁,那么首先分别需要对数据库A、表、页上意向锁IX,最后对r上X锁。如果有任何一个部分导致等待,那么该操作需要等待粗粒度锁的完成(意味着当检测到表上有IX后,将进行等待,不需要继续向下判断,提高效率)。

意向锁页分为意向共享锁(IS Lock)意向排他锁(IX Lock).

              

 

一致性非锁定读

一致性非锁定读是指InnoDB通过行多版本控制的方式来读取当前执行时间数据库中行的数据。如果读取的行正在执行修改操作,那么此时的读取操作不会等行锁的释放。其会去读取行的一个快照数据,其快照数据的方式是通过undo段来完成的。一致性非锁定读极大的提高了数据库的并发性。默认为此读取方式,但是对于不同的事务隔离级别,读取的方式不同。即使都使用非锁定一致性读,对于快照数据的定义也不同。

在事务隔离级别READ COMMITED 和 REPEATABLE READ下,innoDB使用非锁定的一致性读。然而,对于快照数据的定义却不相同。READ COMMITED下,非一致性读总是读取被锁定行的最新一份快照数据。而在REPEATABLE READ下,非一致性读总是读取事务开始时的行数据版本。

一致性锁定读

用户事务隔离级别为REPEATABLE READ模式下,select操作使用一致性非锁定读。某些情况下需要显示的进行加锁

select  ... from update  X锁

select ... lock in share mode S锁

自增长与锁

自增长在Innodb内存结构中,是对每个有其的表都有一个自增长计数器。当对含有自增长计数器的表进行插入操作时,这个计数器会被初始化。插入操作会根据这个自增长的计数器值加1赋予自增长列。这种实现方式称为AUTO-INC Locking.这种锁其实是一种特殊的表锁机制。为了提高插入的性能,当该sql插入后就会被释放,而不是等事务结束后才释放。但是其对于大批量的数据插入依然存在性能问题。mysql5.1.22开始,提供了一种轻量级互斥量的自增长实现机制。可以通过innodb_autoinc_lock_mode来控制自增长的模式,默认为1。

         

             

外键和锁

对于一个外键列,如果没有显式的进行加索引,那么innodb会自动对其加一个索引。对于外键值得插入或者更新,首先需要查找父表中得记录,即select 父表。对于父表的select操作,使用select ... lock in share mode方式。

锁的算法

行锁的3种算法

  • Record Lock 单个行记录上的锁
  • Gap Lock   间隙锁,锁定记录范围,但是不包含记录本身
  • Next-Key Lock GapLock+Record Lock 锁住一个范围,并且锁定记录本身

采用Next-Key Lock的锁定技术称为Next-Key Locking。其设计的目的是为了解决幻读的问题。

锁的问题

脏读

脏读就是在不同的事务下,当前事务可以读到其他事务未提交的数据。

不可重复读

不可重复读是一个事务读到其他事务已提交的数据。

丢失更新

丢失更新是一个事务的更新操作会被另一个事务的更新操作所覆盖,从而导致数据的不一致。

                     

对于innodb的机制不会导致该问题发生,但是在生产种会发生丢失更新的问题。该问题并不是mysql本身导致的。可以将事务隔离级别修改未串行化来解决这个问题。

            

阻塞

由于不同锁之间的兼容关系,有些时刻一个事务中的锁需要等待另一个事务种的锁释放他所占用的资源,这就是阻塞。innodb中,innodb_lock_wait_timeout来控制等待的时间,默认为50秒。

死锁

死锁是指两个或以上事务在执行过程中,因锁资源而造成的一种互相等待的现象。

解决方法一:解决死锁最简单的方法是超时,当一个线程因超时而进行回滚时,另一个线程就能继续执行。如果超时的事务比较多,那么会产生大量的undo log,造成回滚事务的时间比执行事务的时间还要多,性能很低。

解决方法二:当前采用wait-for graph(等待图)的方式来进行死锁检测。

wait-for graph存放锁的信息链表和事务等待链表。通过这些可以构造出一个图,若图存在回路,那么就代表有死锁。例如:

                         

上图有四个事务,所以构成的图有四个节点,根据他们之间的依赖关系,构建的图如下:t1t2之间有死锁

                                                                         

死锁示例

锁升级

锁升级是指当前锁粒度的降低。即行锁升级为页锁。

例如:Sql server 在一个单独的sql在一个对象上持有的锁的数量超过了阈值,默认为5000或者锁资源占用超过内存的40%就会发送锁升级。

Innodb是不存在锁升级问题的,因为其不是根据每个记录来产生行锁的。相反,其根据每个事务访问的页来对锁进行管理,采用位图的方式。

假设一张表有3000000个数据页,每个页大概有100条记录,那么总共有300000000条记录。若根据每行记录进行加锁,每个锁占用10字节,那么仅对锁管理就需要3G内存。而innodb存储引擎,根据页进行加锁,并采用位图的方式,假设每个锁占用30个字节,则锁对象仅需90M.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值