不同於Oracle:MySQL的insert會阻塞update

来源:https://www.cnblogs.com/greatsql/archive/2023/03/28/17264312.html
-Advertisement-
Play Games

GreatSQL社區原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源。 GreatSQL是MySQL的國產分支版本,使用上與MySQL一致。 作者: 王慶勛 文章來源:GreatSQL社區原創 某銀行客戶在從Oracle遷移到MySQL的開發中,MySQL在READ-COMMITTED隔離級別 ...


  • 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為主鍵。

mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.26    |
+-----------+
1 row in set (0.02 sec)

mysql> show variables like 'transaction_isolation';
+-----------------------+----------------+
| Variable_name         | Value          |
+-----------------------+----------------+
| transaction_isolation | READ-COMMITTED |
+-----------------------+----------------+
1 row in set (0.00 sec)

mysql> desc t;
+-------+------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------+------+-----+---------+-------+
| a     | int  | NO   | PRI | NULL    |       |
| b     | int  | YES  |     | NULL    |       |
+-------+------+------+-----+---------+-------+
2 rows in set (0.01 sec)

mysql> select * from t;
+---+------+
| a | b    |
+---+------+
| 7 |    7 |
+---+------+
1 row in set (0.00 sec)

1.2. insert阻塞update的操作步驟

insert語句未提交時,update同樣主鍵的數據會被阻塞。

session1 session2
插入一條數據(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; <<掛起,等待innodb_lock_wait_timeout超時

2.分析原因

2.1.檢查事務鎖信息

mysql> select * from information_schema.innodb_trx\G
*************************** 1. row ***************************
                    trx_id: 3795
                 trx_state: LOCK WAIT
               trx_started: 2022-10-11 16:03:38
     trx_requested_lock_id: 139727275779216:52:4:3:139724882995456
          trx_wait_started: 2022-10-11 16:03:38
                trx_weight: 2
       trx_mysql_thread_id: 9346
                 trx_query: update t set b=0 where a=8
       trx_operation_state: starting index read
         trx_tables_in_use: 1
         trx_tables_locked: 1
          trx_lock_structs: 2
     trx_lock_memory_bytes: 1128
           trx_rows_locked: 1
         trx_rows_modified: 0
   trx_concurrency_tickets: 0
       trx_isolation_level: READ COMMITTED
         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
       trx_schedule_weight: 1
*************************** 2. row ***************************
                    trx_id: 3790
                 trx_state: RUNNING
               trx_started: 2022-10-11 16:03:29
     trx_requested_lock_id: NULL
          trx_wait_started: NULL
                trx_weight: 3
       trx_mysql_thread_id: 9320
                 trx_query: NULL
       trx_operation_state: NULL
         trx_tables_in_use: 0
         trx_tables_locked: 1
          trx_lock_structs: 2
     trx_lock_memory_bytes: 1128
           trx_rows_locked: 1
         trx_rows_modified: 1
   trx_concurrency_tickets: 0
       trx_isolation_level: READ COMMITTED
         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
       trx_schedule_weight: NULL
2 rows in set (0.00 sec)

說明:通過InnoDB的事務表innodb_trx查詢到thread_id=9346的事務3795正在等待鎖(trx_state: LOCK WAIT),
thread_id=9320的事務3790正在執行(trx_state: RUNNING)。

mysql> select * from performance_schema.data_locks\G
*************************** 1. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 139727275779216:1113:139724882998560
ENGINE_TRANSACTION_ID: 3795
            THREAD_ID: 9441
             EVENT_ID: 5000
        OBJECT_SCHEMA: testdb
          OBJECT_NAME: t
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: NULL
OBJECT_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:139724882995456
ENGINE_TRANSACTION_ID: 3795
            THREAD_ID: 9441
             EVENT_ID: 5012
        OBJECT_SCHEMA: testdb
          OBJECT_NAME: t
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: PRIMARY
OBJECT_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:139724883017072
ENGINE_TRANSACTION_ID: 3790
            THREAD_ID: 9415
             EVENT_ID: 15467
        OBJECT_SCHEMA: testdb
          OBJECT_NAME: t
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: NULL
OBJECT_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:139724883013968
ENGINE_TRANSACTION_ID: 3790
            THREAD_ID: 9441
             EVENT_ID: 5007
        OBJECT_SCHEMA: testdb
          OBJECT_NAME: t
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 139724883013968
            LOCK_TYPE: RECORD
            LOCK_MODE: X,REC_NOT_GAP
          LOCK_STATUS: GRANTED
            LOCK_DATA: 8
4 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 9320
sql_kill_blocking_connection: KILL 9320
1 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: [email protected]
                    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: NULL
last_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: [email protected]
                    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)。

說明:

MySQL的隔離級別是通過索引上的鎖實現併發事務控制的。在READ-COMMITTED隔離級別下,session1在執行insert語句時,在主鍵索引上獲取了a=8的行記錄獨占鎖,以禁止插入相同主鍵的數據;session2如果同時插入相同的主鍵數據被阻塞,容易理解(Oracle也同樣阻塞)。出於同樣的原因session2執行update時,由於無法獲取a=8的行記錄獨占鎖,同樣也會被阻塞。

2.2.驗證MySQL事務未提交時已寫入數據文件

驗證事務未提交時,insert語句已將數據寫入數據文件,索引數據也已生成。

測試表test1
mysql> CREATE TABLE `test1` (
    ->   `id` int NOT NULL AUTO_INCREMENT,
    ->   `k` int NOT NULL DEFAULT '0',
    ->   `c` char(120) NOT NULL DEFAULT '',
    ->   `pad` char(60) NOT NULL DEFAULT '',
    ->   PRIMARY KEY (`id`),
    ->   KEY `k_1` (`k`)
    -> ) ;
Query OK, 0 rows affected (0.07 sec)

開啟一個事務,插入10萬條數據。
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test1 select * from sbtest1;
Query OK, 100000 rows affected (1.44 sec)
Records: 100000  Duplicates: 0  Warnings: 0

檢查表的data_length和index_length
mysql> show table status where name like 'test1'\G
*************************** 1. row ***************************
           Name: test1
         Engine: InnoDB
        Version: 10
     Row_format: Dynamic
           Rows: 98712
 Avg_row_length: 228
    Data_length: 22593536
Max_data_length: 0
   Index_length: 2637824
      Data_free: 4194304
 Auto_increment: 100001
    Create_time: 2022-10-11 22:14:50
    Update_time: NULL
     Check_time: NULL
      Collation: utf8mb4_0900_ai_ci
       Checksum: NULL
 Create_options: 
        Comment: 
1 rows in set (0.01 sec)

回滾insert操作
mysql> rollback;
Query OK, 0 rows affected (1.35 sec)

更新統計信息
mysql> analyze table test1;

再次檢查表的data_length和index_length
mysql> show table status where name like 'test1'\G 
*************************** 1. row ***************************
           Name: test1
         Engine: InnoDB
        Version: 10
     Row_format: Dynamic
           Rows: 0
 Avg_row_length: 0
    Data_length: 16384
Max_data_length: 0
   Index_length: 16384
      Data_free: 29360128
 Auto_increment: 100001
    Create_time: 2022-10-11 22:22:36
    Update_time: NULL
     Check_time: NULL
      Collation: utf8mb4_0900_ai_ci
       Checksum: NULL
 Create_options: 
        Comment: 
1 row in set (0.00 sec)

檢查數據文件的大小
[root@host73 testdb]# ll *test1.ibd
-rw-r----- 1 greatdb greatdb 30408704 Oct 11 15:12 sbtest1.ibd
-rw-r----- 1 greatdb greatdb 33554432 Oct 11 22:24 test1.ibd

說明:

MySQL在執行insert 語句進行數據插入,未提交時,數據也已寫入表的聚集索引,輔助索引也已生成。MySQL可以使用未提交數據的索引,通過鎖機制實現事務的併發控制。

3.Oracle中insert沒有阻塞update

在Oracle中,創建同樣的測試表t,執行同樣的insert和update,但insert不會阻塞update。

CREATE TABLE t (
  a int NOT NULL PRIMARY KEY ,
  b int DEFAULT NULL
);
insert into t values(7,7);
commit;

執行相同的insert和update語句。

session1 session2
SQL> insert into t values(8,8);
1 row created. SQL> update t set b=0 where a=8;0 rows updated.

Enjoy GreatSQL

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1. 概述 1.1. SQL-92標準裡加入的最有用的特性 1.2. 寫法 1.2.1. 簡單CASE表達式 CASE sex WHEN '1' THEN ’男’ WHEN '2' THEN ’女’ ELSE ’其他’ END 1.2.1.1. 寫法簡單,但能實現的事情比較有限 1.2.2. 搜索C ...
  • 背景 早上收到某系統的告警tidb節點掛掉無法訪問,情況十萬火急。登錄中控機查了一下display信息,4個TiDB、Prometheus、Grafana全掛了,某台機器hang死無法連接,經過快速重啟後集群恢復,經排查後是昨天上線的某個SQL導致頻繁OOM。 於是開始亡羊補牢,來一波近期慢SQL巡 ...
  • 超詳細【入門精講】數據倉庫原理&實戰 一步一步搭建數據倉庫 內附相應實驗代碼和鏡像數據和腳本,參考B站up主哈嘍鵬程視頻撰寫而成,感謝!!! ...
  • 存儲引擎 一. MySQL體繫結構 MySQL Server 連接層:連接的處理、認證授權、安全方案、檢查是否超過最大連接數等。 服務層:SQL介面、解析器、查詢優化器、緩存 引擎層:引擎是數據存儲和提取的方式,引擎層有許多引擎可供使用,也可以自定義引擎。索引是在存儲引擎層實現的。 存儲層:存儲數據 ...
  • ##【問題描述】 開發有天碰到一個很奇怪的問題,他的場景是這樣子的: 通過Canal來訂閱MySQL的binlog, 當捕獲到有數據變化時,回到資料庫,反查該數據的明細,然後做進一步處理。 有一次,他碰到一個詭異的現象: 1. Canal收到消息,有一條主鍵id=31019319的數據插入 2. 1 ...
  • sql腳本 表結構設置 點擊查看代碼 if exists(select * from sys.objects where name='Department' and type='U') drop table Department create table Department ( --id iden ...
  • 摘要:華為雲EI DTSE技術佈道師/華為雲數倉GaussDB(DWS)首席架構師曾凱,針對數據倉庫的起源、演進過程、未來技術發展趨勢,與開發者和伙伴朋友們展開交流互動,幫助開發者快速瞭解數據倉庫相關信息與能力。 本文分享自華為雲社區《直播回顧 | 從數倉發展史淺析數倉未來技術趨勢》,作者:胡辣湯。 ...
  • 摘要: 介紹如何設置採樣大小和表級控制analyze。 本文分享自華為雲社區《GaussDB(DWS) 如何表級控制analyze》,作者:leapdb。 一、控制採樣大小 【設置全局採樣大小】 通過參數default_statistics_target設置全局預設採樣大小。 a.default_s ...
一周排行
    -Advertisement-
    Play Games
  • 概述:本文代碼示例演示瞭如何在WPF中使用LiveCharts庫創建動態條形圖。通過創建數據模型、ViewModel和在XAML中使用`CartesianChart`控制項,你可以輕鬆實現圖表的數據綁定和動態更新。我將通過清晰的步驟指南包括詳細的中文註釋,幫助你快速理解並應用這一功能。 先上效果: 在 ...
  • openGauss(GaussDB ) openGauss是一款全面友好開放,攜手伙伴共同打造的企業級開源關係型資料庫。openGauss採用木蘭寬鬆許可證v2發行,提供面向多核架構的極致性能、全鏈路的業務、數據安全、基於AI的調優和高效運維的能力。openGauss深度融合華為在資料庫領域多年的研 ...
  • openGauss(GaussDB ) openGauss是一款全面友好開放,攜手伙伴共同打造的企業級開源關係型資料庫。openGauss採用木蘭寬鬆許可證v2發行,提供面向多核架構的極致性能、全鏈路的業務、數據安全、基於AI的調優和高效運維的能力。openGauss深度融合華為在資料庫領域多年的研 ...
  • 概述:本示例演示了在WPF應用程式中實現多語言支持的詳細步驟。通過資源字典和數據綁定,以及使用語言管理器類,應用程式能夠在運行時動態切換語言。這種方法使得多語言支持更加靈活,便於維護,同時提供清晰的代碼結構。 在WPF中實現多語言的一種常見方法是使用資源字典和數據綁定。以下是一個詳細的步驟和示例源代 ...
  • 描述(做一個簡單的記錄): 事件(event)的本質是一個委托;(聲明一個事件: public event TestDelegate eventTest;) 委托(delegate)可以理解為一個符合某種簽名的方法類型;比如:TestDelegate委托的返回數據類型為string,參數為 int和 ...
  • 1、AOT適合場景 Aot適合工具類型的項目使用,優點禁止反編 ,第一次啟動快,業務型項目或者反射多的項目不適合用AOT AOT更新記錄: 實實在在經過實踐的AOT ORM 5.1.4.117 +支持AOT 5.1.4.123 +支持CodeFirst和非同步方法 5.1.4.129-preview1 ...
  • 總說周知,UWP 是運行在沙盒裡面的,所有許可權都有嚴格限制,和沙盒外交互也需要特殊的通道,所以從根本杜絕了 UWP 毒瘤的存在。但是實際上 UWP 只是一個應用模型,本身是沒有什麼許可權管理的,許可權管理全靠 App Container 沙盒控制,如果我們脫離了這個沙盒,UWP 就會放飛自我了。那麼有沒... ...
  • 目錄條款17:讓介面容易被正確使用,不易被誤用(Make interfaces easy to use correctly and hard to use incorrectly)限制類型和值規定能做和不能做的事提供行為一致的介面條款19:設計class猶如設計type(Treat class de ...
  • title: 從零開始:Django項目的創建與配置指南 date: 2024/5/2 18:29:33 updated: 2024/5/2 18:29:33 categories: 後端開發 tags: Django WebDev Python ORM Security Deployment Op ...
  • 1、BOM對象 BOM:Broswer object model,即瀏覽器提供我們開發者在javascript用於操作瀏覽器的對象。 1.1、window對象 視窗方法 // BOM Browser object model 瀏覽器對象模型 // js中最大的一個對象.整個瀏覽器視窗出現的所有東西都 ...