事务

# ACID

# 原子性 Atomicity

一个事务必须被视为一个不可分割的最小单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚。

# 一致性 Consistency

数据库总是从一个一致性状态转换到另一个一致性的状态。

# 隔离性 Isolation

通常来说,一个事务所做的修改在提交之前,对其他事务是不可见的。

# 持久性 Durability

一旦事务提交,则其所做的修改会永久保存在数据库中。此时即使系统崩溃,修改的数据也不会丢失。

持久性是一个有点模糊的概念,因为实际上持久性也分为很多不同的级别。有些持久性策略可以提供非常强的安全保障,而有些则未必。而且不可能有能做到100%的持久性保证的策略。(如果数据库本身能做到真正的持久性,那么备份又怎么能增加持久性呢?)

# 事务隔离机制

  • READ UNCOMMITTED

    可以读取到未提交数据,事务中的修改,即使未提交,对其他事务也是可见的,事务可以读取未提交的数据,也即脏读。

  • READ COMMITTED

    只要提交了就会被读到。大多数系统的默认隔离级别(但不包括MySQL)。这个隔离级别也叫做不可重复读,因为在事务执行过程中,可以读取到其他事务提交了的数据,所以可能两次读取的数据会不一样。这种做法解决了脏读的问题,但会产生幻读(通过间隙锁解决)。

  • REPEATABLE READ

    可重复读,即开启事务后,即使数据被其他事务修改,还是读到之前的数据。通过数据版本号实现,即对数据的修改都是变更版本号,而当前事务只会读取小于事务开启时的版本号的数据。

  • SERIALIZABLE

    线性事务,即事务排队执行。

# 查看隔离级别

show variables like '%tx_isolation%';select @@tx_isolation;

8之后版本

show variables like '%transaction_isolation%';select @@transaction_isolation;

# 修改隔离级别

SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}

  • SESSION 只修改当前会话,新创建会话还是原隔离级别
  • GLOBAL 全局修改,当前会话不会更改,新创建会话生效

例:set global transaction isolation level READ COMMITTED;

# 脏读与幻读

mysql通过mvcc(多版本并发控制)解决了脏读与幻读问题。

  1. 脏读

READ UNCOMMITTED级别,读到其他事物未提交的数据,READ COMMITED级别解决了这个问题。

通过增加两列版本号(初始事务版本、当前事务版本),更改是在追加行上进行。并通过版本号实现可重复读(快照读),解决了脏读问题。

  1. 幻读

幻读,并不是说两次读取获取的结果集不同,幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读。

通过MVCC机制虽然让数据变得可重复读,但读到的是快照数据,可能不是当前数据。

MySQL在REPEATABLEREAD级别,通过使用临键锁的方式来解决幻读。使用select ... for update当数据不存在时锁住索引之间的间隙,来防止其他事务的插入。

# 死锁

死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环的线下。当多个事务试图以不同顺序锁定资源时,就有可能产生死锁。多个是事务同时锁定同一个资源时,也会产生死锁。

InnoDB 处理死锁的方法是,将持有最少行级排它锁的事务进行回滚(这是相对比较简单的死锁回滚算法)。

# 事务日志

事务日志可以帮助提高事务的效率。使用事务日志,存储引擎在修改表数据时只需要修改其内存的拷贝,再把该修改行为记录到持久在硬盘上的事务日志中,而不用每次都将修改的数据本身持久到磁盘。

事务日志采用的是追加的方式,因此写日志的操作是磁盘上一小块区域的顺序I/O,而不像随机I/O需要在磁盘的多个地方移动磁头,所以采用事务日志的方式相对来说要快很多。事务日志持久后,内存中被修改的数据在后台可以慢慢刷回到磁盘。

目前大多数存储引擎是这样实现的,我们通常称之为预写试日志(Write-Ahead Logging),修改数据需要写两次磁盘。

# PlatformTransactionManager的常见实现

  • DataSourceTransactionManager
  • JpaTransactionManager
  • JMSTransactionManager
  • JtaTransactionManager

# mysql事务涉及的三张表

information_shcema下的三张表(通过这三张表可以更新监控当前事物并且分析存在的锁问题) —— innodb_trx ( 打印innodb内核中的当前活跃(ACTIVE)事务) —— innodb_locks ( 打印当前状态产生的innodb锁 仅在有锁等待时打印) —— innodb_lock_waits (打印当前状态产生的innodb锁等待 仅在有锁等待时打印)

  1. innodb_trx表结构说明 (摘取最能说明问题的8个字段) 字段名 说明 trx_id innodb存储引擎内部唯一的事物ID trx_state 当前事物状态(running和lock wait两种状态) trx_started 事物的开始时间 trx_requested_lock_id 等待事物的锁ID,如trx_state的状态为Lock wait,那么该值带表当前事物等待之前事物占用资源的ID,若trx_state不是Lock wait 则该值为NULL trx_wait_started 事物等待的开始时间 trx_weight 事物的权重,在innodb存储引擎中,当发生死锁需要回滚的时,innodb存储引擎会选择该值最小的进行回滚 trx_mysql_thread_id mysql中的线程id, 即show processlist显示的结果 trx_query 事物运行的SQL语句
  2. innodb_locks表结构说明

字段名 说明 lock_id 锁的ID lock_trx_id 事物的ID lock_mode 锁的模式(S锁与X锁两种模式) lock_type 锁的类型 表锁还是行锁(RECORD) lock_table 要加锁的表 lock_index 锁住的索引 lock_space 锁住对象的space id lock_page 事物锁定页的数量,若是表锁则该值为NULL lock_rec 事物锁定行的数量,若是表锁则该值为NULL lock_data 事物锁定记录主键值,若是表锁则该值为NULL(此选项不可信) 3. innodb_lock_waits表结构说明 字段名 说明 requesting_trx_id申请锁资源的事物ID requested_lock_id申请的锁的ID blocking_trx_id阻塞其他事物的事物ID blocking_lock_id阻塞其他锁的锁ID

上次更新: 2023/10/31, 13:49:43
最近更新
01
go-admin-ui项目仿写练手1-登录页
06-29
02
maven依赖问题
06-17
03
JVM相关命令
02-21
更多文章>