MySQL 死锁
一、引言
在 MySQL 数据库的使用过程中,死锁问题就像一颗隐藏在暗处的 “定时炸弹”,平时可能感觉不到它的存在,但一旦触发,就可能导致数据库事务无法正常推进,严重影响系统的性能和可用性。对于开发人员和数据库管理员来说,深入了解 MySQL 死锁的产生原因、表现形式以及如何预防和解决它,是保障数据库高效稳定运行的关键一环。今天,咱们就一起深入探究 MySQL 死锁这个既复杂又重要的话题,通过大量详细的示例和深入浅出的讲解,揭开它神秘的面纱,让大家能够在实际工作中轻松应对这一棘手的问题。
二、MySQL 事务与锁基础回顾
(一)MySQL 事务概述
MySQL 中的事务是一组数据库操作的逻辑单元,这些操作要么全部成功执行,要么全部失败回滚,保证了数据的一致性和完整性。例如,在一个银行转账系统中,从账户 A 转出一笔钱到账户 B,这个过程涉及到两个操作:一是账户 A 的余额减少,二是账户 B 的余额增加,这两个操作就应该放在一个事务里,只有当两个操作都成功完成时,整个转账事务才算成功,要是其中一个操作出现问题,比如账户 A 余额不足无法转出,那么整个事务就要回滚,账户 B 的余额也不会增加,这样就能保证数据始终处于正确的状态。
事务具有四个重要的特性,也就是常说的 ACID 特性:
- 原子性(Atomicity):事务是不可分割的最小工作单元,事务中的所有操作要么全部执行,要么全部不执行。
- 一致性(Consistency):事务执行前后,数据库的状态必须保持一致,比如遵循各种业务规则和数据约束。
- 隔离性(Isolation):多个并发事务之间相互隔离,互不干扰,每个事务感觉不到其他事务的并发执行,不过不同的隔离级别会对并发事务的可见性和相互影响程度有不同的规定。
- 持久性(Durability):一旦事务提交成功,对数据库中数据的修改就是永久性的,即使后续系统出现故障也不会丢失。
(二)MySQL 锁机制
MySQL 使用锁来控制多个并发事务对数据库资源(如行、表等)的访问,确保数据的一致性和完整性,同时协调并发操作。锁主要分为以下几类:
- 共享锁(Shared Lock,简称 S 锁):也叫读锁,多个事务可以同时获取同一个资源上的共享锁,用于只读操作,比如多个用户同时查询同一条数据记录时,可以都加上共享锁来并发读取,互相之间不会产生冲突,因为读操作不会修改数据。
- 排他锁(Exclusive Lock,简称 X 锁):也叫写锁,当一个事务获取了某个资源上的排他锁后,其他事务既不能再获取该资源上的排他锁,也不能获取共享锁,只有持有排他锁的事务完成操作并释放锁后,其他事务才能获取相应的锁来操作这个资源,这是为了保证在写操作时数据的唯一性和完整性,避免其他事务同时修改或读取正在被修改的数据。
例如,事务 A 要修改一行数据,它会先给这行数据加上排他锁,此时如果事务 B 也想修改或者读取这行数据(读取在某些隔离级别下也会受排他锁影响),事务 B 就需要等待事务 A 释放锁后才能继续操作,这就是锁机制在协调并发事务访问资源时的基本原理。
三、什么是 MySQL 死锁
(一)死锁的定义
MySQL 死锁是指在多个并发事务相互等待对方释放锁资源的一种僵持状态,导致这些事务都无法继续推进,就好像两辆车在狭窄的道路上迎面相遇,谁都不肯后退,结果都堵在那里动弹不得。在数据库中,每个事务都持有一部分资源的锁,同时又在等待其他事务持有的锁资源,这样就形成了一个死循环,使得事务无法正常完成,严重影响数据库的正常运行。
(二)死锁产生的简单示例
假设有两个事务,事务 A 和事务 B,以及一张 accounts
表,表中有 id
(主键)、balance
(余额)两个列,现在有两条记录分别对应账户 1 和账户 2 的信息。
以下是可能产生死锁的操作步骤:
- 事务 A 开始:
事务 A 首先开启,执行以下语句,它想先更新账户 1 的余额,于是给账户 1 对应的记录加上排他锁。
START TRANSACTION;
UPDATE accounts SET balance = balance + 100 WHERE id = 1;
- 事务 B 开始:
几乎同时,事务 B 也开启了,它想更新账户 2 的余额,所以给账户 2 对应的记录加上排他锁。
START TRANSACTION;
UPDATE accounts SET balance = balance + 200 WHERE id = 2;
-
事务 A 继续操作:
事务 A 接着想更新账户 2 的余额(可能是后续业务逻辑需要同时操作多个账户),但此时账户 2 的记录已经被事务 B 加上了排他锁,所以事务 A 只能等待事务 B 释放锁。 -
事务 B 继续操作:
事务 B 接下来想更新账户 1 的余额(同样基于其业务逻辑),然而账户 1 的记录被事务 A 加上了排他锁,事务 B 也只能等待事务 A 释放锁。
就这样,事务 A 等待事务 B 释放账户 2 的锁,事务 B 等待事务 A 释放账户 1 的锁,形成了一个互相等待的死循环,产生了死锁情况,两个事务都无法继续往下执行了,陷入了僵持状态。
四、MySQL 死锁产生的常见原因及分析
(一)不同事务对资源的加锁顺序不一致
- 原理分析:
当多个并发事务需要获取多个资源(比如多条数据记录或者多个表)上的锁时,如果它们获取这些资源锁的顺序不同,就很容易产生死锁。就好比几个人去拿不同颜色的积木,甲先拿红色再拿蓝色,乙却先拿蓝色再拿红色,要是不巧两人都拿到了一半,就可能出现互相等待对方手里积木的情况,在数据库里就是互相等待对方释放锁资源。
例如,有事务 A、事务 B 和三个资源(资源 R1、资源 R2、资源 R3)。事务 A 按照先获取资源 R1 的锁,再获取资源 R3 的锁的顺序进行操作;而事务 B 按照先获取资源 R3 的锁,再获取资源 R1 的锁的顺序来操作。假如在某个时刻,事务 A 获取了资源 R1 的锁,事务 B 获取了资源 R3 的锁,然后事务 A 去申请资源 R3 的锁时发现被事务 B 占用了,需要等待,同时事务 B 去申请资源 R1 的锁时发现被事务 A 占用了,也要等待,这样就形成了死锁。
- 示例演示:
假设有一张products
表,包含id
(主键)、name
(产品名称)、stock
(库存)等列,有以下两个事务的操作:
事务 A:
START TRANSACTION;
-- 先锁住产品1
SELECT * FROM products WHERE id = 1 FOR UPDATE;
-- 再尝试锁住产品2
SELECT * FROM products WHERE id = 2 FOR UPDATE;
事务 B:
START TRANSACTION;
-- 先锁住产品2
SELECT * FROM products WHERE id = 2 FOR UPDATE;
-- 再尝试锁住产品1
SELECT * FROM products WHERE id = 1 FOR UPDATE;
在上述示例中,事务 A 先对产品 1 加排他锁(通过 FOR UPDATE
语句实现,用于在查询时加排他锁,常用于后续要进行更新操作的场景),然后想对产品 2 加排他锁;而事务 B 刚好相反,先对产品 2 加排他锁,再想对产品 1 加排他锁。如果这两个事务并发执行,就很容易出现事务 A 等待事务 B 释放产品 2 的锁,事务 B 等待事务 A 释放产品 1 的锁的情况,从而产生死锁,导致两个事务都无法继续执行下去。
(二)事务的嵌套与锁等待
-
原理分析:
在复杂的业务场景中,事务可能会嵌套使用,也就是在一个事务里面又开启了另一个事务。当内层事务和外层事务都需要获取锁资源,并且出现互相等待对方释放锁的情况时,就容易引发死锁。比如外层事务获取了一部分资源的锁,然后进入内层事务又去申请其他被别的事务占用的锁资源,同时别的事务也可能因为类似情况在等待外层事务释放锁,这样就形成了复杂的等待关系,最终导致死锁。 -
示例演示:
假设有一个库存管理系统,涉及到两张表,一张是warehouses
(仓库表,包含id
(主键)、warehouse_name
(仓库名称)等列),另一张是products_in_warehouse
(仓库产品表,包含id
(主键)、product_id
(产品 ID)、warehouse_id
(仓库 ID)、quantity
(数量)等列)。
事务 A 的操作如下(这里为了方便演示简化了一些业务逻辑,实际可能更复杂):
START TRANSACTION;
-- 先锁住某个仓库记录
SELECT * FROM warehouses WHERE id = 1 FOR UPDATE;
-- 开启内层事务
START TRANSACTION;
-- 在内层事务中尝试锁住该仓库中的某个产品记录
SELECT * FROM products_in_warehouse WHERE warehouse_id = 1 AND product_id = 10 FOR UPDATE;
-- 内层事务提交(假设这里没有出现错误等情况)
COMMIT;
-- 外层事务继续其他操作(这里省略其他操作步骤)
事务 B 的操作:
START TRANSACTION;
-- 先锁住上述仓库中的某个产品记录(与事务A内层事务尝试锁的产品记录相同)
SELECT * FROM products_in_warehouse WHERE warehouse_id = 1 AND product_id = 10 FOR UPDATE;
-- 开启内层事务
START TRANSACTION;
-- 在内层事务中尝试锁住仓库记录(与事务A外层事务锁的仓库记录相同)
SELECT * FROM warehouses WHERE id = 1 FOR UPDATE;
-- 内层事务提交
COMMIT;
-- 外层事务继续其他操作(省略)
在这个示例中,事务 A 先锁住了仓库记录,然后进入内层事务想锁住仓库里的某个产品记录;而事务 B 先锁住了那个产品记录,再进入内层事务想锁住仓库记录。这样就出现了事务 A 等待事务 B 释放产品记录的锁(在内层事务中),事务 B 等待事务 A 释放仓库记录的锁(也在内层事务中)的情况,形成了死锁,两个事务都无法按计划完成操作了。
(三)并发事务中的锁升级
- 原理分析:
锁升级是指数据库系统将较低级别的锁(比如行级锁)提升为较高级别的锁(比如表级锁)的过程。在并发事务环境下,如果多个事务对同一表中的不同行加了行级锁,然后由于某些操作(比如进行大量的更新操作且满足一定条件等)触发了锁升级,使得原本可以并发进行的操作变成了互斥的,当这些事务之间又存在相互等待其他事务释放锁资源的情况时,就可能导致死锁。
例如,在一个有大量数据行的表中,多个事务开始都对不同的数据行加了行级锁进行更新操作,随着操作的推进,如果数据库判断需要将这些行级锁升级为表级锁来更好地管理和保证数据一致性,那么一旦升级完成,其他事务原本能访问的其他行(之前加行级锁时不影响对其他行的操作)现在也无法访问了,要是此时事务之间刚好存在互相等待对方释放锁资源的情况,就容易引发死锁。
- 示例演示:
假设有一张orders
表,包含id
(主键)、customer_id
(客户 ID)、order_amount
(订单金额)等列,有很多行订单记录。
事务 A:
START TRANSACTION;
-- 对订单1加行级锁并进行更新操作
UPDATE orders SET order_amount = order_amount + 100 WHERE id = 1;
-- 进行一些其他相关操作(这里假设这些操作可能会触发锁升级条件,比如更新的数据量达到一定阈值等,实际情况依数据库配置和业务逻辑而定)
-- 继续对其他订单(假设为订单2)进行操作,但此时如果已经触发锁升级为表级锁了,就需要等待其他事务释放表锁
UPDATE orders SET order_amount = order_amount + 200 WHERE id = 2;
事务 B:
START TRANSACTION;
-- 对订单2加行级锁并进行更新操作
UPDATE orders SET order_amount = order_amount + 300 WHERE id = 2;
-- 进行一些类似的其他操作(同样可能触发锁升级)
-- 继续对订单1进行操作,但因为事务A那边可能已经升级为表级锁了,需要等待事务A释放表锁,而事务A又在等事务B释放相关锁(比如之前对订单2操作时的锁情况等),形成死锁
UPDATE orders SET order_amount = order_amount + 400 WHERE id = 1;
在这个示例中,事务 A 和事务 B 开始都对不同的订单记录加行级锁进行更新操作,随着各自操作的推进,假如因为满足了数据库的锁升级条件,都将行级锁升级为表级锁了,然后就出现了事务 A 等待事务 B 释放对订单 2 相关的锁(现在是表级锁了),事务 B 等待事务 A 释放对订单 1 相关的锁的情况,导致死锁产生,两个事务都被困住无法继续完成后续的更新操作了。
(四)索引问题导致的锁范围扩大
-
原理分析:
当查询语句中没有合适的索引或者索引失效时,数据库在执行操作(比如更新、删除等需要加锁的操作)时可能无法精准地锁定具体的行,而是会扩大锁的范围,甚至可能升级为表级锁。在并发环境下,这种因索引问题导致的锁范围扩大就容易引发死锁。比如多个事务本来只需要对表中的少数行加锁进行操作,但由于索引问题变成了对整个表加锁,当这些事务之间存在互相等待对方释放锁资源的情况时,就会陷入死锁状态。 -
示例演示:
假设有一张customers
表,包含id
(主键)、name
(姓名)、email
(邮箱)等列,并且在name
列上有索引,但在某个业务场景中,对name
列进行了函数操作,导致索引失效(前面我们讲过对索引列进行函数操作会使索引失效的情况)。
事务 A:
START TRANSACTION;
-- 对满足特定函数条件的客户记录进行更新(由于索引失效,会扩大锁范围,可能锁住整个表)
UPDATE customers SET email = 'new_email@example.com' WHERE YEAR(name) = 2000;
-- 接着想对其他记录进行操作(但因为前面锁范围扩大了,可能需要等待其他事务释放表锁等情况)
UPDATE customers SET phone = '123456789' WHERE id = 1;
事务 B:
START TRANSACTION;
-- 对另外一些满足类似函数条件的客户记录进行更新(同样因索引失效扩大锁范围)
UPDATE customers SET address = 'new_address' WHERE MONTH(name) = 3;
-- 然后想对事务A操作涉及的记录(比如id = 1的记录)进行操作,就需要等待事务A释放锁,而事务A又在等事务B释放相关锁,形成死锁
UPDATE customers SET credit_limit = 10000 WHERE id = 1;
在这个示例中,由于在查询条件中对 name
列使用了函数操作导致索引失效,事务 A 和事务 B 在执行更新操作时都扩大了锁的范围,可能都锁住了整个表或者很大一部分记录,然后在后续操作中就出现了互相等待对方释放锁资源的情况,最终产生了死锁,使得两个事务都无法顺利完成对客户记录的更新操作了。
(五)长事务与并发操作
-
原理分析:
长事务是指那些执行时间较长的事务,在其执行过程中会长期持有锁资源。当有多个长事务并发运行,并且它们之间存在对相同或相关资源的操作需求时,就很容易因为长时间持有锁以及互相等待对方释放锁而产生死锁。比如一个事务在进行复杂的数据处理,可能涉及到多个表的大量数据操作,需要长时间锁住一些资源,而其他并发的长事务也需要操作这些被锁住的资源,就容易陷入互相等待的僵局,导致死锁出现。 -
示例演示:
假设有一个电商系统,涉及到订单处理、库存管理、用户积分管理等多个模块,对应的有orders
表、stock
表、user_points
表等。
事务 A(一个长事务,模拟复杂的订单处理流程):
START TRANSACTION;
-- 首先锁住订单相关记录,进行一系列订单状态更新等操作(可能需要较长时间)
UPDATE orders SET status = 'processing' WHERE order_id IN (1, 2, 3);
-- 接着根据订单情况去更新库存记录(需要获取库存表相关记录的锁)
UPDATE stock SET quantity = quantity - 1 WHERE product_id IN (SELECT product_id FROM orders WHERE order_id IN (1, 2, 3));
-- 再去更新用户积分记录(又需要获取用户积分表相关记录的锁)
UPDATE user_points SET points = points + 10 WHERE user_id IN (SELECT user_id FROM orders WHERE order_id IN (1, 2, 3));
事务 B(也是一个长事务,模拟库存盘点与调整以及相关用户操作):
START TRANSACTION;
-- 先锁住库存相关记录进行盘点操作(可能耗时较长)
UPDATE stock SET checked = true WHERE product_id IN (4, 5, 6);
-- 然后根据库存情况去更新部分订单的状态(需要获取订单表相关记录的锁)
UPDATE orders SET status = 'checked' WHERE product_id IN (4, 5, 6);
-- 再去更新对应的用户积分记录(需要获取用户积分表相关记录的锁)
UPDATE user_points SET points = points - 5 WHERE user_id IN (SELECT user_id FROM orders WHERE product_id IN (4, 5, 6));
在这个示例中,事务 A 和事务 B 都是长事务,各自执行着复杂且耗时的操作流程。事务 A 先锁住了订单相关记录并持有锁,在后续操作中需要去操作库存表和用户积分表的相关记录;而事务 B 先锁住了库存相关记录并持有锁,后续又要去操作订单表和用户积分表的相关记录。由于它们执行时间长且都长时间持有一部分资源的锁,很容易出现事务 A 等待事务 B 释放库存或用户积分相关记录的锁,同时事务 B 等待事务 A 释放订单或用户积分相关记录的锁的情况,这样就形成了死锁,两个事务都卡在那里无法继续推进,严重影响整个电商系统的业务流程正常运转。
五、MySQL 死锁的检测与排查方法
(一)查看数据库错误日志
-
原理与作用:
MySQL 数据库本身会将一些重要的运行信息,包括死锁相关的情况记录到错误日志中。当发生死锁时,数据库会检测到这种僵持的状态,并将死锁的相关详细信息,比如涉及的事务、锁等待的资源、产生死锁的语句等记录下来。通过查看错误日志,我们可以快速了解到是哪些事务之间产生了死锁,以及大概是在什么操作上出现了互相等待的情况,这是排查死锁问题的一个重要入口。 -
查看步骤示例(以常见的 Linux 系统下 MySQL 安装为例):
通常,MySQL 的错误日志文件位置可以在配置文件(一般是my.cnf
或者my.ini
)中进行配置,默认情况下,在 Linux 系统中可能位于/var/log/mysql/error.log
这个路径(不同的安装和配置方式可能会有差异)。
可以使用文本编辑工具(如 vim
、nano
等)或者直接通过命令行查看工具(如 cat
、less
、more
等)来查看错误日志内容。例如,使用 cat
命令查看错误日志的基本操作如下:
cat /var/log/mysql/error.log
在日志文件中,当出现死锁情况时,会有类似下面这样的记录信息(这里是一个简化示例,实际的日志内容会更详细):
LATEST DETECTED DEADLOCK
------------------------
2024-11-20 10:00:00 0x7f8c12345678
*** (1) TRANSACTION:
TRANSACTION 12345, ACTIVE 100 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock structs, heap size 1024, 1 row lock(s)
MySQL thread id 123, OS thread handle 0x7f8c12345678, query id 45678 localhost root updating
UPDATE accounts SET balance = balance + 100 WHERE id = 1
*** (2) TRANSACTION:
TRANSACTION 23456, ACTIVE 80 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock structs, heap size 1024, 1 row lock(s)
MySQL thread id 124, OS thread handle 0x7f8c87654321, query id 45679 localhost root updating
UPDATE accounts SET balance = balance + 200 WHERE id = 2
*** WE ROLL BACK TRANSACTION (1)
从上述日志信息中,我们可以清晰地看到有两个事务(事务 ID 分别为 12345 和 23456)产生了死锁,以及它们正在执行的更新语句分别是什么,这样就能初步定位到是对 accounts
表中 id
为 1 和 id
为 2 的记录操作时出现了互相等待锁资源的情况,进而去分析对应的业务逻辑和代码,查找死锁产生的根源。
(二)使用 SHOW ENGINE INNODB STATUS
命令
-
功能介绍:
对于使用 InnoDB 存储引擎的 MySQL 数据库(InnoDB 是 MySQL 中常用且功能强大的存储引擎,很多情况下都会默认使用),SHOW ENGINE INNODB STATUS
命令是一个非常强大的工具,它能提供关于 InnoDB 存储引擎内部状态的详细信息,其中就包括死锁相关的详细情况。这个命令会输出大量的信息,涵盖了事务、锁、缓存等多个方面的状态情况,我们可以从中筛选出死锁部分的内容来进行深入分析。 -
示例操作与结果解读:
在 MySQL 客户端(比如通过命令行登录 MySQL 后),可以直接执行以下命令:
SHOW ENGINE INNODB STATUS;
执行后会返回一大段文本信息,其中关于死锁部分的内容大致如下(同样是简化示例,实际更复杂):
------------------------
LATEST DETECTED DEADLOCK
------------------------
2024-11-20 10:30:00 0x7f8c56781234
*** (1) TRANSACTION:
TRANSACTION 34567, ACTIVE 50 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock structs, heap size 1024, 2 row lock(s)
MySQL thread id 125, OS thread handle 0x7f8c56781234, query id 45680 localhost root insert
INSERT INTO products (name, stock) VALUES ('New Product', 100)
*** (2) TRANSACTION:
TRANSACTION 45678, ACTIVE 40 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock structs, heap size 1024, 2 row lock(s)
MySQL thread id 126, OS thread handle 0x7f8c98765432, query id 45681 localhost root insert
INSERT INTO products (name, stock) VALUES ('Another Product', 200)
*** WE ROLL BACK TRANSACTION (1)
从这里我们可以获取到和查看错误日志类似的关键信息,知道是哪两个事务(事务 ID 为 34567 和 45678)产生了死锁,以及它们正在执行的插入语句内容,从而分析出可能是在同时向 products
表插入数据时因为锁资源竞争出现了互相等待的情况,然后结合业务场景去进一步排查为什么会在这个插入操作上产生死锁,比如是否是没有合理控制并发插入顺序等原因导致的。
(三)借助性能监控工具(如 Percona Toolkit 等)
-
工具优势与适用场景:
像 Percona Toolkit 这样的专业性能监控工具,提供了更强大、更便捷的功能来帮助我们检测和分析死锁问题。它可以实时地监控数据库的运行状态,不仅能够及时捕获死锁发生的瞬间并记录详细信息,还能对一段时间内的死锁情况进行统计分析,比如统计死锁发生的频率、涉及的表和语句等情况,方便我们从整体上把握数据库中死锁问题的严重程度以及变化趋势,对于复杂的数据库环境和频繁出现死锁的情况,这类工具能大大提高我们排查问题的效率。 -
使用示例(以 Percona Toolkit 中的
pt-deadlock-logger
工具为例):
首先需要安装 Percona Toolkit 工具集(安装过程因操作系统等因素略有不同,这里假设已经安装完成)。
然后可以通过以下命令来使用 pt-deadlock-logger
工具,让它开始监控并记录死锁情况:
pt-deadlock-logger --user=root --password=your_password --host=localhost --database=your_database
这里需要将 your_password
替换为实际的 MySQL 登录密码,your_database
替换为要监控的数据库名称。
这个工具会在后台持续运行,一旦检测到死锁发生,它会将详细的死锁信息记录到指定的文件中(默认是在当前目录下按照一定格式命名的文件,也可以通过参数指定文件路径和名称),例如记录的文件内容可能如下:
2024-11-20 11:00:00 [localhost] [your_database]
Deadlock found between transactions:
Transaction 1:Session ID: 127Query: UPDATE users SET age = age + 1 WHERE id = 1Locks held: row lock on users.id = 1
Transaction 2:Session ID: 128Query: UPDATE users SET age = age + 2 WHERE id = 2Locks held: row lock on users.id = 2
Deadlock graph:-> Transaction 1 waits for row lock on users.id = 2 held by Transaction 2-> Transaction 2 waits for row lock on users.id = 1 held by Transaction 1
Rolled back transaction: Transaction 1
通过这样详细且有条理的记录,我们可以非常直观地了解到每次死锁涉及的事务、具体的查询语句以及锁等待的关系等关键信息,便于快速定位问题所在,采取相应的解决措施来避免死锁再次发生。
六、MySQL 死锁的解决方法与预防策略
(一)解决方法
- 手动干预让事务回滚:
当通过检测手段发现死锁后,一种常见的解决方式就是手动让其中一个或部分涉及的事务回滚,打破死锁的僵持状态。比如从数据库的错误日志或者通过SHOW ENGINE INNODB STATUS
等方式确定了产生死锁的事务 ID 后,可以在 MySQL 客户端使用ROLLBACK
命令来手动回滚相应的事务。例如,如果发现事务 ID 为 12345 的事务陷入死锁了,可以执行以下命令:
ROLLBACK;
这样,该事务释放了它持有的锁资源,其他被阻塞的事务就有可能继续推进了。不过这种方式需要人工及时介入,而且要根据具体的业务场景判断回滚哪个事务更合适,避免影响业务的正常数据逻辑。
- 调整事务的并发执行顺序:
根据死锁产生的原因分析,如果是因为不同事务对资源的加锁顺序不一致导致的死锁,那么可以通过调整事务的并发执行顺序,让所有事务按照统一的顺序去获取锁资源,从而避免死锁的发生。比如前面提到的事务 A 和事务 B 对产品 1 和产品 2 加锁的示例,我们可以规定所有事务都先对产品 1 加锁,再对产品 2 加锁,那么就不会出现互相等待对方释放锁的情况了。在代码实现层面,可以通过合理的业务逻辑编排或者使用锁的排队机制等方式来确保并发事务按照既定的顺序获取锁。
例如,在 Java 代码中通过使用 synchronized
关键字或者更高级的并发控制框架(如 java.util.concurrent
包中的相关类)来实现锁的排队获取,确保多个线程(对应多个并发事务)按照相同的顺序去访问需要加锁的数据库资源,以下是一个简单的 Java 代码示例(这里假设通过 JDBC 连接 MySQL 进行操作,简化了部分异常处理等逻辑):
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;public class DeadlockPreventionExample {private static final String DB_URL = "jdbc:mysql://localhost:3006/your_database";private static final String DB_USER = "root";private static final String DB_PASSWORD = "your_password";public static void main(String[] args) {// 模拟两个并发事务操作(这里通过两个线程来表示)Thread thread1 = new Thread(() -> {try (Connection connection = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);Statement statement = connection.createStatement()) {// 先获取产品1的锁(这里通过数据库的FOR UPDATE语句来模拟加排他锁)statement.execute("START TRANSACTION;");statement.execute("SELECT * FROM products WHERE id = 1 FOR UPDATE;");// 再获取产品2的锁statement.execute("SELECT * FROM products WHERE id = 2 FOR UPDATE;");// 模拟业务操作完成后提交事务statement.execute("COMMIT;");} catch (SQLException e) {e.printStackTrace();}});Thread thread2 = new Thread(() -> {try (Connection connection = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);Statement statement = connection.createStatement()) {// 同样先获取产品1的锁statement.execute("START TRANSACTION;");statement.execute("SELECT * FROM products WHERE id = 1 FOR UPDATE;");// 再获取产品2的锁statement.execute("SELECT * FROM products WHERE id = 2 FOR UPDATE;");// 提交事务statement.execute("COMMIT;");} catch (SQLException e) {e.printStackTrace();}});thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}}
}
在上述代码中,虽然是两个不同的线程模拟并发事务,但它们都按照先获取产品 1 的锁,再获取产品 2 的锁的顺序来操作,这样就避免了因加锁顺序不一致而产生死锁的情况。
- 优化查询语句与索引使用:
如果死锁是由于索引问题导致锁范围扩大等原因引起的,那么优化查询语句和合理使用索引就非常关键。比如避免在查询条件中对索引列进行函数操作、保证数据类型匹配以防止隐式类型转换导致索引失效等(前面我们详细讲过索引失效的相关情况及解决方法)。
例如,原本有一个查询语句 UPDATE customers SET email = 'new_email@example.com' WHERE YEAR(name) = 2000
因为对 name
列使用函数 YEAR
导致索引失效进而可能引发死锁,我们可以优化为通过日期范围来查询,比如:
UPDATE customers SET email = 'new_email@example.com'
WHERE name >= '2000-01-01' AND name < '2001-01-01';
这样就能利用 name
列上的索引进行精准的行级锁锁定,减少锁范围扩大导致死锁的风险。
(二)预防策略
- 尽量缩短事务的执行时间:
长事务持有锁资源的时间长,容易与其他并发事务产生锁冲突进而引发死锁,所以要尽量缩短事务的执行时间。可以通过合理拆分业务逻辑,将一个复杂的长事务拆分成多个小事务来执行,每个小事务完成相对独立且简单的操作,快速获取锁资源并释放,减少长时间占用锁的情况。
例如,在前面提到的电商系统的长事务示例中,事务 A 原本要同时完成订单状态更新、库存更新和用户积分更新等多个操作,可以拆分成三个小事务:
第一个小事务只负责订单状态更新:
START TRANSACTION;
UPDATE orders SET status = 'processing' WHERE order_id IN (1, 2, 3);
COMMIT;
第二个小事务负责库存更新:
START TRANSACTION;
UPDATE stock SET quantity = quantity - 1 WHERE product_id IN (SELECT product_id FROM orders WHERE order_id IN (1, 2, 3));
COMMIT;
第三个小事务负责用户积分更新:
START TRANSACTION;
UPDATE user_points SET points = points + 10 WHERE user_id IN (SELECT user_id FROM orders WHERE order_id IN (1, 2, 3));
COMMIT;
这样每个小事务执行时间短,快速释放锁资源,降低了与其他并发事务产生死锁的可能性。
- 合理设置隔离级别:
不同的隔离级别对并发事务之间的相互影响以及锁的使用情况有不同的规定,合理选择隔离级别可以在一定程度上预防死锁的发生。例如,在一些对并发读要求较高且允许一定程度数据不一致性(在可接受范围内)的场景下,可以选择读已提交(Read Committed)隔离级别,它相比于可重复读(Repeatable Read)等隔离级别,锁的持有时间可能更短,减少了因长时间锁等待导致死锁的风险。
不过选择隔离级别要综合考虑业务对数据一致性、并发性能等多方面的需求,不能仅仅为了预防死锁而牺牲了数据的准确性和完整性等重要因素。
- 定期进行数据库性能分析与优化:
通过定期使用性能监控工具(如前面提到的 Percona Toolkit 等)对数据库进行全面的性能分析,及时发现潜在的死锁风险以及其他性能瓶颈问题。比如统计死锁发生的频率、分析涉及的表和语句等情况,然后针对性地进行优化,可能是优化查询语句、调整索引、改进并发控制逻辑等多方面的操作,从整体上提升数据库的并发处理能力,从整体上提升数据库的并发处理能力,预防死锁等问题的频繁出现。例如,每周可以安排特定时间进行一次数据库性能的深度扫描,查看是否有死锁相关的告警或者异常的锁等待情况,对于频繁出现死锁的业务模块对应的数据库操作进行重点排查和优化。
同时,结合业务的发展和数据量的变化,适时调整数据库的相关配置参数,比如调整锁等待超时时间(innodb_lock_wait_timeout
参数,控制着事务等待锁的最长时间,超过这个时间如果还没获取到锁,事务就会自动回滚)等,让数据库能更好地适应实际的业务负载,在保证数据安全和一致性的前提下,高效地处理并发事务,减少死锁产生的土壤。
- 进行充分的代码审查与测试:
在开发阶段,要对涉及数据库操作的代码进行严格的审查,尤其是那些存在并发访问数据库资源的业务逻辑部分。检查代码中事务的开启、提交、回滚是否合理,对锁的获取和释放操作是否正确规范,是否存在可能导致不同事务加锁顺序不一致或者长时间持有锁的隐患等。
并且,通过全面的测试来模拟高并发场景下数据库的运行情况,比如使用性能测试工具(如 JMeter
等)来模拟大量并发用户对数据库进行操作,观察是否会出现死锁以及其他性能问题。在测试过程中一旦发现死锁情况,及时分析原因并调整代码逻辑,确保在正式上线前尽可能地消除死锁隐患,提高系统的稳定性和可靠性。
七、不同业务场景下 MySQL 死锁问题案例分析
(一)电商系统中的死锁案例
-
场景描述:
在一个电商系统中,有多个模块会同时对数据库进行操作,比如订单模块、库存模块和用户模块等。订单模块在处理用户下单操作时,需要先锁住订单记录进行状态更新(如标记为已支付、待发货等),同时可能会根据订单中的商品信息去更新库存记录;库存模块会定期进行盘点和补货操作,这期间也需要锁住库存记录进行修改;用户模块则会根据用户的购买行为更新用户的积分、等级等信息,而且这些操作往往都是并发进行的。 -
死锁产生过程及分析:
假设现在有两个并发的业务操作。一个是用户下单成功后,订单模块的事务 A 开始执行以下操作:
START TRANSACTION;
-- 锁住订单记录进行状态更新
UPDATE orders SET status = 'paid' WHERE order_id = 123;
-- 根据订单商品去更新库存记录
UPDATE stock SET quantity = quantity - 1 WHERE product_id IN (SELECT product_id FROM order_items WHERE order_id = 123);
-- 准备更新用户积分(但还未执行到这一步)
-- UPDATE user_points SET points = points + 10 WHERE user_id IN (SELECT user_id FROM orders WHERE order_id = 123);
同时,库存模块的事务 B 在进行库存盘点后发现某商品库存不足需要补货,执行以下操作:
START TRANSACTION;
-- 锁住库存记录准备更新库存数量
UPDATE stock SET quantity = quantity + 100 WHERE product_id = 456;
-- 根据库存变化去更新相关订单的状态(比如标记某些等待发货的订单可以发货了)
UPDATE orders SET status = 'shipped' WHERE product_id = 456 AND status = 'waiting';
-- 准备更新用户相关信息(但未执行到)
-- UPDATE user_points SET points = points - 5 WHERE user_id IN (SELECT user_id FROM orders WHERE product_id = 456);
在这个过程中,事务 A 先锁住了订单 order_id = 123
的记录,然后要去更新库存记录;而事务 B 先锁住了库存 product_id = 456
的记录,接着要去更新订单记录。这样就出现了事务 A 等待事务 B 释放库存记录的锁,事务 B 等待事务 A 释放订单记录的锁的情况,产生了死锁,导致两个事务都无法继续完成后续的用户积分更新等操作,影响了电商系统业务流程的正常推进。
- 解决与预防措施应用:
- 解决措施:可以通过查看数据库的错误日志或者使用
SHOW ENGINE INNODB STATUS
命令确定产生死锁的事务 ID,然后手动回滚其中一个事务(比如回滚事务 A),打破死锁状态,让另一个事务(事务 B)能够继续执行。但这只是临时解决办法,后续还需要从根源上预防死锁再次发生。 - 预防措施:
- 调整业务逻辑,缩短事务执行时间。比如将订单模块的操作拆分成更小的事务,先更新订单状态后,通过消息队列等方式异步通知库存模块进行库存更新,这样减少了两个模块在同一事务中直接竞争锁资源的情况,也缩短了每个事务持有锁的时间。
- 优化查询语句和索引使用,确保在更新库存和订单记录时能够精准地锁定行,避免锁范围扩大。例如,在
UPDATE stock
和UPDATE orders
的语句中,确保涉及的WHERE
条件列都有合适的索引,并且不进行会导致索引失效的操作,这样能更精细地控制锁的获取和释放,降低死锁风险。
- 解决措施:可以通过查看数据库的错误日志或者使用
(二)金融系统中的死锁案例
-
场景描述:
在金融系统中,像银行转账、账户余额查询与更新、理财产品购买等操作都涉及到数据库的并发操作。以银行转账为例,需要同时对转出账户和转入账户进行操作,确保转出账户余额减少,转入账户余额增加,并且这些操作都要保证数据的准确性和一致性,通常会放在一个事务里进行处理。 -
死锁产生过程及分析:
假设有两个转账业务同时进行,事务 A 是从账户 A 向账户 B 转账,执行以下操作:
START TRANSACTION;
-- 锁住转出账户A的记录,准备更新余额
SELECT * FROM accounts WHERE account_id = 'A' FOR UPDATE;
-- 锁住转入账户B的记录,准备更新余额
SELECT * FROM accounts WHERE account_id = 'B' FOR UPDATE;
-- 更新转出账户A的余额
UPDATE accounts SET balance = balance - 1000 WHERE account_id = 'A';
-- 更新转入账户B的余额
UPDATE accounts SET balance = balance + 1000 WHERE account_id = 'B';
事务 B 是从账户 C 向账户 D 转账,执行操作如下:
START TRANSACTION;
-- 锁住转出账户C的记录,准备更新余额
SELECT * FROM accounts WHERE account_id = 'C' FOR UPDATE;
-- 锁住转入账户D的记录,准备更新余额
SELECT * FROM accounts WHERE account_id = 'D' FOR UPDATE;
-- 更新转出账户C的余额
UPDATE accounts SET balance = balance - 500 WHERE account_id = 'C';
-- 更新转入账户D的余额
UPDATE accounts SET balance = balance + 500 WHERE account_id = 'D';
但是,假如在执行过程中,事务 A 先成功锁住了账户 A 的记录,事务 B 先成功锁住了账户 D 的记录,然后事务 A 去锁账户 D 的记录时需要等待事务 B 释放锁,而事务 B 去锁账户 A 的记录时需要等待事务 A 释放锁,就形成了死锁,导致两个转账事务都无法完成,严重影响金融系统的正常资金流转。
- 解决与预防措施应用:
- 解决措施:同样可以通过数据库提供的死锁检测手段确定涉及的事务 ID,然后选择回滚其中一个事务(比如事务 A)来打破死锁僵局,使另一个事务(事务 B)能够继续执行转账操作。不过这会影响到对应的转账业务,需要及时通知相关用户并进行适当的处理(如提示转账失败,稍后重新操作等)。
- 预防措施:
- 统一加锁顺序,规定所有转账事务都先按照账户 ID 从小到大的顺序去获取转出账户和转入账户的锁,这样就能避免因加锁顺序不一致导致的死锁情况。例如,不管是从哪个账户向哪个账户转账,都先锁定 ID 较小的账户记录,再锁定 ID 较大的账户记录,这样所有事务在获取锁资源时就遵循了相同的规则,不会出现互相等待的死锁情况了。
- 合理设置隔离级别,考虑到金融系统对数据一致性要求非常高,可以选择可重复读(Repeatable Read)隔离级别,同时通过优化数据库连接池、合理控制并发转账请求数量等方式,在保证数据安全的前提下,尽量减少因并发操作导致的死锁等性能问题,确保金融系统稳定可靠地运行。
八、总结
MySQL 死锁问题是在数据库并发操作中一个比较复杂且关键的问题,它可能会导致数据库事务无法正常推进,进而影响整个系统的业务流程和性能表现。我们深入探讨了死锁产生的常见原因,包括加锁顺序不一致、事务嵌套、锁升级、索引问题以及长事务等多个方面,并且通过详细的示例展示了这些原因是如何在实际操作中引发死锁的。
相关文章:
MySQL 死锁
一、引言 在 MySQL 数据库的使用过程中,死锁问题就像一颗隐藏在暗处的 “定时炸弹”,平时可能感觉不到它的存在,但一旦触发,就可能导致数据库事务无法正常推进,严重影响系统的性能和可用性。对于开发人员和数据库管理员…...
M|大脑越狱
rating: 7.0 豆瓣: 7.6 上映时间: “2015” 类型: M悬疑 导演: 约瑟夫怀特 Joseph White 主演: 亚历山大欧文 Alexander Owen爱德华富兰克林 Edward Franklin 国家/地区: 英国 片长/分钟: 20分钟 M|大脑越狱 想法不错,但是逻辑比较一般。属于…...
CSS3_媒体查询(十一)
CSS3_响应式布局 1、媒体样式 在不同媒体上显示不同的样式。 常用阈值: 小于768px:小屏幕;768px-992px:中等屏幕;992px-1200px:大屏幕;大于1200px:超大屏幕。 <!DOCTYPE html>…...
Spring |(四)IoC/DI配置管理第三方bean
文章目录 📚数据源对象管理🐇环境准备🐇实现Druid管理🐇实现C3P0管理 📚加载properties文件🐇第三方bean属性优化🐇读取单个属性 学习来源:黑马程序员SSM框架教程_SpringSpringMVCMa…...
Qt桌面应用开发 第六天(鼠标事件 定时器事件 定时器类 事件分发器 事件过滤器)
目录 1.1鼠标进入和离开enterEvent\leaveEvent 1.2鼠标按下释放和移动mousePressEvent\mouseReleaseEvent\mouseMoveEvent 1.3定时器事件timerEvent 1.4定时器类QTimer 1.5事件分发器event 1.6事件过滤器eventFilter 1.1鼠标进入和离开enterEvent\leaveEvent 事件&#x…...
MySQL-存储过程
目录 一、存储过程定义 二、存储过程创建 1、变量的定义 2、存储过程的分支语句 2.1 双分支IF语句 2.2 多分支IF语句 3、存储过程的循环语句 三、存储过程调用 四、存储过程显示和删除 一、存储过程定义 存储过程(Stored Procedure)是一种在数…...
数据指标与标签在数据分析中的关系与应用
导读:分享数据指标体系的文章很多,但讲数据标签的文章很少。实际上,标签和指标一样,是数据分析的左膀右臂,两者同样重要。实际上,很多人分析不深入,就是因为缺少对标签的应用。今天系统的讲解下…...
LWE详细介绍
LWE问题,即学习误差问题(Learning With Errors problem),是密码学中一个重要的硬问题,尤其在后量子密码学中占有核心地位。LWE问题由Regev在2005年提出,其设计基于格理论,被认为是一个在平均情况…...
Linux系统Docker部署开源在线协作笔记Trilium Notes与远程访问详细教程
目录 ⛳️推荐 前言 1. 安装docker与docker-compose 2. 启动容器运行镜像 3. 本地访问测试 4.安装内网穿透 5. 创建公网地址 6. 创建固定公网地址 ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下…...
【Spring MVC】初步了解Spring MVC的基本概念与如何与浏览器建立连接
前言 🌟🌟本期讲解关于SpringMVC的基础概念,以及如何实现与浏览器的连接,参数的传递~~~ 🌈感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客 🔥 你的点赞就是小编不断更新的最大动力 …...
游戏AI实现-决策树
代码实现: 定义一个决策树节点 class DecisionTreeNode{public DecisionTreeNode(){} } 定义一个行为类: class Action : DecisionTreeNode{ } 定义一个决策类: class Decision : DecisionTreeNode{ } 应用: 参考书…...
9个最佳WordPress PDF插件(查看器、嵌入和下载)
在过去的几年里,我们一直在使用不同的 PDF 插件在我们的网站上创建、编辑和嵌入文档。 然而,经过多次尝试和错误,我们意识到并不是每个插件都是相同的。事实上,为您的企业或电子商务网站选择合适的 PDF 插件可能是一项艰巨的任务…...
odoo18中模型的常用字段类型
字段的公共属性: Char 字符类型,对应数据库中varchar类型,除了通用类型外接收另外两个参数: size: 字符长度,超出的长度将被截断 trim: 默认True,是否字段值应该被去空白。 Text 文本类型,对应数据库…...
VUE 指令 事件绑定,.stop阻止冒泡
1、VUE 的模板语法和指令 目的增强html的功能 所有的指令以自定义属性的方式去写 v-xxx ,指令就是vue提供给我们能够更方便将数据和页面展示出来的操作,具体就是以数据去驱动DOM ,简化DOM操作的行为。 2、内容渲染指令 ① {{}} 模板渲染(模板引擎&am…...
在Excel中处理不规范的日期格式数据并判断格式是否正确
有一个Excel表,录入的日期格式很混乱,有些看着差不多,但实际多一个空格少一个字符很难发现,希望的理想格式是 1980-01-01,10位,即:“YYYY-mm-dd”,实际上数据表中这样的格式都有 19…...
webpack基础配置
文章目录 一、默认入口和默认出口二、资源配置三、输出文件3.1 多文件入口3.2 HtmlWebpackPlugin插件 四、环境4.1 环境变量4.2 热更新 五、代码分离5.1 公共模块5.2 懒加载5.3 预获取/预加载模块 六、缓存七、Tree Shaking八、公共路径 webpack 是一个用于现代 JavaScript 应用…...
stm32利用LED配置基础寄存器+体验滴答定时器+hal库环境配置
P1 LED控制与流水灯效果实现 概述 大家好,今天我们来学习一下如何在STM32上控制LED灯,并且实现一个流水灯的效果。这不仅是一个基础的实践,也是嵌入式开发中非常常见的需求。 LED控制 1. LED初始化 首先,我们需要对LED灯对应…...
Spark RDD sortBy算子执行时进行数据 “采样”是什么意思?
一、sortBy 和 RangePartitioner sortBy 在 Spark 中会在执行排序时采用 rangePartitioner 进行分区,这会影响数据的分区方式,并且这一步骤是通过对数据进行 “采样” 来计算分区的范围。不过,重要的是,sortBy 本身仍然是一个 tr…...
Ubuntu24.04下的docker问题
按官网提示是可以安装成功的,但是curl无法使用https下载,会造成下述语句执行失败 # Add Dockers official GPG key: sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https…...
Tri Mode Ethernet MAC IP核详解
本文对 Vivado 的三速 MAC IP 核(Tri Mode Ethernet MAC,TEMAC)进行介绍。 在自行实现三速以太网 MAC 控制器时,GMII/RGMII 接口可以通过 IDDR、ODDR 原语实现,然而实际使用中自己实现的模块性能不是很稳定(…...
【U盘车载音乐】某宝198的3068首车载专用音乐合集【高音质】24G
「【U盘车载音乐】某宝198的3068首车载专用音乐合集【高音质】24G」 复制下方口令,打开最新版「夸克APP」即可获取保存(防止和谐!!!) 口令: 动作懿范鉴真渡多好备用口令: /~19dc35…...
android 实现答题功能
一、效果 二、实现思路 1、界面实现 实现起来其实不难,首先我们可以看到,界面是由答题进度、题目、选项ABCD组成,现在就是要考虑实现方式,答题进度可以使用Textviewprogressbar实现,题目直接使用Textview,…...
JSONP处理跨域请求
JSONP 背景 由于浏览器存在安全策略,所以当访问的请求中的协议、域名、端口其中一个与本站不同时就会形成跨域,这里介绍一种比较简单的方案——jsonp。 原理 浏览器对 script、img这些带有src属性的的标签在发送请求时是不会触发跨域的校验ÿ…...
栈的应用,力扣394.字符串解码力扣946.验证栈序列力扣429.N叉树的层序遍历力扣103.二叉树的锯齿形层序遍历
目录 力扣394.字符串解码 力扣946.验证栈序列 力扣429.N叉树的层序遍历 力扣103.二叉树的锯齿形层序遍历 力扣394.字符串解码 看见括号,由内而外,转向用栈解决。使用两个栈处理,一个用String,一个用Integer 遇到数字:提取数字放入到数字栈…...
华为手机启用ADB无线调试功能
打开开发者模式,勾选USB调试,和“仅充电”模式下允许ADB调试 确认 设置添加adb路径到PATH变量 使用adb查看安卓设置 切换为无线模式: 查看手机IP...
HTML 元素类型介绍
目录 1. 块级元素(Block-level Elements) 2. 行级元素(Inline Elements) 3. 行内块级元素(Inline-block Elements) 4. 表格相关元素 5. 列表相关元素 6. 表单相关元素 示例代码 示例效果 编辑 …...
前端反向代理的配置和實現
反向代理是位於客戶端和服務器之間的一個中間層,它代表客戶端向伺服器發起請求,然後將伺服器的回應返回給客戶端。與傳統的正向代理不同,反向代理是由伺服器端配置的,客戶端通常不知道它的存在。在前端開發中,反向代理…...
日志分析工具
一、nginx_log_analysis工具 1、工具下载,http://linux5588.blog.51cto.com/,它是用python语言写的,只是用来分析nginx日志,它的输出比较简单,以IP为主,可以查看每个IP的访问的流量,次数&#…...
python: Serialize and Deserialize complex JSON using jsonpickle
# encoding: utf-8 # 版权所有 2024 ©涂聚文有限公司 # 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎 # Serialize and Deserialize complex JSON in Python # 描述:pip install jsonpickle https://github.com/jsonpi…...
【分布式锁解决超卖问题】setnx实现
目录 使用场景的描述 并发安全问题 悲观锁与乐观锁问题 一人一单的问题 服务器负载均衡问题 分布式锁 分布式锁的实现 获取锁 释放锁 实现思路 误删情况的分析 解决误删的方法 代码优化 分布式锁的原子性分析 文章代码地址:分布式锁1.0 使用场景的描述 今天的主人…...
【MySQL实战45讲笔记】基础篇——事务隔离
系列文章 基础篇——MySQL 的基础架构 基础篇——redo log 和 binlog 目录 系列文章1. 事务隔离1.1 隔离性与隔离级别1.2 如何实现事务隔离1.3 事务的启动方式1.4 思考: 使用什么方案来避免长事务 1. 事务隔离 简单来说,事务就是要保证一组数据库操作&…...
RFdiffusion calculate_igso3函数解读
calculate_igso3 函数旨在对 IGSO(3) 分布的概率密度函数 (PDF)、累积分布函数 (CDF)、以及相关统计量进行数值近似计算,特别用于预计算以加速后续操作(例如采样、反向扩散等)。 calculate_igso3函数源代码: def calculate_igso3(*, num_sigma, num_omega, min_sigma, ma…...
Vue3 + Vite 项目引入 postcss + tailwindcss
一、PostCSS 1. 简介 PostCSS 是一个强大的 CSS 处理工具,它本身是一个工具库,但其核心功能是通过插件扩展,来对 CSS 进行编译、转换和优化。它适用于现代 CSS 开发,提供更灵活、高效的方式来处理样式表。 2. 主要作用 增强 CS…...
AI Large Language Model
AI 的 Large Language model LLM , 大语言模型: 是AI的模型,专门设计用来处理自然语言相关任务。它们通过深度学习和庞大的训练数据集,在理解和生成自然语言文本方面表现出色。常见的 LLM 包括 OpenAI 的 GPT 系列、Google 的 PaLM 和 Meta…...
Linux系统性能优化技巧
系统性能优化 在当今的信息技术领域,Linux系统的性能优化变得越来越重要。随着Linux操作系统的广泛应用,从桌面环境到大型服务器集群,性能优化不仅可以提升系统的响应速度和吞吐量,还能降低资源消耗,从而延长硬件使用…...
基于CNN+RNNs(LSTM, GRU)的红点位置检测(pytorch)
1 项目背景 需要在图片精确识别三跟红线所在的位置,并输出这三个像素的位置。 其中,每跟红线占据不止一个像素,并且像素颜色也并不是饱和度和亮度极高的红黑配色,每个红线放大后可能是这样的。 而我们的目标是精确输出每个红点的…...
【AI系统】GPU 架构回顾(从2018年-2024年)
Turing 架构 2018 年 Turing 图灵架构发布,采用 TSMC 12 nm 工艺,总共 18.6 亿个晶体管。在 PC 游戏、专业图形应用程序和深度学习推理方面,效率和性能都取得了重大进步。相比上一代 Volta 架构主要更新了 Tensor Core(专门为执行…...
Vue 内置组件 keep-alive 中 LRU 缓存淘汰策略和实现
LRU(Least Recently Used,最近最少使用)是通过记录缓存项的访问顺序来决定淘汰的策略:当缓存满时,移除最久未被使用的项。 核心概念: 缓存存储:使用 Map 存储键值对,用于快速访问缓…...
联邦学习安全聚合算法综述(论文解析)以及如何确定自己研究方向的方法
自己写相关论文的方法: 可以重点看看综述类论文的未来研究方向和引言中前人已经做过的内容 联邦学习安全聚合算法综述 auth:江萍 1 通讯作者 李芯蕊 1 赵晓阳 2 杭永凯 摘要 摘要:随着深度学习技术的发展,人工智能在社会的各个方面有着重要…...
【JAVA 笔记】12 带有数据库文件的完整的JDBC访问例子,命令行界面
【JAVA 笔记】12 带有数据库文件的完整的JDBC访问例子,命令行界面 代码结构解析 1. 导入必要的包 import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; import java.sql.PreparedStatement; impo…...
基于java+SpringBoot+Vue的在线宠物用品交易网站设计与实现
项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: Springboot mybatis Maven mysql5.7或8.0等等组成&#x…...
智慧社区管理系统平台提升物业运营效率与用户体验
内容概要 智慧社区管理系统平台是一个集成了多项功能的综合性解决方案,旨在通过先进的技术手段提升物业管理的效率和居民的生活质量。该平台不仅关注物业运营的各个方面,还强调用户体验的重要性。随着科技的发展,社区管理方式正发生着翻天覆…...
el-table-column自动生成序号在序号前插入图标
实现效果: 代码如下: 在el-table里加入这个就可以了,需要拿到值可以用scope.$index <el-table-column type"index" label"序号" show-overflow-tooltip"true" min-width"40">…...
深度学习之目标检测的常用标注工具
1 LabelImg LabelImg 是一款开源的图像标注工具,标签可用于分类和目标检测,它是用 Python 编写的,并使用Qt作为其图形界面,简单好用。注释以 PASCAL VOC 格式保存为 XML 文件,这是 ImageNet 使用的格式。 此外&…...
「Mac玩转仓颉内测版21」基础篇1 - 仓颉程序的基本组成
本篇将系统介绍Cangjie编程语言中程序的基本组成部分,涵盖 main 函数的定义、包与模块的使用、变量类型、作用域和代码结构原则,帮助开发者理解Cangjie程序的整体结构。 关键词 程序入口点main函数包与模块变量类型与作用域值类型与引用类型代码结构与规…...
【计算机网络】解决bind error
服务器有时可以立即重启,有时候无法立即重启 — bind error 首先要知道:四次挥手动作完成之后,主动断开连接的一方要维持一段时间的TIME_WAIT bind error原因:因为是服务器主动断开的,所以服务器要去维持TIME_WAIT状…...
【SVN和GIT】版本控制系统详细下载使用教程
文章目录 ** 参考文章一、什么是SVN和GIT二、软件使用介绍1 SVN安装1.1 服务端SVN下载地址1.2 客户端SVN下载地址2 SVN使用2.1 服务端SVN基础使用2.1.1 创建存储库和用户成员2.1.2 为存储库添加访问人员2.2 客户端SVN基础使用2.2.1 在本地下载库中的内容2.2.2 版本文件操作--更…...
初识 Pynecone:构建现代化 Web 应用的 Python 框架
初识 Pynecone:构建现代化 Web 应用的 Python 框架 引言 在 Web 开发的世界里,Python 是后端开发的常客,但如果想用 Python 直接构建现代化的前端应用会怎样?这正是 Pynecone 框架的用武之地!Pynecone 是一个全栈 Pyt…...
Go-RPC关键指标分析与企业实践
1.稳定性-保障策略 熔断:保护调用方 限流:保护被调用方 超时控制:避免浪费 2.稳定性-请求成功率(用重复发送 负载均衡) 3.稳定性-长尾请求(用备份请求) 4.稳定性-注册中间件 易用性:…...
社交电商专业赋能高校教育与产业协同发展:定制开发AI智能名片及2+1链动商城小程序的创新驱动
摘要:本文围绕社交电商有望成为高校常态专业这一趋势展开深入探讨,剖析国家政策认可下其学科发展前景,着重阐述在专业建设进程中面临的师资短缺及实践教学难题。通过引入定制开发AI智能名片与21链动商城小程序,探究如何借助这些新…...