Cet article vous apporte des connaissances pertinentes sur mysql, qui présente principalement les caractéristiques ACID de transaction de MySQL et la syntaxe du processus de contrôle de transaction MySQL, et présente des situations anormales pouvant survenir lors du traitement de transactions simultanées, telles que des lectures sales, des lectures fantômes. , lectures non répétables, etc. Enfin, j'introduis le niveau d'isolation des transactions, j'espère qu'il sera utile à tout le monde.
Apprentissage recommandé : Tutoriel d'apprentissage mysql
Dans les scénarios commerciaux réels, comment garantir l'intégrité des opérations est une question importante. Une série d'opérations logiquement liées sont exécutées en séquence. Si une erreur se produit à mi-chemin, elle est exécutée en séquence. est très susceptible de conduire à une confusion des données.
Imaginez la scène d'un retrait d'argent à un guichet automatique. Lorsque nous retirons mille yuans, le guichet automatique crachera mille yuans à la fois une fois le comptage terminé, au lieu de cracher cent yuans à chaque fois. assurer l'intégrité de l'opération, soit mille yuans sont complètement retirés et le solde est déduit, soit aucun centime n'est retiré et le solde reste inchangé, sans incohérence des données causée par une panne de machine au milieu. Une telle opération complète est appelée une transaction
. Toutes les opérations d'une transaction sont soit exécutées avec succès, soit pas du tout. 事务 transaction
,一个事务中的所有操作要么全部成功执行,要么完全不执行。
本文将会介绍 MySQL 的事务 ACID
特性和 MySQL 事务控制流程的语法,并介绍事务并发处理中可能出现的异常情况,比如脏读、幻读、不可重复读等等,最后介绍事务隔离级别。
关于实现事务隔离性的锁和 MVCC,将会在之后的文章进行介绍。
事务处理是一种对必须整批执行的 MySQL 操作的管理机制,在事务过程中,除非整批操作全部正确执行,否则中间的任何一个操作出错,都会回滚 (Rollback)
到最初的安全状态以确保不会对系统数据造成错误的改动。
之前的文章中我们提到过,MySQL 5.5 之后,默认的存储引擎从 MyISAM 替换成了 InnoDB,这其中的一个重要原因就是因为 InnoDB 支持事务,我们用 SHOW ENGINES
来看一下 MySQL 中对各种存储引擎的描述。
事务最重要的四个特性通常被称为 ACID
特性A - Atomicity 原子性
: 一个事务是一个不可分割的最小单位,事务中的所有操作要么全部成功,要么全部失败,没有中间状态。原子性主要是通过事务日志中的回滚日志(undo log)
来实现的,当事务对数据库进行修改时,InnoDB 会根据操作生成相反操作的 undo log,比如说对 insert 操作,会生成 delete 记录,如果事务执行失败或者调用了 rollback,就会根据 undo log 的内容恢复到执行之前的状态。
C - Consistency 一致性
: 事务执行之前和执行之后数据都是合法的一致性状态,即使发生了异常,也不会因为异常引而破坏数据库的完整性约束,比如唯一性约束等。
I - Isolation 隔离性
: 每个事务是彼此独立的,不会受到其他事务的执行影响,事务在提交之前对其他事务不可见。隔离性通过事务的隔离级别来定义,并用锁机制来保证写操作的隔离性,用 MVCC 来保证读操作的隔离性,将在下文详细介绍。
D - Durability 持久性
: 事务提交之后对数据的修改是持久性的,即使数据库宕机也不会丢失,通过事务日志中的重做日志(redo log)
来保证。事务修改之前,会先把变更信息预写到 redo log 中,如果数据库宕机,恢复后会读取 redo log 中的记录来恢复数据。
MySQL 事务控制有几个重要节点,分别是事务的开启,提交,回滚和保存点。
开启事务代表事务开始执行,语句为 START TRANSACTION
或者 BEGIN
,提交事务代表将事务中的所有更新都写到磁盘的物理数据库,事务正常执行结束,语句为 COMMIT
,如果发生异常需要回滚,语句为 ROLLBACK
。要注意的是,一旦事务已经提交,就不能回滚了,因此,在代码执行过程中捕获到异常的时候需要直接执行 rollback 而不是 commit。
比如 A 向 B 转账 100 元的事务:
// 正常执行,提交 BEGIN; # 开启事务 UPDATE account_balance SET balance = balance - 100.00 WHERE account_name = 'A'; UPDATE account_balance SET balance = balance + 100.00 WHERE account_name = 'B'; COMMIT; # 提交事务 // 发生异常,回滚 BEGIN; # 开启事务 UPDATE account_balance SET balance = balance - 100.00 WHERE account_name = 'A'; UPDATE account_balance SET balance = balance + 100.00 WHERE account_name = 'B'; ROLLBACK; # 事务回滚
在复杂场景中,有时我们不需要全盘回滚整个操作,而是分批执行,回滚到某个节点就好了,相当于是在一个大事务下嵌套了若干个子事务,在 MySQL 中可以使用保留点 SAVEPOINT
ACID
de MySQL et la syntaxe du processus de contrôle des transactions MySQL, et présentera des exceptions possibles dans le traitement des transactions simultanées, telles que des lectures sales, des lectures fantômes, des lectures non répétables. , etc. , et enfin introduire le niveau d'isolement des transactions. 🎜🎜À propos des verrous et du MVCC pour réaliser l'isolation des transactions, nous les présenterons dans un article ultérieur. 🎜Revenir en arrière
à un état sûr d'origine pour garantir qu'aucune modification erronée n'est apportée aux données du système. 🎜🎜Nous avons mentionné dans l'article précédent qu'après MySQL 5.5, le moteur de stockage par défaut a été remplacé de MyISAM par InnoDB. L'une des raisons importantes à cela est que InnoDB prend en charge les transactions. un aperçu des descriptions des différents moteurs de stockage dans MySQL. ACID
A - Atomicité
: Une transaction est une plus petite unité indivisible. Toutes les opérations réussissent ou échouent, il y a. pas d'état intermédiaire. L'atomicité est principalement obtenue via le Journal d'annulation (journal d'annulation)
dans le journal des transactions. Lorsqu'une transaction modifie la base de données, InnoDB générera un journal d'annulation de l'opération opposée en fonction de l'opération, telle que l'opération d'insertion. générer un enregistrement de suppression. Si l'exécution de la transaction échoue ou si une restauration est appelée, elle sera restaurée à l'état avant l'exécution en fonction du contenu du journal d'annulation. 🎜🎜C - Cohérence
: Les données sont dans un état de cohérence juridique avant et après l'exécution de la transaction. Même si une exception se produit, les contraintes d'intégrité de la base de données ne seront pas violées en raison d'exceptions, telles que. comme contraintes d'unicité, etc. 🎜🎜I - Isolation
: Chaque transaction est indépendante les unes des autres et ne sera pas affectée par l'exécution des autres transactions. La transaction n'est pas visible par les autres transactions avant d'être validée. L'isolement est défini par le niveau d'isolement de la transaction, et le mécanisme de verrouillage est utilisé pour assurer l'isolement des opérations d'écriture, et MVCC est utilisé pour assurer l'isolement des opérations de lecture, qui seront présentées en détail ci-dessous. 🎜🎜D - Durabilité
: Les modifications apportées aux données après la soumission de la transaction sont persistantes et ne seront pas perdues même si la base de données est en panne via le redo log dans le journal des transactions (redo). log )
pour garantir. Avant que la transaction ne soit modifiée, les informations de modification seront pré-écrites dans le journal redo. Si la base de données tombe en panne, les enregistrements du journal redo seront lus après la récupération pour restaurer les données. 🎜START TRANSACTION
ou BEGIN
. disque, et la transaction est exécutée normalement. À la fin, l'instruction est COMMIT
. Si une exception se produit et qu'une restauration est requise, l'instruction est ROLLBACK
. Il convient de noter qu'une fois qu'une transaction a été validée, elle ne peut pas être annulée. Par conséquent, lorsqu'une exception est interceptée lors de l'exécution du code, l'annulation doit être exécutée directement au lieu de la validation. 🎜🎜Par exemple, A transfère 100 yuans à B : 🎜BEGIN; insert into user_tbl (id) values (1) ; insert into user_tbl (id) values (2) ; ROLLBACK; # 1,2 都没有写入 BEGIN; insert into user_tbl (id) values (1) ; SAVEPOINT s1; insert into user_tbl (id) values (2) ; ROLLBACK TO s1; # 回滚到保留点 s1, 因此 1 成功写入,2 被回滚, 最终结果为 1 RELEASE SAVEPOINT s1; # 释放保留点
SAVEPOINT
dans MySQL. 🎜BEGIN; insert into user_tbl (id) values (1) ; insert into user_tbl (id) values (2) ; ROLLBACK; # 1,2 都没有写入 BEGIN; insert into user_tbl (id) values (1) ; SAVEPOINT s1; insert into user_tbl (id) values (2) ; ROLLBACK TO s1; # 回滚到保留点 s1, 因此 1 成功写入,2 被回滚, 最终结果为 1 RELEASE SAVEPOINT s1; # 释放保留点
顺便提一下,事务有隐式事务(自动提交)和显示事务(必须手动提交)两种,MySQL 默认为隐式事务,会进行自动提交,通过 autocommit
参数来控制。
# 查看变量 SHOW VARIABLES LIKE 'autocommit'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ # 开启自动提交(默认) SET autocommit = 1; # 关闭自动提交 SET autocommit = 0;
在自动提交状态下,如果没有显示的开启事务,那每一条语句都是一个事务,系统会自动对每一条 sql 执行 commit 操作。使用 BEGIN 或 START TRANSACTION 开启一个事务之后,自动提交将保持禁用状态,直到使用 COMMIT 或 ROLLBACK 结束事务之后,自动提交模式会恢复到之前的状态。
关于事务还有另一个参数 completion_type
,默认取值为 0 (NO_CHAIN)
# 查看变量 SHOW VARIABLES LIKE 'completion_type'; +-----------------+----------+ | Variable_name | Value | +-----------------+----------+ | completion_type | NO_CHAIN | +-----------------+----------+
completion_type = 0: 默认值,执行 commit 后不会自动开启新的事务。
completion_type = 1: 执行 commit 时,相当于执行 COMMIT AND CHAIN,自动开启一个相同隔离级别的事务。
completion_type = 2: 执行 commit 时,相当于执行 COMMIT AND RELEASE,提交事务后自动断开服务器连接。
在实际产线环境下,可能会存在大规模并发请求的情况,如果没有妥善的设置事务的隔离级别,就可能导致一些异常情况的出现,最常见的几种异常为脏读(Dirty Read)
、幻读(Phantom Read)
和不可重复读(Unrepeatable Read)
。
脏读指一个事务访问到了另一个事务未提交的数据,如下过程:
不可重复读指一个事务多次读取同一数据的过程中,数据值 内容 发生了改变,导致没有办法读到相同的值,描述的是针对同一条数据 update/delete 的现象,如下过程:
幻读指一个事务多次读取同一数据的过程中,全局数据(如数据行数)发生了改变,仿佛产生了幻觉,描述的是针对全表 insert/delete 的现象,如下过程:
或者是另一种场景,比如对于有唯一性约束的字段(如 id),发生如下过程:
串行化的事务处理方式当然是最安全的,但是串行无法满足数据库高并发访问的需求,作为妥协,有时不得不降低数据库的隔离标准来换取事务的并发能力,通过在可控的范围内牺牲正确性来换取效率的提升,这种权衡通过事务的隔离级别来实现。
数据库有 4 种事务隔离级别,由低到高依次为 读未提交 Read Uncommitted
、读已提交 Read Committed
、可重复读 Repeatable Read
、串行化 Serializable
。
读未提交 Read Uncommitted
允许读取未提交的内容,这种级别下的查询不会加锁,因此脏读、不可重复读、幻读都有可能发生。
读已提交 Read Committed
只允许读取已提交的内容,这种级别下的查询不会发生脏读,因为脏数据属于未提交的数据,所以不会被读取,但是依然有可能发生不可重复读和幻读。
可重复读 Repeatable Read (MySQL 的默认隔离级别)
使用行级锁来保证一个事务在相同查询条件下两次查询得到的数据结果一致,可以避免脏读和不可重复读,但是没有办法避免幻读。
需要特殊注意的是,Innodb 在 Repeatable Read 下通过 MVCC 提供了稳定的视图,因此 Innodb 的 RR 隔离级别下是不会出现上述幻读异常中的第一个场景的,但第二个场景还是会出现。
串行化 Serializable
使用表级锁来保证所有事务的串行化,可以防止所有的异常情况,但是牺牲了系统的并发性。
查看隔离级别的命令为
SHOW VARIABLES LIKE 'transaction_isolation'; # 或者 SELECT @@global.tx_isolation, @@tx_isolation;
第二种方式可以查看全局和当前会话的隔离级别。
设置隔离级别的命令为
# 将当前会话的隔离级别设为读未提交 SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; # 将全局的隔离级别设为读未提交 SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
本文简单介绍了 MySQL 事务的语法和 ACID
特性,以及事务并发处理中可能出现的异常情况和为了防止这些异常而设计的事务隔离级别。有兴趣的朋友可以尝试在两个不同的 MySQL 客户端来模拟四种隔离级别下三种异常的发生情况,在之后的文章中,会继续深入探讨 MySQL 是如何实现隔离级别的。
推荐学习:mysql学习视频教程
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!