全局锁
当我们的数据库获得全局锁的时候,意味着我们只能对数据库执行读的操作,其他的增删改的操作都会失效,直到这个锁被释放,释放锁有两种方式,一种是主动释放,另外一种是数据库关闭后会自动释放
为什么我们需要全局锁呢?其实我们可以看一下以下的场景。
当我们做数据库备份的时候,我们不希望说在备份的时候还执行一些增删改的操作,使得我们备份的数据和现在的数据不一致。
尤其是在电商项目中
当我们备份数据库的时候,如果我们先备份用户表,此时有一个用户购买了商品,然后我们再备份商品表,此时我们备份的数据会出现这种情况,用户余额没有减少,但是商品库存减少了,出现了用户白嫖商品的情况,这是不被允许的。
在Innodb中我们可以借助MVCC来解决这个问题,在默认的可重复读的情况下,我们在备份数据库之前就开启一个事务,得到一个read view,此时我们数据库备份都是依照这个read view备份的,即使现在有其他的事务对现在这个数据库执行其他操作,也不会影响到此次数据库备份的完整性。
在MyISAM不支持事务的引擎,那么我们只能设置一个全局锁来保持数据的一致性,虽然这样会阻塞数据库操作。
表级锁
表锁
表锁分为读锁和写锁,它的作用不只是局限于其他线程也包含了本线程,简单来说如果A线程现在获得了一个对student表的写锁,对school表的读锁,那么此时我们B,C线程没办法对这个student执行读写操作,也不能对school执行写操作只能执行读操作,同时A线程也只能对student执行读写操作,对school执行读操作,没办法对其他表执行其他操作。
元数据锁(MDL)
我们不需要显示的调用MDL,因为在我们对数据库表进行操作的时候就会自动调用MDL
- MDL读锁:当我们对数据库表实行CURD操作的时候会获取读锁
- MDL写锁:当我们对数据库表执行表结构的更改的时候会获取写锁
比如说,线程A现在对student表格执行一次select操作,获得了读锁没有释放,线程B现在对student执行select操作是可以成功的,但是如果线程C执行更改表结构的操作此时是失败的
如果A线程现在对student执行更新表结构的操作获得了写锁,然后没有释放,其他线程是没办法对这个表执行其他操作的
现在我们会遇到一个问题,在长事务中,如果线程A执行了CURD操作,获得了读锁,事务没有提交,也就是MDL锁没有释放,后面的事务是可以对这个表执行CURD操作的,但是如果此时C线程想要更改表结构申请获得写锁,但是没有拿到,此时就会阻塞,并且在线程C后面的CURD操作都会被阻塞,因为申请MDL锁的操作会形成一个队列,队列中写锁获取的优先级会大于读锁,也就是后面的CURD都会被阻塞。
所以为了能够安全的对表结构执行变更,我们可以先查看此时的长事务,查看是否有有事务对表上了读锁,并且考虑是否需要手动kill掉这个事务避免阻塞获得写锁,再去执行表结构变更的事务
意向锁
意向锁的目的是为了快速判断表内是否加锁
- 当我们需要对一个记录加行级共享锁(读锁)之前,我们会对表加个意向读锁
- 当我们要对一个记录加行级独占锁(写锁)之前,我们会对表加个意向写锁
意向锁是表锁不会与行锁发生冲突只会与表级的共享锁和独占锁发生冲突。
当我们需要给一个表加一个独占锁的时候我们需要判断此时表内是否有记录已经获取了行级的独占锁,因为我们不能对同一行记录获取两次独占锁这是互斥的,如果一行一行的去遍历的话这样效率不高,我们可以直接查看存不存在意向独占锁,有的话就说明表内存在有行记录拥有了独占锁。
AUTO-INC 锁
这个锁是用在自增主键上的
当我们执行插入语句的时候会获取这个锁,防止这个时候有其他的插入语句获取到的id值重复,当我们的插入语句执行结束后就会释放这个锁而不用等整个事务结束。
在InnoDB中可以设置这个AUTO-INC 锁,就是innodb_autoinc_lock_mode,=0的时候代表插入结束后释放锁,等于2的时候是一种轻量级锁,在申请到自增id之后就释放,等于1的时候分两种情况,如果是普通的插入语句申请到自增id后就释放,如果是批量插入,会等插入语句结束后释放。
讲到模式2我们如果binlog的日志格.式是statement的时候会出现主从数据库不一致的问题,当我们对一个数据库开两个session并且同时执行插入语句的时候,这时候两条sql语句的插入可能交叉进行,这样获得的主键id是不可控的,不是按照sql语句的完整记录顺序的。这时候binlog发给从数据库的时候,是按照两个sql语句来记录执行的,不会同时执行,这时候生成的id顺序就会和主库的顺序不一样,从而导致数据库主从不一致的问题。
这个时候我们只需要把binlog的记录模式改成row就可以了,此时主库的id是怎么样的从库的就会是怎么样的
行级锁
innoDB是支持行级锁的,myISAM是不支持行级锁的
行级锁大致有以下三类
- Record lock,记录锁,锁住当前行数据
- Gap lock,间隙锁,锁住一个范围的数据不包括当前行
- next-key lock ,记录锁和间隙锁的结合,既锁当前行数据也锁住范围数据
Record Lock记录锁
记录锁也分为读锁和写锁
在行级的的读锁和读锁之间是兼容的,写锁和写锁,写锁和读锁之间是不兼容的
比如说当前有个事务对一条记录加了读锁,那么其他的事务也可以对这个记录加读锁但是没办法加写锁
如果当前有个事务对一条记录加了写锁,那么其他事务不能对这个记录加任何锁
Gap Lock间隙锁
间隙锁只存在可重复读的隔离级别,它也有读间隙锁和写间隙锁,但是这两个锁都是互相兼容的,实质上都是为了锁住一个范围的数据,尽量防止幻读的现象。
比如两个事务可以同时拥有包含相同范围的间隙锁
Next-key lock临键锁
是record lock 和gap lock的结合,锁定一个范围和数据本身,它的读锁和读锁是兼容的,但是写锁和读锁写锁是冲突的
插入意向锁
一条事务在执行插入操作的时候,会先判断这个区间内是否被设置了间隙锁,如果被设置了间隙锁,这个事务是会被阻塞的,同时对于这个位置生成一个插入意向锁,等待间隙锁被释放后,才能成功执行
mysql在生成锁的时候,是先生成锁结构,再设置锁的状态的,如果锁状态是等待状态,并不意味事务成功获取到锁,只有锁状态是正常的时候才是成功获取到锁。