이 기사에서는 mysql에 대한 관련 지식을 제공합니다. MySQL의 트랜잭션 ACID 특성과 MySQL 트랜잭션 제어 프로세스의 구문을 주로 소개하고 더티 읽기, 팬텀 읽기 등 동시 트랜잭션 처리에서 발생할 수 있는 비정상적인 상황을 소개합니다. , 반복 불가능한 읽기 등. 마지막으로 트랜잭션 격리 수준을 소개합니다. 이것이 모든 사람에게 도움이 되기를 바랍니다.
추천 학습: mysql 학습 튜토리얼
실제 비즈니스 시나리오에서는 작업의 무결성을 어떻게 보장할 것인지가 중요한 문제입니다. 도중에 오류가 발생하면 논리적으로 관련된 일련의 작업이 실행됩니다. 데이터 혼란을 초래할 가능성이 매우 높습니다.
ATM에서 돈을 인출하는 장면을 상상해 보세요. 천 위안을 인출하면 ATM은 10번에 100위안을 뱉어내는 것이 아니라 계산이 완료된 후 한 번에 천 위안을 뱉어내는 것입니다. 작업 무결성을 보장합니다. 천 위안이 완전히 인출되어 잔액이 공제되거나, 1페니도 인출되지 않고 잔액이 변경되지 않고 유지되며, 중간에 기계 오류로 인한 데이터 불일치가 발생하지 않습니다. 이러한 완전한 작업을 트랜잭션
이라고 합니다. 트랜잭션의 모든 작업은 성공적으로 실행되거나 전혀 실행되지 않습니다. 事务 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
기능과 MySQL 트랜잭션 제어 프로세스의 구문을 소개하고 더티 읽기(dirty read), 팬텀 읽기(phantom read), 비 읽기(non) 등 동시 트랜잭션 처리에서 발생할 수 있는 예외를 소개합니다. -반복 읽기 등을 수행하고 마지막으로 트랜잭션 격리 수준을 소개합니다. 🎜🎜트랜잭션 격리를 달성하기 위한 잠금 및 MVCC에 대해서는 이후 기사에서 소개하겠습니다. 🎜롤백
하여 시스템 데이터가 잘못 변경되지 않도록 하세요. 🎜🎜이전 기사에서 MySQL 5.5 이후 기본 스토리지 엔진이 MyISAM에서 InnoDB로 교체되었다고 언급했습니다. 이에 대한 중요한 이유 중 하나는 InnoDB가 SHOW ENGINES
를 사용하여 Take를 수행하기 때문입니다. MySQL의 다양한 스토리지 엔진에 대한 설명을 살펴보세요. ACID
특성이라고 합니다.A - 원자성
: 트랜잭션은 분할할 수 없는 가장 작은 단위입니다. 모든 작업은 성공하거나 실패합니다. 중간 상태가 없습니다. 원자성은 주로 트랜잭션 로그의 롤백 로그(undo log)
를 통해 달성됩니다. 트랜잭션이 데이터베이스를 수정하면 InnoDB는 삽입 작업과 같은 작업을 기반으로 반대 작업의 실행 취소 로그를 생성합니다. 삭제 기록을 생성한다. 트랜잭션 실행이 실패하거나 롤백이 호출되면 Undo 로그 내용을 기반으로 실행 전 상태로 복원된다. 🎜🎜C - 일관성
: 트랜잭션 실행 전후에 데이터가 법적 일관성 상태에 있습니다. 예외가 발생하더라도 데이터베이스의 무결성 제약 조건을 위반하지 않습니다. 고유성 제약 조건 등 🎜🎜I - 격리 격리
: 각 트랜잭션은 서로 독립적이며 다른 트랜잭션 실행의 영향을 받지 않습니다. 트랜잭션은 커밋되기 전에 다른 트랜잭션에 표시되지 않습니다. Isolation은 트랜잭션의 Isolation 수준에 따라 정의되며, 쓰기 동작의 Isolation을 보장하기 위해 Lock 메커니즘을 사용하고, Read 동작의 Isolation을 보장하기 위해 MVCC를 사용하는데, 이에 대해서는 아래에서 자세히 소개하겠습니다. 🎜🎜D - 내구성
: 트랜잭션이 제출된 후 데이터에 대한 수정 사항은 지속되며 트랜잭션 로그의 리두 로그를 통해(리두) 데이터베이스가 다운되더라도 손실되지 않습니다. log )
를 확인하세요. 트랜잭션이 수정되기 전에 변경 정보가 리두 로그에 미리 기록됩니다. 데이터베이스가 다운되면 복구 후 리두 로그의 레코드를 읽어 데이터를 복원합니다. 🎜START TRANSACTION
또는 BEGIN
입니다. 트랜잭션의 모든 업데이트를 실제 데이터베이스에 기록하는 것입니다. 디스크에 저장되고 트랜잭션은 정상적으로 실행됩니다. 마지막에 명령문은 COMMIT
입니다. 예외가 발생하고 롤백이 필요한 경우 명령문은 ROLLBACK
입니다. 한번 커밋된 트랜잭션은 롤백할 수 없으므로 코드 실행 중 예외가 발생하면 커밋 대신 직접 롤백을 실행해야 합니다. 🎜🎜예를 들어, A가 B에게 100위안을 이체합니다. 🎜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
를 사용하여 구현할 수 있습니다. 🎜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学习视频教程
위 내용은 MySQL 트랜잭션의 상세 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!