浅谈Mysql insert on duplicate key 死锁问题定位与解决

yizhihongxing

浅谈Mysql insert on duplicate key 死锁问题定位与解决

问题描述

在MySQL中,执行insert操作时,可以使用on duplicate key来进行唯一键冲突时的处理。但是,当多个线程并发地执行insert操作的时候,可能会出现死锁问题。

定位死锁问题

当出现死锁问题时,可以使用show processlist命令查看正在执行的线程状态,看看是否有线程处于Waiting for table metadata lock状态。

mysql> show processlist;
+-----+------+-----------+------+---------+------+--------------------------+---------------------------------------+----------+
| Id  | User | Host      | db   | Command | Time | State                    | Info                                  | Progress |
+-----+------+-----------+------+---------+------+--------------------------+---------------------------------------+----------+
| 195 | root | localhost | test | Query   |    0 | starting                 | show processlist                      |    0.000 |
| 196 | root | localhost | test | Sleep   |   75 |                          | NULL                                  |    0.000 |
| 197 | root | localhost | test | Query   |   75 | Waiting for table metadata lock | INSERT INTO table (id, name) VALUES (1, 'test') ON DUPLICATE KEY UPDATE name='test' |
+-----+------+-----------+------+---------+------+--------------------------+---------------------------------------+----------+

如上所示,发现有一个线程处于Waiting for table metadata lock状态,并且是在执行insert语句,因此可以初步定位是insert on duplicate key导致的死锁问题。

接下来,可以使用show engine innodb status命令查看InnoDB的状态,并找到死锁信息。

mysql> show engine innodb status\G
*************************** 1. row ***************************
  Type: InnoDB
  Name: 
Status: 
=====================================
2022-02-14 19:10:49 0x700009955000 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 1 seconds

SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 438814, signal count 1458752
Mutex spin waits 1553835, rounds 3379891, OS waits 63580
RW-shared spins 139670, rounds 1042817, OS waits 22313
RW-excl spins 114627, rounds 5660164, OS waits 142902
Spin rounds per wait: 2.17 mutex, 7.47 RW-shared, 49.36 RW-excl

[..省略..],不是死锁信息

TRANSACTIONS
------------
Trx id counter 7038591

[..省略..],不是死锁信息

INNODB MONITOR OUTPUT
-------
[...省略...]
------------------------
LATEST DETECTED DEADLOCK
------------------------
2022-02-14 13:03:03 0x7fa1964f1700
*** (1) TRANSACTION:
TRANSACTION 23305241, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 10 lock struct(s), heap size 1128, 7 row lock(s), undo log entries 7
MySQL thread id 11970, OS thread handle 0x7fa1b0fc3700, query id 79369552 192.168.170.129 root update
INSERT INTO `test`.`test` (`col1`, `col2`, `col3`) VALUES (33544124, 46158925, 7910988) ON DUPLICATE KEY UPDATE `col2`=VALUES(`col2`),`col3`=VALUES(`col3`)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 389 page no 3 n bits 72 index PRIMARY of table `test`.`test` trx id 23305241 lock_mode X locks rec but not gap waiting
Record lock, heap no 5 PHYSICAL RECORD: n_fields 8; compact format; info bits 0
 0: len 4; hex 8004aa1c; asc     ;;
 1: len 6; hex 000022c80a12; asc   "    ;;
 2: len 7; hex 00000010003d6d; asc      =m;;
 3: len 5; hex 99914597bd; asc   E  ;;
 4: len 5; hex 99914599f7; asc   E  ;;
 5: len 5; hex 9991459cb9; asc   E  ;;
 6: len 1; hex 81; asc  ;;
 7: len 4; hex 8000000d; asc     ;;

[...省略...]

------------------------
LATEST FOREIGN KEY ERROR
------------------------
2022-02-14 19:34:18 0x7f88914e9700 Error in foreign key constraint of table `mydb`.`t2`:

可以找到上面的LATEST DETECTED DEADLOCK信息,来进行死锁问题的定位。

解决死锁问题

  1. 减小事务并发度

当多个并发事务同时访问MySQL数据时,就可能出现死锁问题。因此,可以尝试减小事务并发度,降低死锁风险。如下所示:

# 修改innodb参数,增加等待超时时间
set global innodb_lock_wait_timeout = 120;

# 减少事务并发度
SET GLOBAL innodb_thread_concurrency = 8;

# 在事务中对数据的访问越少,就越不容易出现死锁问题
START TRANSACTION;
SELECT …;
UPDATE …;
COMMIT;

使用上述方法,可以在一定程度上减少死锁的发生。

  1. 修改程序逻辑

当多个线程并发地执行insert操作时,会出现死锁问题。因此,可以通过修改程序逻辑,将insert操作转换为update操作等方式来规避死锁问题。如下所示:

# 将insert操作转换为update操作
INSERT INTO table (id, name) VALUES (1, 'test') ON DUPLICATE KEY UPDATE name='test', id=LAST_INSERT_ID(id);

# 使用select for update避免死锁
START TRANSACTION;
SELECT name FROM table WHERE id=1 FOR UPDATE;
UPDATE table SET name='test' WHERE id=1;
COMMIT;

上述两种方式,可以有效的避免insert on duplicate key死锁问题的发生。

示例说明

示例1:死锁问题的原因

假设有一张test表,其中有一条数据:

CREATE TABLE `test` (
  `id` int(11) NOT NULL,
  `name` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

INSERT INTO `test` (`id`, `name`) VALUES (1, 'tom');

并发执行以下insert语句可能会出现死锁问题:

INSERT INTO test (id, name) VALUES (1, 'jerry') ON DUPLICATE KEY UPDATE name='jerry';

因为当多个线程同时执行insert语句时,会占用相同的行锁,从而导致死锁问题的产生。

示例2:解决死锁问题

为了解决上述的死锁问题,可以将insert语句转换为update语句,如下所示:

INSERT INTO `test` (`id`, `name`) VALUES (1, 'jerry') ON DUPLICATE KEY UPDATE `name`='jerry', `id`=LAST_INSERT_ID(`id`);

在执行上述语句时,并不会出现死锁问题。因为在update操作时,会将id作为LAST_INSERT_ID()返回,从而避免了相同行的操作。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:浅谈Mysql insert on duplicate key 死锁问题定位与解决 - Python技术站

(0)
上一篇 2023年5月21日
下一篇 2023年5月21日

相关文章

  • 什么是数据库?

    简单来说,数据库(Database)指的是长期存储在计算机内的、有组织的、可共享的数据集合。 数据库是一个可访问的数据集合,它以有组织的方式存储和管理信息。在数据库中,数据按照一定的规则被存储和组织,可以很方便地进行访问、协作和管理。常见的数据库类型包括关系型数据库和非关系型数据库。 数据库的作用是什么? 数据库可以存储和管理大量的结构化和非结构化数据,便于…

    2023年3月8日
    00
  • 最全的mysql 5.7.13 安装配置方法图文教程(linux) 强烈推荐!

    最全的mysql 5.7.13 安装配置方法图文教程(linux) 强烈推荐! 简介 MySQL 是一种关系型数据库管理系统,其足以胜任各类型规模企业数据的存储与管理。本教程将针对 Linux 系统的用户介绍 MySQL 5.7.13 的安装与配置。 步骤一:下载 MySQL 首先,我们访问 MySQL 的官网并下载最新的版 MySQL5.7.13。如下所示…

    database 2023年5月22日
    00
  • Redis教程(十五):C语言连接操作代码实例

    转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/143.html 在之前的博客中已经非常详细的介绍了Redis的各种操作命令、运行机制和服务器初始化参数配置。本篇博客是该系列博客中的最后一篇,在这里将给出基于Redis客户端组件访问并操作Redis服务器的代码示例。然而需要说明的是,…

    Redis 2023年4月13日
    00
  • MySQL中库的基本操作指南(推荐!)

    MySQL是一种流行的开源关系型数据库管理系统,由于其使用方便、性能稳定、扩展能力强等特点,广泛被应用于各种Web应用程序中。在MySQL中,我们可以通过许多操作来管理和维护数据库,下面是一份MySQL中库的基本操作指南: 创建数据库 如果我们希望在MySQL中创建一个新的数据库,可以使用以下命令: CREATE DATABASE database_name…

    database 2023年5月22日
    00
  • mysql修改记录时update操作 字段=字段+字符串

    当需要在 MySQL 中修改记录时,我们可以使用 UPDATE 命令来实现。通常情况下,我们会使用等号操作符将新的值赋给要修改的字段。例如: UPDATE mytable SET name=’new name’ WHERE id=1; 但是,有时候我们需要将原有的字段值和一些字符串进行拼接,而不是完全覆盖原有的值。这种情况下,我们可以使用 CONCAT 函数…

    database 2023年5月22日
    00
  • mysql中json_remove函数的使用?

    需求描述:   今天看json记录,可以通过json_remove函数对一个key或多个key从个json记录中去掉. 操作过程: 1.查看一个已经存在的json表 mysql> select * from tab_json; +—-+———————————————————…

    MySQL 2023年4月13日
    00
  • MyBatisPlus 大数据量查询慢的问题解决

    长时间的 SQL 执行和慢查询经常是面对大量数据时的问题。在使用 MyBatisPlus 进行大数据量的查询时,也会遇到这样的问题。下面详细讲解如何解决这些问题。 问题背景 在使用 MyBatisPlus 进行大数据量的查询时,由于返回的数据较多,可能会导致 SQL 执行时间较长,甚至会出现慢查询的情况。下面列举几个可能会导致查询缓慢的因素: 数据库存储介质…

    database 2023年5月21日
    00
  • sql更新语句中update set from用法实现

    “update set from”是SQL更新语句中的一种语法,它可以用于将一张表中的数据更新为另一张表中的数据。下面是关于“update set from”的详细攻略及示例: update set from语法格式 在使用“update set from”时,SQL更新语句的语法格式如下: UPDATE <table_name> SET &lt…

    database 2023年5月21日
    00
合作推广
合作推广
分享本页
返回顶部