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

锁技能进阶(二)

topdba 2019-12-09
1658

再来温故一些基础概念。

锁机制

数据库区别与文件系统的一个关键特性之一。用于管理对共享资源的并发访问。

各种关系型数据库虽可能类似,但锁实现方式上又有各自的特点。

MySQL InnoDB 存储引擎,锁设计粒度:行锁。

MySQL MyISAM存储引擎,设计粒度:

其他数据库如SQL server 锁设计粒度:页锁。热点数据页并发访问。后通过乐观/悲观行锁控制并发访问(但会造成锁升级)

lock对象是事务,用来锁定的是数据库中的对象:表,页,行。并且lock对象仅在事务commitrollback后进行释放。(不同隔离级别释放的时间可能不同)


InnoDB支持多粒度锁定。允许事务在行级和表级上的锁同时存在。为了支持在不同粒度上加锁操作 >>> 意向锁(Intention Lock)应运而生。

兼容矩阵:

模拟一个简单加锁:

查看数据库锁请求情况(一般通过如下方式)

1.show full processlist;


2.show engine innodb status\G

root@localhost:(none) 5.7.26-log 11:51:27> show engine innodb status\G (省略与本文无关输出信息)

*************************** 1. row ***************************

  Type: InnoDB

  Name: 

Status: 

=====================================

2019-12-09 11:52:42 0x7f6d5d1c8700 INNODB MONITOR OUTPUT

=====================================

Per second averages calculated from the last 14 seconds

.......

------------

TRANSACTIONS

------------

Trx id counter 1593

Purge done for trx's n:o < 1590 undo n:o < 0 state: running but idle

History list length 8

LIST OF TRANSACTIONS FOR EACH SESSION:

---TRANSACTION 421582651166320, not started

0 lock struct(s), heap size 1136, 0 row lock(s)

---TRANSACTION 421582651165408, not started

0 lock struct(s), heap size 1136, 0 row lock(s)

---TRANSACTION 1592, ACTIVE 5 sec starting index read

mysql tables in use 1, locked 1

LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)

MySQL thread id 1128, OS thread handle 140107690583808, query id 2591 localhost root updating

update userinfo set age=32 where id=2

------- TRX HAS BEEN WAITING 5 SEC FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 37 page no 3 n bits 80 index PRIMARY of table `jiale`.`userinfo` trx id 1592 lock_mode X locks rec but not gap waiting

Record lock, heap no 3 PHYSICAL RECORD: n_fields 7; compact format; info bits 0

 0: len 4; hex 80000002; asc     ;;

 1: len 6; hex 00000000062d; asc      -;;

 2: len 7; hex d400000187011c; asc        ;;

 3: len 8; hex 800000000000000b; asc         ;;

 4: len 7; hex 6368656e6c6569; asc chenlei;;

 5: len 1; hex a8; asc  ;;

 6: len 1; hex 80; asc  ;;


------------------

---TRANSACTION 1591, ACTIVE 36 sec

2 lock struct(s), heap size 1136, 9 row lock(s)

MySQL thread id 1127, OS thread handle 140106502825728, query id 2589 localhost root

--------

.......


tips: innodb_status_output_locks=on ,show engine innodb status更多信息


3.查看innodb_trx,innodb_locks,innodb_lock_waits 可知锁情况。

root@localhost:information_schema 5.7.26-log 11:54:25> select * from INNODB_TRX\G

*************************** 1. row ***************************

                    trx_id: 1592

                 trx_state: LOCK WAIT

               trx_started: 2019-12-09 11:52:37

     trx_requested_lock_id: 1592:37:3:3

          trx_wait_started: 2019-12-09 11:52:37

                trx_weight: 2

       trx_mysql_thread_id: 1128

                 trx_query: update userinfo set age=32 where id=2

       trx_operation_state: starting index read

         trx_tables_in_use: 1

         trx_tables_locked: 1

          trx_lock_structs: 2

     trx_lock_memory_bytes: 1136

           trx_rows_locked: 1

         trx_rows_modified: 0

   trx_concurrency_tickets: 0

       trx_isolation_level: REPEATABLE READ

         trx_unique_checks: 1

    trx_foreign_key_checks: 1

trx_last_foreign_key_error: NULL

 trx_adaptive_hash_latched: 0

 trx_adaptive_hash_timeout: 0

          trx_is_read_only: 0

trx_autocommit_non_locking: 0

*************************** 2. row ***************************

                    trx_id: 1591

                 trx_state: RUNNING

               trx_started: 2019-12-09 11:52:06

     trx_requested_lock_id: NULL

          trx_wait_started: NULL

                trx_weight: 2

       trx_mysql_thread_id: 1127

                 trx_query: NULL

       trx_operation_state: NULL

         trx_tables_in_use: 0

         trx_tables_locked: 1

          trx_lock_structs: 2

     trx_lock_memory_bytes: 1136

           trx_rows_locked: 9

         trx_rows_modified: 0

   trx_concurrency_tickets: 0

       trx_isolation_level: REPEATABLE READ

         trx_unique_checks: 1

    trx_foreign_key_checks: 1

trx_last_foreign_key_error: NULL

 trx_adaptive_hash_latched: 0

 trx_adaptive_hash_timeout: 0

          trx_is_read_only: 0

trx_autocommit_non_locking: 0

2 rows in set (0.00 sec)


root@localhost:information_schema 5.7.26-log 11:54:29> select * from INNODB_LOCKS;

+-------------+-------------+-----------+-----------+--------------------+------------+------------+-----------+----------+-----------+

| lock_id     | lock_trx_id | lock_mode | lock_type | lock_table         | lock_index | lock_space | lock_page | lock_rec | lock_data |

+-------------+-------------+-----------+-----------+--------------------+------------+------------+-----------+----------+-----------+

| 1592:37:3:3 | 1592        | X         | RECORD    | `jiale`.`userinfo` | PRIMARY    |         37 |         3 |        3 | 2         |

| 1591:37:3:3 | 1591        | X         | RECORD    | `jiale`.`userinfo` | PRIMARY    |         37 |         3 |        3 | 2         |

+-------------+-------------+-----------+-----------+--------------------+------------+------------+-----------+----------+-----------+

2 rows in set, 1 warning (0.00 sec)


Warning (Code 1681): 'INFORMATION_SCHEMA.INNODB_LOCKS' is deprecated and will be removed in a future release.

root@localhost:information_schema 5.7.26-log 11:54:43> select * from INNODB_LOCK_WAITS;

+-------------------+-------------------+-----------------+------------------+

| requesting_trx_id | requested_lock_id | blocking_trx_id | blocking_lock_id |

+-------------------+-------------------+-----------------+------------------+

| 1592              | 1592:37:3:3       | 1591            | 1591:37:3:3      |

+-------------------+-------------------+-----------------+------------------+

1 row in set, 1 warning (0.00 sec)


很多时候我们知道被阻塞事务SQL语句,却不知到底是谁阻塞(后续)事务运行。查看processlist很可能Command为 sleep。此时


4.可通过events_statements_current查看持有锁SQL。(MySQL5.7+)

root@localhost:performance_schema 5.7.26-log 12:00:20> select * from performance_schema.events_statements_current where THREAD_ID=1127\G

*************************** 1. row ***************************

              THREAD_ID: 1127

               EVENT_ID: 9

           END_EVENT_ID: 9

             EVENT_NAME: statement/sql/select

                 SOURCE: 

            TIMER_START: 2080305515241809000

              TIMER_END: 2080305515906717000

             TIMER_WAIT: 664908000

              LOCK_TIME: 293000000

               SQL_TEXT: select * from userinfo where id>1 for update

                 DIGEST: bcac85243232e159f51f6e5604c95b20

            DIGEST_TEXT: SELECT * FROM `userinfo` WHERE `id` > ? FOR UPDATE 

         CURRENT_SCHEMA: jiale

            OBJECT_TYPE: NULL

          OBJECT_SCHEMA: NULL

            OBJECT_NAME: NULL

  OBJECT_INSTANCE_BEGIN: NULL

            MYSQL_ERRNO: 0

      RETURNED_SQLSTATE: NULL

           MESSAGE_TEXT: NULL

                 ERRORS: 0

               WARNINGS: 0

          ROWS_AFFECTED: 0

              ROWS_SENT: 8

          ROWS_EXAMINED: 8

CREATED_TMP_DISK_TABLES: 0

     CREATED_TMP_TABLES: 0

       SELECT_FULL_JOIN: 0

 SELECT_FULL_RANGE_JOIN: 0

           SELECT_RANGE: 1

     SELECT_RANGE_CHECK: 0

            SELECT_SCAN: 0

      SORT_MERGE_PASSES: 0

             SORT_RANGE: 0

              SORT_ROWS: 0

              SORT_SCAN: 0

          NO_INDEX_USED: 0

     NO_GOOD_INDEX_USED: 0

       NESTING_EVENT_ID: NULL

     NESTING_EVENT_TYPE: NULL

    NESTING_EVENT_LEVEL: 0

1 row in set (0.00 sec)


现在我们了解两个概念:一致性非锁定读/一致性锁定读

  1. 一致性非锁定读(理解示意图含义)


在事务隔离级别READ COMMITTED(RC),REPEATABLE READ(RR)下,InnoDB存储引擎使用非锁定一致性读。但是对于快照数据定义却不相同。

RC:非一致性读总是读取被锁定行的最新(一份快照)数据。

RR:非一致性读总是读取事务开始时行版本数据。

我们来看一下如下场景查询结果会是怎样?并给出自己的解释(知其所以然)

2. 一致性锁定读(locking read)

select ... for update;

select ... lock in share mode;


2.1 一致性锁定读加锁方式:

select ... for update; 对读取行记录➕ X锁,其他事务不能对已锁定行加任何锁。

select ... lock in share mode; 对读取行记录➕S锁,其他事务可以对已锁定行加S锁,但是如果加X锁会被阻塞。



2.2 再回头说明下MySQL InnoDB 行锁算法:大致可分为三种算法。

Record Lock:单个行记录上的锁

Gap Lock:间隙锁,锁定的一个范围,但不包括记录本身

Next-key Lock:Gap Lock+Record Lock ,锁定一个范围,并且锁定记录本身(RR模式下防止幻读)

重点:查询条件含有唯一属性时,InnoDB(优化锁)next-key lock 降级为record lock


模拟加锁,思考事务运行状态:

如何查看加锁过程:

tips:MySQL-8.0新增performance_schema.data_locks表。

通过查看此表可清晰知道MySQL的加锁过程。再结合兼容矩阵可理解阻塞原因。


留个疑问:在RR和RC隔离级别下加锁过程有何区别。建议模拟动手实验


本文分享自微信公众号 - topdba,如有侵权,请联系 service001@enmotech.com 删除。
最后修改时间:2019-12-18 15:19:35
文章转载自topdba,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论