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

【MySQL入门】之MySQL数据库的锁机制(二)

Mysql technology 2020-05-09
690


  上篇文章主要聊了全局锁和表锁,并详细分析MDL锁的作用以及可能带来的问题。今天我们主要来聊一聊Innodb存储引擎的行锁。MySQL的行锁是在引擎层由引擎自己实现的,并不是所有的引擎都支持行锁,MyISAM 引擎就不支持行锁。行锁,顾名思义就是针对数据表中的行记录的锁。比如事物A更新了一行,而事物B也要更新同一行,就必须等待事物A的操作完成后才能进行。下面我们就介绍下行锁的种类,针对不同的锁进行操作演示。

一. 行锁的种类



1.单个记录的锁(record lock),在RC隔离级别下只有record  lock记录锁模式。

2.间隙锁(Gap lock

3.记录锁和间隙锁的组合(next-key lock加锁的基本单位就是next-key lock

以下所有演示都是在RR隔离级别下进行的。

演示表t,主键是id,索引是age字段。

表数据如下:

二. 单个记录的锁



Session A

    mysql> begin;
    Query OK, 0 rows affected (0.00 sec)
    mysql> update t set name='lili' where age=24;
    Query OK, 0 rows affected (0.00 sec)
    Rows matched: 1  Changed: 1  Warnings: 0

    Session B

      mysql> begin;
      Query OK, 0 rows affected (0.00 sec)
      mysql> update t set name='jim' where age=24;

      试验结果:Session B

       

      通过这个演示可以看出两个事务针对同一行数据修改时,后执行的事务会出现锁等待现象,超过innodb_lock_wait_timeout(默认50s)定义的时间后会超时。

       

      我们再做个试验,把age上面的索引删除,看看会出现什么样的试验效果。

        alter table t drop index idx_age;//删除索引

        session  A

          mysql> begin;
          Query OK, 0 rows affected (0.00 sec)
          mysql> update t set name='lili' where age=24;
          Query OK, 1 row affected (0.00 sec)
          Rows matched: 1 Changed: 1 Warnings: 0

          Session B

            mysql> begin;
            Query OK, 0 rows affected (0.00 sec)
            mysql> update t set name='jim' where age=16;

            试验结果:Session B

            通过试验看出,虽然更新了不同的行,也出现了锁超时的情况。两个实现不同的地方就是试验2把索引删除了,试验结果就截然不同了。通过这个试验,我们可以看出,就是Innodb的行锁是加在索引上的。

            三. 间隙锁(Gap lock)



            RR隔离级别下,为了避免幻读现象,引入了Gap lock。它锁定行记录的范围,不包含记录的本身,不允许在此范围内插入任何数据。

            表数据如下:

            Session A

              mysql> begin;
              Query OK, 0 rows affected (0.00 sec)
              mysql> select * from t where age<24 lock in share mode;
              +----+------+------+
              | id | name | age  |
              +----+------+------+
              |  4 | jack |   16 |
              +----+------+------+
              1 row in set (0.01 sec)

              Session B

                mysql> begin;
                Query OK, 0 rows affected (0.00 sec)
                mysql> insert into t(name,age) value('angel',20);

                实验结果:Session B

                可以看出innodb在age<24的范围内加了间隙锁,导致session B插入age=20的记录时阻塞。

                注意:间隙锁只在RR隔离级别下生效,RC隔离级别下是允许出现幻读现象的。

                四. Next-key Lock



                Next-keys Lock是记录锁和间隙锁的组合,Innodb扫描索引记录时,会先对索引记录加上记录锁(Record Lock),再对索引记录之间的间隙加上间隙锁(Gap Lock)。

                Innodb加锁的基本单位是next-key lock,是前开后闭区间。

                Session A

                  mysql> begin;
                  Query OK, 0 rows affected (0.00 sec)
                  mysql> select * from t where age >=16 and age<24 for update;
                  +----+------+------+
                  | id | name | age  |
                  +----+------+------+
                  |  4 | jack |   16 |
                  +----+------+------+
                  1 row in set (0.00 sec)

                  session B

                    mysql> begin;
                    Query OK, 0 rows affected (0.00 sec)
                    mysql>insert into t(name,age) value('lucy',17);

                    Session C

                      mysql> begin;
                      Query OK, 0 rows affected (0.00 sec)
                      mysql>update t set name='lucy' where age=24;

                      试验结果:

                      Session B

                      Session C

                      Innodb引擎加锁有个原则:查找过程中访问到的对象才会加锁。

                      在上面的例子中当用到age=16的时候,会在索引age上 (5,16]范围内加next-key锁,同时在(16,24]范围内加next-key锁,这样session A加锁的范围就是(5,24]。从结果上看也符合这个预期。

                      五. 锁监控



                      关于MySQL锁的监控,我们一般可以通过show processlistshow engine innodb status来查看和监控数据库的锁信息,其实还有一些更简单的方法,MySQL把事务和锁的信息记录在了information_schema库中,设计到的三张表分别是INNODB_TRXINNODB_LOCKSINNODB_LOCK_WAITS

                      我们模拟了一个锁等待的场景,以下是从这三张表收集的信息

                      通过以下命令可以查看锁源头:

                        SELECT 
                        r.trx_wait_started AS wait_started,
                        TIMEDIFF(NOW(),
                        r.trx_wait_started) AS wait_age,
                        rl.lock_table AS locked_table,
                        rl.lock_index AS locked_index,
                        rl.lock_type AS locked_type,
                        r.trx_id AS waiting_trx_id,
                        r.trx_mysql_thread_id AS waiting_pid,
                        sys.format_statement(r.trx_query) AS waiting_query,
                        rl.lock_id AS waiting_lock_id,
                        rl.lock_mode AS waiting_lock_mode,
                        b.trx_id AS blocking_trx_id,
                        b.trx_mysql_thread_id AS blocking_pid,
                        sys.format_statement(b.trx_query) AS blocking_query,
                        bl.lock_id AS blocking_lock_id,
                        bl.lock_mode AS blocking_lock_mode 
                        FROM information_schema.INNODB_LOCK_WAITS w 
                        INNER JOIN information_schema.INNODB_TRX b 
                        ON b.trx_id = w.blocking_trx_id 
                        INNER JOIN information_schema.INNODB_TRX r ON r.trx_id = w.requesting_trx_id 
                        INNER JOIN information_schema.INNODB_LOCKS bl ON bl.lock_id = w.blocking_lock_id 
                        INNER JOIN information_schema.INNODB_LOCKS rl ON rl.lock_id = w.requested_lock_id ORDER BY r.trx_wait_started\G


                             【MySQL入门】之MySQL数据库的锁机制(一)




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

                        评论