暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL 锁机制

小明的编程笔记 2020-03-06
337

一、简介

1.1 概述

  • MyISAM 和 Memory 存储引擎使用的是表级锁,BDB 引擎使用的是页级锁,也支持表级锁。由于 BDB 引擎基本已经成为历史,因此就不再介绍了。

  • InnoDB 存储引擎既支持行级锁,也支持表级锁,默认情况下使用行级锁。

  • 所谓表级锁,它直接锁住的是一个表,开销小,加锁快,不会出现 死锁的情况,锁定粒度大,发生锁冲突的概率更高,并发度最低。

  • 所谓行级锁,它直接锁住的是一条记录,开销大,加锁慢,发生锁冲突的概率较低,并发度很高。

  • 所谓页级锁,它是锁住的一个页面,它的开销介于表级锁和行级锁中间,也可能会出现死锁,锁定粒度也介于表级锁和行级锁中间,并发度也介于表级锁和行级锁中间。

  • 行级锁更适合大量按照索引条件并发更新少量不同的数据,同时还有并发查询的应用

 看你的mysql现在已提供什么存储引擎:

mysql> show engines;
复制

 看你的mysql当前默认的存储引擎:

mysql> show variables like '%storage_engine%';
复制

1.2 innoDB行级锁

    行级锁本身与表级锁的实现差别就很大,而事务的引入也带来了很多新问题,尤其是事务的隔离性,与锁机制息息相关。

    对于事务的基本操作,对于不同隔离级别可能引发的问题,像脏读、不可重复读等问题。

    数据库实现事务隔离的方式,基本可以分为两种:
    (1)在操纵数据之前,先对其加锁,防止其他事务对数据进行修改。这就需要各个事务串行操作才可以实现。
    (2)不加任何锁,通过生成一系列特定请求时间点的一致性数据快照,并通过这个快照来提供一致性读取。

    上面的第二种方式就是数据多版本并发控制,也就是多版本数据库,一般简称为 MVCC 或者 MCC,它是 Multi Version Concurrency Control 的简写。

    数据库的事务隔离越严格,并发的副作用就越小,当然付出的代价也就越大,因为事务隔离机制实质上是使得事务在一定程度上”串行化”,这与并行是矛盾的。

    InnoDB有两种类型的行级锁,两种内部使用的意向锁;

  • 共享锁(S):允许一个事务读一行数据时,阻止其他的事务读取相同数据的排他锁。

  • 排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据的共享锁和排他锁。

  • 意向共享锁(IS):事务打算给数据行加行共享锁。事务在给一个数据行加共享锁前必须先取得该表的IS锁。

  • 意向排他锁(IX):事务打算给数据行加行排他锁。事务在给一个数据行加排他锁前必须先取得该表的IX锁。

  • 悲观锁(抽象,不真实存在的锁)

  • 乐观锁(抽象,不真实存在的锁)

InnoDB 行级锁是通过给索引上的索引项加锁来实现的,InnoDB行级锁只有通过索引条件检索数据,才使用行级锁;否则,InnoDB使用表锁

在不通过索引(主键)条件查询的时候,InnoDB是表锁而不是行锁

1.3 4种锁的共存逻辑关系表

共享锁语句

select * from table_name lock in share mode;
复制

排他锁语句

select * from table_name for update;
复制

1.4 innodb间隙锁

    可以理解为是对于一定范围内的数据进行锁定,如果说这个区间没有这条数据的话也是会锁住的;主要是解决幻读的问题,如果没有添加间隙锁,如果其他事物中添加id在1到100之间的某条记录,此时会发生幻读;另一方面,视为了满足其恢复和赋值的需求。

默认情况下,innodb_locks_unsafe_for_binlog是0(禁用),这意味着启用了间隙锁定:InnoDB使用下一个键锁进行搜索和索引扫描。若要启用该变量,请将其设置为1。这将导致禁用间隙锁定:InnoDB只使用索引记录锁进行搜索和索引扫描

例如:

注意:间隙锁容易导致死锁


二、死锁

2.1 死锁的原因

    官方定义如下:两个事务都持有对方需要的锁,并且在等待对方释放,并且双方都不会释放自己的锁。

    MySQL有两种死锁处理方式:

  • 等待,直到超时(innodb_lock_wait_timeout=50s)。

  • 发起死锁检测,主动回滚一条事务,让其他事务继续执行(innodb_deadlock_detect=on)。

    由于性能原因,一般都是使用死锁检测来进行处理死锁。

    死锁检测的原理是构建一个以事务为顶点、锁为边的有向图,判断有向图是否存在环,存在即有死锁。检测到死锁之后,选择插入更新或者删除的行数最少的事务回滚,基于 INFORMATION_SCHEMA.INNODB_TRX 表中的 trx_weight 字段来判断

2.2 对于锁与事务的建议

  • 收集死锁信息:
        1) 利用命令 SHOW ENGINE INNODB STATUS查看死锁原因。
        2) 调试阶段开启 innodb_print_all_deadlocks,收集所有死锁日志。

  • 减少死锁:
    1) 使用事务,不使用 lock tables 。
    2) 保证没有长事务。
    3) 操作完之后立即提交事务,特别是在交互式命令行中。
    4) 如果在用 (SELECT … FOR UPDATE or SELECT … LOCK IN SHARE MODE),尝试降低隔离级别。
    5) 修改多个表或者多个行的时候,将修改的顺序保持一致。
    6) 创建索引,可以使创建的锁更少。
    7) 最好不要用 (SELECT … FOR UPDATE or SELECT … LOCK IN SHARE MODE)。
    5) 如果上述都无法解决问题,那么尝试使用 lock tables t1, t2, t3 锁多张表

文章转载自小明的编程笔记,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论