事务的特性

事务具有四个特性,这四个特性通常被称为ACID特性,它们是:

原子性(Atomicity):事务是一个原子操作单元,其对数据的修改要么全部执行,要么全不执行。如果一个事务在执行过程中出错,那么该事务会被回滚,即撤销它的所有操作。

一致性(Consistency):事务必须保证数据库的一致性。在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的任何数据都必须满足所有设置的规则,包括数据的完整性和约束性。

隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务。隔离性是指并发中的事务,它们的执行结果应该与这些事务的执行顺序相同。

持久性(Durability):一旦事务提交,则其结果永久保存在数据库中。即使系统崩溃,重新启动后数据库还能恢复到提交事务后的状态,它的结果不会丢失。

这些特性保证了数据库事务的安全性和可靠性。在数据库系统中,事务是实现数据操作和保证数据一致性的重要手段。

举例说明事务的四个特性:

  • 原子性:

    假设我们有一个银行转账的场景,张三和李四分别拥有账户A和账户B,他们的账户金额分别为1000元和2000元。现在张三要转账给李四100元。

    原子性保证这个转账过程是一个原子操作单元,要么全部执行成功,要么全部执行失败。如果在这个过程中,数据库出现故障,那么张三和李四的账户金额不会被部分更新,数据库会回滚到转账操作前的状态。

  • 一致性:

    在转账过程中,一致性保证转账前和转账后,数据库的完整性约束没有被破坏。比如在这个例子中,无论转账是否成功,张三和李四的所有钱数之和应该是保持不变的,即1000(张三)+ 2000(李四)= 3000元。

  • 隔离性:

    在并发数据操作时,不同的事务拥有各自的数据空间,各自的操作不会对彼此产生影响。比如在这个例子中,如果有其他事务同时对这两个账户进行操作,那么这个转账操作不会影响其他事务的操作结果。

  • 持久性:

    一旦事务提交成功,即使数据库发生故障,数据库也能将其数据恢复到提交后的状态。在这个例子中,即使在张三和李四的转账操作完成后,数据库发生故障并重新启动,数据库也能恢复到转账后的状态,保证数据的持久性。

事务隔离级别

事务隔离级别用于在数据库中限定事务内外哪些数据是可见的,哪些是不可见的。数据库事务隔离级别主要分为四种级别,从低到高依次为读未提交(Read Uncommitted)、读提交(Read Committed)、可重复读(Repeatable Read)和串行化(Serializable)。

读未提交(Read Uncommitted):该级别允许一个事务读取另一个未提交事务的数据。这种隔离级别会导致很多问题,如脏读、不可重复读和幻读。脏读是指一个事务读取了另一个未提交事务的数据,并在此基础上做出了修改,导致前一个事务的数据被覆盖。不可重复读是指在一个事务内部,多次读取同一数据返回的结果不一致,因为其他事务对数据的修改导致返回结果发生了变化。幻读是指在一个事务内部,无法看到其他并行事务插入的新记录,因为这些新记录被其他事务锁定,导致当前事务无法读取到这些新记录。

读提交(Read Committed):该级别仅允许一个事务读取已经提交的事务数据。这种隔离级别避免了脏读问题,但仍然存在不可重复读和幻读问题。

可重复读(Repeatable Read):该级别保证了在同一事务内部多次读取同一数据时,结果是一致的。这种隔离级别通过在事务期间锁定数据来实现,即使其他事务对数据进行修改,也无法影响当前事务的读取。不可重复读和幻读问题在该级别都得到了解决。

串行化(Serializable):该级别是最严格的隔离级别,它通过强制事务串行执行来避免并发问题。在这种隔离级别下,事务一个接一个地执行,前一个事务完成后,后一个事务才能开始。这种隔离级别可以完全避免脏读、不可重复读和幻读等并发问题,但同时也带来了最大的性能损失。

什么是可重复读

事务的可重复读(Repeatable Read)是MySQL数据库中的一种事务隔离级别,它解决了不可重复读的问题。在不可重复读隔离级别下,事务在读取某行数据时,其他事务可以修改该行数据,这就可能导致在同一事务中多次读取同一行数据时,结果不一致。而可重复读隔离级别可以保证在同一事务中多次读取同一行数据时,结果是一致的。

可重复读隔离级别下,事务开始时读取到的数据被锁定其他事务无法修改直到该事务提交或回滚才会释放锁。这样可以保证在同一事务中多次读取同一行数据时,不会被其他事务修改,从而保持数据的一致性。

但是,可重复读隔离级别也可能会引入幻读(Phantom Read)问题。幻读是指在一个事务内部无法看到其他事务插入的新记录,而只有在事务提交后才能看到。这是因为新记录被其他事务锁定,导致当前事务无法读取到这些新记录。为了解决幻读问题,MySQL在可重复读隔离级别下使用了多版本并发控制(MVCC)机制,通过创建历史版本的数据来让事务能够读取到其他事务插入的新记录。

假设我们有一个银行转账的场景,两个用户A和B分别拥有账户1和账户2,他们的账户金额分别为1000元和2000元。现在A要转账给B 100元。

在事务的可重复读隔离级别下,整个转账过程可以保证数据的一致性,避免并发修改导致的不一致。

A发起转账请求,事务开始。

A查询自己的账户余额为1000元。

B查询自己的账户余额为2000元。

A事务将100元转账给B。

B事务将100元存入自己的账户中,更新账户余额为2100元。

A再次查询自己的账户余额为900元(1000 - 100)。

B再次查询自己的账户余额为2100元(2000 + 100)。

在这个过程中,A和B的事务是独立的,互不干扰。在A事务中,第一次查询自己的账户余额为1000元,然后进行转账操作。在B事务中,第一次查询自己的账户余额为2000元,然后进行存款操作。由于两个事务都是可重复读隔离级别,所以无论其他事务如何修改数据,都保证了两个事务在多次读取同一数据时是一致的。

什么是不可重复读

不可重复读是指在数据库访问中,一个事务范围内两个相同的查询却返回了不同数据。这是由于查询时系统中其他事务修改的提交而引起的。比如事务T1读取某一数据,事务T2读取并修改了该数据,T1为了对读取值进行检验而再次读取该数据,便得到了不同的结果。

在数据库中,事务是一种机制,用于保证一系列数据库操作的原子性、一致性、隔离性和持久性。不可重复读是数据库事务中的一种问题,发生在事务之间没有正确地隔离,导致不同的事务读取到不同的情况。

不可重复读问题通常分为两种情况:

在事务T1中读取了一些数据,然后事务T2修改了这些数据,导致在事务T1再次读取相同的数据时,数据已经发生了改变,因此无法重复读取相同的数据。

在事务T1中读取了一些数据,然后事务T2回滚了,这意味着事务T2的修改被撤销。然而,在事务T1再次读取相同的数据时,由于事务T2的修改已经被撤销,数据又恢复到原始状态,导致无法重复读取相同的数据。

为了解决不可重复读问题,需要正确地设置事务之间的隔离级别和锁定策略,以确保在事务执行期间,其他事务不能修改或读取该事务已经读取的数据。这样可以避免在不同的事务中读取到不同的数据,并确保数据库的一致性和正确性。

什么是幻读

幻读(Phantom Read)是指在一个事务内部,无法看到其他并行事务插入的新记录,因为这些新记录被其他事务锁定,导致当前事务无法读取到这些新记录。这种现象在可重复读隔离级别下才会出现。

下面是一个幻读的例子:

假设我们有一个表格,记录了每个用户的登录次数,表名为login_count,表结构为(user_id int, count int)。

事务A执行了如下操作:

sql 复制代码
BEGIN TRANSACTION;  
SELECT user_id, count FROM login_count WHERE user_id = 1; -- 查询用户id为1的登录次数  
-- 其他事务在该事务查询后,插入了新的记录  
COMMIT; -- 提交事务

在事务A执行过程中,其他事务插入了新的记录,导致user_id为1的登录次数发生了变化。但是,当事务A再次查询时,它无法读取到这些新插入的记录,因为这些新记录被其他事务锁定。这就出现了幻读的问题。

为了避免幻读的问题,我们可以将事务的隔离级别提高到串行化(Serializable),这样可以保证事务的完全隔离性,避免并发问题。但是,提高隔离级别会降低系统的并发性能,需要根据实际情况进行权衡。

什么是MVCC机制

MVCC(Multi-Version Concurrency Control)是一种并发控制方法,通常用于数据库管理系统和事务型编程中,以解决并发场景下的读写问题。MVCC是通过创建历史版本(或快照)来解决并发冲突的一种机制。在MVCC中,每个事务读到的数据项都是一个历史版本,而不是最新的数据项。如果一个事务需要更新数据项,它将创建一个新版本,而不会覆盖现有版本。这样,其他事务可以继续读取旧版本,而不会阻塞等待更新。

在数据库管理系统中,MVCC是通过行级锁来实现的。当一个事务需要更新一行数据时,它会对该行数据加锁,并创建一个新版本。其他事务可以继续读取旧版本,但无法更新该行数据,直到当前事务提交或回滚。

在编程语言中实现事务内存时,MVCC机制也可以用来解决并发读写问题。在MVCC中,每个事务都有自己的视图(或快照),并且只能看到在其开始之前已经提交的数据项的最新版本。

总之,MVCC机制通过创建历史版本来解决并发读写问题,使得多个事务可以同时访问和修改数据,而不会发生冲突。

相关推荐
LUCIAZZZ4 分钟前
项目拓展-Apache对象池,对象池思想结合ThreadLocal复用日志对象
java·jvm·数据库·spring·apache·springboot
爱瑞瑞20 分钟前
云原生学习笔记(七) Docker 实战:使用 Docker 快速构建 Oracle 12c 容器
docker·oracle
水木石画室25 分钟前
Druid 连接池详解
数据库·mysql
yzpyzp28 分钟前
gradle的 build时kaptDebugKotlin 处理数据库模块
android·数据库
软件20535 分钟前
【redis——缓存雪崩(Cache Avalanche)】
数据库·redis·缓存
訾博ZiBo1 小时前
使用 Navicat 成功导入 2.73GB 超大 SQL 文件的实战经验(Win10,64GB内存)
数据库·mysql
小胖同学~2 小时前
SQL 增删改查 —— 笔记篇
服务器·数据库
梦兮林夕2 小时前
Docker + Gin + Gorm Gen:现代 Go Web 开发高效数据库实践
数据库·go·gin
Lx3522 小时前
SQL参数化查询:防注入与计划缓存的双重优势
后端·sql·mysql
徐Sir的IT技术备忘录2 小时前
WinServer2025安装OracleDB 19.27实测及applyRU问题复盘
数据库