翼度科技»论坛 编程开发 mysql 查看内容

不同于Oracle:MySQL的insert会阻塞update

6

主题

6

帖子

18

积分

新手上路

Rank: 1

积分
18

  • GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。
  • GreatSQL是MySQL的国产分支版本,使用上与MySQL一致。
  • 作者: 王庆勋
  • 文章来源:GreatSQL社区原创
某银行客户在从Oracle迁移到MySQL的开发中,MySQL在READ-COMMITTED隔离级别下,出现了insert阻塞update的情况,但同样的情况下,Oracle的insert则不会阻塞update。本文通过复现该问题,分析MySQL的锁信息,确认是MySQL与Oracle在并发事务处理上的差异,在进行数据库迁移改造的程序开发应予以关注。
1.问题复现

1.1.环境准备

MySQL版本的8.0.26,隔离级别是READ-COMMITTED ,测试表t的字段a为主键。
  1. mysql> select version();
  2. +-----------+
  3. | version() |
  4. +-----------+
  5. | 8.0.26    |
  6. +-----------+
  7. 1 row in set (0.02 sec)
  8. mysql> show variables like 'transaction_isolation';
  9. +-----------------------+----------------+
  10. | Variable_name         | Value          |
  11. +-----------------------+----------------+
  12. | transaction_isolation | READ-COMMITTED |
  13. +-----------------------+----------------+
  14. 1 row in set (0.00 sec)
  15. mysql> desc t;
  16. +-------+------+------+-----+---------+-------+
  17. | Field | Type | Null | Key | Default | Extra |
  18. +-------+------+------+-----+---------+-------+
  19. | a     | int  | NO   | PRI | NULL    |       |
  20. | b     | int  | YES  |     | NULL    |       |
  21. +-------+------+------+-----+---------+-------+
  22. 2 rows in set (0.01 sec)
  23. mysql> select * from t;
  24. +---+------+
  25. | a | b    |
  26. +---+------+
  27. | 7 |    7 |
  28. +---+------+
  29. 1 row in set (0.00 sec)
复制代码
1.2. insert阻塞update的操作步骤

insert语句未提交时,update同样主键的数据会被阻塞。
session1session2插入一条数据(a=8)后未提交。 mysql> begin; Query OK, 0 rows affected (0.00 sec)  mysql> insert into t values(8,8); Query OK, 1 row affected (0.01 sec)更改数据,条件是a=8,将会被阻塞 mysql> update t set b=0 where a=8;  select * from performance_schema.data_locks\G*************************** 1. row ***************************               ENGINE: INNODB       ENGINE_LOCK_ID: 139727275779216:1113:139724882998560ENGINE_TRANSACTION_ID: 3795            THREAD_ID: 9441             EVENT_ID: 5000        OBJECT_SCHEMA: testdb          OBJECT_NAME: t       PARTITION_NAME: NULL    SUBPARTITION_NAME: NULL           INDEX_NAME: NULLOBJECT_INSTANCE_BEGIN: 139724882998560            LOCK_TYPE: TABLE            LOCK_MODE: IX          LOCK_STATUS: GRANTED            LOCK_DATA: NULL*************************** 2. row ***************************               ENGINE: INNODB       ENGINE_LOCK_ID: 139727275779216:52:4:3:139724882995456ENGINE_TRANSACTION_ID: 3795            THREAD_ID: 9441             EVENT_ID: 5012        OBJECT_SCHEMA: testdb          OBJECT_NAME: t       PARTITION_NAME: NULL    SUBPARTITION_NAME: NULL           INDEX_NAME: PRIMARYOBJECT_INSTANCE_BEGIN: 139724882995456            LOCK_TYPE: RECORD            LOCK_MODE: X,REC_NOT_GAP          LOCK_STATUS: WAITING            LOCK_DATA: 8*************************** 3. row ***************************               ENGINE: INNODB       ENGINE_LOCK_ID: 139727275781640:1113:139724883017072ENGINE_TRANSACTION_ID: 3790            THREAD_ID: 9415             EVENT_ID: 15467        OBJECT_SCHEMA: testdb          OBJECT_NAME: t       PARTITION_NAME: NULL    SUBPARTITION_NAME: NULL           INDEX_NAME: NULLOBJECT_INSTANCE_BEGIN: 139724883017072            LOCK_TYPE: TABLE            LOCK_MODE: IX          LOCK_STATUS: GRANTED            LOCK_DATA: NULL*************************** 4. row ***************************               ENGINE: INNODB       ENGINE_LOCK_ID: 139727275781640:52:4:3:139724883013968ENGINE_TRANSACTION_ID: 3790            THREAD_ID: 9441             EVENT_ID: 5007        OBJECT_SCHEMA: testdb          OBJECT_NAME: t       PARTITION_NAME: NULL    SUBPARTITION_NAME: NULL           INDEX_NAME: PRIMARYOBJECT_INSTANCE_BEGIN: 139724883013968            LOCK_TYPE: RECORD            LOCK_MODE: X,REC_NOT_GAP          LOCK_STATUS: GRANTED            LOCK_DATA: 84 rows in set (0.00 sec)说明:事务3795正在等待LOCK_TYPE: RECORD,LOCK_MODE:X,REC_NOT_GAP,等待的主键值为8;事务3790已获取主键值为8的LOCK_TYPE: RECORD,LOCK_MODE:X,REC_NOT_GAP锁。mysql> select * from sys.innodb_lock_waits\G*************************** 1. row ***************************                wait_started: 2022-10-11 16:03:38                    wait_age: 00:02:50               wait_age_secs: 170                locked_table: `testdb`.`t`         locked_table_schema: testdb           locked_table_name: t      locked_table_partition: NULL   locked_table_subpartition: NULL                locked_index: PRIMARY                 locked_type: RECORD              waiting_trx_id: 3795         waiting_trx_started: 2022-10-11 16:03:38             waiting_trx_age: 00:02:50     waiting_trx_rows_locked: 1   waiting_trx_rows_modified: 0                 waiting_pid: 9346               waiting_query: update t set b=0 where a=8             waiting_lock_id: 139727275779216:52:4:3:139724882995456           waiting_lock_mode: X,REC_NOT_GAP             blocking_trx_id: 3790                blocking_pid: 9320              blocking_query: NULL            blocking_lock_id: 139727275781640:52:4:3:139724883013968          blocking_lock_mode: X,REC_NOT_GAP        blocking_trx_started: 2022-10-11 16:03:29            blocking_trx_age: 00:02:59    blocking_trx_rows_locked: 1  blocking_trx_rows_modified: 1     sql_kill_blocking_query: KILL QUERY 9320sql_kill_blocking_connection: KILL 93201 row in set (0.01 sec)说明:事务3795等待testdb.t上的rec_not_gap独占锁,事务3790持有该独占锁。mysql> select distinct,* from sys.processlist where conn_id in (select trx_mysql_thread_id from information_schema.innodb_trx)\G*************************** 1. row ***************************                thd_id: 9441               conn_id: 9346                  user: admin@172.17.128.73                    db: testdb               command: Query                 state: updating                  time: 141     current_statement: update t set b=0 where a=8     statement_latency: 2.37 min              progress: NULL          lock_latency: 431.00 us         rows_examined: 0             rows_sent: 0         rows_affected: 0            tmp_tables: 0       tmp_disk_tables: 0             full_scan: NO        last_statement: NULLlast_statement_latency: NULL        current_memory: 140.15 KiB             last_wait: wait/io/table/sql/handler     last_wait_latency: Still Waiting                source: handler.cc:3250           trx_latency: 13.30 min             trx_state: ACTIVE        trx_autocommit: NO                   pid: 9632          program_name: mysql*************************** 2. row ***************************                thd_id: 9415               conn_id: 9320                  user: admin@172.17.128.73                    db: testdb               command: Sleep                 state: NULL                  time: 801     current_statement: NULL     statement_latency: NULL              progress: NULL          lock_latency: 288.00 us         rows_examined: 0             rows_sent: 0         rows_affected: 1            tmp_tables: 0       tmp_disk_tables: 0             full_scan: NO        last_statement: insert into t values(8,8)last_statement_latency: 765.23 us        current_memory: 218.19 KiB             last_wait: wait/io/socket/sql/client_connection     last_wait_latency: Still Waiting                source: viosocket.cc:146           trx_latency: 13.52 min             trx_state: ACTIVE        trx_autocommit: NO                   pid: 9600          program_name: mysql说明:被阻塞事务执行的sql语句update t set b=0 where a=8,阻塞事务执行的sql语句是insert into t values(8,8)。[/code]说明:
MySQL的隔离级别是通过索引上的锁实现并发事务控制的。在READ-COMMITTED隔离级别下,session1在执行insert语句时,在主键索引上获取了a=8的行记录独占锁,以禁止插入相同主键的数据;session2如果同时插入相同的主键数据被阻塞,容易理解(Oracle也同样阻塞)。出于同样的原因session2执行update时,由于无法获取a=8的行记录独占锁,同样也会被阻塞。
2.2.验证MySQL事务未提交时已写入数据文件

验证事务未提交时,insert语句已将数据写入数据文件,索引数据也已生成。
  1. mysql> select * from information_schema.innodb_trx\G
  2. *************************** 1. row ***************************
  3.                     trx_id: 3795
  4.                  trx_state: LOCK WAIT
  5.                trx_started: 2022-10-11 16:03:38
  6.      trx_requested_lock_id: 139727275779216:52:4:3:139724882995456
  7.           trx_wait_started: 2022-10-11 16:03:38
  8.                 trx_weight: 2
  9.        trx_mysql_thread_id: 9346
  10.                  trx_query: update t set b=0 where a=8
  11.        trx_operation_state: starting index read
  12.          trx_tables_in_use: 1
  13.          trx_tables_locked: 1
  14.           trx_lock_structs: 2
  15.      trx_lock_memory_bytes: 1128
  16.            trx_rows_locked: 1
  17.          trx_rows_modified: 0
  18.    trx_concurrency_tickets: 0
  19.        trx_isolation_level: READ COMMITTED
  20.          trx_unique_checks: 1
  21.     trx_foreign_key_checks: 1
  22. trx_last_foreign_key_error: NULL
  23. trx_adaptive_hash_latched: 0
  24. trx_adaptive_hash_timeout: 0
  25.           trx_is_read_only: 0
  26. trx_autocommit_non_locking: 0
  27.        trx_schedule_weight: 1
  28. *************************** 2. row ***************************
  29.                     trx_id: 3790
  30.                  trx_state: RUNNING
  31.                trx_started: 2022-10-11 16:03:29
  32.      trx_requested_lock_id: NULL
  33.           trx_wait_started: NULL
  34.                 trx_weight: 3
  35.        trx_mysql_thread_id: 9320
  36.                  trx_query: NULL
  37.        trx_operation_state: NULL
  38.          trx_tables_in_use: 0
  39.          trx_tables_locked: 1
  40.           trx_lock_structs: 2
  41.      trx_lock_memory_bytes: 1128
  42.            trx_rows_locked: 1
  43.          trx_rows_modified: 1
  44.    trx_concurrency_tickets: 0
  45.        trx_isolation_level: READ COMMITTED
  46.          trx_unique_checks: 1
  47.     trx_foreign_key_checks: 1
  48. trx_last_foreign_key_error: NULL
  49. trx_adaptive_hash_latched: 0
  50. trx_adaptive_hash_timeout: 0
  51.           trx_is_read_only: 0
  52. trx_autocommit_non_locking: 0
  53.        trx_schedule_weight: NULL
  54. 2 rows in set (0.00 sec)
  55. 说明:通过InnoDB的事务表innodb_trx查询到thread_id=9346的事务3795正在等待锁(trx_state: LOCK WAIT),
  56. thread_id=9320的事务3790正在执行(trx_state: RUNNING)。
  57. mysql> select * from performance_schema.data_locks\G
  58. *************************** 1. row ***************************
  59.                ENGINE: INNODB
  60.        ENGINE_LOCK_ID: 139727275779216:1113:139724882998560
  61. ENGINE_TRANSACTION_ID: 3795
  62.             THREAD_ID: 9441
  63.              EVENT_ID: 5000
  64.         OBJECT_SCHEMA: testdb
  65.           OBJECT_NAME: t
  66.        PARTITION_NAME: NULL
  67.     SUBPARTITION_NAME: NULL
  68.            INDEX_NAME: NULL
  69. OBJECT_INSTANCE_BEGIN: 139724882998560
  70.             LOCK_TYPE: TABLE
  71.             LOCK_MODE: IX
  72.           LOCK_STATUS: GRANTED
  73.             LOCK_DATA: NULL
  74. *************************** 2. row ***************************
  75.                ENGINE: INNODB
  76.        ENGINE_LOCK_ID: 139727275779216:52:4:3:139724882995456
  77. ENGINE_TRANSACTION_ID: 3795
  78.             THREAD_ID: 9441
  79.              EVENT_ID: 5012
  80.         OBJECT_SCHEMA: testdb
  81.           OBJECT_NAME: t
  82.        PARTITION_NAME: NULL
  83.     SUBPARTITION_NAME: NULL
  84.            INDEX_NAME: PRIMARY
  85. OBJECT_INSTANCE_BEGIN: 139724882995456
  86.             LOCK_TYPE: RECORD
  87.             LOCK_MODE: X,REC_NOT_GAP
  88.           LOCK_STATUS: WAITING
  89.             LOCK_DATA: 8
  90. *************************** 3. row ***************************
  91.                ENGINE: INNODB
  92.        ENGINE_LOCK_ID: 139727275781640:1113:139724883017072
  93. ENGINE_TRANSACTION_ID: 3790
  94.             THREAD_ID: 9415
  95.              EVENT_ID: 15467
  96.         OBJECT_SCHEMA: testdb
  97.           OBJECT_NAME: t
  98.        PARTITION_NAME: NULL
  99.     SUBPARTITION_NAME: NULL
  100.            INDEX_NAME: NULL
  101. OBJECT_INSTANCE_BEGIN: 139724883017072
  102.             LOCK_TYPE: TABLE
  103.             LOCK_MODE: IX
  104.           LOCK_STATUS: GRANTED
  105.             LOCK_DATA: NULL
  106. *************************** 4. row ***************************
  107.                ENGINE: INNODB
  108.        ENGINE_LOCK_ID: 139727275781640:52:4:3:139724883013968
  109. ENGINE_TRANSACTION_ID: 3790
  110.             THREAD_ID: 9441
  111.              EVENT_ID: 5007
  112.         OBJECT_SCHEMA: testdb
  113.           OBJECT_NAME: t
  114.        PARTITION_NAME: NULL
  115.     SUBPARTITION_NAME: NULL
  116.            INDEX_NAME: PRIMARY
  117. OBJECT_INSTANCE_BEGIN: 139724883013968
  118.             LOCK_TYPE: RECORD
  119.             LOCK_MODE: X,REC_NOT_GAP
  120.           LOCK_STATUS: GRANTED
  121.             LOCK_DATA: 8
  122. 4 rows in set (0.00 sec)
  123. 说明:事务3795正在等待LOCK_TYPE: RECORD,LOCK_MODE:X,REC_NOT_GAP,等待的主键值为8;
  124. 事务3790已获取主键值为8的LOCK_TYPE: RECORD,LOCK_MODE:X,REC_NOT_GAP锁。
  125. mysql> select * from sys.innodb_lock_waits\G
  126. *************************** 1. row ***************************
  127.                 wait_started: 2022-10-11 16:03:38
  128.                     wait_age: 00:02:50
  129.                wait_age_secs: 170
  130.                 locked_table: `testdb`.`t`
  131.          locked_table_schema: testdb
  132.            locked_table_name: t
  133.       locked_table_partition: NULL
  134.    locked_table_subpartition: NULL
  135.                 locked_index: PRIMARY
  136.                  locked_type: RECORD
  137.               waiting_trx_id: 3795
  138.          waiting_trx_started: 2022-10-11 16:03:38
  139.              waiting_trx_age: 00:02:50
  140.      waiting_trx_rows_locked: 1
  141.    waiting_trx_rows_modified: 0
  142.                  waiting_pid: 9346
  143.                waiting_query: update t set b=0 where a=8
  144.              waiting_lock_id: 139727275779216:52:4:3:139724882995456
  145.            waiting_lock_mode: X,REC_NOT_GAP
  146.              blocking_trx_id: 3790
  147.                 blocking_pid: 9320
  148.               blocking_query: NULL
  149.             blocking_lock_id: 139727275781640:52:4:3:139724883013968
  150.           blocking_lock_mode: X,REC_NOT_GAP
  151.         blocking_trx_started: 2022-10-11 16:03:29
  152.             blocking_trx_age: 00:02:59
  153.     blocking_trx_rows_locked: 1
  154.   blocking_trx_rows_modified: 1
  155.      sql_kill_blocking_query: KILL QUERY 9320
  156. sql_kill_blocking_connection: KILL 9320
  157. 1 row in set (0.01 sec)
  158. 说明:事务3795等待testdb.t上的rec_not_gap独占锁,事务3790持有该独占锁。
  159. mysql> select distinct,* from sys.processlist where conn_id in (select trx_mysql_thread_id from information_schema.innodb_trx)\G
  160. *************************** 1. row ***************************
  161.                 thd_id: 9441
  162.                conn_id: 9346
  163.                   user: admin@172.17.128.73
  164.                     db: testdb
  165.                command: Query
  166.                  state: updating
  167.                   time: 141
  168.      current_statement: update t set b=0 where a=8
  169.      statement_latency: 2.37 min
  170.               progress: NULL
  171.           lock_latency: 431.00 us
  172.          rows_examined: 0
  173.              rows_sent: 0
  174.          rows_affected: 0
  175.             tmp_tables: 0
  176.        tmp_disk_tables: 0
  177.              full_scan: NO
  178.         last_statement: NULL
  179. last_statement_latency: NULL
  180.         current_memory: 140.15 KiB
  181.              last_wait: wait/io/table/sql/handler
  182.      last_wait_latency: Still Waiting
  183.                 source: handler.cc:3250
  184.            trx_latency: 13.30 min
  185.              trx_state: ACTIVE
  186.         trx_autocommit: NO
  187.                    pid: 9632
  188.           program_name: mysql
  189. *************************** 2. row ***************************
  190.                 thd_id: 9415
  191.                conn_id: 9320
  192.                   user: admin@172.17.128.73
  193.                     db: testdb
  194.                command: Sleep
  195.                  state: NULL
  196.                   time: 801
  197.      current_statement: NULL
  198.      statement_latency: NULL
  199.               progress: NULL
  200.           lock_latency: 288.00 us
  201.          rows_examined: 0
  202.              rows_sent: 0
  203.          rows_affected: 1
  204.             tmp_tables: 0
  205.        tmp_disk_tables: 0
  206.              full_scan: NO
  207.         last_statement: insert into t values(8,8)
  208. last_statement_latency: 765.23 us
  209.         current_memory: 218.19 KiB
  210.              last_wait: wait/io/socket/sql/client_connection
  211.      last_wait_latency: Still Waiting
  212.                 source: viosocket.cc:146
  213.            trx_latency: 13.52 min
  214.              trx_state: ACTIVE
  215.         trx_autocommit: NO
  216.                    pid: 9600
  217.           program_name: mysql
  218. 说明:被阻塞事务执行的sql语句update t set b=0 where a=8,
  219. 阻塞事务执行的sql语句是insert into t values(8,8)。
复制代码
说明:
MySQL在执行insert 语句进行数据插入,未提交时,数据也已写入表的聚集索引,辅助索引也已生成。MySQL可以使用未提交数据的索引,通过锁机制实现事务的并发控制。
3.Oracle中insert没有阻塞update

在Oracle中,创建同样的测试表t,执行同样的insert和update,但insert不会阻塞update。
  1. 测试表test1
  2. mysql> CREATE TABLE `test1` (
  3.     ->   `id` int NOT NULL AUTO_INCREMENT,
  4.     ->   `k` int NOT NULL DEFAULT '0',
  5.     ->   `c` char(120) NOT NULL DEFAULT '',
  6.     ->   `pad` char(60) NOT NULL DEFAULT '',
  7.     ->   PRIMARY KEY (`id`),
  8.     ->   KEY `k_1` (`k`)
  9.     -> ) ;
  10. Query OK, 0 rows affected (0.07 sec)
  11. 开启一个事务,插入10万条数据。
  12. mysql> begin;
  13. Query OK, 0 rows affected (0.00 sec)
  14. mysql> insert into test1 select * from sbtest1;
  15. Query OK, 100000 rows affected (1.44 sec)
  16. Records: 100000  Duplicates: 0  Warnings: 0
  17. 检查表的data_length和index_length
  18. mysql> show table status where name like 'test1'\G
  19. *************************** 1. row ***************************
  20.            Name: test1
  21.          Engine: InnoDB
  22.         Version: 10
  23.      Row_format: Dynamic
  24.            Rows: 98712
  25. Avg_row_length: 228
  26.     Data_length: 22593536
  27. Max_data_length: 0
  28.    Index_length: 2637824
  29.       Data_free: 4194304
  30. Auto_increment: 100001
  31.     Create_time: 2022-10-11 22:14:50
  32.     Update_time: NULL
  33.      Check_time: NULL
  34.       Collation: utf8mb4_0900_ai_ci
  35.        Checksum: NULL
  36. Create_options:
  37.         Comment:
  38. 1 rows in set (0.01 sec)
  39. 回滚insert操作
  40. mysql> rollback;
  41. Query OK, 0 rows affected (1.35 sec)
  42. 更新统计信息
  43. mysql> analyze table test1;
  44. 再次检查表的data_length和index_length
  45. mysql> show table status where name like 'test1'\G
  46. *************************** 1. row ***************************
  47.            Name: test1
  48.          Engine: InnoDB
  49.         Version: 10
  50.      Row_format: Dynamic
  51.            Rows: 0
  52. Avg_row_length: 0
  53.     Data_length: 16384
  54. Max_data_length: 0
  55.    Index_length: 16384
  56.       Data_free: 29360128
  57. Auto_increment: 100001
  58.     Create_time: 2022-10-11 22:22:36
  59.     Update_time: NULL
  60.      Check_time: NULL
  61.       Collation: utf8mb4_0900_ai_ci
  62.        Checksum: NULL
  63. Create_options:
  64.         Comment:
  65. 1 row in set (0.00 sec)
  66. 检查数据文件的大小
  67. [root@host73 testdb]# ll *test1.ibd
  68. -rw-r----- 1 greatdb greatdb 30408704 Oct 11 15:12 sbtest1.ibd
  69. -rw-r----- 1 greatdb greatdb 33554432 Oct 11 22:24 test1.ibd
复制代码
执行相同的insert和update语句。
[table]session1session2SQL> insert into t values(8,8);1 row created.SQL> update t set b=0 where a=8;0 rows updated.
Enjoy GreatSQL
来源:https://www.cnblogs.com/greatsql/p/17264312.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具