当前位置: 首页 > ai >正文

【MySQL】剖析事务和锁

 🔥个人主页: 中草药

 🔥专栏:【MySQL】探秘:数据库世界的瑞士军刀 


一、事务

回顾概念

MySQL中的事务(Transaction)是数据库操作的基本单位,它能确保一系列操作要么全部成功,要么全部失败,以此来维护数据的一致性和完整性。事务是关系型数据库中的一个核心特性,特别是在处理金融、银行、电子商务等领域中对数据准确性要求极高的应用场景时尤为重要,比如转账。以下是MySQL中事务的几个关键概念和操作:

事务的ACID特性*

  1. 原子性(Atomicity):事务被视为一个不可分割的最小工作单元,事务中的所有操作要么全部执行,要么都不执行,如果事务执行过程发生错误,会回滚(Rollback)到事务开始的状态。
  2. 一致性(Consistency):事务执行前后,数据库的状态都必须保持一致,即符合所有的预定义规则。
  3. 隔离性(Isolation):多个事务并发执行时,彼此之间互不影响,仿佛是在串行执行一样。MySQL提供了多种事务隔离级别来控制这一特性。下文做详细概述:
  4. 持久性(Durability):一旦事务提交,其结果就会永久保存在数据库中,即使系统发生故障也不会丢失。

通过以下语句可以完成对事务的控制:

  • START TRANSACTION 或 BEGIN 开始一个新的事务;
  • COMMIT 提交当前事务,并对更改持久化保存;
  • ROLLBACK 回滚当前事务,取消其更改;
  • SET autocommit 禁用或启用当前会话的默认自动提交模式,autocommit 是一个系统变量,可以通过选项指定也可以通过命令行设置 --autocommit[={OFF|ON}]

注意:只要使用 START TRANSACTION 或 BEGIN 开启事务,必须要通过 COMMIT 提交才会持久化,与是否设置 SET autocommit 无关。

实现原子性

        在一个事务的执行过程中,如果多条DML语句顺利执行,那么结果最终会写入数据库;如果在事务的执行过程中,其中一条DML语句出现异常,导致后面的语句无法继续执行或即使继续执行也会导致数据不完整、不一致,这时前面执行的语句已经对数据做了修改,如果要保证一致性,就需要对之前的修改做撤销操作,这个撤销操作称为回滚 rollback,如下图所示:

        在事务执行每个DML之前,把原始数据记录在一个日志里,做为回滚的依据,这个日志称为 Undo Log(回滚日志或撤销日志),在不考虑缓存和刷盘的条件下,执行过程如下所示:

        当需要回滚操作时,MySQL根据操作类型,在Insert Undo链或Update Undo链中读取相应的日志记录,并反向执行修改,使数据还原,完成回滚。

        通过 Undo Log 实现了数据的回滚操作,这时就可以保证在事务成功的时候全部的SQL语句都执行成功,在事务失败的时候全部的SOL语句都执行失败,实现在原子性。 

实现持久性

        提交的事务要把数据写入(持久化到)存储介质,比如磁盘。

        在正常情况下大多没有问题,可是在服务器崩溃或突然断电的情况下,一个事务中的多个修改操作,只有一部分写入了数据文件,而另一部分没有写入,如果不做针对处理的话,就会造成数据的丢失,从而导致数据不完整,也就不能保证一致性。

        在真正写入数据文件之前,MySQL会把事务中的所有DML操作以日志的形式记录下来,以便在服务器下次启动的时候进行恢复操作,恢复操作的过程就是把日志中没有写到数据文件的记录重新执行-遍,保证所有的需要保存的数据都持久化到存储介质中,我们把这个日志称为 Redo Log(重做日志);生成重做日志是保证数据一致性的重要环节。在持久化的处理过程中,还包括缓冲池、Doublewrite Buffer(双写缓冲区)、 Binary Log(二进制日志)等。

实现隔离性

        多线程在并发执行的过程中,多个线程对同一个共享变量进行修改时,在不加限制的情况下会出现线程安全问题,我们解决线程安全问题时,一般的做法是通过对修改操作进行加锁;同理,多个事务在对同一个表中的同一条数据进行修改时,如果要实现事务间的隔离也可以通过锁来完成,在MySQL中常见的锁包括:读锁,写锁,行锁,间隙锁,Next-Key锁等,不同的锁策略联合多版本并发控制可以实现事务间不同程度的隔离,称为事务的隔离级别;不同的隔离级别在性能和安全方面做了取舍,有的隔离级别注重并发性,有的注重安全性,有的则是并发和安全适中;在MySOL的InnoDB引擎中事务的隔离级别有四种,分别是:

  • 读未提交(READ UNCOMMITTED):性能最高,但是最不安全
  • 读已提交(READ COMMITTED):解决了脏读问题,但不可重复读和幻读仍可能发生。
  • 可重复读(REPEATABLE READ):事务内多次读取同一数据结果一致,即使其他事务已提交修改。解决了脏读和不可重复读的问题,但在标准的SQL隔离级别定义中,理论上幻读仍可能发生。然而,在MySQL的InnoDB引擎中,通过多版本并发控制(MVCC)机制,实际上很大程度上避免了幻读的发生,他同时也是MySQL的默认级别。
  • 串行化(SERIALIZABLE):通过强制事务串行执行,解决了所有三种问题,但这是以牺牲并发性为代价的。

事务的隔离性通过锁和MVCC共同实现的

多版本并发控制 MVCC

MySQL 的 MVCC(Multi-Version Concurrency Control,多版本并发控制) 是一种用于提高数据库并发性能的机制,

实现原理

        MVCC的实现是基于 Undo Log 版本链和 ReadView 来完成的,Undo Log做为回滚的基础,在执行Update或Delete操作时,会将每次操作的上一个版本记录在UndoLog中,每条UndoLog中都记录一个叫做 roll_pointer 的引用信息,通过 roll_pointer 就可以将某条数据对应的Undo Log组织成一个Undo链,在数据行的头部通过数据行中的 roll_pointer 与Undo Log中的第一条日志进行关联,这样就构成一条完整的数据版本链,如下图所示

ReadView

Read View 是事务执行快照读(Snapshot Read 事务使用select查询数据)时生成的当前数据库状态的视图,里面记录了该版本链的一些统计值,以便于后续查询处理无需遍历所有版本链

它决定了事务能看到哪些版本的数据,核心包含以下信息:

  • m_ids:当前所有活跃事务的集合

  • m_low_limit_id:下一个将被分配的事务ld,也就是 版本链头的事务Id +1

  • m_up_limit_id:活跃事务集合中最小事务ld

  • m_creator_trx_id:创建当前 ReadView的事务ld

举例

接下来找到版本链头,从链头开始遍历所有版本,根据四步查找规则,判断每个版本:

  • 第一步:判断该版本是否为当前事务创建,若m_creator_trx_id等于该版本事务 id,意味着读取自己修改的数据,可以直接访问,如果不等则到第二步。
  • 第二步:若该版本事务 Id < m_up_limit_id(最小事务 Id),意味着该版本在 ReadView 生成之前已经提交,可以直接访问,如果不是则到第三步。
  • 第三步:或该版本事务 Id >= m_low_limit_id(最大事务 Id),意味着该版本在 ReadView 生成之后才创建,所以肯定不能被当前事务访问,所以无需第四步判断,直接遍历下一个版本,如果不是则到第四步。
  • 第四步:若该版本事务 Id 在m_up_limit_id(最小事务 Id) 和m_low_limit_id(最大事务 Id) 之间,同时该版本不在活跃事务列表中,意味着创建 ReadView 时该版本已经提交,可以直接访问,如果不是则遍历并判断下一个版本。

这样从版本链头遍历判断到版本链尾,找到首个符合要求的版本即可,就可以实现查询到的结果都是已经提交事务的数据,解决了脏读问题。 

MVCC 是否可以解决不可重复读与幻读

  • 首先幻读无法通过 MVCC 单独解决
  • 对于不可重复读问题,在事务中的第一个查询时创建一个 ReadView,后续查询都是用这个 ReadView 进行判断,所以每次的查询结果都是一样的,从而解决不可重复读问题,在 REPEATABLE READ 可重复读隔离级别下就采用的这种方式
  • 如果事务每次查询都创建一个新的 ReadView,这样就会出现不可重复读问题,在 READ COMMITTED 读已提交的隔离级别下就是这种实现方式

查看并设置隔离级别

事务的隔离级别分为全局作用域和会话作用域

查找

--全局作用域
SELECT @@GLOBAL.transaction_isolation--会话作用域
SELECT @@SESSION.transaction_isolation

 设置

# 通过GLOBAL|SESSION分别指定不同作⽤域的事务隔离级别
SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level|access_mode;# 隔离级别
level: {REPEATABLE READ # 可重复读| READ COMMITTED # 读已提交| READ UNCOMMITTED # 读未提交| SERIALIZABLE # 串⾏化
}# 访问模式
access_mode: {READ WRITE # 表⽰事务可以对数据进⾏读写| READ ONLY # 表⽰事务是只读,不能对数据进⾏读写
}# ⽰例
# 设置全局事务隔离级别为串⾏化
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
# 设置会话事务隔离级别为串⾏化
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
# 如果不指定任何作⽤域,设置将在下⼀个事务开始⽣效
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

通过选项文件指定事务的隔离级别,以便MySQL启动的时候读取并设置

[mysqld]
transaction-isolation = REPEATABLE-READ     # 隔离级别为可重复读
transaction-read-only = OFF     # 关闭只读意味着访问模式为读写

通过SET语法设置系统变量的方式设置事务的隔离级别

# ⽅式⼀
SET GLOBAL transaction_isolation = 'SERIALIZABLE';
# 注意使⽤SET语法时有空格要⽤"-"代替
SET SESSION transaction_isolation = 'REPEATABLE-READ'; # ⽅式⼆
SET @@GLOBAL.transaction_isolation='SERIALIZABLE';
# 注意使⽤SET语法时有空格要⽤"-"代替
SET @@SESSION.transaction_isolation='REPEATABLE-READ';

读未提交(Read Uncommitted)

特点:事务可以读取其他未提交事务的修改(“脏读”)。

实现:

  • 读取时:不加任何锁,直接读取版本链中的最新版本,也就是当前读,可能会出现脏读,不可重复读、幻读问题;
  • 更新时:加共享行锁(S锁),事务结束时释放,在数据修改完成之前,其他事务不能修改当前数据,但可以被其他事务读取。

适用场景:统计类业务(如实时大盘监控),对数据准确性要求极低,但需要极高性能。

读已提交(Read Committed)

特点:事务只能读取其他事务已提交的数据。

解决的问题:脏读。

遗留问题

  • 不可重复读:事务内多次读取同一数据,可能因其他事务提交修改而结果不同。

  • 幻读:其他事务新增或删除数据,导致当前事务两次查询结果行数不同。

实现机制

  • 读取时:不加锁,但使用快照读,即按照 MVCC机制读取符合 ReadView要求的版本数据每次查询都会构造一个新的 ReadView,可以解决脏读,但无法解决不可重复读和幻读问题;

  • 更新时:加独占行锁(X),事务结束时释放,数据在修改完毕之前,其他事务不能修改也不能读取这行数据。

适用场景:多数OLTP系统(如电商订单处理),平衡性能与一致性。

可重复读(Repeatable Read)

特点:事务内多次读取同一数据结果一致,即使其他事务已提交修改。

解决的问题:脏读、不可重复读。

遗留问题

  • 幻读:其他事务新增数据可能导致当前事务两次查询结果行数不同。

实现机制

  • 读取时:不加锁,也使用快照读,按照MVCC机制读取符合ReadView要求的版本数据,但无论事务中有几次查询,只会在首次查询时生成一个ReadView,可以解决脏读、不可重复读,配合Next-Key行锁可以解决一部分幻读问题;

  • 更新时:加Next-Key行锁,事务结束时释放,在一个范围内的数据修改完成之前,其他事务不能对这个范围内的数据进行修改、插入和删除操作,同时也不能被查询。

数据库差异

  • MySQL默认隔离级别:通过间隙锁避免幻读。

  • Oracle/PostgreSQL:实际通过MVCC实现,可能允许幻读。

适用场景:财务系统、库存管理(需保证事务内数据稳定)。

串行化(Serializable)

特点:事务串行执行,完全隔离。

解决的问题:脏读、不可重复读、幻读。

实现机制

  • 读取时:加共享表锁读取版本链中的最新版本,事务结束时释放;

  • 更新时:加独占表锁,事务结束时释放,完全串行操作,可以解决所有事务问题。

缺点:并发性能极低,容易导致死锁。

适用场景:银行转账、证券交易(绝对数据一致性优先)。

二、锁

        实现事务隔离级别的过程中用到了锁,所谓锁就是在事务A修改某些数据时,对这些数据加一把锁,防止其他事务同时对这些数据执行修改操作;当事务A完成修改操作后,释放当前持有的锁,以便其他事务再次上锁执行对应的操作。

锁信息

        锁的信息包括锁的请求(申请),锁的持有以及阻塞状态等等,都保存在 performance_schema库的 data_locks 表中,可以通过以下方式查看,

mysql> SELECT * FROM performance_schema.data_locks\G
*************************** 1. row ***************************ENGINE: INNODB # 持有或请求锁的存储引擎ENGINE_LOCK_ID: 139664434886512:1059:139664350547912 # 存储引擎持有或请求
的锁的ID
ENGINE_TRANSACTION_ID: 2569 # 请求锁的事务对应的存储引擎内部IDTHREAD_ID: 46 # 创建锁的会话的线程IDEVENT_ID: 12 # 请求锁的事件IDOBJECT_SCHEMA: test_db # 锁定表所在的数据库OBJECT_NAME: account # 被锁定表的名称PARTITION_NAME: NULL # 锁定分区的名称; 没有使⽤表分区时为NULLSUBPARTITION_NAME: NULL # 锁定⼦分区的名称; 没有使⽤⼦分区时为NULLINDEX_NAME: NULL # 锁定索引的名称; 没有使⽤索引时为NULL
OBJECT_INSTANCE_BEGIN: 139664350547912 # 锁在内存中的地址LOCK_TYPE: TABLE # 锁的类型LOCK_MODE: IX # 锁的模式,表⽰如何请求锁LOCK_STATUS: GRANTED # 锁请求的状态; GRANTED(持有),WAITING(等待)LOCK_DATA: NULL # 与锁相关的数据(如果有)
*************************** 2. row ***************************ENGINE: INNODBENGINE_LOCK_ID: 139664434886512:2:4:1:139664350544872
ENGINE_TRANSACTION_ID: 2569THREAD_ID: 46EVENT_ID: 12OBJECT_SCHEMA: test_db OBJECT_NAME: account
PARTITION_NAME: NULLSUBPARTITION_NAME: NULLINDEX_NAME: GEN_CLUST_INDEX
OBJECT_INSTANCE_BEGIN: 139664350544872LOCK_TYPE: RECORD # 锁的类型LOCK_MODE: X # 锁的模式,表⽰如何请求锁LOCK_STATUS: GRANTED # 锁请求的状态; LOCK_DATA: supremum pseudo-record

锁类型

        锁类型依赖于存储引擎,在InnoDB存储引擎中按照锁的粒度分为,行级锁 RECORD和表级锁TABLE:

  • 行级锁也叫行锁,是对表中的某些具体的数据行进行加锁;
  • 表级锁也叫表锁,是对整个数据表进行加锁。

在之前版本的BDB存储引擎中还支持页级锁,锁定的是一个数据页,MSOL8中没有页级锁

锁模式

        锁模式,用来描述如何请求(申请)锁,分为共享锁(S)、独占锁(X)、意向共享锁(IS)、意向独占锁IX)、记录锁、间隙锁、Next-Key锁、AUTO-INC 锁、空间索引的谓词锁等

共享锁和独占锁-Shared and Exclusive Locks

共享锁(S锁):允许持有该锁的事务读取表中的一行记录,同时允许其他事务在锁定行上加另一个共享锁并读取被锁定的对象,但不能对其进行写操作。

SELECT * FROM table_name WHERE condition FOR SHARE;  -- MySQL语法,显式加共享锁

独占锁(X锁):也叫排他锁,允许持有该锁的事务对数据行进行更新或删除,同时不论其他事务对锁定行进行读取或修改都不允许对锁定行进行加锁;

SELECT * FROM table_name WHERE condition FOR UPDATE;  -- MySQL语法,显式加独占锁
UPDATE table_name SET column = value WHERE condition;  -- 隐式加独占锁

如果事务T1持有R行上的共享锁(S),那么事务T2请求R行上的锁时会有如下处理:

  • T2请求S锁会立即被授予,此时T1和T2都对R行持有S锁;
  • T2请求X锁不能立即被授予,阻塞到T1释放持有的锁

如果事务T1持有R行上的独占锁(X),那么T2请求R行上的任意类型锁都不能立即被授予,事务T2必须等待事务T1释放R行上的锁。

读锁是共享锁的一种实现,写锁是排他锁的一种实现

意向锁-Intention Locks

        意向锁(Intention Lock)是数据库中一种表级锁,它支持多粒度锁,允许行锁和表锁共存,用于辅助管理行级锁与表级锁之间的兼容性,避免在复杂的加锁场景下进行全表扫描来检查锁冲突,来提高并发性能和锁管理效率,它并不是真正意义上的加锁,本质上是一种锁信息,而只是在 data_locks 中记录事务以后要对表中的哪一行加哪种类型的锁(共享锁或排他锁),意向锁分为两种:。

  • 意向共享锁(IS):表示事务打算对表中的单个行设置共享锁。
  • 意向排他锁(IX):表示事务打算对表中的单个行设置排他锁。

在获取意向锁时有如下协议:

  • 在事务获得表中某一行的共享锁(S)之前,它必须首先获得该表上的IS锁或更强的锁。
  • 在事务获得表中某一行的排他锁(X)之前,它必须首先获得该表上的IX锁

意向锁可以提高加锁的性能,在真正加锁之前不需要遍历表中的行是否加锁,只需要查看一下表中的意向锁即可;


        在请求锁的过程中,如果将要请求的锁与现有锁兼容,则将锁授予请求的事务,如果与现有锁冲突,则不会授予;事务将阻塞等待,直到冲突的锁被释放;意向锁与行级锁的兼容性如下表:

XIXSIS
X冲突冲突冲突冲突
IX冲突兼容冲突兼容
S冲突冲突兼容兼容
IS冲突兼容兼容兼容

        除了全表锁定请求之外,意向锁不会阻止任何锁请求;意向锁的主要目的是表示事务正在锁定某行或者正在意图锁定某行。

索引记录锁

索引记录锁也叫记录锁或精准行锁,是数据库中行级锁的一种,是对索引记录进行加锁,而非直接对数据行加锁

--防⽌任何其他事务插⼊、更新或删除值为1的⾏,id为索引列
SELECT * FROM account WHERE id = 1 For UPDATE;

间隙锁

        间隙锁锁定的是索引记录之间的间隙,或者第一个索引记录之前,再或者最后一个索引记录之后的间隙,主要用于防止幻读。如图所示位置,根据不同的查询条件都可能会加间隙锁:

        例如有如下SOL,锁定的是ID(10,20)之间的间隙,注意不包括10和20的行,目的是防止其他事务将ID值为15的列插入到列 account 表中(无论是否已经存在要插入的数据列),因为指定范围值之间的间隙被锁定了;

        可以跨越单个或多个索引值

        不同事务的间隙锁可以共存,一个事务的间隙锁不会阻止另一个事务在相同的间隙上使用间隙锁;共享间隙锁和独占间隙锁之间没有区别。

        当事务隔离级别设置为 READCOMMITTED 时间隙锁会被禁用,对于搜索和索引扫描不再使用间隙锁定

临键锁-Next-Key Locks

Next-key锁是索引记录锁和索引记录之前间隙上间隙锁的组合

        InnoDB搜索或扫描一个表的索引时,执行行级锁策略,具体方式是:在扫描过程中遇到的索引记录上设置共享锁或排他锁,因此,行级锁策略实际上应用的是索引记录锁。索引记录上的 next-key 锁也会影响该索引记录之前的"间隙",也就是说,next-key 锁是索引记录锁加上索引记录前面的间隙锁。如果一个会话对索引中的一条记录R具有共享锁或排他锁,则另一个会话不能在索引记录R之前的空白中插入新的索引记录行。

        默认情况下,REPEATABLE READ 事务隔离级别开启 next-key 锁并进行搜索和索引扫描,可以防止幻象行,从而解决幻读问题

插入意向锁

        在并发插入时,允许不同事务在同一索引间隙中插入不冲突的行,从而提高性能。

        插入意向锁是一个特殊的间隙锁,在向索引记录之前的间隙进行insert操作插入数据时使用,如果多个事务向相同索引间隙中不同位置插入记录,则不需要彼此等待。假设已经存在值为10和20的索引记录,两个事务分别尝试插入索引值为15和16的行,在获得插入行上的排他锁之前,每个事务都用插入意向锁锁住10到20之间的间隙,但不会相互阻塞,因为他们所操作的行并不冲突;

 与普通间隙锁的区别

  • 普通间隙锁:直接阻止其他事务在间隙内进行任何插入。

  • 插入意向锁:仅表明“插入意图”,允许其他事务在不同位置插入,仅在实际冲突时阻塞(如行锁竞争)。

AUTO-INC Locks

        AUTO-INC锁也叫自增锁是一个表级锁,服务于配置了 AUTO INCREMENT 自增列的表。在插入数据时会在表上加自增锁,并生成自增值,同时阻塞其他的事务操作,核心目标是保证自增字段的唯一性和连续性。需要注意的是,当一个事务执行新增操作已生成自增值,但是事务回滚了,申请到的主键值不会回退,这意味着在表中会出现自增值不连续的情况。


死锁

定义

死锁是数据库并发控制中的一种现象,指两个或多个事务(或进程)因争夺资源而陷入相互等待的状态,导致所有事务都无法继续执行。例如:

  • 事务A 持有锁1,请求锁2;

  • 事务B 持有锁2,请求锁1;

  • 两者均因对方未释放锁而永久阻塞。


死锁产生的必要条件

死锁的发生需同时满足以下四个条件:*

  1. 互斥(Mutual Exclusion)
    资源(如数据行、锁)一次只能被一个事务独占使用。

  2. 请求与保持(Hold and Wait)
    事务在持有资源的同时,请求新的资源。

  3. 不可抢占(No Preemption)
    已分配给事务的资源不能被强制剥夺,只能由其主动释放。

  4. 循环等待(Circular Wait)
    事务之间形成环形等待链,每个事务都在等待下一个事务释放资源。

InnoDB 对死锁的检测

  • InnoDB 在运行时会对死锁进行检测,当死锁检测启用时 (默认),InnoDB 自动检测事务死锁,并回滚一个或多个事务来打破死锁。InnoDB 尝试选择小事务进行回滚,其中事务的大小由插入、更新或删除的行数决定。
  • 如果系统变量 innodb_table_locks = 1 (默认) 和 autocommit = 0 ,InnoDB 可以检测到表级锁和行级锁级别发生的死锁;否则,无法检测到由 lock TABLES 语句设置的表锁或由非 InnoDB 存储引擎设置的锁,对于无法检测到的死锁,可以通过设置系统变量 innodb_lock_wait_timeout 的值来指定锁的超时时间来解决死锁问题
  • 当超过 200 个事务等待锁资源或等待的锁个数超过 1,000,000 个时也会被视为死锁,并尝试将等待列表的事务回滚。
  • 在高并发系统中,多个线程等待相同的锁时,死锁检测可能会导致性能降性变慢,此时禁用死锁检测并依赖 innodb_lock_wait_timeout 设置进行事务回滚可能性能更高。可以通过设置系统变量 innodb_deadlock_detect [={OFF|ON}] 禁用死锁检测。

如何避免死锁

MySQL 是一个多线程程序,死锁的情况大概率会发生,但并不可怕,除非频繁出现,导致某些事务无法运行。

InnoDB 使用自动行级锁,即使在只插入或删除单行的事务中,也可能出现死锁。这是因为插入或删除行不是真正的 “原子” 操作,同时会对索引记录进行修改并设置锁。

使用以下技术来处理死锁并降低发生死锁的可能性:

  • 使用事务而不是使用 LOCK TABLES 语句手动加锁,并使用 innodb_lock_wait_timeout 变量设置锁的超时时间,保证任何情况下锁都可以自动释放。
  • 经常使用 SHOW ENGINE INNODB STATUS 命令来确定最近一次死锁的原因。这可以帮助我们修改应用程序以避免死锁。
  • 如果出现频繁的死锁警告,可以通过启用 innodb_print_all_deadlocks 变量来收集调试信息。对于死锁的信息,都记录在 MySQL 错误日志中,调试完成后记得禁用此选项。
  • 如果事务由于死锁而失败,记得重新发起事务,再执行一次。
  • 尽量避免大事务,保持事务粒度小且持续时间短,这样事务之间就不容易发生冲突,从而降低发生死锁的概率。
  • 修改完成后立即提交事务也可以降低死锁发生的概率。特别注意的是,不要在一个交互式会话中长时间打开一个未提交的事务。
  • 当事务中要修改多个表或同一表中的不同行时,每次都要以一致的顺序执行这些操作,使事务中的修改操作形成定义良好的队列,可以避免死锁。而不是在不同的位置编写多个类似的 INSERT、UPDATE 和 DELETE 语句。我们写的程序其实就是把一系列操作组织成一个方法或函数。
  • 向表中添加适当的索引,以便查询时扫描更少的索引并设置更少的锁,可以使用 EXPLAIN SELECT 来确定哪些索引用于当前的查询。
  • 使用表级锁防止对表进行并发更新,可以避免死锁,但代价是系统的并发性降低。
  • 如果在查询时加锁,比如 SELECT...FOR UPDATE 或 SELECT...FOR SHARE,尝试使用较低的隔离级别,比如 READ COMMITTED。

要为重活而高兴,不要为死去的忧伤。                                                                ——林清玄

🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀

以上,就是本期的全部内容啦,若有错误疏忽希望各位大佬及时指出💐

  制作不易,希望能对各位提供微小的帮助,可否留下你免费的赞呢🌸 

http://www.xdnf.cn/news/8359.html

相关文章:

  • 疏锦行Python打卡 DAY 9 热力图和子图的绘制
  • 如何备份和恢复Linux系统?
  • RHCSA Linux 系统 硬盘管理
  • linux 内核warn_on/Bug_on
  • 【深度学习-Day 16】梯度下降法 - 如何让模型自动变聪明?
  • 应对进行性核上性麻痹,健康护理铸就温暖防线
  • MATLAB NLP 工具箱 文本预处理教程
  • 四、GPU是如何成为当前电脑中不可或缺的一部分的,opengl在其中起到了什么效果
  • HTA8111 18W内置升压单声道D类音频功放
  • SAP Business One, Web Client: The Advantages of All Worlds
  • 微服务架构下的智能规则解析:Java 解释器模式的高可用实现
  • 【438. 找到字符串中所有字母异位词】
  • 【MySQL】第九弹——索引(下)
  • Unity基础学习(七)Mono中的重要内容(3)协同程序的本质
  • PyQt5安装,在Pycharm上配置以及使用教程
  • 设计模式-备忘录模式
  • 【安装指南】Canal 环境的安装与使用
  • 手写一个简单的线程池
  • SQL实战之索引失效案例详解
  • Python在自动驾驶中的多传感器融合——让智能汽车“看得更清楚”
  • “Agent上车”浪潮来临,谁在引领新一轮的AI座舱交互变革?
  • JMeter 教程:监控性能指标 - 第三方插件安装(PerfMon)
  • SQL SERVER中实现类似LEAST函数的功能,返回多列数据中的最小值
  • 6个月Python学习计划 Day 2
  • python 实现一个完整的基于Python的多视角三维重建系统,包含特征提取与匹配、相机位姿估计、三维重建、优化和可视化等功能
  • Javase易混点专项复习02_static关键字
  • Day125 | 灵神 | 二叉树 | 二叉树中的第K大层和
  • 003-类和对象(二)
  • Ubuntu Linux系统的基本命令详情
  • 李宏毅《机器学习2025》笔记 —— 更新中