关于数据库锁的那些事

锁对于传统数据库来讲是很是重要的, 里面也掺杂各类权衡, 概念类较多, 本文只针对部份内容作了讲解.mysql

1. 从影响的数据范围看数据库锁

行锁

单独给一行数据记录加锁, mysql 中 咱们经常使用的 InnoDB 引擎支持行锁.sql

优点: 是常见关系型数据库中锁粒度最小的一种锁, 可以有效的提升并发操做.数据库

劣势: 消耗更多资源, 用法不规范容易产生死锁bash

表锁

顾名思义, 对当前操做的整张表加锁,是目前 mysql 锁粒度最大的一种并发

最多见的 myisam 引擎使用表锁.高并发

InnoDB 引擎 sql 使用不当(如非主键、索引条件)会退化为表锁.性能

优点: 并发读没问题, 消耗资源少, 加减锁速度快, 能够避免死锁.ui

劣势: 大部分状况下DML操做并发低, 频繁对全表加锁, 发生锁冲突的几率很是高.spa

页锁

页锁是介于比表锁和行锁之间的一种锁, 只是对数据页进行锁定线程

页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但并发低,行级并发高,但耗资源。因此取了折衷的页级,一次锁定数据页中相邻的一组记录

mysql 中的BDB引擎支持页级锁

大概锁的层次结构关系

2. 深刻 mysql 的锁

锁分类 (latch 和 lock)

在 mysql 数据库中, 从全局看, 共分为这两种锁, 但他们的用处有很大区别.

latch 直译过来为“门闩(shuān)”, 相似于下图

它在数据库中的学名叫作 闩锁(一种轻量级的锁), 在 InnoDB 存储引擎中,latch又能够分为 mutex(互斥量)和 rwlock(读写锁), 其目的是用来保证并发线程操做临界资源的正确性,而且一般没有死锁检测的机制.

# 查看关于闩锁的统计信息
SHOW ENGINE INNODB MUTEX ;  
+--------+------------------------+---------+
| Type   | Name                   | Status  |
+--------+------------------------+---------+
| InnoDB | rwlock: log0log.cc:838 | waits=1 |
+--------+------------------------+---------+
复制代码

lock 锁, 是咱们打交道最多的一种, 它主要针对的是事务,用来锁定的是数据库中的对象,如前面提到的表锁、页锁、行锁。而且通常lock的对象仅在事务 commit 或 rollback 后进行释放(不一样事务隔离级别释放的时间可能不一样)。此外,lock,正如在大多数数据库中同样,是有死锁机制的.

3. InnoDB 实现 lock 锁分类

共享锁 (S Lock)

容许事物读取数据

当你拿到了共享锁, 你能够读取这条数据, 另外一我的一样能够拿到共享锁去读取数据(这里咱们称为锁兼容)

可是同时另外一我的也来了, 他同时想要拿排他锁去更新此条数据,那么他必须等待释放共享锁才能够拿到排他锁更新数据(这里咱们称之为锁不兼容)

排他锁 (X Lock)

容许事物删除或更新一行数据

前面咱们举例了 若是别人已经持有共享锁了, 其余人是不能拥有排他锁的.

当一我的拿到此条数据的排他锁, 不能同时再拿到排他锁和共享锁的(锁不兼容).

排他锁(X Lock)和任何锁都不兼容.

4. Innodb 面对锁是怎么读取的

一致性的非锁定读

咱们知道 当咱们频繁 更新数据 加排他锁 (X Lock) 的时候, 由于其锁的不兼容性, 会严重影响正常的数据查询性能.

一致性的非锁定读 是指 InnoDB 存储引擎经过行多版本控制(multi versioning)的方式来读取当前执行时间数据库中行的数据。若是读取的行正在执行 DELETE 或 UPDATE 操做,这时读取操做不会所以去等待行上锁的释放。相反地,InnoDB 存储引擎会去读取行的一个快照数据

一致性非锁定读的快照数据实际上是读的 undo 数据(undo是用来回滚事务中的数据), 没有额外的锁操做, 因此读取速度很是的快.

一致性的非锁定读由于不须要等待排他锁 (X Lock) 的释放, 因此极大的提升了并发的性能, 在 innodb 事务隔离级别 不可重复读(read-committed)、可重复读(repeatable-read 默认级别) select 使用的是 一致性的非锁定读.

思考: 一致性的非锁定读 解决了什么问题? 带来了什么问题?

一致性锁定读

在一些强一致的场景, 咱们是但愿让用户读取到的永远是最新的数据. 这时候, 咱们须要使用 一致性锁定读 的场景.

下面展现了两种基于数据库查询语句上X锁和S锁, 通常与显示的事务组合使用.

SELECT…FOR UPDATE对读取的行记录加一个X锁,其余事务不能对已锁定的行加上任何锁

SELECT…LOCK IN SHARE MODE对读取的行记录加一个S锁,其余事务能够向被锁定的行加S锁,可是若是加X锁,则会被阻塞

FOR UPDATE 也是咱们常常所说的悲观锁, 对应的还有乐观锁, 乐观锁更可能是业务层面的实现, 这里再也不讲述.

死锁

死锁是指两个或两个以上的进程在执行过程当中,因为竞争资源或者因为彼此通讯而形成的一种阻塞的现象,若无外力做用,它们都将没法推动下去.

目前主要仍是靠业务逻辑来解决, 若是程序是串行, 就不会产生死锁, 只有并发状况下才有可能产生, 程序并行+数据库并行(行级锁)运行, 可能会发生死锁.

这里可使用此命令查看全部线程, 对应的提示直接 kill 掉便可

show processlist
复制代码

查看目前锁的状况

1:查看当前的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;

2:查看当前锁定的事务

SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;

3:查看当前等锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
复制代码

固然你也能够经过配置 innodb_lock_wait_timeout 属性,来指定锁的超时时间, 超时数据库系统自动 kill.

不过若是出现了长时间获取不到锁, 数据库会自动进行死锁检测, 并进行终止.

固然,保证业务中操做数据库的执行顺序, 避免交叉执行, 基本可以避免的死锁状况。

总结

本文一部分参考概念 《mysql 技术内幕》一书.

更多有趣的计算机技术关注 呆呆熊一点通 :

相关文章
相关标签/搜索