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

浅谈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日

相关文章

  • 探索ORACLE之ASM概念(完整版)

    “探索ORACLE之ASM概念(完整版)”这篇文章主要介绍了ORACLE中的ASM(Automatic Storage Management)概念及其实现方式、优缺点等内容。以下为该文章的详细攻略: 概述 通过阅读该文,可以了解到何为ASM,ASM的实现方式及其提供的优缺点等内容 ASM的概念 对ASM基本概念的介绍:ASM是一种Oracle软件驱动的存储管…

    database 2023年5月21日
    00
  • Centos7安装和配置Mysql5.7

    下载并安装Mysql5.7 首先,需要下载并安装Mysql5.7。可以通过Centos官方源安装,也可以手动下载安装。以下是手动安装的步骤: 第一步,下载Mysql5.7的安装包 wget https://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm 第二步,安装Mysql源 su…

    database 2023年5月22日
    00
  • golang进程在docker中OOM后hang住问题解析

    下面是详细讲解“golang进程在docker中OOM后hang住问题解析”的完整攻略。 问题描述 在使用 Docker 运行 Golang 应用时,有时会发现进程在被 oom 后 hang 住,不退出。这种情况在日常开发和生产中都可能遇到,会导致容器被占用,无法被正常删除或者重启。 问题原因 这个问题的原因是 Golang 在 OOM 时,由于其内存管理机…

    database 2023年5月22日
    00
  • MySQL 中的锁有哪些类型,MySQL 中加锁的原则

    MySQL 中的锁理解 锁的类型 全局锁 缺点 适用范围 表级锁 表锁 元数据锁 意向锁 自增锁 行锁 Record Lock Gap Lock Next-Key Lock 插入意向锁 加锁的原则 1、主键等值查询 2、非唯一索引等值查询 3、主键索引范围锁 4、非唯一索引范围查询 5、非唯一索引等值查询 6、limit 语句加锁 总结 参考 MySQL 中…

    MySQL 2023年4月11日
    00
  • Redis集群搭建全记录

    Redis集群搭建全记录 在本攻略中,我将会详细讲解如何搭建Redis集群。本攻略中的示例基于Ubuntu操作系统的环境,其他操作系统也类似,仅可能存在些许不同。 准备工作 在开始搭建Redis集群之前,我们需要做一些准备工作: 下载Redis安装包。可以在Redis官网上下载最新版本的Redis安装包,也可以使用系统包管理器直接安装Redis。 安装和配置…

    database 2023年5月22日
    00
  • python监控linux内存并写入mongodb(推荐)

    下面是 Python 监控 Linux 内存并写入 MongoDB 的完整攻略: 步骤一:安装 pymongo pymongo 是 Python 与 MongoDB 交互的一个库,安装方式如下: pip install pymongo 步骤二:获取系统内存信息 在 Python 中获取系统内存信息需要使用 psutil 这个库,以获取总内存和空闲内存为例,代…

    database 2023年5月22日
    00
  • mysql定时自动备份数据库的方法步骤

    下面是关于如何使用MySQL实现定时自动备份数据库的方法步骤及示例说明。 一、准备工作 在进行MySQL定时自动备份操作之前,需要做好以下准备工作: 确认备份策略:定期备份是保障数据安全的重要措施,但需要根据业务需求制定好备份策略,包括备份频率、存储位置、备份方式等。 安装定时任务工具:MySQL自带定时任务功能,但不太方便,因此建议安装第三方定时任务工具,…

    database 2023年5月22日
    00
  • sql中left join的效率分析与提高效率方法

    让我详细讲解一下“SQL中LEFT JOIN的效率分析与提高效率方法”的完整攻略。 什么是LEFT JOIN? 在SQL中,LEFT JOIN是一种关联查询(也被称为左外连接、左连接),用于显示左表中所有记录和右表中与其匹配的记录。如果右表中没有匹配的记录,则显示NULL。 例如,假设我们有两个表,一个是students,另一个是scores。我们想要根据学…

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