聊聊MySQL的锁机制
在MySQL中,除了对事务的管理,还有对锁机制的操纵。一直以来,这块我都不是很明白,于是,今天打算好好撸它一撸。
MySQL中的锁的划分原则:
- 按照锁的粒度划分:全局锁、表锁、页锁、行锁、
- 按照锁的实现方式:共享锁(S锁,也称读锁)、排他锁(X锁,也称写锁)
- 按思想划分:悲观锁、乐观锁
- 在Innodb中还引入了“意向锁”
全局锁
顾名思义,全局锁就是对整个数据库实例加锁。命令是 Flush tables with read lock (FTWRL)。当你需要让整个库处于只读状态的时候,可以使用这个命令,之后其他线程的以下语句会被阻塞:数据更新语句(数据的增删改)、数据定义语句(包括建表、修改表结构等)和更新类事务的提交语句。
全局锁的典型使用场景是,做全库逻辑备份
表锁
表锁是MySQL中锁粒度最大的锁,它针对整张表进行加锁,支持的并发度低,同一时刻系统需要处理的请求低,因此系统资源开销少。MyISAM和InnoDB都支持表级锁,但是InnoDB默认的是行级锁。这里提一个面试题:
Innodb什么时候会加上表锁呢?(上次有赞一面的面试题)
只有通过索引条件检索数据的时候加的是行锁,否则加表锁!
页锁
页锁这个用得很少,因为当今主流的两种数据库引擎都不支持页锁,只有BDB支持。级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。
行锁
行级就是加在记录行上的所。是MySQL中锁粒度最小的锁,支持的并发度高,因此系统资源开销也大。在Innodb中,有三种关于行锁的实现算法:
- Record Lock:单个行记录上的锁。
- Record Lock总是会去锁住索引记录,如果InnoDB存储引擎表在建立的时候没有设置任何一个索引,那么这时InnoDB存储引擎会使用隐式的主键来进行锁定
- Gap Lock:间隙锁,锁定一个范围,但不包含记录本身。
- 在索引的间隙之间加上锁,这是为什么Repeatable Read隔离级别下能防止幻读的主要原因。
- Next-Key Lock:(Gap Lock+Record Lock),锁定一个范围,并且锁定记录本身。
- Next-Key Lock锁机制其实就是前面两个锁相结合的机制,行锁就是采用这个锁的机制用来防止幻读!
意向锁
在Innodb中引入了意向锁这个概念。先解释一下,什么叫意向锁?
如果另一个任务试图在该表级别上应用共享或排它锁,则受到由第一个任务控制的表级别意向锁的阻塞。第二个任务在锁定该表前不必检查各个页或行锁,而只需检查表上的意向锁。
简单概括一下,比如说现在有个事务A要对某条记录修改(update、insert和delete都是隐式加排他锁的),那么它就要对这个记录加行锁,而这个时候事务B要对该表进行操作,那么它必须先全表扫描,检测哪个行被加了行锁。这种操作MySQL认为十分耗时、低效。于是乎,就引入了意向锁,在引入之后,事务B要对该表进行操作时,就不必对该表进行全表扫描了,只需等这个意向锁释放掉,再继续操作就行。整个过程省略了全表扫描监测被锁行的操作。
这里要特别提出一点,意向锁不会与行级的共享 / 排他锁互斥!!!,它只会与表锁冲突。详情如下所示:
意向共享锁(IS) | 意向排他锁(IX) | |
---|---|---|
共享锁(S) | 兼容 | 互斥 |
排他锁(X) | 互斥 | 互斥 |
多版本并发控制—MVCC
MVCC(Multi-Version Concurrency Control)是乐观锁的一种实现。为什么要引入MVCC呢?我们知道MySQL一般都是使用锁机制来进行事务隔离,从而避免事务安全问题。但是,就好比与Java中的锁一样,虽然它保证了安全性,但是同时它也大大降低了效率。MySQL的锁机制也是同样的道理,在一个读多写少的场景下,明显使用锁效率就比较低。这个时候,MVCC就派上了用场。
基本原理
通过保存数据在某个时间点的快照来实现的。这意味着一个事务无论运行多长时间,在同一个事务里能够看到数据一致的视图。根据事务开始的时间不同,同时也意味着在同一个时刻不同事务看到的相同表里的数据可能是不同的。它只在 READ COMMITTED 和 REPEATABLE READ 两个隔离级别下工作
三种Log的区别和应用
- redo log:记录了数据操作在物理层面的修改,mysql中使用了大量缓存,缓存存在于内存中,修改操作时会直接修改内存,而不是立刻修改磁盘,当内存和磁盘的数据不一致时,称内存中的数据为脏页(dirty page)。为了保证数据的安全性,事务进行中时会不断的产生redo log,在事务提交时进行一次flush操作,保存到磁盘中, redo log是按照顺序写入的,磁盘的顺序读写的速度远大于随机读写。当数据库或主机失效重启时,会根据redo log进行数据的恢复,如果redo log中有事务提交,则进行事务提交修改数据。这样实现了事务的原子性、一致性和持久性。
- undo log :除了记录redo log外,当进行数据修改时还会记录undo log,undo log用于数据的撤回操作,它记录了修改的反向操作,比如,插入对应删除,修改对应修改为原来的数据,通过undo log可以实现事务回滚,并且可以根据undo log回溯到某个特定的版本的数据,实现MVCC。
- bin log:是mysql服务层产生的日志,常用来进行数据恢复、数据库复制,常见的mysql主从架构,就是采用slave同步master的binlog实现的, 另外通过解析binlog能够实现mysql到其他数据源(如ElasticSearch)的数据复制。