# 事务
## 事务的四大特性有哪些?
ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性)
1. 原子性
事务是一个不可分割的工作单元,要么完全执行,要么完全不执行。如果在事务执行的过程中发生了错误,系统会撤销事务中已经执行的操作,将数据库恢复到事务开始前的状态。原子性是通过 undo log(回滚日志) 来保证的。
1. 一致性
确保事务将数据库从一个一致的状态转变为另一个一致的状态。事务执行的结果必须满足数据库的完整性约束和规则,保持数据库的一致性。一致性则是通过持久性+原子性+隔离性来保证的。
1. 隔离性
多个事务并发执行时,每个事务都不能看到其他事务的中间状态。每个事务都应该感觉就像它是唯一在数据库上运行的事务一样。防止了多个事务之间的相互干扰。隔离性是通过 MVCC(多版本并发控制) 或锁机制来保证的。
1. 持久性
一旦事务被提交,其结果将永久保存在数据库中,即使系统发生故障。即使系统发生崩溃,事务的结果也不应该丢失,持久性是通过 redo log (重做日志)来保证的。
## 数据库的事务隔离级别有哪些?
隔离级别是指多个并发事务之间相互隔离的程度,SQL标准定义了4个隔离级别
1. 读未提交:
最低的隔离级别。在这个级别下,一个事务可以读取到另一个事务未提交的数据。这可能导致脏读(Dirty Reads)和不可重复读、幻读等问题。
1. 读提交:
在这个级别下,一个事务只能读取到已经提交的其他事务的数据。这解决了脏读的问题,但仍可能遇到不可重复读的问题。
1. 可重复读
在这个级别下,一个事务在其生命周期内多次执行相同的查询,将始终看到相同的数据,但是,仍可能发生幻读。也是**MySQL InnoDB 引擎的默认隔离级别**;
1. 可串行化
提供了最高的隔离级别。会对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行,在这个级别下,事务的执行效果就好像它们是按顺序执行的,事务之间没有并发。这可以防止脏读、不可重复读和幻读,但也可能导致性能下降,因为并发性降低。
选择隔离级别需要根据应用程序的要求和性能需求进行权衡。较低的隔离级别提供更高的并发性能,但可能牺牲一致性。较高的隔离级别提供更强的一致性,但可能降低并发性能。
- 在「读未提交」隔离级别下,可能发生脏读、不可重复读和幻读现象;
- 在「读提交」隔离级别下,可能发生不可重复读和幻读现象,但是不可能发生脏读现象;
- 在「可重复读」隔离级别下,可能发生幻读现象,但是不可能脏读和不可重复读现象;
- 在「串行化」隔离级别下,脏读、不可重复读和幻读现象都不可能会发生。
## 并行事务会出现什么问题
并行事务是指多个事务同时执行,这可以提高数据库系统的性能和吞吐量。但是并行事务也可能引发一些问题
1. **脏读**:读到其他事务未提交的数据
一个事务读取了另一个事务未提交的数据,如果另一个事务后来回滚,读取的数据就是无效的。读到了并一定最终存在的数据,这就是脏读。
1. **不可重复读**:前后读取的数据不一致
在一个事务内,同一查询可能返回不同的结果,因为在事务执行期间其他事务可能修改了数据。
1. **幻读**:前后读取的记录数量不一致
在一个事务内多次查询某个符合查询条件的「记录数量」,如果出现前后两次查询到的记录数量不一样的情况,就意味着发生了「幻读」现象。
## MySQL中的锁等待和死锁是什么?如何处理?
- **锁等待**:当一个线程请求锁定某个资源时,如果资源已被其他线程锁定,该线程会进入等待状态,直到资源释放。锁等待是正常的并发访问的一部分。
- **死锁**:死锁是指多个线程互相持有对方需要的锁,导致它们无法继续执行。死锁会造成资源的无限等待和程序阻塞。
- 处理方法
:-
死锁检测
:MySQL会自动检测死锁并回滚其中一个事务来打破死锁。
- **死锁预防**:通过合理设计事务的执行顺序、减少锁的粒度或使用较低的事务隔离级别来预防死锁。
- **锁等待超时**:设置锁等待超时,防止长时间等待锁的情况。
## 解释一下数据库的隔离级别与锁的关系?
#### 隔离级别
数据库提供的隔离级别有读未提交、读已提交、可重复读、串行化,四个级别,用来解决脏读、可重复读和幻读的问题。 脏读:事务在执行过程中,还未提交,但被另一个事务读到了数据 不可重复读:事务在执行过程中,先后两次读取同一条记录获得的数据不一致, 幻读:针对的是记录的集合,先后获取同一集合的数据,结果不一致
#### 锁的种类
全局锁、表锁和行锁
#### 关系
读已提交:只允许读已提交的数据,对记录加行锁,即记录锁,来解决脏读,但可能发生可重复读的问题 可重复读:保证在同一个事务中多次读取同一数据集合时,保持不变。通过MVCC保证了可重复读(能够感知到数据的修改),间隙锁保证了不会发生幻读(能够防止整行数据的掺入删除)。 串行化:事务串行执行,避免了所有并发问题,同时性能也是最差的
## 解释一下数据库的并发控制和事务管理?
- **并发控制**:确保多个事务并发执行时,能保持数据库的正确性和一致性。常用技术有锁机制和隔离级别。
- **事务管理**:确保一组数据库操作作为一个整体执行,要么全部成功,要么全部失败。事务的ACID特性(原子性、一致性、隔离性、持久性)保证了这一点。
## 幻读是如何解决的
**MySQL InnoDB 引擎的默认隔离级别虽然是「可重复读」,但是它很大程度上可以避免幻读现象。**解决的方案有两种:
- 针对**快照读**(普通 select 语句),是**通过 MVCC 方式解决了幻读**
- 针对当前读:(select ... for update 等语句),是**通过 next-key lock(记录锁+间隙锁)方式解决了幻读**,因为当执行 select ... for update 语句的时候,会加上 next-key lock,如果有其他事务在 next-key lock 锁范围内插入。
## 事务隔离的实现
隔离级别具体是如何实现的呢?
- 对于读未提交,可以读到未提交事务修改的数据,所以直接读取最新的数据就可以。
- 对于串行化:加读写锁的方式来避免并行访问
- 对于读提交和可重复读,通过
```
Read View
```
来实现的
- 「读提交」隔离级别是在每个 select 都会生成一个新的 Read View,也意味着,事务期间的多次读取同一条数据,前后两次读的数据可能会出现不一致,因为可能这期间另外一个事务修改了该记录,并提交了事务。
- 「可重复读」隔离级别是启动事务时生成一个 Read View,然后整个事务期间都在用这个 Read View,这样就保证了在事务期间读到的数据都是事务启动前的记录。
## 读已提交是怎么实现的
使用`MVCC(多版本并法控制)`实现的
- 读操作:在MVCC中,每个事务在读取数据时都可以看到数据的一个版本。已提交的事务产生一个版本,而未提交的事务则不会对其他事务可见。因此,读已提交只能看到已提交事务的版本。
- 写操作: 写操作创建一个新的数据版本,而不是直接修改原始数据。这确保了正在进行的事务不会看到未提交事务的更改。
## 事务的启动方式
显式启动事务语句
```
begin/start transaction、commit、rollback
```
关闭线程的自提交
```
set autocommit=0;
```
事务会持续存在直到你主动执行commit或者rollback,或者断开连接,所以,如果采取了第二种方法,就导致了接下来的查询都在事务中,如果是长连接,就导致了长事务。
所以,建议使用 `set autocommit=1`
**长事务的查询:**
在informationschema库下的innodbtrx表中查询。
**举个栗子**
如何避免长事务对业务的影响?
**从应用开发端来看:**
1. 确定是否使用了set autocommit=0,如是,则改成1
2. 确定是否有不必要的只读事务
3. 业务连接数据库的时候,控制每个语句执行的最长时间 set maxexecutiontime
**从数据库端来看:**
1. 监控相关表,设置长事务阈值,超过就报警/kill
2. 可使用percona的pt-kill工具
3. 在功能测试阶段输出所有的log,分析日志提前发现问题
4. 把innodbundotablespaces设置成2或者更大的值