文章目录
- 事务四大特性
- 事务隔离级别
- 事务传播属性
- 事务失效场景
- Q:
-
- Mysql数据库事务和Spring事务是一个东西吗?
- 区别?
- 有没有一些异常,代码层面捕获异常,不回滚事务,但是mysql数据库的事务回滚了?
- 如何检测死锁?
- mysql如何检测死锁?
- [mysql 数据库超时检测时长默认多少?](#mysql 数据库超时检测时长默认多少?)
- 受检异常
- 扩展:
-
- 事务有哪几种隔离级别呢?可以详细谈谈吗?
- 说说脏读、不可重复读、幻读、可重复读以及幻读与不可重复读的区别
- 事务并发一般都会产生哪些问题?
- 应该如何解决?
- 什么是MVCC?
- MVCC工作原理是啥?
- [MySQL 事务实现原理是什么?](#MySQL 事务实现原理是什么?)
- [InnoDB 如何开启手动提交事务?](#InnoDB 如何开启手动提交事务?)
事务四大特性
- 原子性: 事务作为一个整体被执行,包含在其中的对数据库的操作要么全部都执行,要么都不执行。 通过事务日志实现
- 一致性: 指在事务开始之前和事务结束以后,数据不会被破坏,假如A账户给B账户转10块钱,不管成功与否,A和B的总金额是不变的。 通过事务日志实现
- 隔离性: 多个事务并发访问时,事务之间是相互隔离的,一个事务不应该被其他事务干扰,多个并发事务之间要相互隔离。 通过锁实现
- 持久性: 表示事务完成提交后,该事务对数据库所作的操作更改,将持久地保存在数据库之中。 通过事务日志实现
通过什么实现特性?
事务的隔离性是通过锁实现,而事务的原子性、一致性和持久性则是通过事务日志实现。
事务日志(Transaction Log)是数据库管理系统(DBMS)用来记录对数据库进行的所有事务操作的一种机制。它用于持久地存储事务的开始、执行和提交或回滚的信息,以确保数据库的持久性和一致性。
事务隔离级别
事务隔离 : 事务之间访问权限控制
-
读未提交(Read Uncommitted)RU:最低的隔离级别,允许一个事务读取另一个事务未提交的数据。可能会导致脏读、不可重复读和幻读等问题。因为事务之间的CRUD都可以看到
-
读已提交(Read Committed)RC:允许一个事务读取另一个事务已提交的数据。在一个事务中多次读取同一数据,可能会出现不可重复读和幻读等问题。没有脏读
-
可重复读(Repeatable Read)RR:保证在一个事务中多次读取同一数据时,结果始终一致。其他事务不能修改已读取的数据,但可以插入新数据。可以避免脏读和不可重复读,但仍可能出现幻读问题。 因为幻读是对另一个事务insert/delete
**SELECT * FROM table FOR UPDATE ** 保证不会产生幻读,与事务一起使用
InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读) ,但是可以通过应用加锁读(例如
select * from table for update
语句)来保证不会产生幻读 ,而这个加锁度使用到的机制就是 Next-Key Lock 锁算法。从而达到了 SQL 标准的 SERIALIZABLE(可串行化) 隔离级别。 是 MySQL 中的一条 SELECT 语句,它用于锁定选中的行,防止其他用户在同一时间对这些行进行修改。在使用
FOR UPDATE
子句时,MySQL 在执行查询之前会获取指定行的排他锁,防止其他用户对这些行进行更新、插入或删除操作,直到该事务提交或回滚为止。这种锁定方式也被称为行级锁定。InnoDB 存储引擎在 分布式事务 的情况下一般会用到**SERIALIZABLE(可串行化)**隔离级别。
-
串行化(Serializable):最高的隔离级别,确保事务串行执行,避免了脏读、不可重复读和幻读等问题。通过对数据加锁来实现,但可能会导致并发性能下降。
不同的隔离级别提供了不同的数据一致性和并发性能。选择合适的隔离级别需要根据具体的业务需求和系统性能进行权衡。
MySQL 默认的事务隔离级别是可重复读(REPEATABLE READ)。
为什么要设置隔离级别?
事务的隔离级别用于控制并发访问数据库时事务之间的可见性和数据一致性。不同的隔离级别提供了不同的数据隔离性,从而在处理并发访问时平衡了数据的一致性和性能需求。
以下是事务分隔离级别的主要原因和作用:
- 数据隔离性:事务可能同时进行读取和写入操作,而并发访问可能导致数据不一致的问题,如脏读、不可重复读和幻读。通过设置适当的隔离级别,可以控制事务之间对数据的可见性,从而避免上述问题的发生。
- 并发性能:更低的隔离级别通常意味着更高的并发性能,因为它允许较多的事务同时访问和修改数据。然而,较低的隔离级别也可能会增加数据不一致性的风险。选择合适的隔离级别需要权衡数据一致性和性能需求之间的关系。
- 数据完整性:合适的隔离级别有助于维护数据的完整性。较高的隔离级别可以减少并发事务之间的冲突和竞争条件,从而确保数据的正确性和一致性。
- 业务需求:不同的业务场景对数据一致性和并发性能有不同的要求。根据具体的应用需求和业务逻辑,选择合适的隔离级别可以满足业务的数据访问需求,并确保数据库的正确性。
总而言之,事务的隔离级别是为了在并发访问的环境中提供数据的一致性、可靠性和并发性能。选择合适的隔离级别取决于应用的具体需求和对数据一致性和并发性能之间的平衡考虑。
如何设置隔离级别?
MySQL 事务隔离级别 MySQL.cnf 文件里设置的(默认目录 /etc/my.cnf),在文件的文末添加配置:transaction-isolation = REPEATABLE-READREAD-UNCOMMITTED/READ-COMMITTED/REPEATABLE-READ/SERIALIZABLE
或:set global | session tx_isolation='READ-COMMITTED';命令修改隔离级别。
set global transaction isolation level read committed; // 设置全局事务隔离级别为 read committed
set session transaction isolation level read committed; // 设置当前会话事务隔离级别为 read committed
查看数据库版本:
SELECT VERSION();
返回当前会话的事务隔离级别的值:
SELECT @@tx_isolation;
SELECT @@transaction_isolation; 8.0版本
设置当前会话事务的隔离级别:
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
返回系统变量事务隔离级别相关的的值:
show variables like '%tx_isolation%'
show variables like '%transaction_isolation%' 8.0版本
事务并发问题模拟?
-
脏读(Dirty Read):一个事务读取了另一个事务未提交的数据。如果后续事务回滚,读取到的数据就是无效的。 男女
-
幻读(Phantom Read):一个事务在同一个事务中多次查询同一范围的数据,但在查询过程中,其他事务插入或删除了符合该范围的数据,导致多次查询的结果不一致。 任务单条目
-
不可重复读(Non-repeatable Read):一个事务在同一个事务中多次读取同一数据,但在读取过程中,其他事务修改了该数据,导致多次读取的结果不一致。 钱
-
丢失更新(Lost Update):两个事务同时修改同一数据,其中一个事务的修改被另一个事务覆盖,导致其中一个事务的修改丢失。男女
这些问题的出现是因为事务并发执行时,多个事务同时访问和修改共享数据,而没有合适的控制机制来保证数据的一致性和完整性。
不可重复度和幻读区别:
不可重复读的重点是修改,幻读的重点在于新增或者删除。
MySQL 中支持事务的存储引擎有InnoDB 和 NDB。InnoDB 是高版本 MySQL 的默认的存储引擎,因此就以 InnoDB 的事务实现为例,
InnoDB 是通过多版本并发控制(MVCC,Multiversion Concurrency Control )解决不可重复读问题,
加上间隙锁(也就是并发控制)解决幻读问题。
因此 InnoDB 的 RR 隔离级别其实实现了串行化级别的效果,而且保留了比较好的并发性能。
建表:
sql
DROP TABLE IF EXISTS `my_test`;
CREATE TABLE `my_test` (
`id` bigint NOT NULL COMMENT 'id',
`name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`age` int NULL DEFAULT NULL,
`sex` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`role` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`money` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of my_test
-- ----------------------------
INSERT INTO `my_test` VALUES (1, 'x', 18, '女', '程序员', '1');
INSERT INTO `my_test` VALUES (2, 'y', 11, '男', '程序员', '200');
INSERT INTO `my_test` VALUES (3, 'z', 11, '男', '程序员', '200');
读未提交
1.脏读:
事务1:
-- 读未提交,脏读
SELECT @@transaction_isolation;
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION
SELECT * from my_test where id = 1
-- 事务2修改了,未提交
SELECT * from my_test where id = 1
-- 事务2回滚了
SELECT * from my_test where id = 1
COMMIT
事务2:
SELECT @@transaction_isolation;
START TRANSACTION
update my_test set sex = '未知' where id =1
ROLLBACK
2.不可重复读:
事务1:
-- 读未提交,不可重复读
SELECT @@transaction_isolation;
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION
SELECT * from my_test where id = 1 -- 1000
-- 事务2修改了,未提交
SELECT * from my_test where id = 1 -- 1
-- 事务2修改了,已提交
SELECT * from my_test where id = 1 -- 1
COMMIT
事务2:
START TRANSACTION
update my_test set money = 1 where id =1
COMMIT
3.幻读:
事务1:
-- 读未提交,幻读
SELECT @@transaction_isolation;
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION
SELECT * from my_test where role= '程序员' -- 3条
-- 事务2新增了1条,未提交
SELECT * from my_test where role = '程序员' -- 4条
-- 事务2删除了1条,未提交
SELECT * from my_test where role = '程序员' -- 3条
COMMIT
事务2:
START TRANSACTION
INSERT INTO my_test (id
, name
, age
, sex
, role
, money
) VALUES (5, 'z', 11, '男', '程序员', '200');
delete from my_test where id = 4
COMMIT
如何解决事务并发啊?
-
锁机制:使用锁来控制对共享数据的访问,保证同一时间只有一个事务可以修改数据,其他事务需要等待锁释放后才能访问。可以使用数据库的行级锁或表级锁 ,或者在应用层使用分布式锁来实现。
使用乐观锁或悲观锁:根据具体场景选择合适的锁机制,乐观锁适用于并发冲突较少的场景,悲观锁适用于并发冲突较多的场景。
- 乐观锁:使用版本号或时间戳等机制,在更新数据之前检查是否有其他事务对数据进行了修改。如果检测到冲突,则回滚当前事务或者重新尝试。
- 悲观锁:在事务访问数据之前,先对数据进行加锁,直到事务结束才释放锁。这样可以确保在事务执行期间其他事务无法修改数据,但会对并发性能产生一定影响。
- 行级锁:对事务中的某些特定行进行锁定,只有拥有锁的事务可以修改对应的行,其他事务需要等待锁释放。这种方式可以减少锁竞争范围,提高并发性能。
-
事务隔离级别(Isolation Level):设置事务的隔离级别,如读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)和串行化(Serializable)。不同的隔离级别提供不同的数据一致性和并发性能。选择合适的隔离级别可以避免脏读、不可重复读和幻读等问题。
串行化事务:将事务的隔离级别设置为最高的串行化,确保每个事务按照严格的顺序执行,避免并发问题。但这会牺牲一定的并发性能。
- **数据库索引优化:**合理设计和使用数据库索引可以提高查询性能,减少锁竞争的时间。根据具体的查询需求和数据访问模式,选择合适的索引策略。
- **调整事务范围:**根据具体业务需求和并发情况,调整事务的范围和粒度,避免长时间持有锁或事务的冲突。
- 并发控制算法:使用并发控制算法,如多版本并发控制(MVCC)或基于时间戳的并发控制(Timestamp-based Concurrency Control),来解决并发访问数据的一致性问题。
- 分布式事务管理:如果系统涉及多个数据库或服务,需要使用分布式事务管理来保证多个事务的一致性。可以使用分布式事务协调器(如XA协议)或基于消息队列的事务消息来实现。
事务传播属性
事务传播属性是用来定义事务在方法调用链中的传播行为。在Spring框架中,常见的事务传播属性有以下几种:
-
REQUIRED:如果当前存在事务,则加入该事务,如果不存在事务,则创建一个新的事务。这是默认的传播属性。
-
SUPPORTS:如果当前存在事务,则加入该事务,如果不存在事务,则以非事务的方式执行。
-
MANDATORY:如果当前存在事务,则加入该事务,如果不存在事务,则抛出异常。
-
REQUIRES_NEW:无论当前是否存在事务,都创建一个新的事务。如果当前存在事务,则将其挂起。 并行执行
-
NOT_SUPPORTED:以非事务的方式执行方法。如果当前存在事务,则将其挂起。
-
NEVER:以非事务的方式执行方法。如果当前存在事务,则抛出异常。
-
NESTED:如果当前存在事务,则在嵌套事务中执行。如果不存在事务,则创建一个新的事务。嵌套事务是外部事务的一部分,可以独立提交或回滚,但如果外部事务回滚,则嵌套事务也会回滚。
除了一些商业数据库外,例如 Oracle,PostgreSQL 等,大多数开源数据库(如 MySQL)都不支持嵌套事务。
这些事务传播属性可以通过在方法上使用@Transactional注解来指定,或者在XML配置文件中进行配置。选择合适的事务传播属性需要根据具体的业务需求和方法调用关系进行权衡。
事务挂起和事务恢复:
在某些情况下,当一个方法或操作需要以非事务的方式执行时,如果当前已经存在一个事务,数据库系统会将该事务挂起(suspend)。
一旦挂起了原来的事务,系统会启动一个新的非事务环境,该环境中的操作不会参与到原有的事务中去。直到非事务环境完成后,原来的事务才会被恢复(resume),继续执行。
事务失效场景
场景:
如果方法 B 被声明为一个接口方法,并且在 serviceImpl 层调用了该方法,那么是否会有事务失效问题取决于具体的实现方式和事务管理器的配置。
如果方法 B 的接口实现类被标记为 Spring 的事务代理对象,并且事务管理器已经正确配置,那么在 serviceImpl 层调用方法 B 时,仍然可以继续沿用事务上下文,事务将正常进行。
但需要注意的是,Spring 的事务传播属性是基于方法调用的,而不是接口层级的。即使方法 B 在接口中声明为有事务,如果在 serviceImpl 层直接调用接口方法,并不会继承调用 serviceImpl 方法的事务上下文,因为接口方法没有使用 Spring 的事务代理。
解决这个问题的一种方式是,在 serviceImpl 层调用接口方法时,通过依赖注入的方式获取接口的实例,而不是直接调用接口方法。这样可以确保方法调用会经过 Spring 的事务代理,从而正确继承事务上下文。
例如,假设存在如下代码:
java
javaCopy Codepublic interface InterfaceB {
void methodB();
}
@Service
public class ServiceImpl implements ServiceA {
@Autowired
private InterfaceB interfaceB;
@Transactional
public void methodA() {
interfaceB.methodB();
}
}
@Service
public class InterfaceBImpl implements InterfaceB {
@Transactional
public void methodB() {
// 方法B的具体实现
}
}
在这个例子中,ServiceA 的实现类 ServiceImpl 在方法 A 上有事务注解,而方法 A 中调用了 InterfaceB 的方法。通过依赖注入方式,在 ServiceImpl 中获取 InterfaceB 的实例并调用其方法,可以确保事务的正确传播和管理。
总结起来,即使方法 B 声明在接口中具有事务,也需要注意在调用该方法时是否经过了 Spring 的事务代理,以确保事务的正确传播和管理。
Q:
Mysql数据库事务和Spring事务是一个东西吗?
MySQL数据库事务和Spring事务不是完全相同的东西。
MySQL数据库事务是数据库管理系统提供的一种机制,用于保证一组数据库操作要么全部成功提交,要么全部回滚。它通过将一组操作封装在一个事务中,并使用ACID(原子性、一致性、隔离性、持久性)属性来确保数据的一致性和完整性。
Spring事务是Spring框架提供的一种事务管理机制,它建立在数据库事务之上,并提供了更高级别的抽象和功能。Spring事务管理器可以与不同的持久化框架(如Hibernate、JPA)集成,并提供声明式事务管理的能力。它可以通过注解或编程方式来定义事务的边界和传播属性,以及事务的隔离级别和超时设置。
Spring事务管理器可以将多个数据库操作封装在一个事务中,并提供更灵活的事务控制和异常处理机制。它还支持分布式事务管理,可以在多个数据库或服务之间保持一致性。
因此,虽然MySQL数据库事务是底层的数据库机制,而Spring事务是在其之上构建的高级抽象,但它们是相关的概念,Spring事务可以使用MySQL数据库事务来实现事务管理。
区别?
- 概念层面:MySQL数据库事务是关于维护数据库的一致性和完整性的一组操作,而Spring事务是在应用程序中管理数据库事务的机制。
- 使用方式:MySQL事务是在SQL语句层面上进行控制的,需要显式地在SQL语句中加入事务相关的命令;而Spring事务是通过aop和注解的方式在Java代码中进行控制的,使用起来更为方便。
- 隔离级别:MySQL事务支持四种隔离级别,可以通过设置事务参数来选择合适的隔离级别;Spring也支持四种隔离级别,并提供了更为灵活的事务控制方式,可以根据业务需求动态调整隔离级别。
- 回滚方式:MySQL事务的回滚是通过抛出异常或手动回滚命令来实现的;而Spring事务可以通过配置回滚规则来自动回滚,也可以通过编程方式实现手动回滚。
- 作用范围:MySQL事务只能控制对数据库的操作,不能控制其他系统资源的访问;而Spring事务不仅可以控制数据库操作,还可以控制其他资源的访问,如消息队列、缓存等。
有没有一些异常,代码层面捕获异常,不回滚事务,但是mysql数据库的事务回滚了?
- 数据库事务超时(Transaction Timeout):如果一个数据库事务执行的时间超过了事务超时设置的时间,数据库会自动回滚事务。
- 连接错误(Connection Error):当数据库连接出现问题,如网络异常、连接中断等,数据库会自动回滚正在执行的事务。
- 数据库死锁(Deadlock):当多个事务相互等待对方所持有的资源时,形成死锁,数据库会选择其中一个事务作为牺牲者,回滚该事务以解除死锁。
如何检测死锁?
- 等待图(Wait-for graph)算法:这是一种常用的死锁检测算法。数据库系统维护一个等待图,其中节点表示事务,有向边表示一个事务等待另一个事务所持有的资源。当一个事务请求一个被其他事务持有的资源时,系统会检查是否存在导致循环等待的路径。如果存在循环等待,则出现死锁。
- 超时检测:数据库系统可以检测事务的等待时间是否超过某个阈值。如果一个事务等待时间过长,超过了设定的超时时间,系统会认为可能发生死锁,并回滚其中一个事务以解除死锁。
mysql如何检测死锁?
MySQL 提供了几种方法来检测死锁:
- 锁冲突日志(InnoDB 锁监控):MySQL 的 InnoDB 存储引擎支持将死锁信息写入错误日志(error log),以便后续分析和诊断。可以通过设置参数 innodb_print_all_deadlocks=ON,使 InnoDB 将所有死锁信息记录到错误日志中。
- 信息查询:可以使用 SHOW ENGINE INNODB STATUS 命令来查看当前的 InnoDB 锁状态。命令的输出中包含了最近发生的死锁信息,可以根据其中的死锁描述和参与事务的会话 ID 进行分析。
- 查询存储引擎插件:MySQL 提供了一些用于检测和解决死锁问题的存储引擎插件,例如 InnoDB deadlock detector 插件。可以安装并启用这些插件,它们会自动检测和解决死锁问题。
- 监控工具:MySQL 的性能监控工具,如 MySQL Enterprise Monitor 和 Percona Toolkit,提供了专门用于检测和分析死锁的功能。这些工具可以实时监控数据库的活动和死锁情况,并生成相应的报告和警告。
需要注意的是,死锁检测是一项资源密集型操作,如果系统中经常发生大量的死锁,可能会对数据库性能产生一定的影响。因此,在实际应用中,需要权衡监测频率和系统性能之间的关系,选择适合的死锁检测方法和工具,并根据实际情况进行调整和优化。
mysql 数据库超时检测时长默认多少?
MySQL 数据库的超时检测时长默认是 28800 秒,也就是 8 小时。这个超时时长可以通过 MySQL 配置文件中的参数 wait_timeout 来进行设置。
wait_timeout 参数指定了 MySQL 服务器与任何客户端的连接在持续空闲的时间长度,超过这个时长,服务器会自动关闭该连接。这样做的目的是为了释放 MySQL 服务器上资源的占用,以提高系统的并发性能。
我遇到的并发修改异常 :Concurrency Failure Exception 并发冲突异常,是在数据库层面上引发的异常,通常由并发操作导致。
需要注意的是,对于一些受检异常(Checked Exception),如果在代码中显式捕获并处理,且没有抛出运行时异常或手动回滚事务,MySQL数据库不会自动回滚事务。这种情况下,如果希望回滚事务,需要在代码中显式调用事务回滚的方法。
受检异常
受检异常(Checked Exception)是Java编程语言中的一种异常类型。与运行时异常(RuntimeException)不同,受检异常在代码中必须显式地捕获或声明,否则编译器会报错。
受检异常通常表示外部环境可能发生的可预知的异常情况,例如文件不存在、网络连接问题、数据库访问异常等。这些异常是在代码编写过程中可以预见到的,因此需要开发人员在代码中进行适当的处理。
如果使用了可能抛出受检异常的方法或代码块,那么就必须使用try-catch语句捕获并处理这些异常,或者在方法签名中使用throws关键字将异常声明抛出。如果不进行捕获或声明,编译器将给出错误提示,阻止代码的编译。
以下是一个示例,展示如何处理受检异常:
java
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class CheckedExceptionExample {
public static void main(String[] args) {
try {
// 尝试打开一个文件
File file = new File("example.txt");
FileInputStream fis = new FileInputStream(file);
// 进行文件读取操作
// ...
// 关闭文件流
fis.close();
} catch (FileNotFoundException e) {
// 捕获文件不存在异常
System.out.println("文件不存在");
} catch (Exception e) {
// 捕获其他异常,并进行相应的处理
e.printStackTrace();
}
}
}
在上面的示例中,我们尝试打开一个文件并进行读取操作。FileInputStream
的构造函数声明了FileNotFoundException
受检异常,因此我们在代码中使用try-catch语句捕获这个异常,并在异常处理块中输出错误信息。
总结来说,受检异常是一种在编译期间必须显式处理的异常。对于可能抛出受检异常的方法或代码块,要么使用try-catch语句捕获并处理异常,要么在方法签名中使用throws关键字声明异常抛出。这样可以确保代码在面对可预知的异常情况时能够做出相应的处理。
扩展:
事务有哪几种隔离级别呢?可以详细谈谈吗?
MySQL中InnoDB存储引擎实现了SQL标准的4种隔离级别,主要用来限定事务内哪些改动是可见的,哪些是不可见的。低级别的隔离一般支持更高的并发处理,并拥有更低的系统开销。
MySQL中可以通过show variables like '%tx_isolation%'查看当前事务的隔离级别。默认使用的是REPEATABLE-READ。记忆技巧:isolate 孤立 e改 tion 表名词。读音:[aslen]
读未提交(read uncommitted)RU:一个事务可以读到其它事务未提交的数据,这种现象叫做脏读,在生产环境不建议使用。数据库的读以提交(read committed)RC:一个事务可以读到其它事务已提交的数据。也可以叫做不可重复读,允许幻读现象的发生,是oracle数据库默认的事务隔离级别。可重复读(repeatable read)RR:它是MySQL默认的事务级别,一个事务开始到结束,都可以反复读取到事务开始时看到的数据,并一直不会发生变化,避免了脏读、不可重复读和幻读现象的发生。串行(serializable):读写都会锁住整张表,保证数据不会出错,在读数据的时候会加表级共享锁,在每次写数据的时候都会加表级排他锁。这个级别会导致InnoDB并发大幅下降,会发生大量超时和锁竞争,开发中很少用。
说说脏读、不可重复读、幻读、可重复读以及幻读与不可重复读的区别
脏读:RU事务隔离级别中,一个事务可以读取到其他事务还没有提交数据的现象。不可重复读与幻读:一个事务读取到其它事务对旧数据做的修改记录,比如delete、update。可重复读:这是MySQL默认事务隔离级别,它可以更好地保证数据一致性。比如会话A在RR级别的事务中新增了一条数据。会话B读取到的还是一开始的数据,并没有读取到新增的那条,如果想要读取到会话A新增的那条需要在语句后面加for update,或者执行commit。这个级别一般用于对事务要求较高的网站,比如某宝网。不可重复读的重点是修改:在同一事务中,同样的条件,第一次读的数据和第二次读的数据不一样。(因为中间有其他事务提交了修改)。幻读的重点在于新增或者删除:在同一事务中,同样的条件,第一次和第二次读出来的记录数不一样。(因为中间有其他事务提交了插入/删除)。
事务并发一般都会产生哪些问题?
更新丢失(Lost Update):当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题,最后的更新覆盖了由其他事务所做的更新。脏读(Dirty Reads):一个事务正在对一条记录做修改,在这个事务没有提交前, 这条记录的数据就一直处于不确定状态; 这时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些脏数据,并据此做进一步的处理,就会产生未提交的数据依赖关系,这种现象被形象地叫做脏读。不可重复读(Non-Repeatable Reads):一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了!这种现象就叫做"不可重复读" 。幻读(Phantom Reads): 一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为"幻读" 。
应该如何解决?
并发事务可能造成:脏读、不可重复读和幻读等问题 ,这些问题其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决,解决方案如下:
加锁:在读取数据前,对其加锁,阻止其他事务对数据进行修改。例如,读的时候加共享锁,此时其他事物无法修改相应的数据,写的时候加排他锁,禁止其他事物读写操作,但是这种做法性能较差。基于性能考虑MySQL提供了数据多版本并发控制(MVCC),也称为多版本数据库:不用加任何锁, 通过一定机制生成一个数据请求时间点的一致性数据快照(Snapshot), 并用这个快照来提供一定级别 (语句级或事务级) 的一致性读取,从用户的角度来看,好象是数据库可以提供同一数据的多个版本。
什么是MVCC?
MVCC 全称是多版本并发控制系统,InnoDB 和 Falcon 存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决幻读问题。
MVCC工作原理是啥?
InnoDB 的 MVCC 是通过在每行记录后面保存两个隐藏的列来实现,这两个列一个保存了行的创建时间,一个保存行的过期时间(删除时间)。当然存储的并不是真实的时间而是系统版本号(system version number)。
每开始一个新的事务,系统版本号都会自动新增,事务开始时刻的系统版本号会作为事务的版本号,用来查询到每行记录的版本号进行比较。
REPEATABLE READ(可重读)隔离级别下 MVCC 如何工作?
SELECT:InnoDB 会根据以下条件检查每一行记录:第一,InnoDB 只查找版本早于当前事务版本的数据行,这样可以确保事务读取的行要么是在开始事务之前已经存在要么是事务自身插入或者修改过的。第二,行的删除版本号要么未定义,要么大于当前事务版本号,这样可以确保事务读取到的行在事务开始之前未被删除。INSERT:InnoDB 为新插入的每一行保存当前系统版本号作为行版本号。DELETE:InnoDB 为删除的每一行保存当前系统版本号作为行删除标识。UPDATE:InnoDB 为插入的一行新纪录保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为删除标识保存这两个版本号,使大多数操作都不用加锁。它不足之处是每行记录都需要额外的存储空间,需要做更多的行检查工作和一些额外的维护工作。
MySQL 事务实现原理是什么?
事务的实现是基于数据库的存储引擎,不同的存储引擎对事务的支持程度不一样。MySQL 中支持事务的存储引擎有InnoDB 和 NDB。InnoDB 是高版本 MySQL 的默认的存储引擎,因此就以 InnoDB 的事务实现为例,InnoDB 是通过多版本并发控制(MVCC,Multiversion Concurrency Control )解决不可重复读问题,加上间隙锁(也就是并发控制)解决幻读问题。因此 InnoDB 的 RR 隔离级别其实实现了串行化级别的效果,而且保留了比较好的并发性能。事务的隔离性是通过锁实现,而事务的原子性、一致性和持久性则是通过事务日志实现。如何设置 MySQL 的事务隔离级别?
MySQL 事务隔离级别 MySQL.cnf 文件里设置的(默认目录 /etc/my.cnf),在文件的文末添加配置:transaction-isolation = REPEATABLE-READREAD-UNCOMMITTED/READ-COMMITTED/REPEATABLE-READ/SERIALIZABLE或:set global | session tx_isolation='READ-COMMITTED';命令修改隔离级别。MySQL> set global transaction isolation level read committed; // 设置全局事务隔离级别为 read committedMySQL> set session transaction isolation level read committed; // 设置当前会话事务隔离级别为 read committed
InnoDB 如何开启手动提交事务?
InnoDB 默认是自动提交事务的,每一次 SQL 操作(非 select 操作)都会自动提交一个事务,如果要手动开启事务需要设置set autocommit=0禁止自动提交事务,相当于开启手动提交事务。
在 InnoDB 中设置了 autocommit=0,添加一条信息之后没有手动执行提交操作,请问这条信息可以被查到吗?
autocommit=0 表示禁止自动事务提交,在添加操作之后没有进行手动提交,默认情况下其他连接客户端是查询不到此条新增数据的。
参考文章:
一文彻底读懂MySQL事务的四大隔离级别-腾讯云开发者社区-腾讯云 (tencent.com)
spring 事务失效的 12 种场景 原文链接:https://blog.csdn.net/hanjiaqian/article/details/120501741